aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorNicolas LÅ“uillet <nicolas.loeuillet@gmail.com>2013-08-25 12:12:53 -0700
committerNicolas LÅ“uillet <nicolas.loeuillet@gmail.com>2013-08-25 12:12:53 -0700
commitc51be6b697da573cdcf0788eb8617130ce5517a4 (patch)
tree642eaf70afb134dee5f274c84bf15b8aab00c117
parent7ba37bd91a43321196e6d867caf9e298e82c6d6c (diff)
parent063fc1a7baaf6f7e1fb08eced058962a6140a471 (diff)
downloadwallabag-c51be6b697da573cdcf0788eb8617130ce5517a4.tar.gz
wallabag-c51be6b697da573cdcf0788eb8617130ce5517a4.tar.zst
wallabag-c51be6b697da573cdcf0788eb8617130ce5517a4.zip
Merge pull request #181 from inthepoche/dev
beta4
-rw-r--r--.gitignore4
-rw-r--r--.travis.yml15
-rw-r--r--CONTRIBUTING.md10
-rw-r--r--COPYING.md (renamed from COPYING)0
-rw-r--r--CREDITS.md (renamed from CREDITS)1
-rw-r--r--INSTALL.md12
-rw-r--r--README.md8
-rw-r--r--TODO.md16
-rwxr-xr-x[-rw-r--r--]assets/.gitignore (renamed from phpunit.xml.dist)0
-rw-r--r--cache/.gitignore1
-rw-r--r--db/.gitignore0
-rw-r--r--inc/3rdparty/content-extractor/ContentExtractor.php612
-rw-r--r--inc/3rdparty/content-extractor/SiteConfig.php184
-rw-r--r--inc/3rdparty/feedwriter/DummySingleItemFeed.php24
-rw-r--r--inc/3rdparty/feedwriter/FeedItem.php167
-rw-r--r--inc/3rdparty/feedwriter/FeedWriter.php434
-rw-r--r--inc/3rdparty/humble-http-agent/CookieJar.php404
-rw-r--r--inc/3rdparty/humble-http-agent/HumbleHttpAgent.php720
-rw-r--r--inc/3rdparty/humble-http-agent/RollingCurl.php402
-rw-r--r--inc/3rdparty/humble-http-agent/SimplePie_HumbleHttpAgent.php79
-rw-r--r--inc/3rdparty/simplepie/LICENSE.txt26
-rw-r--r--inc/3rdparty/simplepie/SimplePie.php56
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Author.php103
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Cache.php109
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Cache/Base.php102
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Cache/DB.php124
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Cache/File.php112
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Cache/Memcache.php118
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Cache/MySQL.php378
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Caption.php131
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Category.php103
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Content/Type/Sniffer.php325
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Copyright.php89
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Core.php2838
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Credit.php103
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Decode/HTML/Entities.php250
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Enclosure.php990
-rw-r--r--inc/3rdparty/simplepie/SimplePie/File.php278
-rw-r--r--inc/3rdparty/simplepie/SimplePie/HTTP/Parser.php492
-rw-r--r--inc/3rdparty/simplepie/SimplePie/IRI.php997
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Item.php2576
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Locator.php314
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Misc.php2365
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Net/IPv6.php258
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Parse/Date.php983
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Parser.php387
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Rating.php88
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Restriction.php102
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Sanitize.php400
-rw-r--r--inc/3rdparty/simplepie/SimplePie/Source.php597
-rw-r--r--inc/3rdparty/simplepie/SimplePie/XML/Declaration/Parser.php362
-rw-r--r--inc/3rdparty/simplepie/SimplePie/gzdecode.php355
-rw-r--r--inc/3rdparty/simplepie/SimplePieAutoloader.php80
-rw-r--r--inc/3rdparty/site_config/README.txt6
-rw-r--r--inc/3rdparty/site_config/custom/inthepoche.com.txt7
-rw-r--r--inc/3rdparty/site_config/index.php3
-rw-r--r--inc/3rdparty/site_config/standard/.wikipedia.org.txt19
-rw-r--r--inc/3rdparty/site_config/standard/index.php3
-rw-r--r--inc/3rdparty/site_config/standard/version.php2
-rw-r--r--inc/poche/Poche.class.php91
-rw-r--r--inc/poche/Tools.class.php37
-rw-r--r--inc/poche/Url.class.php401
-rwxr-xr-xinc/poche/config.inc.php21
-rw-r--r--inc/poche/define.inc.php10
-rw-r--r--index.php3
-rwxr-xr-xinstall/poche.sqlitebin360448 -> 360448 bytes
-rw-r--r--install/update.php62
-rw-r--r--install/update_to_1beta3.php79
-rw-r--r--locale/de_DE.utf8/LC_MESSAGES/de_DE.utf8.mobin2871 -> 5799 bytes
-rw-r--r--locale/de_DE.utf8/LC_MESSAGES/de_DE.utf8.po400
-rw-r--r--locale/fr_FR.utf8/LC_MESSAGES/fr_FR.utf8.mobin5699 -> 5706 bytes
-rw-r--r--locale/fr_FR.utf8/LC_MESSAGES/fr_FR.utf8.po8
-rw-r--r--poche_compatibility_test.php353
-rw-r--r--tpl/_head.twig3
-rw-r--r--tpl/config.twig25
-rw-r--r--tpl/css/print.css19
-rw-r--r--tpl/css/style-light.css4
-rw-r--r--tpl/css/style.css160
-rw-r--r--tpl/error.twig5
-rw-r--r--tpl/home.twig16
-rw-r--r--tpl/img/light/shaarli.pngbin0 -> 729 bytes
-rw-r--r--tpl/layout.twig10
-rw-r--r--tpl/login.twig2
-rw-r--r--tpl/view.twig38
84 files changed, 21095 insertions, 376 deletions
diff --git a/.gitignore b/.gitignore
index 17af57cf..25818a8d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,8 @@
1assets/*
2cache/*
1vendor 3vendor
2composer.phar 4composer.phar
3db/poche.sqlite 5db/poche.sqlite
4output 6output
5phpdoc* 7phpdoc*
6inc/config/myconfig.inc.php \ No newline at end of file 8inc/poche/myconfig.inc.php \ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 9d6ba132..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,15 +0,0 @@
1language: php
2
3php:
4 - 5.4
5
6branches:
7 only:
8 - dev
9
10before_script:
11 - composer install
12
13notifications:
14 email:
15 - nicolas.loeuillet@gmail.com \ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9818ae00..85132e65 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,3 +1,11 @@
1# How contributing 1# How contributing
2 2
3When you create an issue on github, don't forget to give us your poche version. You can find it in config screen or in ./inc/poche/config.inc.php. \ No newline at end of file 3## You found a bug
4Please [open a new issue](https://github.com/inthepoche/poche/issues/new).
5
6To fix the bug quickly, we need some infos:
7* your poche version (in ./inc/poche/myconfig.inc.php)
8* the link you want to poche and which causes problem
9
10## You want to fix a bug or to add a feature
11Please fork poche and work with the dev branch. Do not work on master branch. \ No newline at end of file
diff --git a/COPYING b/COPYING.md
index ee7d6a54..ee7d6a54 100644
--- a/COPYING
+++ b/COPYING.md
diff --git a/CREDITS b/CREDITS.md
index a6dedce4..6046a6aa 100644
--- a/CREDITS
+++ b/CREDITS.md
@@ -1,5 +1,6 @@
1poche is based on : 1poche is based on :
2* PHP Readability https://bitbucket.org/fivefilters/php-readability 2* PHP Readability https://bitbucket.org/fivefilters/php-readability
3* Full Text RSS http://code.fivefilters.org/full-text-rss/src
3* Encoding https://github.com/neitanod/forceutf8 4* Encoding https://github.com/neitanod/forceutf8
4* logo by Brightmix http://www.iconfinder.com/icondetails/43256/128/jeans_monotone_pocket_icon 5* logo by Brightmix http://www.iconfinder.com/icondetails/43256/128/jeans_monotone_pocket_icon
5* icons http://icomoon.io 6* icons http://icomoon.io
diff --git a/INSTALL.md b/INSTALL.md
index 63000f26..dfe013b7 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -1,8 +1,16 @@
1# Installing poche 1# Installing poche
2 2
3## requirements 3## requirements
4 4* PHP 5.2.0 or higher
5it's highly recommended to have php cURL and tidy_parse_string to fetch articles content. 5* XML ([?](http://php.net/xml))
6* PCRE ([?](http://php.net/pcre))
7* Data filtering ([?](http://uk.php.net/manual/en/book.filter.php))
8* Tidy ([?](http://php.net/tidy))
9* cURL ([?](http://php.net/curl))
10* Parallel URL fetching
11* allow_url_fopen ([?](http://www.php.net/manual/en/filesystem.configuration.php#ini.allow-url-fopen))
12
13To see if your server is ok to run poche, execute http://yourpoche/poche_compatibility_test.php.
6 14
7## you don't want to install twig (the template engine) by yourself 15## you don't want to install twig (the template engine) by yourself
8 16
diff --git a/README.md b/README.md
index 27f549d9..ef1ecc4d 100644
--- a/README.md
+++ b/README.md
@@ -2,24 +2,24 @@
2Abandon Pocket, Instapaper and other Readability service : adopt poche. It is the same, but it is free (like in freedom) and open source. 2Abandon Pocket, Instapaper and other Readability service : adopt poche. It is the same, but it is free (like in freedom) and open source.
3 3
4## Some features 4## Some features
5
6* adding, deleting, archiving and setting as favorite a link 5* adding, deleting, archiving and setting as favorite a link
7* import from pocket / readability / instapaper 6* import from pocket / readability / instapaper
8* share links by email and on twitter 7* share links by email and on twitter
9* a design adapted to tablets and smartphones 8* a design adapted to tablets and smartphones
10* extensions for Chrome and Firefox 9* extensions for Chrome and Firefox
11* Android application 10* Android application
12* multi languages (very soon!) 11* multi languages: french, english, spanish, german.
13* multi users (very soon!) 12* multi users (very soon!)
14* update notification in configuration screen 13* update notification in configuration screen
15* many storage modes (sqlite, mysql, postgresql) 14* many storage modes (sqlite, mysql, postgresql)
16* many templates 15* many templates: [have a look here](https://github.com/inthepoche/poche-themes).
17* ... 16* ...
18 17
19To test poche, a demo website is online : [demo.inthepoche.com](http://demo.inthepoche.com) (login poche, password poche). 18To test poche, a demo website is online : [demo.inthepoche.com](http://demo.inthepoche.com) (login poche, password poche).
20 19
21## Installation 20To use poche hosting, [you can create an account here](http://app.inthepoche.com/).
22 21
22## Installation
23Read the [INSTALL.md file](https://github.com/inthepoche/poche/blob/master/INSTALL.md). 23Read the [INSTALL.md file](https://github.com/inthepoche/poche/blob/master/INSTALL.md).
24 24
25## License 25## License
diff --git a/TODO.md b/TODO.md
index ac3c0e98..fdba2a51 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,11 +1,9 @@
1# TODO 1# TODO
2 2
3pouvoir annuler la suppression 3* pouvoir annuler la suppression
4conventions codage ? phing ? vérifier error_log qui trainent 4* conventions codage ? phing ? vérifier error_log qui trainent
5phpDocumentor 5* phpDocumentor
6minifier css 6* minifier css
7revoir tous les css 7* barre fixe d'admin sur la page d'un billet ?
8barre fixe d'admin sur la page d'un billet ? 8* revoir export (export vers pocket &cie ? )
9revoir export (export vers pocket &cie ? ) 9* raccourcis clavier \ No newline at end of file
10raccourcis clavier
11date d'ajout d'un lien \ No newline at end of file
diff --git a/phpunit.xml.dist b/assets/.gitignore
index e69de29b..e69de29b 100644..100755
--- a/phpunit.xml.dist
+++ b/assets/.gitignore
diff --git a/cache/.gitignore b/cache/.gitignore
index f59ec20a..e69de29b 100644
--- a/cache/.gitignore
+++ b/cache/.gitignore
@@ -1 +0,0 @@
1* \ No newline at end of file
diff --git a/db/.gitignore b/db/.gitignore
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/db/.gitignore
diff --git a/inc/3rdparty/content-extractor/ContentExtractor.php b/inc/3rdparty/content-extractor/ContentExtractor.php
new file mode 100644
index 00000000..db371c6a
--- /dev/null
+++ b/inc/3rdparty/content-extractor/ContentExtractor.php
@@ -0,0 +1,612 @@
1<?php
2/**
3 * Content Extractor
4 *
5 * Uses patterns specified in site config files and auto detection (hNews/PHP Readability)
6 * to extract content from HTML files.
7 *
8 * @version 0.8
9 * @date 2012-02-21
10 * @author Keyvan Minoukadeh
11 * @copyright 2011 Keyvan Minoukadeh
12 * @license http://www.gnu.org/licenses/agpl-3.0.html AGPL v3
13 */
14
15class ContentExtractor
16{
17 protected static $tidy_config = array(
18 'clean' => true,
19 'output-xhtml' => true,
20 'logical-emphasis' => true,
21 'show-body-only' => false,
22 'new-blocklevel-tags' => 'article, aside, footer, header, hgroup, menu, nav, section, details, datagrid',
23 'new-inline-tags' => 'mark, time, meter, progress, data',
24 'wrap' => 0,
25 'drop-empty-paras' => true,
26 'drop-proprietary-attributes' => false,
27 'enclose-text' => true,
28 'enclose-block-text' => true,
29 'merge-divs' => true,
30 'merge-spans' => true,
31 'char-encoding' => 'utf8',
32 'hide-comments' => true
33 );
34 protected $html;
35 protected $config;
36 protected $title;
37 protected $author = array();
38 protected $language;
39 protected $date;
40 protected $body;
41 protected $success = false;
42 public $fingerprints = array();
43 public $readability;
44 public $debug = false;
45
46 function __construct($path, $fallback=null) {
47 SiteConfig::set_config_path($path, $fallback);
48 }
49
50 protected function debug($msg) {
51 if ($this->debug) {
52 $mem = round(memory_get_usage()/1024, 2);
53 $memPeak = round(memory_get_peak_usage()/1024, 2);
54 echo '* ',$msg;
55 echo ' - mem used: ',$mem," (peak: $memPeak)\n";
56 ob_flush();
57 flush();
58 }
59 }
60
61 public function reset() {
62 $this->html = null;
63 $this->readability = null;
64 $this->config = null;
65 $this->title = null;
66 $this->body = null;
67 $this->author = array();
68 $this->language = null;
69 $this->date = null;
70 $this->success = false;
71 }
72
73 public function findHostUsingFingerprints($html) {
74 $this->debug('Checking fingerprints...');
75 $head = substr($html, 0, 8000);
76 foreach ($this->fingerprints as $_fp => $_fphost) {
77 $lookin = 'html';
78 if (is_array($_fphost)) {
79 if (isset($_fphost['head']) && $_fphost['head']) {
80 $lookin = 'head';
81 }
82 $_fphost = $_fphost['hostname'];
83 }
84 if (strpos($$lookin, $_fp) !== false) {
85 $this->debug("Found match: $_fphost");
86 return $_fphost;
87 }
88 }
89 return false;
90 }
91
92 // returns true on success, false on failure
93 // $smart_tidy indicates that if tidy is used and no results are produced, we will
94 // try again without it. Tidy helps us deal with PHP's patchy HTML parsing most of the time
95 // but it has problems of its own which we try to avoid with this option.
96 public function process($html, $url, $smart_tidy=true) {
97 $this->reset();
98 // extract host name
99 $host = @parse_url($url, PHP_URL_HOST);
100 if (!($this->config = SiteConfig::build($host))) {
101 // no match, check HTML for fingerprints
102 if (!empty($this->fingerprints) && ($_fphost = $this->findHostUsingFingerprints($html))) {
103 $this->config = SiteConfig::build($_fphost);
104 }
105 unset($_fphost);
106 if (!$this->config) {
107 // no match, so use defaults
108 $this->config = new SiteConfig();
109 }
110 }
111 // store copy of config in our static cache array in case we need to process another URL
112 SiteConfig::add_to_cache($host, $this->config);
113
114 // do string replacements
115 foreach ($this->config->replace_string as $_repl) {
116 $html = str_replace($_repl[0], $_repl[1], $html);
117 }
118 unset($_repl);
119
120 // use tidy (if it exists)?
121 // This fixes problems with some sites which would otherwise
122 // trouble DOMDocument's HTML parsing. (Although sometimes it
123 // makes matters worse, which is why you can override it in site config files.)
124 $tidied = false;
125 if ($this->config->tidy && function_exists('tidy_parse_string') && $smart_tidy) {
126 $this->debug('Using Tidy');
127 $tidy = tidy_parse_string($html, self::$tidy_config, 'UTF8');
128 if (tidy_clean_repair($tidy)) {
129 $original_html = $html;
130 $tidied = true;
131 // $html = $tidy->value;
132 }
133 $body = $tidy->body();
134 if (preg_replace('/\s+/', '', $body->value) !== "<body></body>") {
135 $html = $tidy->value;
136 }
137 unset($tidy);
138 }
139
140 // load and parse html
141 $this->readability = new Readability($html, $url);
142
143 // we use xpath to find elements in the given HTML document
144 // see http://en.wikipedia.org/wiki/XPath_1.0
145 $xpath = new DOMXPath($this->readability->dom);
146
147 // try to get title
148 foreach ($this->config->title as $pattern) {
149 $elems = @$xpath->evaluate($pattern, $this->readability->dom);
150 if (is_string($elems)) {
151 $this->debug('Title expression evaluated as string');
152 $this->title = trim($elems);
153 break;
154 } elseif ($elems instanceof DOMNodeList && $elems->length > 0) {
155 $this->debug('Title matched');
156 $this->title = $elems->item(0)->textContent;
157 // remove title from document
158 try {
159 $elems->item(0)->parentNode->removeChild($elems->item(0));
160 } catch (DOMException $e) {
161 // do nothing
162 }
163 break;
164 }
165 }
166
167 // try to get author (if it hasn't already been set)
168 if (empty($this->author)) {
169 foreach ($this->config->author as $pattern) {
170 $elems = @$xpath->evaluate($pattern, $this->readability->dom);
171 if (is_string($elems)) {
172 $this->debug('Author expression evaluated as string');
173 if (trim($elems) != '') {
174 $this->author[] = trim($elems);
175 break;
176 }
177 } elseif ($elems instanceof DOMNodeList && $elems->length > 0) {
178 foreach ($elems as $elem) {
179 if (!isset($elem->parentNode)) continue;
180 $this->author[] = trim($elem->textContent);
181 }
182 if (!empty($this->author)) break;
183 }
184 }
185 }
186
187 // try to get language
188 $_lang_xpath = array('//html[@lang]/@lang', '//meta[@name="DC.language"]/@content');
189 foreach ($_lang_xpath as $pattern) {
190 $elems = @$xpath->evaluate($pattern, $this->readability->dom);
191 if (is_string($elems)) {
192 if (trim($elems) != '') {
193 $this->language = trim($elems);
194 break;
195 }
196 } elseif ($elems instanceof DOMNodeList && $elems->length > 0) {
197 foreach ($elems as $elem) {
198 if (!isset($elem->parentNode)) continue;
199 $this->language = trim($elem->textContent);
200 }
201 if ($this->language) break;
202 }
203 }
204
205 // try to get date
206 foreach ($this->config->date as $pattern) {
207 $elems = @$xpath->evaluate($pattern, $this->readability->dom);
208 if (is_string($elems)) {
209 $this->debug('Date expression evaluated as string');
210 $this->date = strtotime(trim($elems, "; \t\n\r\0\x0B"));
211 } elseif ($elems instanceof DOMNodeList && $elems->length > 0) {
212 $this->debug('Date matched');
213 $this->date = $elems->item(0)->textContent;
214 $this->date = strtotime(trim($this->date, "; \t\n\r\0\x0B"));
215 // remove date from document
216 // $elems->item(0)->parentNode->removeChild($elems->item(0));
217 }
218 if (!$this->date) {
219 $this->date = null;
220 } else {
221 break;
222 }
223 }
224
225 // strip elements (using xpath expressions)
226 foreach ($this->config->strip as $pattern) {
227 $elems = @$xpath->query($pattern, $this->readability->dom);
228 // check for matches
229 if ($elems && $elems->length > 0) {
230 $this->debug('Stripping '.$elems->length.' elements (strip)');
231 for ($i=$elems->length-1; $i >= 0; $i--) {
232 $elems->item($i)->parentNode->removeChild($elems->item($i));
233 }
234 }
235 }
236
237 // strip elements (using id and class attribute values)
238 foreach ($this->config->strip_id_or_class as $string) {
239 $string = strtr($string, array("'"=>'', '"'=>''));
240 $elems = @$xpath->query("//*[contains(@class, '$string') or contains(@id, '$string')]", $this->readability->dom);
241 // check for matches
242 if ($elems && $elems->length > 0) {
243 $this->debug('Stripping '.$elems->length.' elements (strip_id_or_class)');
244 for ($i=$elems->length-1; $i >= 0; $i--) {
245 $elems->item($i)->parentNode->removeChild($elems->item($i));
246 }
247 }
248 }
249
250 // strip images (using src attribute values)
251 foreach ($this->config->strip_image_src as $string) {
252 $string = strtr($string, array("'"=>'', '"'=>''));
253 $elems = @$xpath->query("//img[contains(@src, '$string')]", $this->readability->dom);
254 // check for matches
255 if ($elems && $elems->length > 0) {
256 $this->debug('Stripping '.$elems->length.' image elements');
257 for ($i=$elems->length-1; $i >= 0; $i--) {
258 $elems->item($i)->parentNode->removeChild($elems->item($i));
259 }
260 }
261 }
262 // strip elements using Readability.com and Instapaper.com ignore class names
263 // .entry-unrelated and .instapaper_ignore
264 // See https://www.readability.com/publishers/guidelines/#view-plainGuidelines
265 // and http://blog.instapaper.com/post/730281947
266 $elems = @$xpath->query("//*[contains(concat(' ',normalize-space(@class),' '),' entry-unrelated ') or contains(concat(' ',normalize-space(@class),' '),' instapaper_ignore ')]", $this->readability->dom);
267 // check for matches
268 if ($elems && $elems->length > 0) {
269 $this->debug('Stripping '.$elems->length.' .entry-unrelated,.instapaper_ignore elements');
270 for ($i=$elems->length-1; $i >= 0; $i--) {
271 $elems->item($i)->parentNode->removeChild($elems->item($i));
272 }
273 }
274
275 // strip elements that contain style="display: none;"
276 $elems = @$xpath->query("//*[contains(@style,'display:none')]", $this->readability->dom);
277 // check for matches
278 if ($elems && $elems->length > 0) {
279 $this->debug('Stripping '.$elems->length.' elements with inline display:none style');
280 for ($i=$elems->length-1; $i >= 0; $i--) {
281 $elems->item($i)->parentNode->removeChild($elems->item($i));
282 }
283 }
284
285 // try to get body
286 foreach ($this->config->body as $pattern) {
287 $elems = @$xpath->query($pattern, $this->readability->dom);
288 // check for matches
289 if ($elems && $elems->length > 0) {
290 $this->debug('Body matched');
291 if ($elems->length == 1) {
292 $this->body = $elems->item(0);
293 // prune (clean up elements that may not be content)
294 if ($this->config->prune) {
295 $this->debug('Pruning content');
296 $this->readability->prepArticle($this->body);
297 }
298 break;
299 } else {
300 $this->body = $this->readability->dom->createElement('div');
301 $this->debug($elems->length.' body elems found');
302 foreach ($elems as $elem) {
303 if (!isset($elem->parentNode)) continue;
304 $isDescendant = false;
305 foreach ($this->body->childNodes as $parent) {
306 if ($this->isDescendant($parent, $elem)) {
307 $isDescendant = true;
308 break;
309 }
310 }
311 if ($isDescendant) {
312 $this->debug('Element is child of another body element, skipping.');
313 } else {
314 // prune (clean up elements that may not be content)
315 if ($this->config->prune) {
316 $this->debug('Pruning content');
317 $this->readability->prepArticle($elem);
318 }
319 $this->debug('Element added to body');
320 $this->body->appendChild($elem);
321 }
322 }
323 }
324 }
325 }
326
327 // auto detect?
328 $detect_title = $detect_body = $detect_author = $detect_date = false;
329 // detect title?
330 if (!isset($this->title)) {
331 if (empty($this->config->title) || $this->config->autodetect_on_failure) {
332 $detect_title = true;
333 }
334 }
335 // detect body?
336 if (!isset($this->body)) {
337 if (empty($this->config->body) || $this->config->autodetect_on_failure) {
338 $detect_body = true;
339 }
340 }
341 // detect author?
342 if (empty($this->author)) {
343 if (empty($this->config->author) || $this->config->autodetect_on_failure) {
344 $detect_author = true;
345 }
346 }
347 // detect date?
348 if (!isset($this->date)) {
349 if (empty($this->config->date) || $this->config->autodetect_on_failure) {
350 $detect_date = true;
351 }
352 }
353
354 // check for hNews
355 if ($detect_title || $detect_body) {
356 // check for hentry
357 $elems = @$xpath->query("//*[contains(concat(' ',normalize-space(@class),' '),' hentry ')]", $this->readability->dom);
358 if ($elems && $elems->length > 0) {
359 $this->debug('hNews: found hentry');
360 $hentry = $elems->item(0);
361
362 if ($detect_title) {
363 // check for entry-title
364 $elems = @$xpath->query(".//*[contains(concat(' ',normalize-space(@class),' '),' entry-title ')]", $hentry);
365 if ($elems && $elems->length > 0) {
366 $this->debug('hNews: found entry-title');
367 $this->title = $elems->item(0)->textContent;
368 // remove title from document
369 $elems->item(0)->parentNode->removeChild($elems->item(0));
370 $detect_title = false;
371 }
372 }
373
374 if ($detect_date) {
375 // check for time element with pubdate attribute
376 $elems = @$xpath->query(".//time[@pubdate] | .//abbr[contains(concat(' ',normalize-space(@class),' '),' published ')]", $hentry);
377 if ($elems && $elems->length > 0) {
378 $this->debug('hNews: found publication date');
379 $this->date = strtotime(trim($elems->item(0)->textContent));
380 // remove date from document
381 //$elems->item(0)->parentNode->removeChild($elems->item(0));
382 if ($this->date) {
383 $detect_date = false;
384 } else {
385 $this->date = null;
386 }
387 }
388 }
389
390 if ($detect_author) {
391 // check for time element with pubdate attribute
392 $elems = @$xpath->query(".//*[contains(concat(' ',normalize-space(@class),' '),' vcard ') and (contains(concat(' ',normalize-space(@class),' '),' author ') or contains(concat(' ',normalize-space(@class),' '),' byline '))]", $hentry);
393 if ($elems && $elems->length > 0) {
394 $this->debug('hNews: found author');
395 $author = $elems->item(0);
396 $fn = @$xpath->query(".//*[contains(concat(' ',normalize-space(@class),' '),' fn ')]", $author);
397 if ($fn && $fn->length > 0) {
398 foreach ($fn as $_fn) {
399 if (trim($_fn->textContent) != '') {
400 $this->author[] = trim($_fn->textContent);
401 }
402 }
403 } else {
404 if (trim($author->textContent) != '') {
405 $this->author[] = trim($author->textContent);
406 }
407 }
408 $detect_author = empty($this->author);
409 }
410 }
411
412 // check for entry-content.
413 // according to hAtom spec, if there are multiple elements marked entry-content,
414 // we include all of these in the order they appear - see http://microformats.org/wiki/hatom#Entry_Content
415 if ($detect_body) {
416 $elems = @$xpath->query(".//*[contains(concat(' ',normalize-space(@class),' '),' entry-content ')]", $hentry);
417 if ($elems && $elems->length > 0) {
418 $this->debug('hNews: found entry-content');
419 if ($elems->length == 1) {
420 // what if it's empty? (some sites misuse hNews - place their content outside an empty entry-content element)
421 $e = $elems->item(0);
422 if (($e->tagName == 'img') || (trim($e->textContent) != '')) {
423 $this->body = $elems->item(0);
424 // prune (clean up elements that may not be content)
425 if ($this->config->prune) {
426 $this->debug('Pruning content');
427 $this->readability->prepArticle($this->body);
428 }
429 $detect_body = false;
430 } else {
431 $this->debug('hNews: skipping entry-content - appears not to contain content');
432 }
433 unset($e);
434 } else {
435 $this->body = $this->readability->dom->createElement('div');
436 $this->debug($elems->length.' entry-content elems found');
437 foreach ($elems as $elem) {
438 if (!isset($elem->parentNode)) continue;
439 $isDescendant = false;
440 foreach ($this->body->childNodes as $parent) {
441 if ($this->isDescendant($parent, $elem)) {
442 $isDescendant = true;
443 break;
444 }
445 }
446 if ($isDescendant) {
447 $this->debug('Element is child of another body element, skipping.');
448 } else {
449 // prune (clean up elements that may not be content)
450 if ($this->config->prune) {
451 $this->debug('Pruning content');
452 $this->readability->prepArticle($elem);
453 }
454 $this->debug('Element added to body');
455 $this->body->appendChild($elem);
456 }
457 }
458 $detect_body = false;
459 }
460 }
461 }
462 }
463 }
464
465 // check for elements marked with instapaper_title
466 if ($detect_title) {
467 // check for instapaper_title
468 $elems = @$xpath->query("//*[contains(concat(' ',normalize-space(@class),' '),' instapaper_title ')]", $this->readability->dom);
469 if ($elems && $elems->length > 0) {
470 $this->debug('title found (.instapaper_title)');
471 $this->title = $elems->item(0)->textContent;
472 // remove title from document
473 $elems->item(0)->parentNode->removeChild($elems->item(0));
474 $detect_title = false;
475 }
476 }
477 // check for elements marked with instapaper_body
478 if ($detect_body) {
479 $elems = @$xpath->query("//*[contains(concat(' ',normalize-space(@class),' '),' instapaper_body ')]", $this->readability->dom);
480 if ($elems && $elems->length > 0) {
481 $this->debug('body found (.instapaper_body)');
482 $this->body = $elems->item(0);
483 // prune (clean up elements that may not be content)
484 if ($this->config->prune) {
485 $this->debug('Pruning content');
486 $this->readability->prepArticle($this->body);
487 }
488 $detect_body = false;
489 }
490 }
491
492 // Find author in rel="author" marked element
493 // We only use this if there's exactly one.
494 // If there's more than one, it could indicate more than
495 // one author, but it could also indicate that we're processing
496 // a page listing different articles with different authors.
497 if ($detect_author) {
498 $elems = @$xpath->query("//a[contains(concat(' ',normalize-space(@rel),' '),' author ')]", $this->readability->dom);
499 if ($elems && $elems->length == 1) {
500 $this->debug('Author found (rel="author")');
501 $author = trim($elems->item(0)->textContent);
502 if ($author != '') {
503 $this->author[] = $author;
504 $detect_author = false;
505 }
506 }
507 }
508
509 // Find date in pubdate marked time element
510 // For the same reason given above, we only use this
511 // if there's exactly one element.
512 if ($detect_date) {
513 $elems = @$xpath->query("//time[@pubdate]", $this->readability->dom);
514 if ($elems && $elems->length == 1) {
515 $this->debug('Date found (pubdate marked time element)');
516 $this->date = strtotime(trim($elems->item(0)->textContent));
517 // remove date from document
518 //$elems->item(0)->parentNode->removeChild($elems->item(0));
519 if ($this->date) {
520 $detect_date = false;
521 } else {
522 $this->date = null;
523 }
524 }
525 }
526
527 // still missing title or body, so we detect using Readability
528 if ($detect_title || $detect_body) {
529 $this->debug('Using Readability');
530 // clone body if we're only using Readability for title (otherwise it may interfere with body element)
531 if (isset($this->body)) $this->body = $this->body->cloneNode(true);
532 $success = $this->readability->init();
533 }
534 if ($detect_title) {
535 $this->debug('Detecting title');
536 $this->title = $this->readability->getTitle()->textContent;
537 }
538 if ($detect_body && $success) {
539 $this->debug('Detecting body');
540 $this->body = $this->readability->getContent();
541 if ($this->body->childNodes->length == 1 && $this->body->firstChild->nodeType === XML_ELEMENT_NODE) {
542 $this->body = $this->body->firstChild;
543 }
544 // prune (clean up elements that may not be content)
545 if ($this->config->prune) {
546 $this->debug('Pruning content');
547 $this->readability->prepArticle($this->body);
548 }
549 }
550 if (isset($this->body)) {
551 // remove scripts
552 $this->readability->removeScripts($this->body);
553 // remove any h1-h6 elements that appear as first thing in the body
554 // and which match our title
555 if (isset($this->title) && ($this->title != '')) {
556 $firstChild = $this->body->firstChild;
557 while ($firstChild->nodeType && ($firstChild->nodeType !== XML_ELEMENT_NODE)) {
558 $firstChild = $firstChild->nextSibling;
559 }
560 if (($firstChild->nodeType === XML_ELEMENT_NODE)
561 && in_array(strtolower($firstChild->tagName), array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'))
562 && (strtolower(trim($firstChild->textContent)) == strtolower(trim($this->title)))) {
563 $this->body->removeChild($firstChild);
564 }
565 }
566 $this->success = true;
567 }
568
569 // if we've had no success and we've used tidy, there's a chance
570 // that tidy has messed up. So let's try again without tidy...
571 if (!$this->success && $tidied && $smart_tidy) {
572 $this->debug('Trying again without tidy');
573 $this->process($original_html, $url, false);
574 }
575
576 return $this->success;
577 }
578
579 private function isDescendant(DOMElement $parent, DOMElement $child) {
580 $node = $child->parentNode;
581 while ($node != null) {
582 if ($node->isSameNode($parent)) return true;
583 $node = $node->parentNode;
584 }
585 return false;
586 }
587
588 public function getContent() {
589 return $this->body;
590 }
591
592 public function getTitle() {
593 return $this->title;
594 }
595
596 public function getAuthors() {
597 return $this->author;
598 }
599
600 public function getLanguage() {
601 return $this->language;
602 }
603
604 public function getDate() {
605 return $this->date;
606 }
607
608 public function getSiteConfig() {
609 return $this->config;
610 }
611}
612?> \ No newline at end of file
diff --git a/inc/3rdparty/content-extractor/SiteConfig.php b/inc/3rdparty/content-extractor/SiteConfig.php
new file mode 100644
index 00000000..089e10c6
--- /dev/null
+++ b/inc/3rdparty/content-extractor/SiteConfig.php
@@ -0,0 +1,184 @@
1<?php
2/**
3 * Site Config
4 *
5 * Each instance of this class should hold extraction patterns and other directives
6 * for a website. See ContentExtractor class to see how it's used.
7 *
8 * @version 0.6
9 * @date 2011-10-30
10 * @author Keyvan Minoukadeh
11 * @copyright 2011 Keyvan Minoukadeh
12 * @license http://www.gnu.org/licenses/agpl-3.0.html AGPL v3
13 */
14
15class SiteConfig
16{
17 // Use first matching element as title (0 or more xpath expressions)
18 public $title = array();
19
20 // Use first matching element as body (0 or more xpath expressions)
21 public $body = array();
22
23 // Use first matching element as author (0 or more xpath expressions)
24 public $author = array();
25
26 // Use first matching element as date (0 or more xpath expressions)
27 public $date = array();
28
29 // Strip elements matching these xpath expressions (0 or more)
30 public $strip = array();
31
32 // Strip elements which contain these strings (0 or more) in the id or class attribute
33 public $strip_id_or_class = array();
34
35 // Strip images which contain these strings (0 or more) in the src attribute
36 public $strip_image_src = array();
37
38 // Additional HTTP headers to send
39 // NOT YET USED
40 public $http_header = array();
41
42 // Process HTML with tidy before creating DOM
43 public $tidy = true;
44
45 // Autodetect title/body if xpath expressions fail to produce results.
46 // Note that this applies to title and body separately, ie.
47 // * if we get a body match but no title match, this option will determine whether we autodetect title
48 // * if neither match, this determines whether we autodetect title and body.
49 // Also note that this only applies when there is at least one xpath expression in title or body, ie.
50 // * if title and body are both empty (no xpath expressions), this option has no effect (both title and body will be auto-detected)
51 // * if there's an xpath expression for title and none for body, body will be auto-detected and this option will determine whether we auto-detect title if the xpath expression for it fails to produce results.
52 // Usage scenario: you want to extract something specific from a set of URLs, e.g. a table, and if the table is not found, you want to ignore the entry completely. Auto-detection is unlikely to succeed here, so you construct your patterns and set this option to false. Another scenario may be a site where auto-detection has proven to fail (or worse, picked up the wrong content).
53 public $autodetect_on_failure = true;
54
55 // Clean up content block - attempt to remove elements that appear to be superfluous
56 public $prune = true;
57
58 // Test URL - if present, can be used to test the config above
59 public $test_url = null;
60
61 // Single-page link - should identify a link element or URL pointing to the page holding the entire article
62 // This is useful for sites which split their articles across multiple pages. Links to such pages tend to
63 // display the first page with links to the other pages at the bottom. Often there is also a link to a page
64 // which displays the entire article on one page (e.g. 'print view').
65 // This should be an XPath expression identifying the link to that page. If present and we find a match,
66 // we will retrieve that page and the rest of the options in this config will be applied to the new page.
67 public $single_page_link = array();
68
69 // Single-page link in feed? - same as above, but patterns applied to item description HTML taken from feed
70 public $single_page_link_in_feed = array();
71
72 // TODO: which parser to use for turning raw HTML into a DOMDocument
73 public $parser = 'libxml';
74
75 // String replacement to be made on HTML before processing begins
76 public $replace_string = array();
77
78 // the options below cannot be set in the config files which this class represents
79
80 public static $debug = false;
81 protected static $config_path;
82 protected static $config_path_fallback;
83 protected static $config_cache = array();
84 const HOSTNAME_REGEX = '/^(([a-zA-Z0-9-]*[a-zA-Z0-9])\.)*([A-Za-z0-9-]*[A-Za-z0-9])$/';
85
86 protected static function debug($msg) {
87 if (self::$debug) {
88 $mem = round(memory_get_usage()/1024, 2);
89 $memPeak = round(memory_get_peak_usage()/1024, 2);
90 echo '* ',$msg;
91 echo ' - mem used: ',$mem," (peak: $memPeak)\n";
92 ob_flush();
93 flush();
94 }
95 }
96
97 public static function set_config_path($path, $fallback=null) {
98 self::$config_path = $path;
99 self::$config_path_fallback = $fallback;
100 }
101
102 public static function add_to_cache($host, SiteConfig $config) {
103 $host = strtolower($host);
104 self::$config_cache[$host] = $config;
105 }
106
107 // returns SiteConfig instance if an appropriate one is found, false otherwise
108 public static function build($host) {
109 $host = strtolower($host);
110 if (substr($host, 0, 4) == 'www.') $host = substr($host, 4);
111 if (!$host || (strlen($host) > 200) || !preg_match(self::HOSTNAME_REGEX, $host)) return false;
112 // check for site configuration
113 $try = array($host);
114 $split = explode('.', $host);
115 if (count($split) > 1) {
116 array_shift($split);
117 $try[] = '.'.implode('.', $split);
118 }
119 foreach ($try as $h) {
120 if (array_key_exists($h, self::$config_cache)) {
121 self::debug("... cached ($h)");
122 return self::$config_cache[$h];
123 } elseif (file_exists(self::$config_path."/$h.txt")) {
124 self::debug("... from file ($h)");
125 $file = self::$config_path."/$h.txt";
126 break;
127 }
128 }
129 if (!isset($file)) {
130 if (isset(self::$config_path_fallback)) {
131 self::debug("... trying fallback ($host)");
132 foreach ($try as $h) {
133 if (file_exists(self::$config_path_fallback."/$h.txt")) {
134 self::debug("... from fallback file ($h)");
135 $file = self::$config_path_fallback."/$h.txt";
136 break;
137 }
138 }
139 if (!isset($file)) {
140 self::debug("... no match in fallback directory");
141 return false;
142 }
143 } else {
144 self::debug("... no match ($host)");
145 return false;
146 }
147 }
148 $config_file = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
149 if (!$config_file || !is_array($config_file)) return false;
150 $config = new SiteConfig();
151 foreach ($config_file as $line) {
152 $line = trim($line);
153
154 // skip comments, empty lines
155 if ($line == '' || $line[0] == '#') continue;
156
157 // get command
158 $command = explode(':', $line, 2);
159 // if there's no colon ':', skip this line
160 if (count($command) != 2) continue;
161 $val = trim($command[1]);
162 $command = trim($command[0]);
163 if ($command == '' || $val == '') continue;
164
165 // check for commands where we accept multiple statements
166 if (in_array($command, array('title', 'body', 'author', 'date', 'strip', 'strip_id_or_class', 'strip_image_src', 'single_page_link', 'single_page_link_in_feed', 'http_header'))) {
167 array_push($config->$command, $val);
168 // check for single statement commands that evaluate to true or false
169 } elseif (in_array($command, array('tidy', 'prune', 'autodetect_on_failure'))) {
170 $config->$command = ($val == 'yes');
171 // check for single statement commands stored as strings
172 } elseif (in_array($command, array('test_url', 'parser'))) {
173 $config->$command = $val;
174 } elseif ((substr($command, -1) == ')') && preg_match('!^([a-z0-9_]+)\((.*?)\)$!i', $command, $match)) {
175 if (in_array($match[1], array('replace_string'))) {
176 $command = $match[1];
177 array_push($config->$command, array($match[2], $val));
178 }
179 }
180 }
181 return $config;
182 }
183}
184?> \ No newline at end of file
diff --git a/inc/3rdparty/feedwriter/DummySingleItemFeed.php b/inc/3rdparty/feedwriter/DummySingleItemFeed.php
new file mode 100644
index 00000000..5f2f871e
--- /dev/null
+++ b/inc/3rdparty/feedwriter/DummySingleItemFeed.php
@@ -0,0 +1,24 @@
1<?php
2// create single item dummy feed object
3class DummySingleItemFeed {
4 public $item;
5 function __construct($url) { $this->item = new DummySingleItem($url); }
6 public function get_title() { return ''; }
7 public function get_description() { return 'Content extracted from '.$this->item->url; }
8 public function get_link() { return $this->item->url; }
9 public function get_language() { return false; }
10 public function get_image_url() { return false; }
11 public function get_items($start=0, $max=1) { return array(0=>$this->item); }
12}
13class DummySingleItem {
14 public $url;
15 function __construct($url) { $this->url = $url; }
16 public function get_permalink() { return $this->url; }
17 public function get_title() { return ''; }
18 public function get_date($format='') { return false; }
19 public function get_author($key=0) { return null; }
20 public function get_authors() { return null; }
21 public function get_description() { return ''; }
22 public function get_enclosure($key=0, $prefer=null) { return null; }
23 public function get_enclosures() { return null; }
24} \ No newline at end of file
diff --git a/inc/3rdparty/feedwriter/FeedItem.php b/inc/3rdparty/feedwriter/FeedItem.php
new file mode 100644
index 00000000..71e6e98c
--- /dev/null
+++ b/inc/3rdparty/feedwriter/FeedItem.php
@@ -0,0 +1,167 @@
1<?php
2 /**
3 * Univarsel Feed Writer
4 *
5 * FeedItem class - Used as feed element in FeedWriter class
6 *
7 * @package UnivarselFeedWriter
8 * @author Anis uddin Ahmad <anisniit@gmail.com>
9 * @link http://www.ajaxray.com/projects/rss
10 */
11 class FeedItem
12 {
13 private $elements = array(); //Collection of feed elements
14 private $version;
15
16 /**
17 * Constructor
18 *
19 * @param contant (RSS1/RSS2/ATOM) RSS2 is default.
20 */
21 function __construct($version = RSS2)
22 {
23 $this->version = $version;
24 }
25
26 /**
27 * Add an element to elements array
28 *
29 * @access public
30 * @param srting The tag name of an element
31 * @param srting The content of tag
32 * @param array Attributes(if any) in 'attrName' => 'attrValue' format
33 * @return void
34 */
35 public function addElement($elementName, $content, $attributes = null)
36 {
37 $this->elements[$elementName]['name'] = $elementName;
38 $this->elements[$elementName]['content'] = $content;
39 $this->elements[$elementName]['attributes'] = $attributes;
40 }
41
42 /**
43 * Set multiple feed elements from an array.
44 * Elements which have attributes cannot be added by this method
45 *
46 * @access public
47 * @param array array of elements in 'tagName' => 'tagContent' format.
48 * @return void
49 */
50 public function addElementArray($elementArray)
51 {
52 if(! is_array($elementArray)) return;
53 foreach ($elementArray as $elementName => $content)
54 {
55 $this->addElement($elementName, $content);
56 }
57 }
58
59 /**
60 * Return the collection of elements in this feed item
61 *
62 * @access public
63 * @return array
64 */
65 public function getElements()
66 {
67 return $this->elements;
68 }
69
70 // Wrapper functions ------------------------------------------------------
71
72 /**
73 * Set the 'dscription' element of feed item
74 *
75 * @access public
76 * @param string The content of 'description' element
77 * @return void
78 */
79 public function setDescription($description)
80 {
81 $tag = ($this->version == ATOM)? 'summary' : 'description';
82 $this->addElement($tag, $description);
83 }
84
85 /**
86 * @desc Set the 'title' element of feed item
87 * @access public
88 * @param string The content of 'title' element
89 * @return void
90 */
91 public function setTitle($title)
92 {
93 $this->addElement('title', $title);
94 }
95
96 /**
97 * Set the 'date' element of feed item
98 *
99 * @access public
100 * @param string The content of 'date' element
101 * @return void
102 */
103 public function setDate($date)
104 {
105 if(! is_numeric($date))
106 {
107 $date = strtotime($date);
108 }
109
110 if($this->version == ATOM)
111 {
112 $tag = 'updated';
113 $value = date(DATE_ATOM, $date);
114 }
115 elseif($this->version == RSS2)
116 {
117 $tag = 'pubDate';
118 $value = date(DATE_RSS, $date);
119 }
120 else
121 {
122 $tag = 'dc:date';
123 $value = date("Y-m-d", $date);
124 }
125
126 $this->addElement($tag, $value);
127 }
128
129 /**
130 * Set the 'link' element of feed item
131 *
132 * @access public
133 * @param string The content of 'link' element
134 * @return void
135 */
136 public function setLink($link)
137 {
138 if($this->version == RSS2 || $this->version == RSS1)
139 {
140 $this->addElement('link', $link);
141 }
142 else
143 {
144 $this->addElement('link','',array('href'=>$link));
145 $this->addElement('id', FeedWriter::uuid($link,'urn:uuid:'));
146 }
147
148 }
149
150 /**
151 * Set the 'encloser' element of feed item
152 * For RSS 2.0 only
153 *
154 * @access public
155 * @param string The url attribute of encloser tag
156 * @param string The length attribute of encloser tag
157 * @param string The type attribute of encloser tag
158 * @return void
159 */
160 public function setEncloser($url, $length, $type)
161 {
162 $attributes = array('url'=>$url, 'length'=>$length, 'type'=>$type);
163 $this->addElement('enclosure','',$attributes);
164 }
165
166 } // end of class FeedItem
167?>
diff --git a/inc/3rdparty/feedwriter/FeedWriter.php b/inc/3rdparty/feedwriter/FeedWriter.php
new file mode 100644
index 00000000..d5d6648a
--- /dev/null
+++ b/inc/3rdparty/feedwriter/FeedWriter.php
@@ -0,0 +1,434 @@
1<?php
2define('RSS2', 1, true);
3define('JSON', 2, true);
4define('ATOM', 3, true);
5
6 /**
7 * Univarsel Feed Writer class
8 *
9 * Genarate RSS2 or JSON (original: RSS 1.0, RSS2.0 and ATOM Feed)
10 *
11 * Modified for FiveFilters.org's Full-Text RSS project
12 * to allow for inclusion of hubs, JSON output.
13 * Stripped RSS1 and ATOM support.
14 *
15 * @package UnivarselFeedWriter
16 * @author Anis uddin Ahmad <anisniit@gmail.com>
17 * @link http://www.ajaxray.com/projects/rss
18 */
19 class FeedWriter
20 {
21 private $self = null; // self URL - http://feed2.w3.org/docs/warning/MissingAtomSelfLink.html
22 private $hubs = array(); // PubSubHubbub hubs
23 private $channels = array(); // Collection of channel elements
24 private $items = array(); // Collection of items as object of FeedItem class.
25 private $data = array(); // Store some other version wise data
26 private $CDATAEncoding = array(); // The tag names which have to encoded as CDATA
27 private $xsl = null; // stylesheet to render RSS (used by Chrome)
28 private $json = null; // JSON object
29
30 private $version = null;
31
32 /**
33 * Constructor
34 *
35 * @param constant the version constant (RSS2 or JSON).
36 */
37 function __construct($version = RSS2)
38 {
39 $this->version = $version;
40
41 // Setting default value for assential channel elements
42 $this->channels['title'] = $version . ' Feed';
43 $this->channels['link'] = 'http://www.ajaxray.com/blog';
44
45 //Tag names to encode in CDATA
46 $this->CDATAEncoding = array('description', 'content:encoded', 'content', 'subtitle', 'summary');
47 }
48
49 public function setFormat($format) {
50 $this->version = $format;
51 }
52
53 // Start # public functions ---------------------------------------------
54
55 /**
56 * Set a channel element
57 * @access public
58 * @param srting name of the channel tag
59 * @param string content of the channel tag
60 * @return void
61 */
62 public function setChannelElement($elementName, $content)
63 {
64 $this->channels[$elementName] = $content ;
65 }
66
67 /**
68 * Set multiple channel elements from an array. Array elements
69 * should be 'channelName' => 'channelContent' format.
70 *
71 * @access public
72 * @param array array of channels
73 * @return void
74 */
75 public function setChannelElementsFromArray($elementArray)
76 {
77 if(! is_array($elementArray)) return;
78 foreach ($elementArray as $elementName => $content)
79 {
80 $this->setChannelElement($elementName, $content);
81 }
82 }
83
84 /**
85 * Genarate the actual RSS/JSON file
86 *
87 * @access public
88 * @return void
89 */
90 public function genarateFeed()
91 {
92 if ($this->version == RSS2) {
93 header('Content-type: text/xml; charset=UTF-8');
94 } elseif ($this->version == JSON) {
95 header('Content-type: application/json; charset=UTF-8');
96 $this->json = new stdClass();
97 }
98 $this->printHead();
99 $this->printChannels();
100 $this->printItems();
101 $this->printTale();
102 if ($this->version == JSON) {
103 echo json_encode($this->json);
104 }
105 }
106
107 /**
108 * Create a new FeedItem.
109 *
110 * @access public
111 * @return object instance of FeedItem class
112 */
113 public function createNewItem()
114 {
115 $Item = new FeedItem($this->version);
116 return $Item;
117 }
118
119 /**
120 * Add a FeedItem to the main class
121 *
122 * @access public
123 * @param object instance of FeedItem class
124 * @return void
125 */
126 public function addItem($feedItem)
127 {
128 $this->items[] = $feedItem;
129 }
130
131 // Wrapper functions -------------------------------------------------------------------
132
133 /**
134 * Set the 'title' channel element
135 *
136 * @access public
137 * @param srting value of 'title' channel tag
138 * @return void
139 */
140 public function setTitle($title)
141 {
142 $this->setChannelElement('title', $title);
143 }
144
145 /**
146 * Add a hub to the channel element
147 *
148 * @access public
149 * @param string URL
150 * @return void
151 */
152 public function addHub($hub)
153 {
154 $this->hubs[] = $hub;
155 }
156
157 /**
158 * Set XSL URL
159 *
160 * @access public
161 * @param string URL
162 * @return void
163 */
164 public function setXsl($xsl)
165 {
166 $this->xsl = $xsl;
167 }
168
169 /**
170 * Set self URL
171 *
172 * @access public
173 * @param string URL
174 * @return void
175 */
176 public function setSelf($self)
177 {
178 $this->self = $self;
179 }
180
181 /**
182 * Set the 'description' channel element
183 *
184 * @access public
185 * @param srting value of 'description' channel tag
186 * @return void
187 */
188 public function setDescription($desciption)
189 {
190 $tag = ($this->version == ATOM)? 'subtitle' : 'description';
191 $this->setChannelElement($tag, $desciption);
192 }
193
194 /**
195 * Set the 'link' channel element
196 *
197 * @access public
198 * @param srting value of 'link' channel tag
199 * @return void
200 */
201 public function setLink($link)
202 {
203 $this->setChannelElement('link', $link);
204 }
205
206 /**
207 * Set the 'image' channel element
208 *
209 * @access public
210 * @param srting title of image
211 * @param srting link url of the imahe
212 * @param srting path url of the image
213 * @return void
214 */
215 public function setImage($title, $link, $url)
216 {
217 $this->setChannelElement('image', array('title'=>$title, 'link'=>$link, 'url'=>$url));
218 }
219
220 // End # public functions ----------------------------------------------
221
222 // Start # private functions ----------------------------------------------
223
224 /**
225 * Prints the xml and rss namespace
226 *
227 * @access private
228 * @return void
229 */
230 private function printHead()
231 {
232 if ($this->version == RSS2)
233 {
234 $out = '<?xml version="1.0" encoding="utf-8"?>'."\n";
235 if ($this->xsl) $out .= '<?xml-stylesheet type="text/xsl" href="'.htmlspecialchars($this->xsl).'"?>' . PHP_EOL;
236 $out .= '<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/">' . PHP_EOL;
237 echo $out;
238 }
239 elseif ($this->version == JSON)
240 {
241 $this->json->rss = array('@attributes' => array('version' => '2.0'));
242 }
243 }
244
245 /**
246 * Closes the open tags at the end of file
247 *
248 * @access private
249 * @return void
250 */
251 private function printTale()
252 {
253 if ($this->version == RSS2)
254 {
255 echo '</channel>',PHP_EOL,'</rss>';
256 }
257 // do nothing for JSON
258 }
259
260 /**
261 * Creates a single node as xml format
262 *
263 * @access private
264 * @param string name of the tag
265 * @param mixed tag value as string or array of nested tags in 'tagName' => 'tagValue' format
266 * @param array Attributes(if any) in 'attrName' => 'attrValue' format
267 * @return string formatted xml tag
268 */
269 private function makeNode($tagName, $tagContent, $attributes = null)
270 {
271 if ($this->version == RSS2)
272 {
273 $nodeText = '';
274 $attrText = '';
275 if (is_array($attributes))
276 {
277 foreach ($attributes as $key => $value)
278 {
279 $attrText .= " $key=\"$value\" ";
280 }
281 }
282 $nodeText .= "<{$tagName}{$attrText}>";
283 if (is_array($tagContent))
284 {
285 foreach ($tagContent as $key => $value)
286 {
287 $nodeText .= $this->makeNode($key, $value);
288 }
289 }
290 else
291 {
292 //$nodeText .= (in_array($tagName, $this->CDATAEncoding))? $tagContent : htmlentities($tagContent);
293 $nodeText .= htmlspecialchars($tagContent);
294 }
295 //$nodeText .= (in_array($tagName, $this->CDATAEncoding))? "]]></$tagName>" : "</$tagName>";
296 $nodeText .= "</$tagName>";
297 return $nodeText . PHP_EOL;
298 }
299 elseif ($this->version == JSON)
300 {
301 $tagName = (string)$tagName;
302 $tagName = strtr($tagName, ':', '_');
303 $node = null;
304 if (!$tagContent && is_array($attributes) && count($attributes))
305 {
306 $node = array('@attributes' => $this->json_keys($attributes));
307 } else {
308 if (is_array($tagContent)) {
309 $node = $this->json_keys($tagContent);
310 } else {
311 $node = $tagContent;
312 }
313 }
314 return $node;
315 }
316 return ''; // should not get here
317 }
318
319 private function json_keys(array $array) {
320 $new = array();
321 foreach ($array as $key => $val) {
322 if (is_string($key)) $key = strtr($key, ':', '_');
323 if (is_array($val)) {
324 $new[$key] = $this->json_keys($val);
325 } else {
326 $new[$key] = $val;
327 }
328 }
329 return $new;
330 }
331
332 /**
333 * @desc Print channels
334 * @access private
335 * @return void
336 */
337 private function printChannels()
338 {
339 //Start channel tag
340 switch ($this->version)
341 {
342 case RSS2:
343 echo '<channel>' . PHP_EOL;
344 // add hubs
345 foreach ($this->hubs as $hub) {
346 //echo $this->makeNode('link', '', array('rel'=>'hub', 'href'=>$hub, 'xmlns'=>'http://www.w3.org/2005/Atom'));
347 echo '<link rel="hub" href="'.htmlspecialchars($hub).'" xmlns="http://www.w3.org/2005/Atom" />' . PHP_EOL;
348 }
349 // add self
350 if (isset($this->self)) {
351 //echo $this->makeNode('link', '', array('rel'=>'self', 'href'=>$this->self, 'xmlns'=>'http://www.w3.org/2005/Atom'));
352 echo '<link rel="self" href="'.htmlspecialchars($this->self).'" xmlns="http://www.w3.org/2005/Atom" />' . PHP_EOL;
353 }
354 //Print Items of channel
355 foreach ($this->channels as $key => $value)
356 {
357 echo $this->makeNode($key, $value);
358 }
359 break;
360 case JSON:
361 $this->json->rss['channel'] = (object)$this->json_keys($this->channels);
362 break;
363 }
364 }
365
366 /**
367 * Prints formatted feed items
368 *
369 * @access private
370 * @return void
371 */
372 private function printItems()
373 {
374 foreach ($this->items as $item)
375 {
376 $thisItems = $item->getElements();
377
378 echo $this->startItem();
379
380 if ($this->version == JSON) {
381 $json_item = array();
382 }
383
384 foreach ($thisItems as $feedItem )
385 {
386 if ($this->version == RSS2) {
387 echo $this->makeNode($feedItem['name'], $feedItem['content'], $feedItem['attributes']);
388 } elseif ($this->version == JSON) {
389 $json_item[strtr($feedItem['name'], ':', '_')] = $this->makeNode($feedItem['name'], $feedItem['content'], $feedItem['attributes']);
390 }
391 }
392 echo $this->endItem();
393 if ($this->version == JSON) {
394 if (count($this->items) > 1) {
395 $this->json->rss['channel']->item[] = $json_item;
396 } else {
397 $this->json->rss['channel']->item = $json_item;
398 }
399 }
400 }
401 }
402
403 /**
404 * Make the starting tag of channels
405 *
406 * @access private
407 * @return void
408 */
409 private function startItem()
410 {
411 if ($this->version == RSS2)
412 {
413 echo '<item>' . PHP_EOL;
414 }
415 // nothing for JSON
416 }
417
418 /**
419 * Closes feed item tag
420 *
421 * @access private
422 * @return void
423 */
424 private function endItem()
425 {
426 if ($this->version == RSS2)
427 {
428 echo '</item>' . PHP_EOL;
429 }
430 // nothing for JSON
431 }
432
433 // End # private functions ----------------------------------------------
434 } \ No newline at end of file
diff --git a/inc/3rdparty/humble-http-agent/CookieJar.php b/inc/3rdparty/humble-http-agent/CookieJar.php
new file mode 100644
index 00000000..d91b711e
--- /dev/null
+++ b/inc/3rdparty/humble-http-agent/CookieJar.php
@@ -0,0 +1,404 @@
1<?php
2/**
3 * Cookie Jar
4 *
5 * PHP class for handling cookies, as defined by the Netscape spec:
6 * <http://curl.haxx.se/rfc/cookie_spec.html>
7 *
8 * This class should be used to handle cookies (storing cookies from HTTP response messages, and
9 * sending out cookies in HTTP request messages). This has been adapted for FiveFilters.org
10 * from the original version used in HTTP Navigator. See http://www.keyvan.net/code/http-navigator/
11 *
12 * This class is mainly based on Cookies.pm <http://search.cpan.org/author/GAAS/libwww-perl-5.65/
13 * lib/HTTP/Cookies.pm> from the libwww-perl collection <http://www.linpro.no/lwp/>.
14 * Unlike Cookies.pm, this class only supports the Netscape cookie spec, not RFC 2965.
15 *
16 * @version 0.5
17 * @date 2011-03-15
18 * @see http://php.net/HttpRequestPool
19 * @author Keyvan Minoukadeh
20 * @copyright 2011 Keyvan Minoukadeh
21 * @license http://www.gnu.org/licenses/agpl-3.0.html AGPL v3
22 */
23
24class CookieJar
25{
26 /**
27 * Cookies - array containing all cookies.
28 *
29 * <pre>
30 * Cookies are stored like this:
31 * [domain][path][name] = array
32 * where array is:
33 * 0 => value, 1 => secure, 2 => expires
34 * </pre>
35 * @var array
36 * @access private
37 */
38 public $cookies = array();
39 public $debug = false;
40
41 /**
42 * Constructor
43 */
44 function __construct() {
45 }
46
47 protected function debug($msg, $file=null, $line=null) {
48 if ($this->debug) {
49 $mem = round(memory_get_usage()/1024, 2);
50 $memPeak = round(memory_get_peak_usage()/1024, 2);
51 echo '* ',$msg;
52 if (isset($file, $line)) echo " ($file line $line)";
53 echo ' - mem used: ',$mem," (peak: $memPeak)\n";
54 ob_flush();
55 flush();
56 }
57 }
58
59 /**
60 * Get matching cookies
61 *
62 * Only use this method if you cannot use add_cookie_header(), for example, if you want to use
63 * this cookie jar class without using the request class.
64 *
65 * @param array $param associative array containing 'domain', 'path', 'secure' keys
66 * @return string
67 * @see add_cookie_header()
68 */
69 public function getMatchingCookies($url)
70 {
71 if (($parts = @parse_url($url)) && isset($parts['scheme'], $parts['host'], $parts['path'])) {
72 $param['domain'] = $parts['host'];
73 $param['path'] = $parts['path'];
74 $param['secure'] = (strtolower($parts['scheme']) == 'https');
75 unset($parts);
76 } else {
77 return false;
78 }
79 // RFC 2965 notes:
80 // If multiple cookies satisfy the criteria above, they are ordered in
81 // the Cookie header such that those with more specific Path attributes
82 // precede those with less specific. Ordering with respect to other
83 // attributes (e.g., Domain) is unspecified.
84 $domain = $param['domain'];
85 if (strpos($domain, '.') === false) $domain .= '.local';
86 $request_path = $param['path'];
87 if ($request_path == '') $request_path = '/';
88 $request_secure = $param['secure'];
89 $now = time();
90 $matched_cookies = array();
91 // domain - find matching domains
92 $this->debug('Finding matching domains for '.$domain, __FILE__, __LINE__);
93 while (strpos($domain, '.') !== false) {
94 if (isset($this->cookies[$domain])) {
95 $this->debug(' domain match found: '.$domain);
96 $cookies =& $this->cookies[$domain];
97 } else {
98 $domain = $this->_reduce_domain($domain);
99 continue;
100 }
101 // paths - find matching paths starting from most specific
102 $this->debug(' - Finding matching paths for '.$request_path);
103 $paths = array_keys($cookies);
104 usort($paths, array($this, '_cmp_length'));
105 foreach ($paths as $path) {
106 // continue to next cookie if request path does not path-match cookie path
107 if (!$this->_path_match($request_path, $path)) continue;
108 // loop through cookie names
109 $this->debug(' path match found: '.$path);
110 foreach ($cookies[$path] as $name => $values) {
111 // if this cookie is secure but request isn't, continue to next cookie
112 if ($values[1] && !$request_secure) continue;
113 // if cookie is not a session cookie and has expired, continue to next cookie
114 if (is_int($values[2]) && ($values[2] < $now)) continue;
115 // cookie matches request
116 $this->debug(' cookie match: '.$name.'='.$values[0]);
117 $matched_cookies[] = $name.'='.$values[0];
118 }
119 }
120 $domain = $this->_reduce_domain($domain);
121 }
122 // return cookies
123 return implode('; ', $matched_cookies);
124 }
125
126 /**
127 * Parse Set-Cookie values.
128 *
129 * Only use this method if you cannot use extract_cookies(), for example, if you want to use
130 * this cookie jar class without using the response class.
131 *
132 * @param array $set_cookies array holding 1 or more "Set-Cookie" header values
133 * @param array $param associative array containing 'host', 'path' keys
134 * @return void
135 * @see extract_cookies()
136 */
137 public function storeCookies($url, $set_cookies)
138 {
139 if (count($set_cookies) == 0) return;
140 $param = @parse_url($url);
141 if (!is_array($param) || !isset($param['host'])) return;
142 $request_host = $param['host'];
143 if (strpos($request_host, '.') === false) $request_host .= '.local';
144 $request_path = @$param['path'];
145 if ($request_path == '') $request_path = '/';
146 //
147 // loop through set-cookie headers
148 //
149 foreach ($set_cookies as $set_cookie) {
150 $this->debug('Parsing: '.$set_cookie);
151 // temporary cookie store (before adding to jar)
152 $tmp_cookie = array();
153 $param = explode(';', $set_cookie);
154 // loop through params
155 for ($x=0; $x<count($param); $x++) {
156 $key_val = explode('=', $param[$x], 2);
157 if (count($key_val) != 2) {
158 // if the first param isn't a name=value pair, continue to the next set-cookie
159 // header
160 if ($x == 0) continue 2;
161 // check for secure flag
162 if (strtolower(trim($key_val[0])) == 'secure') $tmp_cookie['secure'] = true;
163 // continue to next param
164 continue;
165 }
166 list($key, $val) = array_map('trim', $key_val);
167 // first name=value pair is the cookie name and value
168 // the name and value are stored under 'name' and 'value' to avoid conflicts
169 // with later parameters.
170 if ($x == 0) {
171 $tmp_cookie = array('name'=>$key, 'value'=>$val);
172 continue;
173 }
174 $key = strtolower($key);
175 if (in_array($key, array('expires', 'path', 'domain', 'secure'))) {
176 $tmp_cookie[$key] = $val;
177 }
178 }
179 //
180 // set cookie
181 //
182 // check domain
183 if (isset($tmp_cookie['domain']) && ($tmp_cookie['domain'] != $request_host) &&
184 ($tmp_cookie['domain'] != ".$request_host")) {
185 $domain = $tmp_cookie['domain'];
186 if ((strpos($domain, '.') === false) && ($domain != 'local')) {
187 $this->debug(' - domain "'.$domain.'" has no dot and is not a local domain');
188 continue;
189 }
190 if (preg_match('/\.[0-9]+$/', $domain)) {
191 $this->debug(' - domain "'.$domain.'" appears to be an ip address');
192 continue;
193 }
194 if (substr($domain, 0, 1) != '.') $domain = ".$domain";
195 if (!$this->_domain_match($request_host, $domain)) {
196 $this->debug(' - request host "'.$request_host.'" does not domain-match "'.$domain.'"');
197 continue;
198 }
199 } else {
200 // if domain is not specified in the set-cookie header, domain will default to
201 // the request host
202 $domain = $request_host;
203 }
204 // check path
205 if (isset($tmp_cookie['path']) && ($tmp_cookie['path'] != '')) {
206 $path = urldecode($tmp_cookie['path']);
207 if (!$this->_path_match($request_path, $path)) {
208 $this->debug(' - request path "'.$request_path.'" does not path-match "'.$path.'"');
209 continue;
210 }
211 } else {
212 $path = $request_path;
213 $path = substr($path, 0, strrpos($path, '/'));
214 if ($path == '') $path = '/';
215 }
216 // check if secure
217 $secure = (isset($tmp_cookie['secure'])) ? true : false;
218 // check expiry
219 if (isset($tmp_cookie['expires'])) {
220 if (($expires = strtotime($tmp_cookie['expires'])) < 0) {
221 $expires = null;
222 }
223 } else {
224 $expires = null;
225 }
226 // set cookie
227 $this->set_cookie($domain, $path, $tmp_cookie['name'], $tmp_cookie['value'], $secure, $expires);
228 }
229 }
230
231 // return array of set-cookie values extracted from HTTP response headers (string $h)
232 public function extractCookies($h) {
233 $x = 0;
234 $lines = 0;
235 $headers = array();
236 $last_match = false;
237 $h = explode("\n", $h);
238 foreach ($h as $line) {
239 $line = rtrim($line);
240 $lines++;
241
242 $trimmed_line = trim($line);
243 if (isset($line_last)) {
244 // check if we have \r\n\r\n (indicating the end of headers)
245 // some servers will not use CRLF (\r\n), so we make CR (\r) optional.
246 // if (preg_match('/\015?\012\015?\012/', $line_last.$line)) {
247 // break;
248 // }
249 // As an alternative, we can check if the current trimmed line is empty
250 if ($trimmed_line == '') {
251 break;
252 }
253
254 // check for continuation line...
255 // RFC 2616 Section 2.2 "Basic Rules":
256 // HTTP/1.1 header field values can be folded onto multiple lines if the
257 // continuation line begins with a space or horizontal tab. All linear
258 // white space, including folding, has the same semantics as SP. A
259 // recipient MAY replace any linear white space with a single SP before
260 // interpreting the field value or forwarding the message downstream.
261 if ($last_match && preg_match('/^\s+(.*)/', $line, $match)) {
262 // append to previous header value
263 $headers[$x-1] .= ' '.rtrim($match[1]);
264 continue;
265 }
266 }
267 $line_last = $line;
268
269 // split header name and value
270 if (preg_match('/^Set-Cookie\s*:\s*(.*)/i', $line, $match)) {
271 $headers[$x++] = rtrim($match[1]);
272 $last_match = true;
273 } else {
274 $last_match = false;
275 }
276 }
277 return $headers;
278 }
279
280 /**
281 * Set Cookie
282 * @param string $domain
283 * @param string $path
284 * @param string $name cookie name
285 * @param string $value cookie value
286 * @param bool $secure
287 * @param int $expires expiry time (null if session cookie, <= 0 will delete cookie)
288 * @return void
289 */
290 function set_cookie($domain, $path, $name, $value, $secure=false, $expires=null)
291 {
292 if ($domain == '') return;
293 if ($path == '') return;
294 if ($name == '') return;
295 // check if cookie needs to go
296 if (isset($expires) && ($expires <= 0)) {
297 if (isset($this->cookies[$domain][$path][$name])) unset($this->cookies[$domain][$path][$name]);
298 return;
299 }
300 if ($value == '') return;
301 $this->cookies[$domain][$path][$name] = array($value, $secure, $expires);
302 return;
303 }
304
305 /**
306 * Clear cookies - [domain [,path [,name]]] - call method with no arguments to clear all cookies.
307 * @param string $domain
308 * @param string $path
309 * @param string $name
310 * @return void
311 */
312 function clear($domain=null, $path=null, $name=null)
313 {
314 if (!isset($domain)) {
315 $this->cookies = array();
316 } elseif (!isset($path)) {
317 if (isset($this->cookies[$domain])) unset($this->cookies[$domain]);
318 } elseif (!isset($name)) {
319 if (isset($this->cookies[$domain][$path])) unset($this->cookies[$domain][$path]);
320 } elseif (isset($name)) {
321 if (isset($this->cookies[$domain][$path][$name])) unset($this->cookies[$domain][$path][$name]);
322 }
323 }
324
325 /**
326 * Compare string length - used for sorting
327 * @access private
328 * @return int
329 */
330 function _cmp_length($a, $b)
331 {
332 $la = strlen($a); $lb = strlen($b);
333 if ($la == $lb) return 0;
334 return ($la > $lb) ? -1 : 1;
335 }
336
337 /**
338 * Reduce domain
339 * @param string $domain
340 * @return string
341 * @access private
342 */
343 function _reduce_domain($domain)
344 {
345 if ($domain == '') return '';
346 if (substr($domain, 0, 1) == '.') return substr($domain, 1);
347 return substr($domain, strpos($domain, '.'));
348 }
349
350 /**
351 * Path match - check if path1 path-matches path2
352 *
353 * From RFC 2965:
354 * <i>For two strings that represent paths, P1 and P2, P1 path-matches P2
355 * if P2 is a prefix of P1 (including the case where P1 and P2 string-
356 * compare equal). Thus, the string /tec/waldo path-matches /tec.</i>
357 * @param string $path1
358 * @param string $path2
359 * @return bool
360 * @access private
361 */
362 function _path_match($path1, $path2)
363 {
364 return (substr($path1, 0, strlen($path2)) == $path2);
365 }
366
367 /**
368 * Domain match - check if domain1 domain-matches domain2
369 *
370 * A few extracts from RFC 2965:
371 * - A Set-Cookie2 from request-host y.x.foo.com for Domain=.foo.com
372 * would be rejected, because H is y.x and contains a dot.
373 *
374 * - A Set-Cookie2 from request-host x.foo.com for Domain=.foo.com
375 * would be accepted.
376 *
377 * - A Set-Cookie2 with Domain=.com or Domain=.com., will always be
378 * rejected, because there is no embedded dot.
379 *
380 * - A Set-Cookie2 from request-host example for Domain=.local will
381 * be accepted, because the effective host name for the request-
382 * host is example.local, and example.local domain-matches .local.
383 *
384 * I'm ignoring the first point for now (must check to see how other browsers handle
385 * this rule for Set-Cookie headers)
386 *
387 * @param string $domain1
388 * @param string $domain2
389 * @return bool
390 * @access private
391 */
392 function _domain_match($domain1, $domain2)
393 {
394 $domain1 = strtolower($domain1);
395 $domain2 = strtolower($domain2);
396 while (strpos($domain1, '.') !== false) {
397 if ($domain1 == $domain2) return true;
398 $domain1 = $this->_reduce_domain($domain1);
399 continue;
400 }
401 return false;
402 }
403}
404?> \ No newline at end of file
diff --git a/inc/3rdparty/humble-http-agent/HumbleHttpAgent.php b/inc/3rdparty/humble-http-agent/HumbleHttpAgent.php
new file mode 100644
index 00000000..7e5834ab
--- /dev/null
+++ b/inc/3rdparty/humble-http-agent/HumbleHttpAgent.php
@@ -0,0 +1,720 @@
1<?php
2/**
3 * Humble HTTP Agent
4 *
5 * This class is designed to take advantage of parallel HTTP requests
6 * offered by PHP's PECL HTTP extension or the curl_multi_* functions.
7 * For environments which do not have these options, it reverts to standard sequential
8 * requests (using file_get_contents())
9 *
10 * @version 1.0
11 * @date 2012-02-09
12 * @see http://php.net/HttpRequestPool
13 * @author Keyvan Minoukadeh
14 * @copyright 2011-2012 Keyvan Minoukadeh
15 * @license http://www.gnu.org/licenses/agpl-3.0.html AGPL v3
16 */
17
18class HumbleHttpAgent
19{
20 const METHOD_REQUEST_POOL = 1;
21 const METHOD_CURL_MULTI = 2;
22 const METHOD_FILE_GET_CONTENTS = 4;
23 //const UA_BROWSER = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1';
24 const UA_BROWSER = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.92 Safari/535.2';
25 const UA_PHP = 'PHP/5.2';
26 const REF_GOOGLE = 'http://www.google.co.uk/url?sa=t&source=web&cd=1';
27
28 protected $requests = array();
29 protected $redirectQueue = array();
30 protected $requestOptions;
31 protected $maxParallelRequests = 5;
32 protected $cache = null; //TODO
33 protected $httpContext;
34 protected $minimiseMemoryUse = false; //TODO
35 protected $debug = false;
36 protected $method;
37 protected $cookieJar;
38 public $rewriteHashbangFragment = true; // see http://code.google.com/web/ajaxcrawling/docs/specification.html
39 public $maxRedirects = 5;
40 public $userAgentMap = array();
41 public $rewriteUrls = array();
42 public $userAgentDefault;
43 public $referer;
44 //public $userAgent = 'Mozilla/5.0';
45
46 // Prevent certain file/mime types
47 // HTTP responses which match these content types will
48 // be returned without body.
49 public $headerOnlyTypes = array();
50 // URLs ending with one of these extensions will
51 // prompt Humble HTTP Agent to send a HEAD request first
52 // to see if returned content type matches $headerOnlyTypes.
53 public $headerOnlyClues = array('pdf','mp3','zip','exe','gif','gzip','gz','jpeg','jpg','mpg','mpeg','png','ppt','mov');
54
55 //TODO: set max file size
56 //TODO: normalise headers
57
58 function __construct($requestOptions=null, $method=null) {
59 $this->userAgentDefault = self::UA_BROWSER;
60 $this->referer = self::REF_GOOGLE;
61 // set the request method
62 if (in_array($method, array(1,2,4))) {
63 $this->method = $method;
64 } else {
65 if (class_exists('HttpRequestPool')) {
66 $this->method = self::METHOD_REQUEST_POOL;
67 } elseif (function_exists('curl_multi_init')) {
68 $this->method = self::METHOD_CURL_MULTI;
69 } else {
70 $this->method = self::METHOD_FILE_GET_CONTENTS;
71 }
72 }
73 if ($this->method == self::METHOD_CURL_MULTI) {
74 require_once(dirname(__FILE__).'/RollingCurl.php');
75 }
76 // create cookie jar
77 $this->cookieJar = new CookieJar();
78 // set request options (redirect must be 0)
79 $this->requestOptions = array(
80 'timeout' => 15,
81 'redirect' => 0 // we handle redirects manually so we can rewrite the new hashbang URLs that are creeping up over the web
82 // TODO: test onprogress?
83 );
84 if (is_array($requestOptions)) {
85 $this->requestOptions = array_merge($this->requestOptions, $requestOptions);
86 }
87 $this->httpContext = array(
88 'http' => array(
89 'ignore_errors' => true,
90 'timeout' => $this->requestOptions['timeout'],
91 'max_redirects' => $this->requestOptions['redirect'],
92 'header' => "Accept: */*\r\n"
93 )
94 );
95 }
96
97 protected function debug($msg) {
98 if ($this->debug) {
99 $mem = round(memory_get_usage()/1024, 2);
100 $memPeak = round(memory_get_peak_usage()/1024, 2);
101 echo '* ',$msg;
102 echo ' - mem used: ',$mem," (peak: $memPeak)\n";
103 ob_flush();
104 flush();
105 }
106 }
107
108 protected function getUserAgent($url, $asArray=false) {
109 $host = @parse_url($url, PHP_URL_HOST);
110 if (strtolower(substr($host, 0, 4)) == 'www.') {
111 $host = substr($host, 4);
112 }
113 if ($host) {
114 $try = array($host);
115 $split = explode('.', $host);
116 if (count($split) > 1) {
117 array_shift($split);
118 $try[] = '.'.implode('.', $split);
119 }
120 foreach ($try as $h) {
121 if (isset($this->userAgentMap[$h])) {
122 $ua = $this->userAgentMap[$h];
123 break;
124 }
125 }
126 }
127 if (!isset($ua)) $ua = $this->userAgentDefault;
128 if ($asArray) {
129 return array('User-Agent' => $ua);
130 } else {
131 return 'User-Agent: '.$ua;
132 }
133 }
134
135 public function rewriteHashbangFragment($url) {
136 // return $url if there's no '#!'
137 if (strpos($url, '#!') === false) return $url;
138 // split $url and rewrite
139 // TODO: is SimplePie_IRI included?
140 $iri = new SimplePie_IRI($url);
141 $fragment = substr($iri->fragment, 1); // strip '!'
142 $iri->fragment = null;
143 if (isset($iri->query)) {
144 parse_str($iri->query, $query);
145 } else {
146 $query = array();
147 }
148 $query['_escaped_fragment_'] = (string)$fragment;
149 $iri->query = str_replace('%2F', '/', http_build_query($query)); // needed for some sites
150 return $iri->get_iri();
151 }
152
153 public function removeFragment($url) {
154 $pos = strpos($url, '#');
155 if ($pos === false) {
156 return $url;
157 } else {
158 return substr($url, 0, $pos);
159 }
160 }
161
162 public function rewriteUrls($url) {
163 foreach ($this->rewriteUrls as $find => $action) {
164 if (strpos($url, $find) !== false) {
165 if (is_array($action)) {
166 return strtr($url, $action);
167 }
168 }
169 }
170 return $url;
171 }
172
173 public function enableDebug($bool=true) {
174 $this->debug = (bool)$bool;
175 }
176
177 public function minimiseMemoryUse($bool = true) {
178 $this->minimiseMemoryUse = $bool;
179 }
180
181 public function setMaxParallelRequests($max) {
182 $this->maxParallelRequests = $max;
183 }
184
185 public function validateUrl($url) {
186 $url = filter_var($url, FILTER_SANITIZE_URL);
187 $test = filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED);
188 // deal with bug http://bugs.php.net/51192 (present in PHP 5.2.13 and PHP 5.3.2)
189 if ($test === false) {
190 $test = filter_var(strtr($url, '-', '_'), FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED);
191 }
192 if ($test !== false && $test !== null && preg_match('!^https?://!', $url)) {
193 return $url;
194 } else {
195 return false;
196 }
197 }
198
199 public function fetchAll(array $urls) {
200 $this->fetchAllOnce($urls, $isRedirect=false);
201 $redirects = 0;
202 while (!empty($this->redirectQueue) && ++$redirects <= $this->maxRedirects) {
203 $this->debug("Following redirects #$redirects...");
204 $this->fetchAllOnce($this->redirectQueue, $isRedirect=true);
205 }
206 }
207
208 // fetch all URLs without following redirects
209 public function fetchAllOnce(array $urls, $isRedirect=false) {
210 if (!$isRedirect) $urls = array_unique($urls);
211 if (empty($urls)) return;
212
213 //////////////////////////////////////////////////////
214 // parallel (HttpRequestPool)
215 if ($this->method == self::METHOD_REQUEST_POOL) {
216 $this->debug('Starting parallel fetch (HttpRequestPool)');
217 try {
218 while (count($urls) > 0) {
219 $this->debug('Processing set of '.min($this->maxParallelRequests, count($urls)));
220 $subset = array_splice($urls, 0, $this->maxParallelRequests);
221 $pool = new HttpRequestPool();
222 foreach ($subset as $orig => $url) {
223 if (!$isRedirect) $orig = $url;
224 unset($this->redirectQueue[$orig]);
225 $this->debug("...$url");
226 if (!$isRedirect && isset($this->requests[$url])) {
227 $this->debug("......in memory");
228 /*
229 } elseif ($this->isCached($url)) {
230 $this->debug("......is cached");
231 if (!$this->minimiseMemoryUse) {
232 $this->requests[$url] = $this->getCached($url);
233 }
234 */
235 } else {
236 $this->debug("......adding to pool");
237 $req_url = $this->rewriteUrls($url);
238 $req_url = ($this->rewriteHashbangFragment) ? $this->rewriteHashbangFragment($req_url) : $req_url;
239 $req_url = $this->removeFragment($req_url);
240 if (!empty($this->headerOnlyTypes) && !isset($this->requests[$orig]['wrongGuess']) && $this->possibleUnsupportedType($req_url)) {
241 $_meth = HttpRequest::METH_HEAD;
242 } else {
243 $_meth = HttpRequest::METH_GET;
244 unset($this->requests[$orig]['wrongGuess']);
245 }
246 $httpRequest = new HttpRequest($req_url, $_meth, $this->requestOptions);
247 // send cookies, if we have any
248 if ($cookies = $this->cookieJar->getMatchingCookies($req_url)) {
249 $this->debug("......sending cookies: $cookies");
250 $httpRequest->addHeaders(array('Cookie' => $cookies));
251 }
252 //$httpRequest->addHeaders(array('User-Agent' => $this->userAgent));
253 $httpRequest->addHeaders($this->getUserAgent($req_url, true));
254 // add referer for picky sites
255 $httpRequest->addheaders(array('Referer' => $this->referer));
256 $this->requests[$orig] = array('headers'=>null, 'body'=>null, 'httpRequest'=>$httpRequest);
257 $this->requests[$orig]['original_url'] = $orig;
258 $pool->attach($httpRequest);
259 }
260 }
261 // did we get anything into the pool?
262 if (count($pool) > 0) {
263 $this->debug('Sending request...');
264 try {
265 $pool->send();
266 } catch (HttpRequestPoolException $e) {
267 // do nothing
268 }
269 $this->debug('Received responses');
270 foreach($subset as $orig => $url) {
271 if (!$isRedirect) $orig = $url;
272 $request = $this->requests[$orig]['httpRequest'];
273 //$this->requests[$orig]['headers'] = $this->headersToString($request->getResponseHeader());
274 // getResponseHeader() doesn't return status line, so, for consistency...
275 $this->requests[$orig]['headers'] = substr($request->getRawResponseMessage(), 0, $request->getResponseInfo('header_size'));
276 // check content type
277 // TODO: use getResponseHeader('content-type') or getResponseInfo()
278 if ($this->headerOnlyType($this->requests[$orig]['headers'])) {
279 $this->requests[$orig]['body'] = '';
280 $_header_only_type = true;
281 $this->debug('Header only type returned');
282 } else {
283 $this->requests[$orig]['body'] = $request->getResponseBody();
284 $_header_only_type = false;
285 }
286 $this->requests[$orig]['effective_url'] = $request->getResponseInfo('effective_url');
287 $this->requests[$orig]['status_code'] = $status_code = $request->getResponseCode();
288 // is redirect?
289 if ((in_array($status_code, array(300, 301, 302, 303, 307)) || $status_code > 307 && $status_code < 400) && $request->getResponseHeader('location')) {
290 $redirectURL = $request->getResponseHeader('location');
291 if (!preg_match('!^https?://!i', $redirectURL)) {
292 $redirectURL = SimplePie_Misc::absolutize_url($redirectURL, $url);
293 }
294 if ($this->validateURL($redirectURL)) {
295 $this->debug('Redirect detected. Valid URL: '.$redirectURL);
296 // store any cookies
297 $cookies = $request->getResponseHeader('set-cookie');
298 if ($cookies && !is_array($cookies)) $cookies = array($cookies);
299 if ($cookies) $this->cookieJar->storeCookies($url, $cookies);
300 $this->redirectQueue[$orig] = $redirectURL;
301 } else {
302 $this->debug('Redirect detected. Invalid URL: '.$redirectURL);
303 }
304 } elseif (!$_header_only_type && $request->getMethod() === HttpRequest::METH_HEAD) {
305 // the response content-type did not match our 'header only' types,
306 // but we'd issues a HEAD request because we assumed it would. So
307 // let's queue a proper GET request for this item...
308 $this->debug('Wrong guess at content-type, queing GET request');
309 $this->requests[$orig]['wrongGuess'] = true;
310 $this->redirectQueue[$orig] = $this->requests[$orig]['effective_url'];
311 }
312 //die($url.' -multi- '.$request->getResponseInfo('effective_url'));
313 $pool->detach($request);
314 unset($this->requests[$orig]['httpRequest'], $request);
315 /*
316 if ($this->minimiseMemoryUse) {
317 if ($this->cache($url)) {
318 unset($this->requests[$url]);
319 }
320 }
321 */
322 }
323 }
324 }
325 } catch (HttpException $e) {
326 $this->debug($e);
327 return false;
328 }
329 }
330
331 //////////////////////////////////////////////////////////
332 // parallel (curl_multi_*)
333 elseif ($this->method == self::METHOD_CURL_MULTI) {
334 $this->debug('Starting parallel fetch (curl_multi_*)');
335 while (count($urls) > 0) {
336 $this->debug('Processing set of '.min($this->maxParallelRequests, count($urls)));
337 $subset = array_splice($urls, 0, $this->maxParallelRequests);
338 $pool = new RollingCurl(array($this, 'handleCurlResponse'));
339 $pool->window_size = count($subset);
340
341 foreach ($subset as $orig => $url) {
342 if (!$isRedirect) $orig = $url;
343 unset($this->redirectQueue[$orig]);
344 $this->debug("...$url");
345 if (!$isRedirect && isset($this->requests[$url])) {
346 $this->debug("......in memory");
347 /*
348 } elseif ($this->isCached($url)) {
349 $this->debug("......is cached");
350 if (!$this->minimiseMemoryUse) {
351 $this->requests[$url] = $this->getCached($url);
352 }
353 */
354 } else {
355 $this->debug("......adding to pool");
356 $req_url = $this->rewriteUrls($url);
357 $req_url = ($this->rewriteHashbangFragment) ? $this->rewriteHashbangFragment($req_url) : $req_url;
358 $req_url = $this->removeFragment($req_url);
359 if (!empty($this->headerOnlyTypes) && !isset($this->requests[$orig]['wrongGuess']) && $this->possibleUnsupportedType($req_url)) {
360 $_meth = 'HEAD';
361 } else {
362 $_meth = 'GET';
363 unset($this->requests[$orig]['wrongGuess']);
364 }
365 $headers = array();
366 //$headers[] = 'User-Agent: '.$this->userAgent;
367 $headers[] = $this->getUserAgent($req_url);
368 // add referer for picky sites
369 $headers[] = 'Referer: '.$this->referer;
370 // send cookies, if we have any
371 if ($cookies = $this->cookieJar->getMatchingCookies($req_url)) {
372 $this->debug("......sending cookies: $cookies");
373 $headers[] = 'Cookie: '.$cookies;
374 }
375 $httpRequest = new RollingCurlRequest($req_url, $_meth, null, $headers, array(
376 CURLOPT_CONNECTTIMEOUT => $this->requestOptions['timeout'],
377 CURLOPT_TIMEOUT => $this->requestOptions['timeout']
378 ));
379 $httpRequest->set_original_url($orig);
380 $this->requests[$orig] = array('headers'=>null, 'body'=>null, 'httpRequest'=>$httpRequest);
381 $this->requests[$orig]['original_url'] = $orig; // TODO: is this needed anymore?
382 $pool->add($httpRequest);
383 }
384 }
385 // did we get anything into the pool?
386 if (count($pool) > 0) {
387 $this->debug('Sending request...');
388 $pool->execute(); // this will call handleCurlResponse() and populate $this->requests[$orig]
389 $this->debug('Received responses');
390 foreach($subset as $orig => $url) {
391 if (!$isRedirect) $orig = $url;
392 // $this->requests[$orig]['headers']
393 // $this->requests[$orig]['body']
394 // $this->requests[$orig]['effective_url']
395 // check content type
396 if ($this->headerOnlyType($this->requests[$orig]['headers'])) {
397 $this->requests[$orig]['body'] = '';
398 $_header_only_type = true;
399 $this->debug('Header only type returned');
400 } else {
401 $_header_only_type = false;
402 }
403 $status_code = $this->requests[$orig]['status_code'];
404 if ((in_array($status_code, array(300, 301, 302, 303, 307)) || $status_code > 307 && $status_code < 400) && isset($this->requests[$orig]['location'])) {
405 $redirectURL = $this->requests[$orig]['location'];
406 if (!preg_match('!^https?://!i', $redirectURL)) {
407 $redirectURL = SimplePie_Misc::absolutize_url($redirectURL, $url);
408 }
409 if ($this->validateURL($redirectURL)) {
410 $this->debug('Redirect detected. Valid URL: '.$redirectURL);
411 // store any cookies
412 $cookies = $this->cookieJar->extractCookies($this->requests[$orig]['headers']);
413 if (!empty($cookies)) $this->cookieJar->storeCookies($url, $cookies);
414 $this->redirectQueue[$orig] = $redirectURL;
415 } else {
416 $this->debug('Redirect detected. Invalid URL: '.$redirectURL);
417 }
418 } elseif (!$_header_only_type && $this->requests[$orig]['method'] == 'HEAD') {
419 // the response content-type did not match our 'header only' types,
420 // but we'd issues a HEAD request because we assumed it would. So
421 // let's queue a proper GET request for this item...
422 $this->debug('Wrong guess at content-type, queing GET request');
423 $this->requests[$orig]['wrongGuess'] = true;
424 $this->redirectQueue[$orig] = $this->requests[$orig]['effective_url'];
425 }
426 // die($url.' -multi- '.$request->getResponseInfo('effective_url'));
427 unset($this->requests[$orig]['httpRequest'], $this->requests[$orig]['method']);
428 }
429 }
430 }
431 }
432
433 //////////////////////////////////////////////////////
434 // sequential (file_get_contents)
435 else {
436 $this->debug('Starting sequential fetch (file_get_contents)');
437 $this->debug('Processing set of '.count($urls));
438 foreach ($urls as $orig => $url) {
439 if (!$isRedirect) $orig = $url;
440 unset($this->redirectQueue[$orig]);
441 $this->debug("...$url");
442 if (!$isRedirect && isset($this->requests[$url])) {
443 $this->debug("......in memory");
444 /*
445 } elseif ($this->isCached($url)) {
446 $this->debug("......is cached");
447 if (!$this->minimiseMemoryUse) {
448 $this->requests[$url] = $this->getCached($url);
449 }
450 */
451 } else {
452 $this->debug("Sending request for $url");
453 $this->requests[$orig]['original_url'] = $orig;
454 $req_url = $this->rewriteUrls($url);
455 $req_url = ($this->rewriteHashbangFragment) ? $this->rewriteHashbangFragment($req_url) : $req_url;
456 $req_url = $this->removeFragment($req_url);
457 // send cookies, if we have any
458 $httpContext = $this->httpContext;
459 $httpContext['http']['header'] .= $this->getUserAgent($req_url)."\r\n";
460 // add referer for picky sites
461 $httpContext['http']['header'] .= 'Referer: '.$this->referer."\r\n";
462 if ($cookies = $this->cookieJar->getMatchingCookies($req_url)) {
463 $this->debug("......sending cookies: $cookies");
464 $httpContext['http']['header'] .= 'Cookie: '.$cookies."\r\n";
465 }
466 if (false !== ($html = @file_get_contents($req_url, false, stream_context_create($httpContext)))) {
467 $this->debug('Received response');
468 // get status code
469 if (!isset($http_response_header[0]) || !preg_match('!^HTTP/\d+\.\d+\s+(\d+)!', trim($http_response_header[0]), $match)) {
470 $this->debug('Error: no status code found');
471 // TODO: handle error - no status code
472 } else {
473 $this->requests[$orig]['headers'] = $this->headersToString($http_response_header, false);
474 // check content type
475 if ($this->headerOnlyType($this->requests[$orig]['headers'])) {
476 $this->requests[$orig]['body'] = '';
477 } else {
478 $this->requests[$orig]['body'] = $html;
479 }
480 $this->requests[$orig]['effective_url'] = $req_url;
481 $this->requests[$orig]['status_code'] = $status_code = (int)$match[1];
482 unset($match);
483 // handle redirect
484 if (preg_match('/^Location:(.*?)$/m', $this->requests[$orig]['headers'], $match)) {
485 $this->requests[$orig]['location'] = trim($match[1]);
486 }
487 if ((in_array($status_code, array(300, 301, 302, 303, 307)) || $status_code > 307 && $status_code < 400) && isset($this->requests[$orig]['location'])) {
488 $redirectURL = $this->requests[$orig]['location'];
489 if (!preg_match('!^https?://!i', $redirectURL)) {
490 $redirectURL = SimplePie_Misc::absolutize_url($redirectURL, $url);
491 }
492 if ($this->validateURL($redirectURL)) {
493 $this->debug('Redirect detected. Valid URL: '.$redirectURL);
494 // store any cookies
495 $cookies = $this->cookieJar->extractCookies($this->requests[$orig]['headers']);
496 if (!empty($cookies)) $this->cookieJar->storeCookies($url, $cookies);
497 $this->redirectQueue[$orig] = $redirectURL;
498 } else {
499 $this->debug('Redirect detected. Invalid URL: '.$redirectURL);
500 }
501 }
502 }
503 } else {
504 $this->debug('Error retrieving URL');
505 //print_r($req_url);
506 //print_r($http_response_header);
507 //print_r($html);
508
509 // TODO: handle error - failed to retrieve URL
510 }
511 }
512 }
513 }
514 }
515
516 public function handleCurlResponse($response, $info, $request) {
517 $orig = $request->url_original;
518 $this->requests[$orig]['headers'] = substr($response, 0, $info['header_size']);
519 $this->requests[$orig]['body'] = substr($response, $info['header_size']);
520 $this->requests[$orig]['method'] = $request->method;
521 $this->requests[$orig]['effective_url'] = $info['url'];
522 $this->requests[$orig]['status_code'] = (int)$info['http_code'];
523 if (preg_match('/^Location:(.*?)$/m', $this->requests[$orig]['headers'], $match)) {
524 $this->requests[$orig]['location'] = trim($match[1]);
525 }
526 }
527
528 protected function headersToString(array $headers, $associative=true) {
529 if (!$associative) {
530 return implode("\n", $headers);
531 } else {
532 $str = '';
533 foreach ($headers as $key => $val) {
534 if (is_array($val)) {
535 foreach ($val as $v) $str .= "$key: $v\n";
536 } else {
537 $str .= "$key: $val\n";
538 }
539 }
540 return rtrim($str);
541 }
542 }
543
544 public function get($url, $remove=false, $gzdecode=true) {
545 $url = "$url";
546 if (isset($this->requests[$url]) && isset($this->requests[$url]['body'])) {
547 $this->debug("URL already fetched - in memory ($url, effective: {$this->requests[$url]['effective_url']})");
548 $response = $this->requests[$url];
549 /*
550 } elseif ($this->isCached($url)) {
551 $this->debug("URL already fetched - in disk cache ($url)");
552 $response = $this->getCached($url);
553 $this->requests[$url] = $response;
554 */
555 } else {
556 $this->debug("Fetching URL ($url)");
557 $this->fetchAll(array($url));
558 if (isset($this->requests[$url]) && isset($this->requests[$url]['body'])) {
559 $response = $this->requests[$url];
560 } else {
561 $this->debug("Request failed");
562 $response = false;
563 }
564 }
565 /*
566 if ($this->minimiseMemoryUse && $response) {
567 $this->cache($url);
568 unset($this->requests[$url]);
569 }
570 */
571 if ($remove && $response) unset($this->requests[$url]);
572 if ($gzdecode && stripos($response['headers'], 'Content-Encoding: gzip')) {
573 if ($html = gzdecode($response['body'])) {
574 $response['body'] = $html;
575 }
576 }
577 return $response;
578 }
579
580 public function parallelSupport() {
581 return class_exists('HttpRequestPool') || function_exists('curl_multi_init');
582 }
583
584 private function headerOnlyType($headers) {
585 if (preg_match('!^Content-Type:\s*(([a-z-]+)/([^;\r\n ]+))!im', $headers, $match)) {
586 // look for full mime type (e.g. image/jpeg) or just type (e.g. image)
587 $match[1] = strtolower(trim($match[1]));
588 $match[2] = strtolower(trim($match[2]));
589 foreach (array($match[1], $match[2]) as $mime) {
590 if (in_array($mime, $this->headerOnlyTypes)) return true;
591 }
592 }
593 return false;
594 }
595
596 private function possibleUnsupportedType($url) {
597 $path = @parse_url($url, PHP_URL_PATH);
598 if ($path && strpos($path, '.') !== false) {
599 $ext = strtolower(trim(pathinfo($path, PATHINFO_EXTENSION)));
600 return in_array($ext, $this->headerOnlyClues);
601 }
602 return false;
603 }
604}
605
606// gzdecode from http://www.php.net/manual/en/function.gzdecode.php#82930
607if (!function_exists('gzdecode')) {
608 function gzdecode($data,&$filename='',&$error='',$maxlength=null)
609 {
610 $len = strlen($data);
611 if ($len < 18 || strcmp(substr($data,0,2),"\x1f\x8b")) {
612 $error = "Not in GZIP format.";
613 return null; // Not GZIP format (See RFC 1952)
614 }
615 $method = ord(substr($data,2,1)); // Compression method
616 $flags = ord(substr($data,3,1)); // Flags
617 if ($flags & 31 != $flags) {
618 $error = "Reserved bits not allowed.";
619 return null;
620 }
621 // NOTE: $mtime may be negative (PHP integer limitations)
622 $mtime = unpack("V", substr($data,4,4));
623 $mtime = $mtime[1];
624 $xfl = substr($data,8,1);
625 $os = substr($data,8,1);
626 $headerlen = 10;
627 $extralen = 0;
628 $extra = "";
629 if ($flags & 4) {
630 // 2-byte length prefixed EXTRA data in header
631 if ($len - $headerlen - 2 < 8) {
632 return false; // invalid
633 }
634 $extralen = unpack("v",substr($data,8,2));
635 $extralen = $extralen[1];
636 if ($len - $headerlen - 2 - $extralen < 8) {
637 return false; // invalid
638 }
639 $extra = substr($data,10,$extralen);
640 $headerlen += 2 + $extralen;
641 }
642 $filenamelen = 0;
643 $filename = "";
644 if ($flags & 8) {
645 // C-style string
646 if ($len - $headerlen - 1 < 8) {
647 return false; // invalid
648 }
649 $filenamelen = strpos(substr($data,$headerlen),chr(0));
650 if ($filenamelen === false || $len - $headerlen - $filenamelen - 1 < 8) {
651 return false; // invalid
652 }
653 $filename = substr($data,$headerlen,$filenamelen);
654 $headerlen += $filenamelen + 1;
655 }
656 $commentlen = 0;
657 $comment = "";
658 if ($flags & 16) {
659 // C-style string COMMENT data in header
660 if ($len - $headerlen - 1 < 8) {
661 return false; // invalid
662 }
663 $commentlen = strpos(substr($data,$headerlen),chr(0));
664 if ($commentlen === false || $len - $headerlen - $commentlen - 1 < 8) {
665 return false; // Invalid header format
666 }
667 $comment = substr($data,$headerlen,$commentlen);
668 $headerlen += $commentlen + 1;
669 }
670 $headercrc = "";
671 if ($flags & 2) {
672 // 2-bytes (lowest order) of CRC32 on header present
673 if ($len - $headerlen - 2 < 8) {
674 return false; // invalid
675 }
676 $calccrc = crc32(substr($data,0,$headerlen)) & 0xffff;
677 $headercrc = unpack("v", substr($data,$headerlen,2));
678 $headercrc = $headercrc[1];
679 if ($headercrc != $calccrc) {
680 $error = "Header checksum failed.";
681 return false; // Bad header CRC
682 }
683 $headerlen += 2;
684 }
685 // GZIP FOOTER
686 $datacrc = unpack("V",substr($data,-8,4));
687 $datacrc = sprintf('%u',$datacrc[1] & 0xFFFFFFFF);
688 $isize = unpack("V",substr($data,-4));
689 $isize = $isize[1];
690 // decompression:
691 $bodylen = $len-$headerlen-8;
692 if ($bodylen < 1) {
693 // IMPLEMENTATION BUG!
694 return null;
695 }
696 $body = substr($data,$headerlen,$bodylen);
697 $data = "";
698 if ($bodylen > 0) {
699 switch ($method) {
700 case 8:
701 // Currently the only supported compression method:
702 $data = gzinflate($body,$maxlength);
703 break;
704 default:
705 $error = "Unknown compression method.";
706 return false;
707 }
708 } // zero-byte body content is allowed
709 // Verifiy CRC32
710 $crc = sprintf("%u",crc32($data));
711 $crcOK = $crc == $datacrc;
712 $lenOK = $isize == strlen($data);
713 if (!$lenOK || !$crcOK) {
714 $error = ( $lenOK ? '' : 'Length check FAILED. ') . ( $crcOK ? '' : 'Checksum FAILED.');
715 return false;
716 }
717 return $data;
718 }
719}
720?> \ No newline at end of file
diff --git a/inc/3rdparty/humble-http-agent/RollingCurl.php b/inc/3rdparty/humble-http-agent/RollingCurl.php
new file mode 100644
index 00000000..fdd021af
--- /dev/null
+++ b/inc/3rdparty/humble-http-agent/RollingCurl.php
@@ -0,0 +1,402 @@
1<?php
2/*
3Authored by Josh Fraser (www.joshfraser.com)
4Released under Apache License 2.0
5
6Maintained by Alexander Makarov, http://rmcreative.ru/
7
8Modified by Keyvan Minoukadeh for the Five Filters project: http://fivefilters.org
9*/
10
11/**
12 * Class that represent a single curl request
13 */
14class RollingCurlRequest {
15 public $url = false;
16 public $url_original = false; // used for tracking redirects
17 public $method = 'GET';
18 public $post_data = null;
19 public $headers = null;
20 public $options = null;
21
22 /**
23 * @param string $url
24 * @param string $method
25 * @param $post_data
26 * @param $headers
27 * @param $options
28 * @return void
29 */
30 function __construct($url, $method = "GET", $post_data = null, $headers = null, $options = null) {
31 $this->url = $url;
32 $this->url_original = $url;
33 $this->method = $method;
34 $this->post_data = $post_data;
35 $this->headers = $headers;
36 $this->options = $options;
37 }
38
39 /**
40 * @param string $url
41 * @return void
42 */
43 public function set_original_url($url) {
44 $this->url_original = $url;
45 }
46 /**
47 * @return void
48 */
49 public function __destruct() {
50 unset($this->url, $this->url_original, $this->method, $this->post_data, $this->headers, $this->options);
51 }
52}
53
54/**
55 * RollingCurl custom exception
56 */
57class RollingCurlException extends Exception {
58}
59
60/**
61 * Class that holds a rolling queue of curl requests.
62 *
63 * @throws RollingCurlException
64 */
65class RollingCurl implements Countable {
66 /**
67 * @var int
68 *
69 * Window size is the max number of simultaneous connections allowed.
70 *
71 * REMEMBER TO RESPECT THE SERVERS:
72 * Sending too many requests at one time can easily be perceived
73 * as a DOS attack. Increase this window_size if you are making requests
74 * to multiple servers or have permission from the receving server admins.
75 */
76 private $window_size = 5;
77
78 /**
79 * @var float
80 *
81 * Timeout is the timeout used for curl_multi_select.
82 */
83 private $timeout = 10;
84
85 /**
86 * @var string|array
87 *
88 * Callback function to be applied to each result.
89 */
90 private $callback;
91
92 /**
93 * @var array
94 *
95 * Set your base options that you want to be used with EVERY request.
96 */
97 protected $options = array(
98 CURLOPT_SSL_VERIFYPEER => 0,
99 CURLOPT_RETURNTRANSFER => 1,
100 CURLOPT_CONNECTTIMEOUT => 30,
101 CURLOPT_TIMEOUT => 30
102 );
103
104 /**
105 * @var array
106 */
107 private $headers = array();
108
109 /**
110 * @var Request[]
111 *
112 * The request queue
113 */
114 private $requests = array();
115
116 /**
117 * @var RequestMap[]
118 *
119 * Maps handles to request indexes
120 */
121 private $requestMap = array();
122
123 /**
124 * @param $callback
125 * Callback function to be applied to each result.
126 *
127 * Can be specified as 'my_callback_function'
128 * or array($object, 'my_callback_method').
129 *
130 * Function should take three parameters: $response, $info, $request.
131 * $response is response body, $info is additional curl info.
132 * $request is the original request
133 *
134 * @return void
135 */
136 function __construct($callback = null) {
137 $this->callback = $callback;
138 }
139
140 /**
141 * @param string $name
142 * @return mixed
143 */
144 public function __get($name) {
145 return (isset($this->{$name})) ? $this->{$name} : null;
146 }
147
148 /**
149 * @param string $name
150 * @param mixed $value
151 * @return bool
152 */
153 public function __set($name, $value) {
154 // append the base options & headers
155 if ($name == "options" || $name == "headers") {
156 $this->{$name} = $value + $this->{$name};
157 } else {
158 $this->{$name} = $value;
159 }
160 return true;
161 }
162
163 /**
164 * Count number of requests added (Countable interface)
165 *
166 * @return int
167 */
168 public function count() {
169 return count($this->requests);
170 }
171
172 /**
173 * Add a request to the request queue
174 *
175 * @param Request $request
176 * @return bool
177 */
178 public function add($request) {
179 $this->requests[] = $request;
180 return true;
181 }
182
183 /**
184 * Create new Request and add it to the request queue
185 *
186 * @param string $url
187 * @param string $method
188 * @param $post_data
189 * @param $headers
190 * @param $options
191 * @return bool
192 */
193 public function request($url, $method = "GET", $post_data = null, $headers = null, $options = null) {
194 $this->requests[] = new RollingCurlRequest($url, $method, $post_data, $headers, $options);
195 return true;
196 }
197
198 /**
199 * Perform GET request
200 *
201 * @param string $url
202 * @param $headers
203 * @param $options
204 * @return bool
205 */
206 public function get($url, $headers = null, $options = null) {
207 return $this->request($url, "GET", null, $headers, $options);
208 }
209
210 /**
211 * Perform POST request
212 *
213 * @param string $url
214 * @param $post_data
215 * @param $headers
216 * @param $options
217 * @return bool
218 */
219 public function post($url, $post_data = null, $headers = null, $options = null) {
220 return $this->request($url, "POST", $post_data, $headers, $options);
221 }
222
223 /**
224 * Execute processing
225 *
226 * @param int $window_size Max number of simultaneous connections
227 * @return string|bool
228 */
229 public function execute($window_size = null) {
230 // rolling curl window must always be greater than 1
231 if (sizeof($this->requests) == 1) {
232 return $this->single_curl();
233 } else {
234 // start the rolling curl. window_size is the max number of simultaneous connections
235 return $this->rolling_curl($window_size);
236 }
237 }
238
239 /**
240 * Performs a single curl request
241 *
242 * @access private
243 * @return string
244 */
245 private function single_curl() {
246 $ch = curl_init();
247 $request = array_shift($this->requests);
248 $options = $this->get_options($request);
249 curl_setopt_array($ch, $options);
250 $output = curl_exec($ch);
251 $info = curl_getinfo($ch);
252
253 // it's not neccesary to set a callback for one-off requests
254 if ($this->callback) {
255 $callback = $this->callback;
256 if (is_callable($this->callback)) {
257 call_user_func($callback, $output, $info, $request);
258 }
259 }
260 else
261 return $output;
262 return true;
263 }
264
265 /**
266 * Performs multiple curl requests
267 *
268 * @access private
269 * @throws RollingCurlException
270 * @param int $window_size Max number of simultaneous connections
271 * @return bool
272 */
273 private function rolling_curl($window_size = null) {
274 if ($window_size)
275 $this->window_size = $window_size;
276
277 // make sure the rolling window isn't greater than the # of urls
278 if (sizeof($this->requests) < $this->window_size)
279 $this->window_size = sizeof($this->requests);
280
281 if ($this->window_size < 2) {
282 throw new RollingCurlException("Window size must be greater than 1");
283 }
284
285 $master = curl_multi_init();
286
287 // start the first batch of requests
288 for ($i = 0; $i < $this->window_size; $i++) {
289 $ch = curl_init();
290
291 $options = $this->get_options($this->requests[$i]);
292
293 curl_setopt_array($ch, $options);
294 curl_multi_add_handle($master, $ch);
295
296 // Add to our request Maps
297 $key = (string) $ch;
298 $this->requestMap[$key] = $i;
299 }
300
301 do {
302 while (($execrun = curl_multi_exec($master, $running)) == CURLM_CALL_MULTI_PERFORM) ;
303 if ($execrun != CURLM_OK)
304 break;
305 // a request was just completed -- find out which one
306 while ($done = curl_multi_info_read($master)) {
307
308 // get the info and content returned on the request
309 $info = curl_getinfo($done['handle']);
310 $output = curl_multi_getcontent($done['handle']);
311
312 // send the return values to the callback function.
313 $callback = $this->callback;
314 if (is_callable($callback)) {
315 $key = (string) $done['handle'];
316 $request = $this->requests[$this->requestMap[$key]];
317 unset($this->requestMap[$key]);
318 call_user_func($callback, $output, $info, $request);
319 }
320
321 // start a new request (it's important to do this before removing the old one)
322 if ($i < sizeof($this->requests) && isset($this->requests[$i]) && $i < count($this->requests)) {
323 $ch = curl_init();
324 $options = $this->get_options($this->requests[$i]);
325 curl_setopt_array($ch, $options);
326 curl_multi_add_handle($master, $ch);
327
328 // Add to our request Maps
329 $key = (string) $ch;
330 $this->requestMap[$key] = $i;
331 $i++;
332 }
333
334 // remove the curl handle that just completed
335 curl_multi_remove_handle($master, $done['handle']);
336
337 }
338
339 // Block for data in / output; error handling is done by curl_multi_exec
340 //if ($running) curl_multi_select($master, $this->timeout);
341 // removing timeout as it causes problems on Windows with PHP 5.3.5 and Curl 7.20.0
342 if ($running) curl_multi_select($master);
343
344 } while ($running);
345 curl_multi_close($master);
346 return true;
347 }
348
349
350 /**
351 * Helper function to set up a new request by setting the appropriate options
352 *
353 * @access private
354 * @param Request $request
355 * @return array
356 */
357 private function get_options($request) {
358 // options for this entire curl object
359 $options = $this->__get('options');
360 // We're managing reirects in PHP - allows us to intervene and rewrite/block URLs
361 // before the next request goes out.
362 $options[CURLOPT_FOLLOWLOCATION] = 0;
363 $options[CURLOPT_MAXREDIRS] = 0;
364 //if (ini_get('safe_mode') == 'Off' || !ini_get('safe_mode')) {
365 // $options[CURLOPT_FOLLOWLOCATION] = 1;
366 // $options[CURLOPT_MAXREDIRS] = 5;
367 //}
368 $headers = $this->__get('headers');
369 // append custom headers for this specific request
370 if ($request->headers) {
371 $headers = $headers + $request->headers;
372 }
373
374 // append custom options for this specific request
375 if ($request->options) {
376 $options = $request->options + $options;
377 }
378
379 // set the request URL
380 $options[CURLOPT_URL] = $request->url;
381
382 if ($headers) {
383 $options[CURLOPT_HTTPHEADER] = $headers;
384 }
385 // return response headers
386 $options[CURLOPT_HEADER] = 1;
387
388 // send HEAD request?
389 if ($request->method == 'HEAD') {
390 $options[CURLOPT_NOBODY] = 1;
391 }
392
393 return $options;
394 }
395
396 /**
397 * @return void
398 */
399 public function __destruct() {
400 unset($this->window_size, $this->callback, $this->options, $this->headers, $this->requests);
401 }
402} \ No newline at end of file
diff --git a/inc/3rdparty/humble-http-agent/SimplePie_HumbleHttpAgent.php b/inc/3rdparty/humble-http-agent/SimplePie_HumbleHttpAgent.php
new file mode 100644
index 00000000..ce76a929
--- /dev/null
+++ b/inc/3rdparty/humble-http-agent/SimplePie_HumbleHttpAgent.php
@@ -0,0 +1,79 @@
1<?php
2/**
3 * Humble HTTP Agent extension for SimplePie_File
4 *
5 * This class is designed to extend and override SimplePie_File
6 * in order to prevent duplicate HTTP requests being sent out.
7 * The idea is to initialise an instance of Humble HTTP Agent
8 * and attach it, to a static class variable, of this class.
9 * SimplePie will then automatically initialise this class
10 *
11 * @date 2011-02-28
12 */
13
14class SimplePie_HumbleHttpAgent extends SimplePie_File
15{
16 protected static $agent;
17 var $url;
18 var $useragent;
19 var $success = true;
20 var $headers = array();
21 var $body;
22 var $status_code;
23 var $redirects = 0;
24 var $error;
25 var $method = SIMPLEPIE_FILE_SOURCE_NONE;
26
27 public static function set_agent(HumbleHttpAgent $agent) {
28 self::$agent = $agent;
29 }
30
31 public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false) {
32 if (class_exists('idna_convert'))
33 {
34 $idn = new idna_convert();
35 $parsed = SimplePie_Misc::parse_url($url);
36 $url = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']);
37 }
38 $this->url = $url;
39 $this->useragent = $useragent;
40 if (preg_match('/^http(s)?:\/\//i', $url))
41 {
42 if (!is_array($headers))
43 {
44 $headers = array();
45 }
46 $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_CURL;
47 $headers2 = array();
48 foreach ($headers as $key => $value) {
49 $headers2[] = "$key: $value";
50 }
51 //TODO: allow for HTTP headers
52 // curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2);
53
54 $response = self::$agent->get($url);
55
56 if ($response === false || !isset($response['status_code'])) {
57 $this->error = 'failed to fetch URL';
58 $this->success = false;
59 } else {
60 // The extra lines at the end are there to satisfy SimplePie's HTTP parser.
61 // The class expects a full HTTP message, whereas we're giving it only
62 // headers - the new lines indicate the start of the body.
63 $parser = new SimplePie_HTTP_Parser($response['headers']."\r\n\r\n");
64 if ($parser->parse()) {
65 $this->headers = $parser->headers;
66 //$this->body = $parser->body;
67 $this->body = $response['body'];
68 $this->status_code = $parser->status_code;
69 }
70 }
71 }
72 else
73 {
74 $this->error = 'invalid URL';
75 $this->success = false;
76 }
77 }
78}
79?> \ No newline at end of file
diff --git a/inc/3rdparty/simplepie/LICENSE.txt b/inc/3rdparty/simplepie/LICENSE.txt
new file mode 100644
index 00000000..a822a4bd
--- /dev/null
+++ b/inc/3rdparty/simplepie/LICENSE.txt
@@ -0,0 +1,26 @@
1Copyright (c) 2004-2007, Ryan Parman and Geoffrey Sneddon.
2All rights reserved.
3
4Redistribution and use in source and binary forms, with or without modification, are
5permitted provided that the following conditions are met:
6
7 * Redistributions of source code must retain the above copyright notice, this list of
8 conditions and the following disclaimer.
9
10 * Redistributions in binary form must reproduce the above copyright notice, this list
11 of conditions and the following disclaimer in the documentation and/or other materials
12 provided with the distribution.
13
14 * Neither the name of the SimplePie Team nor the names of its contributors may be used
15 to endorse or promote products derived from this software without specific prior
16 written permission.
17
18THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
19OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
21AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file
diff --git a/inc/3rdparty/simplepie/SimplePie.php b/inc/3rdparty/simplepie/SimplePie.php
new file mode 100644
index 00000000..9e07c137
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie.php
@@ -0,0 +1,56 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46/**
47 * SimplePie class.
48 *
49 * Class for backward compatibility.
50 *
51 * @package SimplePie
52 */
53class SimplePie extends SimplePie_Core
54{
55
56} \ No newline at end of file
diff --git a/inc/3rdparty/simplepie/SimplePie/Author.php b/inc/3rdparty/simplepie/SimplePie/Author.php
new file mode 100644
index 00000000..77e5dc58
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Author.php
@@ -0,0 +1,103 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47class SimplePie_Author
48{
49 var $name;
50 var $link;
51 var $email;
52
53 // Constructor, used to input the data
54 public function __construct($name = null, $link = null, $email = null)
55 {
56 $this->name = $name;
57 $this->link = $link;
58 $this->email = $email;
59 }
60
61 public function __toString()
62 {
63 // There is no $this->data here
64 return md5(serialize($this));
65 }
66
67 public function get_name()
68 {
69 if ($this->name !== null)
70 {
71 return $this->name;
72 }
73 else
74 {
75 return null;
76 }
77 }
78
79 public function get_link()
80 {
81 if ($this->link !== null)
82 {
83 return $this->link;
84 }
85 else
86 {
87 return null;
88 }
89 }
90
91 public function get_email()
92 {
93 if ($this->email !== null)
94 {
95 return $this->email;
96 }
97 else
98 {
99 return null;
100 }
101 }
102}
103
diff --git a/inc/3rdparty/simplepie/SimplePie/Cache.php b/inc/3rdparty/simplepie/SimplePie/Cache.php
new file mode 100644
index 00000000..819ddeea
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Cache.php
@@ -0,0 +1,109 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47class SimplePie_Cache
48{
49 /**
50 * Cache handler classes
51 *
52 * These receive 3 parameters to their constructor, as documented in
53 * {@see register()}
54 * @var array
55 */
56 protected static $handlers = array(
57 'mysql' => 'SimplePie_Cache_MySQL',
58 'memcache' => 'SimplePie_Cache_Memcache',
59 );
60
61 /**
62 * Don't call the constructor. Please.
63 */
64 private function __construct() { }
65
66 /**
67 * Create a new SimplePie_Cache object
68 */
69 public static function create($location, $filename, $extension)
70 {
71 $type = explode(':', $location, 2);
72 $type = $type[0];
73 if (!empty(self::$handlers[$type]))
74 {
75 $class = self::$handlers[$type];
76 return new $class($location, $filename, $extension);
77 }
78
79 return new SimplePie_Cache_File($location, $filename, $extension);
80 }
81
82 /**
83 * Register a handler
84 *
85 * @param string $type DSN type to register for
86 * @param string $class Name of handler class. Must implement SimplePie_Cache_Base
87 */
88 public static function register($type, $class)
89 {
90 self::$handlers[$type] = $class;
91 }
92
93 /**
94 * Parse a URL into an array
95 *
96 * @param string $url
97 * @return array
98 */
99 public static function parse_URL($url)
100 {
101 $params = parse_url($url);
102 $params['extras'] = array();
103 if (isset($params['query']))
104 {
105 parse_str($params['query'], $params['extras']);
106 }
107 return $params;
108 }
109}
diff --git a/inc/3rdparty/simplepie/SimplePie/Cache/Base.php b/inc/3rdparty/simplepie/SimplePie/Cache/Base.php
new file mode 100644
index 00000000..e3cfa8af
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Cache/Base.php
@@ -0,0 +1,102 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47interface SimplePie_Cache_Base
48{
49 /**
50 * Feed cache type
51 */
52 const TYPE_FEED = 'spc';
53
54 /**
55 * Image cache type
56 */
57 const TYPE_IMAGE = 'spi';
58
59 /**
60 * Create a new cache object
61 *
62 * @param string $location Location string (from SimplePie::$cache_location)
63 * @param string $name Unique ID for the cache
64 * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data
65 */
66 public function __construct($location, $name, $type);
67
68 /**
69 * Save data to the cache
70 *
71 * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property
72 */
73 public function save($data);
74
75 /**
76 * Retrieve the data saved to the cache
77 *
78 * @return array Data for SimplePie::$data
79 */
80 public function load();
81
82 /**
83 * Retrieve the last modified time for the cache
84 *
85 * @return int Timestamp
86 */
87 public function mtime();
88
89 /**
90 * Set the last modified time to the current time
91 *
92 * @return bool Success status
93 */
94 public function touch();
95
96 /**
97 * Remove the cache
98 *
99 * @return bool Success status
100 */
101 public function unlink();
102}
diff --git a/inc/3rdparty/simplepie/SimplePie/Cache/DB.php b/inc/3rdparty/simplepie/SimplePie/Cache/DB.php
new file mode 100644
index 00000000..9a1f2ffc
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Cache/DB.php
@@ -0,0 +1,124 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46abstract class SimplePie_Cache_DB implements SimplePie_Cache_Base
47{
48 protected static function prepare_simplepie_object_for_cache(&$data)
49 {
50 $items = $data->get_items();
51 $items_by_id = array();
52
53 if (!empty($items))
54 {
55 foreach ($items as $item)
56 {
57 $items_by_id[$item->get_id()] = $item;
58 }
59
60 if (count($items_by_id) !== count($items))
61 {
62 $items_by_id = array();
63 foreach ($items as $item)
64 {
65 $items_by_id[$item->get_id(true)] = $item;
66 }
67 }
68
69 if (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]))
70 {
71 $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0];
72 }
73 elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]))
74 {
75 $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0];
76 }
77 elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]))
78 {
79 $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0];
80 }
81 elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0]))
82 {
83 $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0];
84 }
85 else
86 {
87 $channel = null;
88 }
89
90 if ($channel !== null)
91 {
92 if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry']))
93 {
94 unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry']);
95 }
96 if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry']))
97 {
98 unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry']);
99 }
100 if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']))
101 {
102 unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']);
103 }
104 if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']))
105 {
106 unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']);
107 }
108 if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item']))
109 {
110 unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item']);
111 }
112 }
113 if (isset($data->data['items']))
114 {
115 unset($data->data['items']);
116 }
117 if (isset($data->data['ordered_items']))
118 {
119 unset($data->data['ordered_items']);
120 }
121 }
122 return array(serialize($data->data), $items_by_id);
123 }
124}
diff --git a/inc/3rdparty/simplepie/SimplePie/Cache/File.php b/inc/3rdparty/simplepie/SimplePie/Cache/File.php
new file mode 100644
index 00000000..f496ff50
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Cache/File.php
@@ -0,0 +1,112 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47class SimplePie_Cache_File implements SimplePie_Cache_Base
48{
49 protected $location;
50 protected $filename;
51 protected $extension;
52 protected $name;
53
54 public function __construct($location, $filename, $extension)
55 {
56 $this->location = $location;
57 $this->filename = $filename;
58 $this->extension = $extension;
59 $this->name = "$this->location/$this->filename.$this->extension";
60 }
61
62 public function save($data)
63 {
64 if (file_exists($this->name) && is_writeable($this->name) || file_exists($this->location) && is_writeable($this->location))
65 {
66 if (is_a($data, 'SimplePie'))
67 {
68 $data = $data->data;
69 }
70
71 $data = serialize($data);
72 return (bool) file_put_contents($this->name, $data);
73 }
74 return false;
75 }
76
77 public function load()
78 {
79 if (file_exists($this->name) && is_readable($this->name))
80 {
81 return unserialize(file_get_contents($this->name));
82 }
83 return false;
84 }
85
86 public function mtime()
87 {
88 if (file_exists($this->name))
89 {
90 return filemtime($this->name);
91 }
92 return false;
93 }
94
95 public function touch()
96 {
97 if (file_exists($this->name))
98 {
99 return touch($this->name);
100 }
101 return false;
102 }
103
104 public function unlink()
105 {
106 if (file_exists($this->name))
107 {
108 return unlink($this->name);
109 }
110 return false;
111 }
112}
diff --git a/inc/3rdparty/simplepie/SimplePie/Cache/Memcache.php b/inc/3rdparty/simplepie/SimplePie/Cache/Memcache.php
new file mode 100644
index 00000000..3535fecc
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Cache/Memcache.php
@@ -0,0 +1,118 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46class SimplePie_Cache_Memcache implements SimplePie_Cache_Base
47{
48 protected $cache;
49 protected $options;
50 protected $name;
51
52 public function __construct($url, $filename, $extension)
53 {
54 $this->options = array(
55 'host' => '127.0.0.1',
56 'port' => 11211,
57 'extras' => array(
58 'timeout' => 3600, // one hour
59 'prefix' => 'simplepie_',
60 ),
61 );
62 $this->options = array_merge_recursive($this->options, SimplePie_Cache::parse_URL($url));
63 $this->name = $this->options['extras']['prefix'] . md5("$filename:$extension");
64
65 $this->cache = new Memcache();
66 $this->cache->addServer($this->options['host'], (int) $this->options['port']);
67 }
68
69 public function save($data)
70 {
71 if (is_a($data, 'SimplePie'))
72 {
73 $data = $data->data;
74 }
75 return $this->cache->set($this->name, serialize($data), MEMCACHE_COMPRESSED, (int) $this->options['extras']['timeout']);
76 }
77
78 public function load()
79 {
80 $data = $this->cache->get($this->name);
81
82 if ($data !== false)
83 {
84 return unserialize($data);
85 }
86 return false;
87 }
88
89 public function mtime()
90 {
91 $data = $this->cache->get($this->name);
92
93 if ($data !== false)
94 {
95 // essentially ignore the mtime because Memcache expires on it's own
96 return time();
97 }
98
99 return false;
100 }
101
102 public function touch()
103 {
104 $data = $this->cache->get($this->name);
105
106 if ($data !== false)
107 {
108 return $this->cache->set($this->name, $data, MEMCACHE_COMPRESSED, (int) $this->duration);
109 }
110
111 return false;
112 }
113
114 public function unlink()
115 {
116 return $this->cache->delete($this->name);
117 }
118}
diff --git a/inc/3rdparty/simplepie/SimplePie/Cache/MySQL.php b/inc/3rdparty/simplepie/SimplePie/Cache/MySQL.php
new file mode 100644
index 00000000..84b2cb6b
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Cache/MySQL.php
@@ -0,0 +1,378 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47class SimplePie_Cache_MySQL extends SimplePie_Cache_DB
48{
49 protected $mysql;
50 protected $options;
51 protected $id;
52
53 public function __construct($url, $name, $extension)
54 {
55 $this->options = array(
56 'user' => null,
57 'pass' => null,
58 'host' => '127.0.0.1',
59 'port' => '3306',
60 'path' => '',
61 'extras' => array(
62 'prefix' => '',
63 ),
64 );
65 $this->options = array_merge_recursive($this->options, SimplePie_Cache::parse_URL($url));
66
67 // Path is prefixed with a "/"
68 $this->options['dbname'] = substr($this->options['path'], 1);
69
70 try
71 {
72 $this->mysql = new PDO("mysql:dbname={$this->options['dbname']};host={$this->options['host']};port={$this->options['port']}", $this->options['user'], $this->options['pass'], array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));
73 }
74 catch (PDOException $e)
75 {
76 $this->mysql = null;
77 return;
78 }
79
80 $this->id = $name . $extension;
81
82 if (!$query = $this->mysql->query('SHOW TABLES'))
83 {
84 $this->mysql = null;
85 return;
86 }
87
88 $db = array();
89 while ($row = $query->fetchColumn())
90 {
91 $db[] = $row;
92 }
93
94 if (!in_array($this->options['extras']['prefix'] . 'cache_data', $db))
95 {
96 $query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'cache_data` (`id` TEXT CHARACTER SET utf8 NOT NULL, `items` SMALLINT NOT NULL DEFAULT 0, `data` BLOB NOT NULL, `mtime` INT UNSIGNED NOT NULL, UNIQUE (`id`(125)))');
97 if ($query === false)
98 {
99 $this->mysql = null;
100 }
101 }
102
103 if (!in_array($this->options['extras']['prefix'] . 'items', $db))
104 {
105 $query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'items` (`feed_id` TEXT CHARACTER SET utf8 NOT NULL, `id` TEXT CHARACTER SET utf8 NOT NULL, `data` TEXT CHARACTER SET utf8 NOT NULL, `posted` INT UNSIGNED NOT NULL, INDEX `feed_id` (`feed_id`(125)))');
106 if ($query === false)
107 {
108 $this->mysql = null;
109 }
110 }
111 }
112
113 public function save($data)
114 {
115 if ($this->mysql === null)
116 {
117 return false;
118 }
119
120 if (is_a($data, 'SimplePie'))
121 {
122 $data = clone $data;
123
124 $prepared = self::prepare_simplepie_object_for_cache($data);
125
126 $query = $this->mysql->prepare('SELECT COUNT(*) FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed');
127 $query->bindValue(':feed', $this->id);
128 if ($query->execute())
129 {
130 if ($query->fetchColumn() > 0)
131 {
132 $items = count($prepared[1]);
133 if ($items)
134 {
135 $sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = :items, `data` = :data, `mtime` = :time WHERE `id` = :feed';
136 $query = $this->mysql->prepare($sql);
137 $query->bindValue(':items', $items);
138 }
139 else
140 {
141 $sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `data` = :data, `mtime` = :time WHERE `id` = :feed';
142 $query = $this->mysql->prepare($sql);
143 }
144
145 $query->bindValue(':data', $prepared[0]);
146 $query->bindValue(':time', time());
147 $query->bindValue(':feed', $this->id);
148 if (!$query->execute())
149 {
150 return false;
151 }
152 }
153 else
154 {
155 $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:feed, :count, :data, :time)');
156 $query->bindValue(':feed', $this->id);
157 $query->bindValue(':count', count($prepared[1]));
158 $query->bindValue(':data', $prepared[0]);
159 $query->bindValue(':time', time());
160 if (!$query->execute())
161 {
162 return false;
163 }
164 }
165
166 $ids = array_keys($prepared[1]);
167 if (!empty($ids))
168 {
169 foreach ($ids as $id)
170 {
171 $database_ids[] = $this->mysql->quote($id);
172 }
173
174 $query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `id` = ' . implode(' OR `id` = ', $database_ids) . ' AND `feed_id` = :feed');
175 $query->bindValue(':feed', $this->id);
176
177 if ($query->execute())
178 {
179 $existing_ids = array();
180 while ($row = $query->fetchColumn())
181 {
182 $existing_ids[] = $row;
183 }
184
185 $new_ids = array_diff($ids, $existing_ids);
186
187 foreach ($new_ids as $new_id)
188 {
189 if (!($date = $prepared[1][$new_id]->get_date('U')))
190 {
191 $date = time();
192 }
193
194 $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'items` (`feed_id`, `id`, `data`, `posted`) VALUES(:feed, :id, :data, :date)');
195 $query->bindValue(':feed', $this->id);
196 $query->bindValue(':id', $new_id);
197 $query->bindValue(':data', serialize($prepared[1][$new_id]->data));
198 $query->bindValue(':date', $date);
199 if (!$query->execute())
200 {
201 return false;
202 }
203 }
204 return true;
205 }
206 }
207 else
208 {
209 return true;
210 }
211 }
212 }
213 else
214 {
215 $query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed');
216 $query->bindValue(':feed', $this->id);
217 if ($query->execute())
218 {
219 if ($query->rowCount() > 0)
220 {
221 $query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = 0, `data` = :data, `mtime` = :time WHERE `id` = :feed');
222 $query->bindValue(':data', serialize($data));
223 $query->bindValue(':time', time());
224 $query->bindValue(':feed', $this->id);
225 if ($this->execute())
226 {
227 return true;
228 }
229 }
230 else
231 {
232 $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:id, 0, :data, :time)');
233 $query->bindValue(':id', $this->id);
234 $query->bindValue(':data', serialize($data));
235 $query->bindValue(':time', time());
236 if ($query->execute())
237 {
238 return true;
239 }
240 }
241 }
242 }
243 return false;
244 }
245
246 public function load()
247 {
248 if ($this->mysql === null)
249 {
250 return false;
251 }
252
253 $query = $this->mysql->prepare('SELECT `items`, `data` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
254 $query->bindValue(':id', $this->id);
255 if ($query->execute() && ($row = $query->fetch()))
256 {
257 $data = unserialize($row[1]);
258
259 if (isset($this->options['items'][0]))
260 {
261 $items = (int) $this->options['items'][0];
262 }
263 else
264 {
265 $items = (int) $row[0];
266 }
267
268 if ($items !== 0)
269 {
270 if (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]))
271 {
272 $feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0];
273 }
274 elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]))
275 {
276 $feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0];
277 }
278 elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]))
279 {
280 $feed =& $data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0];
281 }
282 elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]))
283 {
284 $feed =& $data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0];
285 }
286 else
287 {
288 $feed = null;
289 }
290
291 if ($feed !== null)
292 {
293 $sql = 'SELECT `data` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :feed ORDER BY `posted` DESC';
294 if ($items > 0)
295 {
296 $sql .= ' LIMIT ' . $items;
297 }
298
299 $query = $this->mysql->prepare($sql);
300 $query->bindValue(':feed', $this->id);
301 if ($query->execute())
302 {
303 while ($row = $query->fetchColumn())
304 {
305 $feed['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry'][] = unserialize($row);
306 }
307 }
308 else
309 {
310 return false;
311 }
312 }
313 }
314 return $data;
315 }
316 return false;
317 }
318
319 public function mtime()
320 {
321 if ($this->mysql === null)
322 {
323 return false;
324 }
325
326 $query = $this->mysql->prepare('SELECT `mtime` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
327 $query->bindValue(':id', $this->id);
328 if ($query->execute() && ($time = $query->fetchColumn()))
329 {
330 return $time;
331 }
332 else
333 {
334 return false;
335 }
336 }
337
338 public function touch()
339 {
340 if ($this->mysql === null)
341 {
342 return false;
343 }
344
345 $query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `mtime` = :time WHERE `id` = :id');
346 $query->bindValue(':time', time());
347 $query->bindValue(':id', $this->id);
348 if ($query->execute() && $query->rowCount() > 0)
349 {
350 return true;
351 }
352 else
353 {
354 return false;
355 }
356 }
357
358 public function unlink()
359 {
360 if ($this->mysql === null)
361 {
362 return false;
363 }
364
365 $query = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id');
366 $query->bindValue(':id', $this->id);
367 $query2 = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :id');
368 $query2->bindValue(':id', $this->id);
369 if ($query->execute() && $query2->execute())
370 {
371 return true;
372 }
373 else
374 {
375 return false;
376 }
377 }
378}
diff --git a/inc/3rdparty/simplepie/SimplePie/Caption.php b/inc/3rdparty/simplepie/SimplePie/Caption.php
new file mode 100644
index 00000000..df6fedc5
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Caption.php
@@ -0,0 +1,131 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47class SimplePie_Caption
48{
49 var $type;
50 var $lang;
51 var $startTime;
52 var $endTime;
53 var $text;
54
55 // Constructor, used to input the data
56 public function __construct($type = null, $lang = null, $startTime = null, $endTime = null, $text = null)
57 {
58 $this->type = $type;
59 $this->lang = $lang;
60 $this->startTime = $startTime;
61 $this->endTime = $endTime;
62 $this->text = $text;
63 }
64
65 public function __toString()
66 {
67 // There is no $this->data here
68 return md5(serialize($this));
69 }
70
71 public function get_endtime()
72 {
73 if ($this->endTime !== null)
74 {
75 return $this->endTime;
76 }
77 else
78 {
79 return null;
80 }
81 }
82
83 public function get_language()
84 {
85 if ($this->lang !== null)
86 {
87 return $this->lang;
88 }
89 else
90 {
91 return null;
92 }
93 }
94
95 public function get_starttime()
96 {
97 if ($this->startTime !== null)
98 {
99 return $this->startTime;
100 }
101 else
102 {
103 return null;
104 }
105 }
106
107 public function get_text()
108 {
109 if ($this->text !== null)
110 {
111 return $this->text;
112 }
113 else
114 {
115 return null;
116 }
117 }
118
119 public function get_type()
120 {
121 if ($this->type !== null)
122 {
123 return $this->type;
124 }
125 else
126 {
127 return null;
128 }
129 }
130}
131
diff --git a/inc/3rdparty/simplepie/SimplePie/Category.php b/inc/3rdparty/simplepie/SimplePie/Category.php
new file mode 100644
index 00000000..ed4b842d
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Category.php
@@ -0,0 +1,103 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47class SimplePie_Category
48{
49 var $term;
50 var $scheme;
51 var $label;
52
53 // Constructor, used to input the data
54 public function __construct($term = null, $scheme = null, $label = null)
55 {
56 $this->term = $term;
57 $this->scheme = $scheme;
58 $this->label = $label;
59 }
60
61 public function __toString()
62 {
63 // There is no $this->data here
64 return md5(serialize($this));
65 }
66
67 public function get_term()
68 {
69 if ($this->term !== null)
70 {
71 return $this->term;
72 }
73 else
74 {
75 return null;
76 }
77 }
78
79 public function get_scheme()
80 {
81 if ($this->scheme !== null)
82 {
83 return $this->scheme;
84 }
85 else
86 {
87 return null;
88 }
89 }
90
91 public function get_label()
92 {
93 if ($this->label !== null)
94 {
95 return $this->label;
96 }
97 else
98 {
99 return $this->get_term();
100 }
101 }
102}
103
diff --git a/inc/3rdparty/simplepie/SimplePie/Content/Type/Sniffer.php b/inc/3rdparty/simplepie/SimplePie/Content/Type/Sniffer.php
new file mode 100644
index 00000000..7be71374
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Content/Type/Sniffer.php
@@ -0,0 +1,325 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47/**
48 * Content-type sniffing
49 *
50 * Based on the rules in http://tools.ietf.org/html/draft-abarth-mime-sniff-06
51 * @package SimplePie
52 */
53class SimplePie_Content_Type_Sniffer
54{
55 /**
56 * File object
57 *
58 * @var SimplePie_File
59 */
60 var $file;
61
62 /**
63 * Create an instance of the class with the input file
64 *
65 * @param SimplePie_Content_Type_Sniffer $file Input file
66 */
67 public function __construct($file)
68 {
69 $this->file = $file;
70 }
71
72 /**
73 * Get the Content-Type of the specified file
74 *
75 * @return string Actual Content-Type
76 */
77 public function get_type()
78 {
79 if (isset($this->file->headers['content-type']))
80 {
81 if (!isset($this->file->headers['content-encoding'])
82 && ($this->file->headers['content-type'] === 'text/plain'
83 || $this->file->headers['content-type'] === 'text/plain; charset=ISO-8859-1'
84 || $this->file->headers['content-type'] === 'text/plain; charset=iso-8859-1'
85 || $this->file->headers['content-type'] === 'text/plain; charset=UTF-8'))
86 {
87 return $this->text_or_binary();
88 }
89
90 if (($pos = strpos($this->file->headers['content-type'], ';')) !== false)
91 {
92 $official = substr($this->file->headers['content-type'], 0, $pos);
93 }
94 else
95 {
96 $official = $this->file->headers['content-type'];
97 }
98 $official = trim(strtolower($official));
99
100 if ($official === 'unknown/unknown'
101 || $official === 'application/unknown')
102 {
103 return $this->unknown();
104 }
105 elseif (substr($official, -4) === '+xml'
106 || $official === 'text/xml'
107 || $official === 'application/xml')
108 {
109 return $official;
110 }
111 elseif (substr($official, 0, 6) === 'image/')
112 {
113 if ($return = $this->image())
114 {
115 return $return;
116 }
117 else
118 {
119 return $official;
120 }
121 }
122 elseif ($official === 'text/html')
123 {
124 return $this->feed_or_html();
125 }
126 else
127 {
128 return $official;
129 }
130 }
131 else
132 {
133 return $this->unknown();
134 }
135 }
136
137 /**
138 * Sniff text or binary
139 *
140 * @return string Actual Content-Type
141 */
142 public function text_or_binary()
143 {
144 if (substr($this->file->body, 0, 2) === "\xFE\xFF"
145 || substr($this->file->body, 0, 2) === "\xFF\xFE"
146 || substr($this->file->body, 0, 4) === "\x00\x00\xFE\xFF"
147 || substr($this->file->body, 0, 3) === "\xEF\xBB\xBF")
148 {
149 return 'text/plain';
150 }
151 elseif (preg_match('/[\x00-\x08\x0E-\x1A\x1C-\x1F]/', $this->file->body))
152 {
153 return 'application/octect-stream';
154 }
155 else
156 {
157 return 'text/plain';
158 }
159 }
160
161 /**
162 * Sniff unknown
163 *
164 * @return string Actual Content-Type
165 */
166 public function unknown()
167 {
168 $ws = strspn($this->file->body, "\x09\x0A\x0B\x0C\x0D\x20");
169 if (strtolower(substr($this->file->body, $ws, 14)) === '<!doctype html'
170 || strtolower(substr($this->file->body, $ws, 5)) === '<html'
171 || strtolower(substr($this->file->body, $ws, 7)) === '<script')
172 {
173 return 'text/html';
174 }
175 elseif (substr($this->file->body, 0, 5) === '%PDF-')
176 {
177 return 'application/pdf';
178 }
179 elseif (substr($this->file->body, 0, 11) === '%!PS-Adobe-')
180 {
181 return 'application/postscript';
182 }
183 elseif (substr($this->file->body, 0, 6) === 'GIF87a'
184 || substr($this->file->body, 0, 6) === 'GIF89a')
185 {
186 return 'image/gif';
187 }
188 elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A")
189 {
190 return 'image/png';
191 }
192 elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF")
193 {
194 return 'image/jpeg';
195 }
196 elseif (substr($this->file->body, 0, 2) === "\x42\x4D")
197 {
198 return 'image/bmp';
199 }
200 elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00")
201 {
202 return 'image/vnd.microsoft.icon';
203 }
204 else
205 {
206 return $this->text_or_binary();
207 }
208 }
209
210 /**
211 * Sniff images
212 *
213 * @return string Actual Content-Type
214 */
215 public function image()
216 {
217 if (substr($this->file->body, 0, 6) === 'GIF87a'
218 || substr($this->file->body, 0, 6) === 'GIF89a')
219 {
220 return 'image/gif';
221 }
222 elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A")
223 {
224 return 'image/png';
225 }
226 elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF")
227 {
228 return 'image/jpeg';
229 }
230 elseif (substr($this->file->body, 0, 2) === "\x42\x4D")
231 {
232 return 'image/bmp';
233 }
234 elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00")
235 {
236 return 'image/vnd.microsoft.icon';
237 }
238 else
239 {
240 return false;
241 }
242 }
243
244 /**
245 * Sniff HTML
246 *
247 * @return string Actual Content-Type
248 */
249 public function feed_or_html()
250 {
251 $len = strlen($this->file->body);
252 $pos = strspn($this->file->body, "\x09\x0A\x0D\x20");
253
254 while ($pos < $len)
255 {
256 switch ($this->file->body[$pos])
257 {
258 case "\x09":
259 case "\x0A":
260 case "\x0D":
261 case "\x20":
262 $pos += strspn($this->file->body, "\x09\x0A\x0D\x20", $pos);
263 continue 2;
264
265 case '<':
266 $pos++;
267 break;
268
269 default:
270 return 'text/html';
271 }
272
273 if (substr($this->file->body, $pos, 3) === '!--')
274 {
275 $pos += 3;
276 if ($pos < $len && ($pos = strpos($this->file->body, '-->', $pos)) !== false)
277 {
278 $pos += 3;
279 }
280 else
281 {
282 return 'text/html';
283 }
284 }
285 elseif (substr($this->file->body, $pos, 1) === '!')
286 {
287 if ($pos < $len && ($pos = strpos($this->file->body, '>', $pos)) !== false)
288 {
289 $pos++;
290 }
291 else
292 {
293 return 'text/html';
294 }
295 }
296 elseif (substr($this->file->body, $pos, 1) === '?')
297 {
298 if ($pos < $len && ($pos = strpos($this->file->body, '?>', $pos)) !== false)
299 {
300 $pos += 2;
301 }
302 else
303 {
304 return 'text/html';
305 }
306 }
307 elseif (substr($this->file->body, $pos, 3) === 'rss'
308 || substr($this->file->body, $pos, 7) === 'rdf:RDF')
309 {
310 return 'application/rss+xml';
311 }
312 elseif (substr($this->file->body, $pos, 4) === 'feed')
313 {
314 return 'application/atom+xml';
315 }
316 else
317 {
318 return 'text/html';
319 }
320 }
321
322 return 'text/html';
323 }
324}
325
diff --git a/inc/3rdparty/simplepie/SimplePie/Copyright.php b/inc/3rdparty/simplepie/SimplePie/Copyright.php
new file mode 100644
index 00000000..cc16f9c4
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Copyright.php
@@ -0,0 +1,89 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47class SimplePie_Copyright
48{
49 var $url;
50 var $label;
51
52 // Constructor, used to input the data
53 public function __construct($url = null, $label = null)
54 {
55 $this->url = $url;
56 $this->label = $label;
57 }
58
59 public function __toString()
60 {
61 // There is no $this->data here
62 return md5(serialize($this));
63 }
64
65 public function get_url()
66 {
67 if ($this->url !== null)
68 {
69 return $this->url;
70 }
71 else
72 {
73 return null;
74 }
75 }
76
77 public function get_attribution()
78 {
79 if ($this->label !== null)
80 {
81 return $this->label;
82 }
83 else
84 {
85 return null;
86 }
87 }
88}
89
diff --git a/inc/3rdparty/simplepie/SimplePie/Core.php b/inc/3rdparty/simplepie/SimplePie/Core.php
new file mode 100644
index 00000000..82a30f2b
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Core.php
@@ -0,0 +1,2838 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46/**
47 * SimplePie Name
48 */
49define('SIMPLEPIE_NAME', 'SimplePie');
50
51/**
52 * SimplePie Version
53 */
54define('SIMPLEPIE_VERSION', '1.3-dev');
55
56/**
57 * SimplePie Build
58 * @todo Hardcode for release (there's no need to have to call SimplePie_Misc::get_build() only every load of simplepie.inc)
59 */
60define('SIMPLEPIE_BUILD', gmdate('YmdHis', SimplePie_Misc::get_build()));
61
62/**
63 * SimplePie Website URL
64 */
65define('SIMPLEPIE_URL', 'http://simplepie.org');
66
67/**
68 * SimplePie Useragent
69 * @see SimplePie::set_useragent()
70 */
71define('SIMPLEPIE_USERAGENT', SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION . ' (Feed Parser; ' . SIMPLEPIE_URL . '; Allow like Gecko) Build/' . SIMPLEPIE_BUILD);
72
73/**
74 * SimplePie Linkback
75 */
76define('SIMPLEPIE_LINKBACK', '<a href="' . SIMPLEPIE_URL . '" title="' . SIMPLEPIE_NAME . ' ' . SIMPLEPIE_VERSION . '">' . SIMPLEPIE_NAME . '</a>');
77
78/**
79 * No Autodiscovery
80 * @see SimplePie::set_autodiscovery_level()
81 */
82define('SIMPLEPIE_LOCATOR_NONE', 0);
83
84/**
85 * Feed Link Element Autodiscovery
86 * @see SimplePie::set_autodiscovery_level()
87 */
88define('SIMPLEPIE_LOCATOR_AUTODISCOVERY', 1);
89
90/**
91 * Local Feed Extension Autodiscovery
92 * @see SimplePie::set_autodiscovery_level()
93 */
94define('SIMPLEPIE_LOCATOR_LOCAL_EXTENSION', 2);
95
96/**
97 * Local Feed Body Autodiscovery
98 * @see SimplePie::set_autodiscovery_level()
99 */
100define('SIMPLEPIE_LOCATOR_LOCAL_BODY', 4);
101
102/**
103 * Remote Feed Extension Autodiscovery
104 * @see SimplePie::set_autodiscovery_level()
105 */
106define('SIMPLEPIE_LOCATOR_REMOTE_EXTENSION', 8);
107
108/**
109 * Remote Feed Body Autodiscovery
110 * @see SimplePie::set_autodiscovery_level()
111 */
112define('SIMPLEPIE_LOCATOR_REMOTE_BODY', 16);
113
114/**
115 * All Feed Autodiscovery
116 * @see SimplePie::set_autodiscovery_level()
117 */
118define('SIMPLEPIE_LOCATOR_ALL', 31);
119
120/**
121 * No known feed type
122 */
123define('SIMPLEPIE_TYPE_NONE', 0);
124
125/**
126 * RSS 0.90
127 */
128define('SIMPLEPIE_TYPE_RSS_090', 1);
129
130/**
131 * RSS 0.91 (Netscape)
132 */
133define('SIMPLEPIE_TYPE_RSS_091_NETSCAPE', 2);
134
135/**
136 * RSS 0.91 (Userland)
137 */
138define('SIMPLEPIE_TYPE_RSS_091_USERLAND', 4);
139
140/**
141 * RSS 0.91 (both Netscape and Userland)
142 */
143define('SIMPLEPIE_TYPE_RSS_091', 6);
144
145/**
146 * RSS 0.92
147 */
148define('SIMPLEPIE_TYPE_RSS_092', 8);
149
150/**
151 * RSS 0.93
152 */
153define('SIMPLEPIE_TYPE_RSS_093', 16);
154
155/**
156 * RSS 0.94
157 */
158define('SIMPLEPIE_TYPE_RSS_094', 32);
159
160/**
161 * RSS 1.0
162 */
163define('SIMPLEPIE_TYPE_RSS_10', 64);
164
165/**
166 * RSS 2.0
167 */
168define('SIMPLEPIE_TYPE_RSS_20', 128);
169
170/**
171 * RDF-based RSS
172 */
173define('SIMPLEPIE_TYPE_RSS_RDF', 65);
174
175/**
176 * Non-RDF-based RSS (truly intended as syndication format)
177 */
178define('SIMPLEPIE_TYPE_RSS_SYNDICATION', 190);
179
180/**
181 * All RSS
182 */
183define('SIMPLEPIE_TYPE_RSS_ALL', 255);
184
185/**
186 * Atom 0.3
187 */
188define('SIMPLEPIE_TYPE_ATOM_03', 256);
189
190/**
191 * Atom 1.0
192 */
193define('SIMPLEPIE_TYPE_ATOM_10', 512);
194
195/**
196 * All Atom
197 */
198define('SIMPLEPIE_TYPE_ATOM_ALL', 768);
199
200/**
201 * All feed types
202 */
203define('SIMPLEPIE_TYPE_ALL', 1023);
204
205/**
206 * No construct
207 */
208define('SIMPLEPIE_CONSTRUCT_NONE', 0);
209
210/**
211 * Text construct
212 */
213define('SIMPLEPIE_CONSTRUCT_TEXT', 1);
214
215/**
216 * HTML construct
217 */
218define('SIMPLEPIE_CONSTRUCT_HTML', 2);
219
220/**
221 * XHTML construct
222 */
223define('SIMPLEPIE_CONSTRUCT_XHTML', 4);
224
225/**
226 * base64-encoded construct
227 */
228define('SIMPLEPIE_CONSTRUCT_BASE64', 8);
229
230/**
231 * IRI construct
232 */
233define('SIMPLEPIE_CONSTRUCT_IRI', 16);
234
235/**
236 * A construct that might be HTML
237 */
238define('SIMPLEPIE_CONSTRUCT_MAYBE_HTML', 32);
239
240/**
241 * All constructs
242 */
243define('SIMPLEPIE_CONSTRUCT_ALL', 63);
244
245/**
246 * Don't change case
247 */
248define('SIMPLEPIE_SAME_CASE', 1);
249
250/**
251 * Change to lowercase
252 */
253define('SIMPLEPIE_LOWERCASE', 2);
254
255/**
256 * Change to uppercase
257 */
258define('SIMPLEPIE_UPPERCASE', 4);
259
260/**
261 * PCRE for HTML attributes
262 */
263define('SIMPLEPIE_PCRE_HTML_ATTRIBUTE', '((?:[\x09\x0A\x0B\x0C\x0D\x20]+[^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?)*)[\x09\x0A\x0B\x0C\x0D\x20]*');
264
265/**
266 * PCRE for XML attributes
267 */
268define('SIMPLEPIE_PCRE_XML_ATTRIBUTE', '((?:\s+(?:(?:[^\s:]+:)?[^\s:]+)\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'))*)\s*');
269
270/**
271 * XML Namespace
272 */
273define('SIMPLEPIE_NAMESPACE_XML', 'http://www.w3.org/XML/1998/namespace');
274
275/**
276 * Atom 1.0 Namespace
277 */
278define('SIMPLEPIE_NAMESPACE_ATOM_10', 'http://www.w3.org/2005/Atom');
279
280/**
281 * Atom 0.3 Namespace
282 */
283define('SIMPLEPIE_NAMESPACE_ATOM_03', 'http://purl.org/atom/ns#');
284
285/**
286 * RDF Namespace
287 */
288define('SIMPLEPIE_NAMESPACE_RDF', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
289
290/**
291 * RSS 0.90 Namespace
292 */
293define('SIMPLEPIE_NAMESPACE_RSS_090', 'http://my.netscape.com/rdf/simple/0.9/');
294
295/**
296 * RSS 1.0 Namespace
297 */
298define('SIMPLEPIE_NAMESPACE_RSS_10', 'http://purl.org/rss/1.0/');
299
300/**
301 * RSS 1.0 Content Module Namespace
302 */
303define('SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT', 'http://purl.org/rss/1.0/modules/content/');
304
305/**
306 * RSS 2.0 Namespace
307 * (Stupid, I know, but I'm certain it will confuse people less with support.)
308 */
309define('SIMPLEPIE_NAMESPACE_RSS_20', '');
310
311/**
312 * DC 1.0 Namespace
313 */
314define('SIMPLEPIE_NAMESPACE_DC_10', 'http://purl.org/dc/elements/1.0/');
315
316/**
317 * DC 1.1 Namespace
318 */
319define('SIMPLEPIE_NAMESPACE_DC_11', 'http://purl.org/dc/elements/1.1/');
320
321/**
322 * W3C Basic Geo (WGS84 lat/long) Vocabulary Namespace
323 */
324define('SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO', 'http://www.w3.org/2003/01/geo/wgs84_pos#');
325
326/**
327 * GeoRSS Namespace
328 */
329define('SIMPLEPIE_NAMESPACE_GEORSS', 'http://www.georss.org/georss');
330
331/**
332 * Media RSS Namespace
333 */
334define('SIMPLEPIE_NAMESPACE_MEDIARSS', 'http://search.yahoo.com/mrss/');
335
336/**
337 * Wrong Media RSS Namespace. Caused by a long-standing typo in the spec.
338 */
339define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG', 'http://search.yahoo.com/mrss');
340
341/**
342 * Wrong Media RSS Namespace #2. New namespace introduced in Media RSS 1.5.
343 */
344define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG2', 'http://video.search.yahoo.com/mrss');
345
346/**
347 * Wrong Media RSS Namespace #3. A possible typo of the Media RSS 1.5 namespace.
348 */
349define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG3', 'http://video.search.yahoo.com/mrss/');
350
351/**
352 * Wrong Media RSS Namespace #4. New spec location after the RSS Advisory Board takes it over, but not a valid namespace.
353 */
354define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG4', 'http://www.rssboard.org/media-rss');
355
356/**
357 * Wrong Media RSS Namespace #5. A possible typo of the RSS Advisory Board URL.
358 */
359define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG5', 'http://www.rssboard.org/media-rss/');
360
361/**
362 * iTunes RSS Namespace
363 */
364define('SIMPLEPIE_NAMESPACE_ITUNES', 'http://www.itunes.com/dtds/podcast-1.0.dtd');
365
366/**
367 * XHTML Namespace
368 */
369define('SIMPLEPIE_NAMESPACE_XHTML', 'http://www.w3.org/1999/xhtml');
370
371/**
372 * IANA Link Relations Registry
373 */
374define('SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY', 'http://www.iana.org/assignments/relation/');
375
376/**
377 * Whether we're running on PHP5
378 */
379define('SIMPLEPIE_PHP5', version_compare(PHP_VERSION, '5.0.0', '>='));
380
381/**
382 * No file source
383 */
384define('SIMPLEPIE_FILE_SOURCE_NONE', 0);
385
386/**
387 * Remote file source
388 */
389define('SIMPLEPIE_FILE_SOURCE_REMOTE', 1);
390
391/**
392 * Local file source
393 */
394define('SIMPLEPIE_FILE_SOURCE_LOCAL', 2);
395
396/**
397 * fsockopen() file source
398 */
399define('SIMPLEPIE_FILE_SOURCE_FSOCKOPEN', 4);
400
401/**
402 * cURL file source
403 */
404define('SIMPLEPIE_FILE_SOURCE_CURL', 8);
405
406/**
407 * file_get_contents() file source
408 */
409define('SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS', 16);
410
411
412
413/**
414 * SimplePie
415 *
416 * @package SimplePie
417 */
418class SimplePie_Core
419{
420 /**
421 * @var array Raw data
422 * @access private
423 */
424 public $data = array();
425
426 /**
427 * @var mixed Error string
428 * @access private
429 */
430 public $error;
431
432 /**
433 * @var object Instance of SimplePie_Sanitize (or other class)
434 * @see SimplePie::set_sanitize_class()
435 * @access private
436 */
437 public $sanitize;
438
439 /**
440 * @var string SimplePie Useragent
441 * @see SimplePie::set_useragent()
442 * @access private
443 */
444 public $useragent = SIMPLEPIE_USERAGENT;
445
446 /**
447 * @var string Feed URL
448 * @see SimplePie::set_feed_url()
449 * @access private
450 */
451 public $feed_url;
452
453 /**
454 * @var object Instance of SimplePie_File to use as a feed
455 * @see SimplePie::set_file()
456 * @access private
457 */
458 public $file;
459
460 /**
461 * @var string Raw feed data
462 * @see SimplePie::set_raw_data()
463 * @access private
464 */
465 public $raw_data;
466
467 /**
468 * @var int Timeout for fetching remote files
469 * @see SimplePie::set_timeout()
470 * @access private
471 */
472 public $timeout = 10;
473
474 /**
475 * @var bool Forces fsockopen() to be used for remote files instead
476 * of cURL, even if a new enough version is installed
477 * @see SimplePie::force_fsockopen()
478 * @access private
479 */
480 public $force_fsockopen = false;
481
482 /**
483 * @var bool Force the given data/URL to be treated as a feed no matter what
484 * it appears like
485 * @see SimplePie::force_feed()
486 * @access private
487 */
488 public $force_feed = false;
489
490 /**
491 * @var bool Enable/Disable XML dump
492 * @see SimplePie::enable_xml_dump()
493 * @access private
494 */
495 public $xml_dump = false;
496
497 /**
498 * @var bool Enable/Disable Caching
499 * @see SimplePie::enable_cache()
500 * @access private
501 */
502 public $cache = true;
503
504 /**
505 * @var int Cache duration (in seconds)
506 * @see SimplePie::set_cache_duration()
507 * @access private
508 */
509 public $cache_duration = 3600;
510
511 /**
512 * @var int Auto-discovery cache duration (in seconds)
513 * @see SimplePie::set_autodiscovery_cache_duration()
514 * @access private
515 */
516 public $autodiscovery_cache_duration = 604800; // 7 Days.
517
518 /**
519 * @var string Cache location (relative to executing script)
520 * @see SimplePie::set_cache_location()
521 * @access private
522 */
523 public $cache_location = './cache';
524
525 /**
526 * @var string Function that creates the cache filename
527 * @see SimplePie::set_cache_name_function()
528 * @access private
529 */
530 public $cache_name_function = 'md5';
531
532 /**
533 * @var bool Reorder feed by date descending
534 * @see SimplePie::enable_order_by_date()
535 * @access private
536 */
537 public $order_by_date = true;
538
539 /**
540 * @var mixed Force input encoding to be set to the follow value
541 * (false, or anything type-cast to false, disables this feature)
542 * @see SimplePie::set_input_encoding()
543 * @access private
544 */
545 public $input_encoding = false;
546
547 /**
548 * @var int Feed Autodiscovery Level
549 * @see SimplePie::set_autodiscovery_level()
550 * @access private
551 */
552 public $autodiscovery = SIMPLEPIE_LOCATOR_ALL;
553
554 /**
555 * @var string Class used for caching feeds
556 * @see SimplePie::set_cache_class()
557 * @access private
558 */
559 public $cache_class = 'SimplePie_Cache';
560
561 /**
562 * @var string Class used for locating feeds
563 * @see SimplePie::set_locator_class()
564 * @access private
565 */
566 public $locator_class = 'SimplePie_Locator';
567
568 /**
569 * @var string Class used for parsing feeds
570 * @see SimplePie::set_parser_class()
571 * @access private
572 */
573 public $parser_class = 'SimplePie_Parser';
574
575 /**
576 * @var string Class used for fetching feeds
577 * @see SimplePie::set_file_class()
578 * @access private
579 */
580 public $file_class = 'SimplePie_File';
581
582 /**
583 * @var string Class used for items
584 * @see SimplePie::set_item_class()
585 * @access private
586 */
587 public $item_class = 'SimplePie_Item';
588
589 /**
590 * @var string Class used for authors
591 * @see SimplePie::set_author_class()
592 * @access private
593 */
594 public $author_class = 'SimplePie_Author';
595
596 /**
597 * @var string Class used for categories
598 * @see SimplePie::set_category_class()
599 * @access private
600 */
601 public $category_class = 'SimplePie_Category';
602
603 /**
604 * @var string Class used for enclosures
605 * @see SimplePie::set_enclosures_class()
606 * @access private
607 */
608 public $enclosure_class = 'SimplePie_Enclosure';
609
610 /**
611 * @var string Class used for Media RSS <media:text> captions
612 * @see SimplePie::set_caption_class()
613 * @access private
614 */
615 public $caption_class = 'SimplePie_Caption';
616
617 /**
618 * @var string Class used for Media RSS <media:copyright>
619 * @see SimplePie::set_copyright_class()
620 * @access private
621 */
622 public $copyright_class = 'SimplePie_Copyright';
623
624 /**
625 * @var string Class used for Media RSS <media:credit>
626 * @see SimplePie::set_credit_class()
627 * @access private
628 */
629 public $credit_class = 'SimplePie_Credit';
630
631 /**
632 * @var string Class used for Media RSS <media:rating>
633 * @see SimplePie::set_rating_class()
634 * @access private
635 */
636 public $rating_class = 'SimplePie_Rating';
637
638 /**
639 * @var string Class used for Media RSS <media:restriction>
640 * @see SimplePie::set_restriction_class()
641 * @access private
642 */
643 public $restriction_class = 'SimplePie_Restriction';
644
645 /**
646 * @var string Class used for content-type sniffing
647 * @see SimplePie::set_content_type_sniffer_class()
648 * @access private
649 */
650 public $content_type_sniffer_class = 'SimplePie_Content_Type_Sniffer';
651
652 /**
653 * @var string Class used for item sources.
654 * @see SimplePie::set_source_class()
655 * @access private
656 */
657 public $source_class = 'SimplePie_Source';
658
659 /**
660 * @var int Maximum number of feeds to check with autodiscovery
661 * @see SimplePie::set_max_checked_feeds()
662 * @access private
663 */
664 public $max_checked_feeds = 10;
665
666 /**
667 * @var array All the feeds found during the autodiscovery process
668 * @see SimplePie::get_all_discovered_feeds()
669 * @access private
670 */
671 public $all_discovered_feeds = array();
672
673 /**
674 * @var string Web-accessible path to the handler_image.php file.
675 * @see SimplePie::set_image_handler()
676 * @access private
677 */
678 public $image_handler = '';
679
680 /**
681 * @var array Stores the URLs when multiple feeds are being initialized.
682 * @see SimplePie::set_feed_url()
683 * @access private
684 */
685 public $multifeed_url = array();
686
687 /**
688 * @var array Stores SimplePie objects when multiple feeds initialized.
689 * @access private
690 */
691 public $multifeed_objects = array();
692
693 /**
694 * @var array Stores the get_object_vars() array for use with multifeeds.
695 * @see SimplePie::set_feed_url()
696 * @access private
697 */
698 public $config_settings = null;
699
700 /**
701 * @var integer Stores the number of items to return per-feed with multifeeds.
702 * @see SimplePie::set_item_limit()
703 * @access private
704 */
705 public $item_limit = 0;
706
707 /**
708 * @var array Stores the default attributes to be stripped by strip_attributes().
709 * @see SimplePie::strip_attributes()
710 * @access private
711 */
712 public $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc');
713
714 /**
715 * @var array Stores the default tags to be stripped by strip_htmltags().
716 * @see SimplePie::strip_htmltags()
717 * @access private
718 */
719 public $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style');
720
721 /**
722 * The SimplePie class contains feed level data and options
723 *
724 * There are two ways that you can create a new SimplePie object. The first
725 * is by passing a feed URL as a parameter to the SimplePie constructor
726 * (as well as optionally setting the cache location and cache expiry). This
727 * will initialise the whole feed with all of the default settings, and you
728 * can begin accessing methods and properties immediately.
729 *
730 * The second way is to create the SimplePie object with no parameters
731 * at all. This will enable you to set configuration options. After setting
732 * them, you must initialise the feed using $feed->init(). At that point the
733 * object's methods and properties will be available to you. This format is
734 * what is used throughout this documentation.
735 *
736 * @access public
737 * @since 1.0 Preview Release
738 */
739 public function __construct()
740 {
741 if (version_compare(PHP_VERSION, '5.0', '<'))
742 {
743 trigger_error('PHP 4.x is no longer supported. Please upgrade to PHP 5.2 or newer.');
744 die();
745 }
746
747 // Other objects, instances created here so we can set options on them
748 $this->sanitize = new SimplePie_Sanitize();
749
750 if (func_num_args() > 0)
751 {
752 trigger_error('Passing parameters to the constructor is no longer supported. Please use set_feed_url(), set_cache_location(), and set_cache_location() directly.');
753 }
754 }
755
756 /**
757 * Used for converting object to a string
758 */
759 public function __toString()
760 {
761 return md5(serialize($this->data));
762 }
763
764 /**
765 * Remove items that link back to this before destroying this object
766 */
767 public function __destruct()
768 {
769 if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode'))
770 {
771 if (!empty($this->data['items']))
772 {
773 foreach ($this->data['items'] as $item)
774 {
775 $item->__destruct();
776 }
777 unset($item, $this->data['items']);
778 }
779 if (!empty($this->data['ordered_items']))
780 {
781 foreach ($this->data['ordered_items'] as $item)
782 {
783 $item->__destruct();
784 }
785 unset($item, $this->data['ordered_items']);
786 }
787 }
788 }
789
790 /**
791 * Force the given data/URL to be treated as a feed no matter what it
792 * appears like
793 *
794 * @access public
795 * @since 1.1
796 * @param bool $enable Force the given data/URL to be treated as a feed
797 */
798 public function force_feed($enable = false)
799 {
800 $this->force_feed = (bool) $enable;
801 }
802
803 /**
804 * This is the URL of the feed you want to parse.
805 *
806 * This allows you to enter the URL of the feed you want to parse, or the
807 * website you want to try to use auto-discovery on. This takes priority
808 * over any set raw data.
809 *
810 * You can set multiple feeds to mash together by passing an array instead
811 * of a string for the $url. Remember that with each additional feed comes
812 * additional processing and resources.
813 *
814 * @access public
815 * @since 1.0 Preview Release
816 * @param mixed $url This is the URL (or array of URLs) that you want to parse.
817 * @see SimplePie::set_raw_data()
818 */
819 public function set_feed_url($url)
820 {
821 if (is_array($url))
822 {
823 $this->multifeed_url = array();
824 foreach ($url as $value)
825 {
826 $this->multifeed_url[] = SimplePie_Misc::fix_protocol($value, 1);
827 }
828 }
829 else
830 {
831 $this->feed_url = SimplePie_Misc::fix_protocol($url, 1);
832 }
833 }
834
835 /**
836 * Provides an instance of SimplePie_File to use as a feed
837 *
838 * @access public
839 * @param object &$file Instance of SimplePie_File (or subclass)
840 * @return bool True on success, false on failure
841 */
842 public function set_file(&$file)
843 {
844 if (is_a($file, 'SimplePie_File'))
845 {
846 $this->feed_url = $file->url;
847 $this->file =& $file;
848 return true;
849 }
850 return false;
851 }
852
853 /**
854 * Allows you to use a string of RSS/Atom data instead of a remote feed.
855 *
856 * If you have a feed available as a string in PHP, you can tell SimplePie
857 * to parse that data string instead of a remote feed. Any set feed URL
858 * takes precedence.
859 *
860 * @access public
861 * @since 1.0 Beta 3
862 * @param string $data RSS or Atom data as a string.
863 * @see SimplePie::set_feed_url()
864 */
865 public function set_raw_data($data)
866 {
867 $this->raw_data = $data;
868 }
869
870 /**
871 * Allows you to override the default timeout for fetching remote feeds.
872 *
873 * This allows you to change the maximum time the feed's server to respond
874 * and send the feed back.
875 *
876 * @access public
877 * @since 1.0 Beta 3
878 * @param int $timeout The maximum number of seconds to spend waiting to retrieve a feed.
879 */
880 public function set_timeout($timeout = 10)
881 {
882 $this->timeout = (int) $timeout;
883 }
884
885 /**
886 * Forces SimplePie to use fsockopen() instead of the preferred cURL
887 * functions.
888 *
889 * @access public
890 * @since 1.0 Beta 3
891 * @param bool $enable Force fsockopen() to be used
892 */
893 public function force_fsockopen($enable = false)
894 {
895 $this->force_fsockopen = (bool) $enable;
896 }
897
898 /**
899 * Enables/disables caching in SimplePie.
900 *
901 * This option allows you to disable caching all-together in SimplePie.
902 * However, disabling the cache can lead to longer load times.
903 *
904 * @access public
905 * @since 1.0 Preview Release
906 * @param bool $enable Enable caching
907 */
908 public function enable_cache($enable = true)
909 {
910 $this->cache = (bool) $enable;
911 }
912
913 /**
914 * Set the length of time (in seconds) that the contents of a feed
915 * will be cached.
916 *
917 * @access public
918 * @param int $seconds The feed content cache duration.
919 */
920 public function set_cache_duration($seconds = 3600)
921 {
922 $this->cache_duration = (int) $seconds;
923 }
924
925 /**
926 * Set the length of time (in seconds) that the autodiscovered feed
927 * URL will be cached.
928 *
929 * @access public
930 * @param int $seconds The autodiscovered feed URL cache duration.
931 */
932 public function set_autodiscovery_cache_duration($seconds = 604800)
933 {
934 $this->autodiscovery_cache_duration = (int) $seconds;
935 }
936
937 /**
938 * Set the file system location where the cached files should be stored.
939 *
940 * @access public
941 * @param string $location The file system location.
942 */
943 public function set_cache_location($location = './cache')
944 {
945 $this->cache_location = (string) $location;
946 }
947
948 /**
949 * Determines whether feed items should be sorted into reverse chronological order.
950 *
951 * @access public
952 * @param bool $enable Sort as reverse chronological order.
953 */
954 public function enable_order_by_date($enable = true)
955 {
956 $this->order_by_date = (bool) $enable;
957 }
958
959 /**
960 * Allows you to override the character encoding reported by the feed.
961 *
962 * @access public
963 * @param string $encoding Character encoding.
964 */
965 public function set_input_encoding($encoding = false)
966 {
967 if ($encoding)
968 {
969 $this->input_encoding = (string) $encoding;
970 }
971 else
972 {
973 $this->input_encoding = false;
974 }
975 }
976
977 /**
978 * Set how much feed autodiscovery to do
979 *
980 * @access public
981 * @see SIMPLEPIE_LOCATOR_NONE
982 * @see SIMPLEPIE_LOCATOR_AUTODISCOVERY
983 * @see SIMPLEPIE_LOCATOR_LOCAL_EXTENSION
984 * @see SIMPLEPIE_LOCATOR_LOCAL_BODY
985 * @see SIMPLEPIE_LOCATOR_REMOTE_EXTENSION
986 * @see SIMPLEPIE_LOCATOR_REMOTE_BODY
987 * @see SIMPLEPIE_LOCATOR_ALL
988 * @param int $level Feed Autodiscovery Level (level can be a
989 * combination of the above constants, see bitwise OR operator)
990 */
991 public function set_autodiscovery_level($level = SIMPLEPIE_LOCATOR_ALL)
992 {
993 $this->autodiscovery = (int) $level;
994 }
995
996 /**
997 * Allows you to change which class SimplePie uses for caching.
998 * Useful when you are overloading or extending SimplePie's default classes.
999 *
1000 * @access public
1001 * @param string $class Name of custom class.
1002 * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
1003 */
1004 public function set_cache_class($class = 'SimplePie_Cache')
1005 {
1006 if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Cache'))
1007 {
1008 $this->cache_class = $class;
1009 return true;
1010 }
1011 return false;
1012 }
1013
1014 /**
1015 * Allows you to change which class SimplePie uses for auto-discovery.
1016 * Useful when you are overloading or extending SimplePie's default classes.
1017 *
1018 * @access public
1019 * @param string $class Name of custom class.
1020 * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
1021 */
1022 public function set_locator_class($class = 'SimplePie_Locator')
1023 {
1024 if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Locator'))
1025 {
1026 $this->locator_class = $class;
1027 return true;
1028 }
1029 return false;
1030 }
1031
1032 /**
1033 * Allows you to change which class SimplePie uses for XML parsing.
1034 * Useful when you are overloading or extending SimplePie's default classes.
1035 *
1036 * @access public
1037 * @param string $class Name of custom class.
1038 * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
1039 */
1040 public function set_parser_class($class = 'SimplePie_Parser')
1041 {
1042 if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Parser'))
1043 {
1044 $this->parser_class = $class;
1045 return true;
1046 }
1047 return false;
1048 }
1049
1050 /**
1051 * Allows you to change which class SimplePie uses for remote file fetching.
1052 * Useful when you are overloading or extending SimplePie's default classes.
1053 *
1054 * @access public
1055 * @param string $class Name of custom class.
1056 * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
1057 */
1058 public function set_file_class($class = 'SimplePie_File')
1059 {
1060 if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_File'))
1061 {
1062 $this->file_class = $class;
1063 return true;
1064 }
1065 return false;
1066 }
1067
1068 /**
1069 * Allows you to change which class SimplePie uses for data sanitization.
1070 * Useful when you are overloading or extending SimplePie's default classes.
1071 *
1072 * @access public
1073 * @param string $class Name of custom class.
1074 * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
1075 */
1076 public function set_sanitize_class($class = 'SimplePie_Sanitize')
1077 {
1078 if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Sanitize'))
1079 {
1080 $this->sanitize = new $class();
1081 return true;
1082 }
1083 return false;
1084 }
1085
1086 /**
1087 * Allows you to change which class SimplePie uses for handling feed items.
1088 * Useful when you are overloading or extending SimplePie's default classes.
1089 *
1090 * @access public
1091 * @param string $class Name of custom class.
1092 * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
1093 */
1094 public function set_item_class($class = 'SimplePie_Item')
1095 {
1096 if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Item'))
1097 {
1098 $this->item_class = $class;
1099 return true;
1100 }
1101 return false;
1102 }
1103
1104 /**
1105 * Allows you to change which class SimplePie uses for handling author data.
1106 * Useful when you are overloading or extending SimplePie's default classes.
1107 *
1108 * @access public
1109 * @param string $class Name of custom class.
1110 * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
1111 */
1112 public function set_author_class($class = 'SimplePie_Author')
1113 {
1114 if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Author'))
1115 {
1116 $this->author_class = $class;
1117 return true;
1118 }
1119 return false;
1120 }
1121
1122 /**
1123 * Allows you to change which class SimplePie uses for handling category data.
1124 * Useful when you are overloading or extending SimplePie's default classes.
1125 *
1126 * @access public
1127 * @param string $class Name of custom class.
1128 * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
1129 */
1130 public function set_category_class($class = 'SimplePie_Category')
1131 {
1132 if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Category'))
1133 {
1134 $this->category_class = $class;
1135 return true;
1136 }
1137 return false;
1138 }
1139
1140 /**
1141 * Allows you to change which class SimplePie uses for feed enclosures.
1142 * Useful when you are overloading or extending SimplePie's default classes.
1143 *
1144 * @access public
1145 * @param string $class Name of custom class.
1146 * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
1147 */
1148 public function set_enclosure_class($class = 'SimplePie_Enclosure')
1149 {
1150 if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Enclosure'))
1151 {
1152 $this->enclosure_class = $class;
1153 return true;
1154 }
1155 return false;
1156 }
1157
1158 /**
1159 * Allows you to change which class SimplePie uses for <media:text> captions
1160 * Useful when you are overloading or extending SimplePie's default classes.
1161 *
1162 * @access public
1163 * @param string $class Name of custom class.
1164 * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
1165 */
1166 public function set_caption_class($class = 'SimplePie_Caption')
1167 {
1168 if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Caption'))
1169 {
1170 $this->caption_class = $class;
1171 return true;
1172 }
1173 return false;
1174 }
1175
1176 /**
1177 * Allows you to change which class SimplePie uses for <media:copyright>
1178 * Useful when you are overloading or extending SimplePie's default classes.
1179 *
1180 * @access public
1181 * @param string $class Name of custom class.
1182 * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
1183 */
1184 public function set_copyright_class($class = 'SimplePie_Copyright')
1185 {
1186 if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Copyright'))
1187 {
1188 $this->copyright_class = $class;
1189 return true;
1190 }
1191 return false;
1192 }
1193
1194 /**
1195 * Allows you to change which class SimplePie uses for <media:credit>
1196 * Useful when you are overloading or extending SimplePie's default classes.
1197 *
1198 * @access public
1199 * @param string $class Name of custom class.
1200 * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
1201 */
1202 public function set_credit_class($class = 'SimplePie_Credit')
1203 {
1204 if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Credit'))
1205 {
1206 $this->credit_class = $class;
1207 return true;
1208 }
1209 return false;
1210 }
1211
1212 /**
1213 * Allows you to change which class SimplePie uses for <media:rating>
1214 * Useful when you are overloading or extending SimplePie's default classes.
1215 *
1216 * @access public
1217 * @param string $class Name of custom class.
1218 * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
1219 */
1220 public function set_rating_class($class = 'SimplePie_Rating')
1221 {
1222 if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Rating'))
1223 {
1224 $this->rating_class = $class;
1225 return true;
1226 }
1227 return false;
1228 }
1229
1230 /**
1231 * Allows you to change which class SimplePie uses for <media:restriction>
1232 * Useful when you are overloading or extending SimplePie's default classes.
1233 *
1234 * @access public
1235 * @param string $class Name of custom class.
1236 * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
1237 */
1238 public function set_restriction_class($class = 'SimplePie_Restriction')
1239 {
1240 if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Restriction'))
1241 {
1242 $this->restriction_class = $class;
1243 return true;
1244 }
1245 return false;
1246 }
1247
1248 /**
1249 * Allows you to change which class SimplePie uses for content-type sniffing.
1250 * Useful when you are overloading or extending SimplePie's default classes.
1251 *
1252 * @access public
1253 * @param string $class Name of custom class.
1254 * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
1255 */
1256 public function set_content_type_sniffer_class($class = 'SimplePie_Content_Type_Sniffer')
1257 {
1258 if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Content_Type_Sniffer'))
1259 {
1260 $this->content_type_sniffer_class = $class;
1261 return true;
1262 }
1263 return false;
1264 }
1265
1266 /**
1267 * Allows you to change which class SimplePie uses item sources.
1268 * Useful when you are overloading or extending SimplePie's default classes.
1269 *
1270 * @access public
1271 * @param string $class Name of custom class.
1272 * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation
1273 */
1274 public function set_source_class($class = 'SimplePie_Source')
1275 {
1276 if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Source'))
1277 {
1278 $this->source_class = $class;
1279 return true;
1280 }
1281 return false;
1282 }
1283
1284 /**
1285 * Allows you to override the default user agent string.
1286 *
1287 * @access public
1288 * @param string $ua New user agent string.
1289 */
1290 public function set_useragent($ua = SIMPLEPIE_USERAGENT)
1291 {
1292 $this->useragent = (string) $ua;
1293 }
1294
1295 /**
1296 * Set callback function to create cache filename with
1297 *
1298 * @access public
1299 * @param mixed $function Callback function
1300 */
1301 public function set_cache_name_function($function = 'md5')
1302 {
1303 if (is_callable($function))
1304 {
1305 $this->cache_name_function = $function;
1306 }
1307 }
1308
1309 /**
1310 * Set options to make SP as fast as possible. Forgoes a
1311 * substantial amount of data sanitization in favor of speed.
1312 *
1313 * @access public
1314 * @param bool $set Whether to set them or not
1315 */
1316 public function set_stupidly_fast($set = false)
1317 {
1318 if ($set)
1319 {
1320 $this->enable_order_by_date(false);
1321 $this->remove_div(false);
1322 $this->strip_comments(false);
1323 $this->strip_htmltags(false);
1324 $this->strip_attributes(false);
1325 $this->set_image_handler(false);
1326 }
1327 }
1328
1329 /**
1330 * Set maximum number of feeds to check with autodiscovery
1331 *
1332 * @access public
1333 * @param int $max Maximum number of feeds to check
1334 */
1335 public function set_max_checked_feeds($max = 10)
1336 {
1337 $this->max_checked_feeds = (int) $max;
1338 }
1339
1340 public function remove_div($enable = true)
1341 {
1342 $this->sanitize->remove_div($enable);
1343 }
1344
1345 public function strip_htmltags($tags = '', $encode = null)
1346 {
1347 if ($tags === '')
1348 {
1349 $tags = $this->strip_htmltags;
1350 }
1351 $this->sanitize->strip_htmltags($tags);
1352 if ($encode !== null)
1353 {
1354 $this->sanitize->encode_instead_of_strip($tags);
1355 }
1356 }
1357
1358 public function encode_instead_of_strip($enable = true)
1359 {
1360 $this->sanitize->encode_instead_of_strip($enable);
1361 }
1362
1363 public function strip_attributes($attribs = '')
1364 {
1365 if ($attribs === '')
1366 {
1367 $attribs = $this->strip_attributes;
1368 }
1369 $this->sanitize->strip_attributes($attribs);
1370 }
1371
1372 public function set_output_encoding($encoding = 'UTF-8')
1373 {
1374 $this->sanitize->set_output_encoding($encoding);
1375 }
1376
1377 public function strip_comments($strip = false)
1378 {
1379 $this->sanitize->strip_comments($strip);
1380 }
1381
1382 /**
1383 * Set element/attribute key/value pairs of HTML attributes
1384 * containing URLs that need to be resolved relative to the feed
1385 *
1386 * @access public
1387 * @since 1.0
1388 * @param array $element_attribute Element/attribute key/value pairs
1389 */
1390 public function set_url_replacements($element_attribute = array('a' => 'href', 'area' => 'href', 'blockquote' => 'cite', 'del' => 'cite', 'form' => 'action', 'img' => array('longdesc', 'src'), 'input' => 'src', 'ins' => 'cite', 'q' => 'cite'))
1391 {
1392 $this->sanitize->set_url_replacements($element_attribute);
1393 }
1394
1395 /**
1396 * Set the handler to enable the display of cached images.
1397 *
1398 * @access public
1399 * @param str $page Web-accessible path to the handler_image.php file.
1400 * @param str $qs The query string that the value should be passed to.
1401 */
1402 public function set_image_handler($page = false, $qs = 'i')
1403 {
1404 if ($page !== false)
1405 {
1406 $this->sanitize->set_image_handler($page . '?' . $qs . '=');
1407 }
1408 else
1409 {
1410 $this->image_handler = '';
1411 }
1412 }
1413
1414 /**
1415 * Set the limit for items returned per-feed with multifeeds.
1416 *
1417 * @access public
1418 * @param integer $limit The maximum number of items to return.
1419 */
1420 public function set_item_limit($limit = 0)
1421 {
1422 $this->item_limit = (int) $limit;
1423 }
1424
1425 public function init()
1426 {
1427 // Check absolute bare minimum requirements.
1428 if ((function_exists('version_compare') && version_compare(PHP_VERSION, '5.0', '<')) || !extension_loaded('xml') || !extension_loaded('pcre'))
1429 {
1430 return false;
1431 }
1432 // Then check the xml extension is sane (i.e., libxml 2.7.x issue on PHP < 5.2.9 and libxml 2.7.0 to 2.7.2 on any version) if we don't have xmlreader.
1433 elseif (!extension_loaded('xmlreader'))
1434 {
1435 static $xml_is_sane = null;
1436 if ($xml_is_sane === null)
1437 {
1438 $parser_check = xml_parser_create();
1439 xml_parse_into_struct($parser_check, '<foo>&amp;</foo>', $values);
1440 xml_parser_free($parser_check);
1441 $xml_is_sane = isset($values[0]['value']);
1442 }
1443 if (!$xml_is_sane)
1444 {
1445 return false;
1446 }
1447 }
1448
1449 // Pass whatever was set with config options over to the sanitizer.
1450 $this->sanitize->pass_cache_data($this->cache, $this->cache_location, $this->cache_name_function, $this->cache_class);
1451 $this->sanitize->pass_file_data($this->file_class, $this->timeout, $this->useragent, $this->force_fsockopen);
1452
1453 if ($this->feed_url !== null || $this->raw_data !== null)
1454 {
1455 $this->error = null;
1456 $this->data = array();
1457 $this->multifeed_objects = array();
1458 $cache = false;
1459
1460 if ($this->feed_url !== null)
1461 {
1462 $parsed_feed_url = SimplePie_Misc::parse_url($this->feed_url);
1463 // Decide whether to enable caching
1464 if ($this->cache && $parsed_feed_url['scheme'] !== '')
1465 {
1466 $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, call_user_func($this->cache_name_function, $this->feed_url), 'spc');
1467 }
1468 // If it's enabled and we don't want an XML dump, use the cache
1469 if ($cache && !$this->xml_dump)
1470 {
1471 // Load the Cache
1472 $this->data = $cache->load();
1473 if (!empty($this->data))
1474 {
1475 // If the cache is for an outdated build of SimplePie
1476 if (!isset($this->data['build']) || $this->data['build'] !== SIMPLEPIE_BUILD)
1477 {
1478 $cache->unlink();
1479 $this->data = array();
1480 }
1481 // If we've hit a collision just rerun it with caching disabled
1482 elseif (isset($this->data['url']) && $this->data['url'] !== $this->feed_url)
1483 {
1484 $cache = false;
1485 $this->data = array();
1486 }
1487 // If we've got a non feed_url stored (if the page isn't actually a feed, or is a redirect) use that URL.
1488 elseif (isset($this->data['feed_url']))
1489 {
1490 // If the autodiscovery cache is still valid use it.
1491 if ($cache->mtime() + $this->autodiscovery_cache_duration > time())
1492 {
1493 // Do not need to do feed autodiscovery yet.
1494 if ($this->data['feed_url'] === $this->data['url'])
1495 {
1496 $cache->unlink();
1497 $this->data = array();
1498 }
1499 else
1500 {
1501 $this->set_feed_url($this->data['feed_url']);
1502 return $this->init();
1503 }
1504 }
1505 }
1506 // Check if the cache has been updated
1507 elseif ($cache->mtime() + $this->cache_duration < time())
1508 {
1509 // If we have last-modified and/or etag set
1510 if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag']))
1511 {
1512 $headers = array(
1513 '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',
1514 );
1515 if (isset($this->data['headers']['last-modified']))
1516 {
1517 $headers['if-modified-since'] = $this->data['headers']['last-modified'];
1518 }
1519 if (isset($this->data['headers']['etag']))
1520 {
1521 $headers['if-none-match'] = $this->data['headers']['etag'];
1522 }
1523
1524 $file = new $this->file_class($this->feed_url, $this->timeout/10, 5, $headers, $this->useragent, $this->force_fsockopen);
1525
1526 if ($file->success)
1527 {
1528 if ($file->status_code === 304)
1529 {
1530 $cache->touch();
1531 return true;
1532 }
1533 else
1534 {
1535 $headers = $file->headers;
1536 }
1537 }
1538 else
1539 {
1540 unset($file);
1541 }
1542 }
1543 }
1544 // If the cache is still valid, just return true
1545 else
1546 {
1547 $this->raw_data = false;
1548 return true;
1549 }
1550 }
1551 // If the cache is empty, delete it
1552 else
1553 {
1554 $cache->unlink();
1555 $this->data = array();
1556 }
1557 }
1558 // If we don't already have the file (it'll only exist if we've opened it to check if the cache has been modified), open it.
1559 if (!isset($file))
1560 {
1561 if (is_a($this->file, 'SimplePie_File') && $this->file->url === $this->feed_url)
1562 {
1563 $file =& $this->file;
1564 }
1565 else
1566 {
1567 $headers = array(
1568 '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',
1569 );
1570 $file = new $this->file_class($this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen);
1571 }
1572 }
1573 // If the file connection has an error, set SimplePie::error to that and quit
1574 if (!$file->success && !($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300)))
1575 {
1576 $this->error = $file->error;
1577 if (!empty($this->data))
1578 {
1579 return true;
1580 }
1581 else
1582 {
1583 return false;
1584 }
1585 }
1586
1587 if (!$this->force_feed)
1588 {
1589 // Check if the supplied URL is a feed, if it isn't, look for it.
1590 $locate = new $this->locator_class($file, $this->timeout, $this->useragent, $this->file_class, $this->max_checked_feeds, $this->content_type_sniffer_class);
1591
1592 if (!$locate->is_feed($file))
1593 {
1594 // We need to unset this so that if SimplePie::set_file() has been called that object is untouched
1595 unset($file);
1596 if ($file = $locate->find($this->autodiscovery, $this->all_discovered_feeds))
1597 {
1598 if ($cache)
1599 {
1600 $this->data = array('url' => $this->feed_url, 'feed_url' => $file->url, 'build' => SIMPLEPIE_BUILD);
1601 if (!$cache->save($this))
1602 {
1603 trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING);
1604 }
1605 $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, call_user_func($this->cache_name_function, $file->url), 'spc');
1606 }
1607 $this->feed_url = $file->url;
1608 }
1609 else
1610 {
1611 $this->error = "A feed could not be found at $this->feed_url. A feed with an invalid mime type may fall victim to this error, or " . SIMPLEPIE_NAME . " was unable to auto-discover it.. Use force_feed() if you are certain this URL is a real feed.";
1612 SimplePie_Misc::error($this->error, E_USER_NOTICE, __FILE__, __LINE__);
1613 return false;
1614 }
1615 }
1616 $locate = null;
1617 }
1618
1619 $headers = $file->headers;
1620 $data = $file->body;
1621 $sniffer = new $this->content_type_sniffer_class($file);
1622 $sniffed = $sniffer->get_type();
1623 }
1624 else
1625 {
1626 $data = $this->raw_data;
1627 }
1628
1629 // This is exposed via get_raw_data()
1630 $this->raw_data = $data;
1631
1632 // Set up array of possible encodings
1633 $encodings = array();
1634
1635 // First check to see if input has been overridden.
1636 if ($this->input_encoding !== false)
1637 {
1638 $encodings[] = $this->input_encoding;
1639 }
1640
1641 $application_types = array('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity');
1642 $text_types = array('text/xml', 'text/xml-external-parsed-entity');
1643
1644 // RFC 3023 (only applies to sniffed content)
1645 if (isset($sniffed))
1646 {
1647 if (in_array($sniffed, $application_types) || substr($sniffed, 0, 12) === 'application/' && substr($sniffed, -4) === '+xml')
1648 {
1649 if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset))
1650 {
1651 $encodings[] = strtoupper($charset[1]);
1652 }
1653 $encodings = array_merge($encodings, SimplePie_Misc::xml_encoding($data));
1654 $encodings[] = 'UTF-8';
1655 }
1656 elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml')
1657 {
1658 if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset))
1659 {
1660 $encodings[] = $charset[1];
1661 }
1662 $encodings[] = 'US-ASCII';
1663 }
1664 // Text MIME-type default
1665 elseif (substr($sniffed, 0, 5) === 'text/')
1666 {
1667 $encodings[] = 'US-ASCII';
1668 }
1669 }
1670
1671 // Fallback to XML 1.0 Appendix F.1/UTF-8/ISO-8859-1
1672 $encodings = array_merge($encodings, SimplePie_Misc::xml_encoding($data));
1673 $encodings[] = 'UTF-8';
1674 $encodings[] = 'ISO-8859-1';
1675
1676 // There's no point in trying an encoding twice
1677 $encodings = array_unique($encodings);
1678
1679 // If we want the XML, just output that with the most likely encoding and quit
1680 if ($this->xml_dump)
1681 {
1682 header('Content-type: text/xml; charset=' . $encodings[0]);
1683 echo $data;
1684 exit;
1685 }
1686
1687 // Loop through each possible encoding, till we return something, or run out of possibilities
1688 foreach ($encodings as $encoding)
1689 {
1690 // Change the encoding to UTF-8 (as we always use UTF-8 internally)
1691 if ($utf8_data = SimplePie_Misc::change_encoding($data, $encoding, 'UTF-8'))
1692 {
1693 // Create new parser
1694 $parser = new $this->parser_class();
1695
1696 // If it's parsed fine
1697 if ($parser->parse($utf8_data, 'UTF-8'))
1698 {
1699 $this->data = $parser->get_data();
1700 if ($this->get_type() & ~SIMPLEPIE_TYPE_NONE)
1701 {
1702 if (isset($headers))
1703 {
1704 $this->data['headers'] = $headers;
1705 }
1706 $this->data['build'] = SIMPLEPIE_BUILD;
1707
1708 // Cache the file if caching is enabled
1709 if ($cache && !$cache->save($this))
1710 {
1711 trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING);
1712 }
1713 return true;
1714 }
1715 else
1716 {
1717 $this->error = "A feed could not be found at $this->feed_url. This does not appear to be a valid RSS or Atom feed.";
1718 SimplePie_Misc::error($this->error, E_USER_NOTICE, __FILE__, __LINE__);
1719 return false;
1720 }
1721 }
1722 }
1723 }
1724
1725 if (isset($parser))
1726 {
1727 // We have an error, just set SimplePie_Misc::error to it and quit
1728 $this->error = sprintf('This XML document is invalid, likely due to invalid characters. XML error: %s at line %d, column %d', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column());
1729 }
1730 else
1731 {
1732 $this->error = 'The data could not be converted to UTF-8. You MUST have either the iconv or mbstring extension installed. Upgrading to PHP 5.x (which includes iconv) is highly recommended.';
1733 }
1734
1735 SimplePie_Misc::error($this->error, E_USER_NOTICE, __FILE__, __LINE__);
1736
1737 return false;
1738 }
1739 elseif (!empty($this->multifeed_url))
1740 {
1741 $i = 0;
1742 $success = 0;
1743 $this->multifeed_objects = array();
1744 foreach ($this->multifeed_url as $url)
1745 {
1746 $this->multifeed_objects[$i] = clone $this;
1747 $this->multifeed_objects[$i]->set_feed_url($url);
1748 $success |= $this->multifeed_objects[$i]->init();
1749 $i++;
1750 }
1751 return (bool) $success;
1752 }
1753 else
1754 {
1755 return false;
1756 }
1757 }
1758
1759 /**
1760 * Return the error message for the occured error
1761 *
1762 * @access public
1763 * @return string Error message
1764 */
1765 public function error()
1766 {
1767 return $this->error;
1768 }
1769
1770 /**
1771 * Return the raw XML
1772 *
1773 * This is the same as setting `$xml_dump = true;`, but returns
1774 * the data instead of printing it.
1775 *
1776 * @return string|boolean Raw XML data, false if the cache is used
1777 */
1778 public function get_raw_data()
1779 {
1780 return $this->raw_data;
1781 }
1782
1783 public function get_encoding()
1784 {
1785 return $this->sanitize->output_encoding;
1786 }
1787
1788 public function handle_content_type($mime = 'text/html')
1789 {
1790 if (!headers_sent())
1791 {
1792 $header = "Content-type: $mime;";
1793 if ($this->get_encoding())
1794 {
1795 $header .= ' charset=' . $this->get_encoding();
1796 }
1797 else
1798 {
1799 $header .= ' charset=UTF-8';
1800 }
1801 header($header);
1802 }
1803 }
1804
1805 public function get_type()
1806 {
1807 if (!isset($this->data['type']))
1808 {
1809 $this->data['type'] = SIMPLEPIE_TYPE_ALL;
1810 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed']))
1811 {
1812 $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_10;
1813 }
1814 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed']))
1815 {
1816 $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_03;
1817 }
1818 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF']))
1819 {
1820 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['channel'])
1821 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['image'])
1822 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item'])
1823 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['textinput']))
1824 {
1825 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_10;
1826 }
1827 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['channel'])
1828 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['image'])
1829 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item'])
1830 || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['textinput']))
1831 {
1832 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_090;
1833 }
1834 }
1835 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss']))
1836 {
1837 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_ALL;
1838 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version']))
1839 {
1840 switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version']))
1841 {
1842 case '0.91':
1843 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091;
1844 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data']))
1845 {
1846 switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data']))
1847 {
1848 case '0':
1849 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_NETSCAPE;
1850 break;
1851
1852 case '24':
1853 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_USERLAND;
1854 break;
1855 }
1856 }
1857 break;
1858
1859 case '0.92':
1860 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_092;
1861 break;
1862
1863 case '0.93':
1864 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_093;
1865 break;
1866
1867 case '0.94':
1868 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_094;
1869 break;
1870
1871 case '2.0':
1872 $this->data['type'] &= SIMPLEPIE_TYPE_RSS_20;
1873 break;
1874 }
1875 }
1876 }
1877 else
1878 {
1879 $this->data['type'] = SIMPLEPIE_TYPE_NONE;
1880 }
1881 }
1882 return $this->data['type'];
1883 }
1884
1885 /**
1886 * @todo If we have a perm redirect we should return the new URL
1887 * @todo When we make the above change, let's support <itunes:new-feed-url> as well
1888 * @todo Also, |atom:link|@rel=self
1889 */
1890 public function subscribe_url()
1891 {
1892 if ($this->feed_url !== null)
1893 {
1894 return $this->sanitize($this->feed_url, SIMPLEPIE_CONSTRUCT_IRI);
1895 }
1896 else
1897 {
1898 return null;
1899 }
1900 }
1901
1902 public function get_feed_tags($namespace, $tag)
1903 {
1904 $type = $this->get_type();
1905 if ($type & SIMPLEPIE_TYPE_ATOM_10)
1906 {
1907 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag]))
1908 {
1909 return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag];
1910 }
1911 }
1912 if ($type & SIMPLEPIE_TYPE_ATOM_03)
1913 {
1914 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag]))
1915 {
1916 return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag];
1917 }
1918 }
1919 if ($type & SIMPLEPIE_TYPE_RSS_RDF)
1920 {
1921 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag]))
1922 {
1923 return $this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag];
1924 }
1925 }
1926 if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION)
1927 {
1928 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag]))
1929 {
1930 return $this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag];
1931 }
1932 }
1933 return null;
1934 }
1935
1936 public function get_channel_tags($namespace, $tag)
1937 {
1938 $type = $this->get_type();
1939 if ($type & SIMPLEPIE_TYPE_ATOM_ALL)
1940 {
1941 if ($return = $this->get_feed_tags($namespace, $tag))
1942 {
1943 return $return;
1944 }
1945 }
1946 if ($type & SIMPLEPIE_TYPE_RSS_10)
1947 {
1948 if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'channel'))
1949 {
1950 if (isset($channel[0]['child'][$namespace][$tag]))
1951 {
1952 return $channel[0]['child'][$namespace][$tag];
1953 }
1954 }
1955 }
1956 if ($type & SIMPLEPIE_TYPE_RSS_090)
1957 {
1958 if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'channel'))
1959 {
1960 if (isset($channel[0]['child'][$namespace][$tag]))
1961 {
1962 return $channel[0]['child'][$namespace][$tag];
1963 }
1964 }
1965 }
1966 if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION)
1967 {
1968 if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'channel'))
1969 {
1970 if (isset($channel[0]['child'][$namespace][$tag]))
1971 {
1972 return $channel[0]['child'][$namespace][$tag];
1973 }
1974 }
1975 }
1976 return null;
1977 }
1978
1979 public function get_image_tags($namespace, $tag)
1980 {
1981 $type = $this->get_type();
1982 if ($type & SIMPLEPIE_TYPE_RSS_10)
1983 {
1984 if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'image'))
1985 {
1986 if (isset($image[0]['child'][$namespace][$tag]))
1987 {
1988 return $image[0]['child'][$namespace][$tag];
1989 }
1990 }
1991 }
1992 if ($type & SIMPLEPIE_TYPE_RSS_090)
1993 {
1994 if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'image'))
1995 {
1996 if (isset($image[0]['child'][$namespace][$tag]))
1997 {
1998 return $image[0]['child'][$namespace][$tag];
1999 }
2000 }
2001 }
2002 if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION)
2003 {
2004 if ($image = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'image'))
2005 {
2006 if (isset($image[0]['child'][$namespace][$tag]))
2007 {
2008 return $image[0]['child'][$namespace][$tag];
2009 }
2010 }
2011 }
2012 return null;
2013 }
2014
2015 public function get_base($element = array())
2016 {
2017 if (!($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION) && !empty($element['xml_base_explicit']) && isset($element['xml_base']))
2018 {
2019 return $element['xml_base'];
2020 }
2021 elseif ($this->get_link() !== null)
2022 {
2023 return $this->get_link();
2024 }
2025 else
2026 {
2027 return $this->subscribe_url();
2028 }
2029 }
2030
2031 public function sanitize($data, $type, $base = '')
2032 {
2033 return $this->sanitize->sanitize($data, $type, $base);
2034 }
2035
2036 public function get_title()
2037 {
2038 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title'))
2039 {
2040 return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0]));
2041 }
2042 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title'))
2043 {
2044 return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0]));
2045 }
2046 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
2047 {
2048 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
2049 }
2050 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
2051 {
2052 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
2053 }
2054 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
2055 {
2056 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
2057 }
2058 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
2059 {
2060 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2061 }
2062 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
2063 {
2064 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2065 }
2066 else
2067 {
2068 return null;
2069 }
2070 }
2071
2072 public function get_category($key = 0)
2073 {
2074 $categories = $this->get_categories();
2075 if (isset($categories[$key]))
2076 {
2077 return $categories[$key];
2078 }
2079 else
2080 {
2081 return null;
2082 }
2083 }
2084
2085 public function get_categories()
2086 {
2087 $categories = array();
2088
2089 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category)
2090 {
2091 $term = null;
2092 $scheme = null;
2093 $label = null;
2094 if (isset($category['attribs']['']['term']))
2095 {
2096 $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT);
2097 }
2098 if (isset($category['attribs']['']['scheme']))
2099 {
2100 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
2101 }
2102 if (isset($category['attribs']['']['label']))
2103 {
2104 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
2105 }
2106 $categories[] = new $this->category_class($term, $scheme, $label);
2107 }
2108 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category)
2109 {
2110 // This is really the label, but keep this as the term also for BC.
2111 // Label will also work on retrieving because that falls back to term.
2112 $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2113 if (isset($category['attribs']['']['domain']))
2114 {
2115 $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT);
2116 }
2117 else
2118 {
2119 $scheme = null;
2120 }
2121 $categories[] = new $this->category_class($term, $scheme, null);
2122 }
2123 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category)
2124 {
2125 $categories[] = new $this->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
2126 }
2127 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category)
2128 {
2129 $categories[] = new $this->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
2130 }
2131
2132 if (!empty($categories))
2133 {
2134 return SimplePie_Misc::array_unique($categories);
2135 }
2136 else
2137 {
2138 return null;
2139 }
2140 }
2141
2142 public function get_author($key = 0)
2143 {
2144 $authors = $this->get_authors();
2145 if (isset($authors[$key]))
2146 {
2147 return $authors[$key];
2148 }
2149 else
2150 {
2151 return null;
2152 }
2153 }
2154
2155 public function get_authors()
2156 {
2157 $authors = array();
2158 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author)
2159 {
2160 $name = null;
2161 $uri = null;
2162 $email = null;
2163 if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
2164 {
2165 $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2166 }
2167 if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
2168 {
2169 $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
2170 }
2171 if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
2172 {
2173 $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2174 }
2175 if ($name !== null || $email !== null || $uri !== null)
2176 {
2177 $authors[] = new $this->author_class($name, $uri, $email);
2178 }
2179 }
2180 if ($author = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author'))
2181 {
2182 $name = null;
2183 $url = null;
2184 $email = null;
2185 if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
2186 {
2187 $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2188 }
2189 if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
2190 {
2191 $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
2192 }
2193 if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
2194 {
2195 $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2196 }
2197 if ($name !== null || $email !== null || $url !== null)
2198 {
2199 $authors[] = new $this->author_class($name, $url, $email);
2200 }
2201 }
2202 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author)
2203 {
2204 $authors[] = new $this->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
2205 }
2206 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author)
2207 {
2208 $authors[] = new $this->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
2209 }
2210 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author)
2211 {
2212 $authors[] = new $this->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
2213 }
2214
2215 if (!empty($authors))
2216 {
2217 return SimplePie_Misc::array_unique($authors);
2218 }
2219 else
2220 {
2221 return null;
2222 }
2223 }
2224
2225 public function get_contributor($key = 0)
2226 {
2227 $contributors = $this->get_contributors();
2228 if (isset($contributors[$key]))
2229 {
2230 return $contributors[$key];
2231 }
2232 else
2233 {
2234 return null;
2235 }
2236 }
2237
2238 public function get_contributors()
2239 {
2240 $contributors = array();
2241 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor)
2242 {
2243 $name = null;
2244 $uri = null;
2245 $email = null;
2246 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
2247 {
2248 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2249 }
2250 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
2251 {
2252 $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
2253 }
2254 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
2255 {
2256 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2257 }
2258 if ($name !== null || $email !== null || $uri !== null)
2259 {
2260 $contributors[] = new $this->author_class($name, $uri, $email);
2261 }
2262 }
2263 foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor)
2264 {
2265 $name = null;
2266 $url = null;
2267 $email = null;
2268 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
2269 {
2270 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2271 }
2272 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
2273 {
2274 $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
2275 }
2276 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
2277 {
2278 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2279 }
2280 if ($name !== null || $email !== null || $url !== null)
2281 {
2282 $contributors[] = new $this->author_class($name, $url, $email);
2283 }
2284 }
2285
2286 if (!empty($contributors))
2287 {
2288 return SimplePie_Misc::array_unique($contributors);
2289 }
2290 else
2291 {
2292 return null;
2293 }
2294 }
2295
2296 public function get_link($key = 0, $rel = 'alternate')
2297 {
2298 $links = $this->get_links($rel);
2299 if (isset($links[$key]))
2300 {
2301 return $links[$key];
2302 }
2303 else
2304 {
2305 return null;
2306 }
2307 }
2308
2309 /**
2310 * Added for parity between the parent-level and the item/entry-level.
2311 */
2312 public function get_permalink()
2313 {
2314 return $this->get_link(0);
2315 }
2316
2317 public function get_links($rel = 'alternate')
2318 {
2319 if (!isset($this->data['links']))
2320 {
2321 $this->data['links'] = array();
2322 if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link'))
2323 {
2324 foreach ($links as $link)
2325 {
2326 if (isset($link['attribs']['']['href']))
2327 {
2328 $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
2329 $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
2330 }
2331 }
2332 }
2333 if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link'))
2334 {
2335 foreach ($links as $link)
2336 {
2337 if (isset($link['attribs']['']['href']))
2338 {
2339 $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
2340 $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
2341
2342 }
2343 }
2344 }
2345 if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
2346 {
2347 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
2348 }
2349 if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
2350 {
2351 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
2352 }
2353 if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
2354 {
2355 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
2356 }
2357
2358 $keys = array_keys($this->data['links']);
2359 foreach ($keys as $key)
2360 {
2361 if (SimplePie_Misc::is_isegment_nz_nc($key))
2362 {
2363 if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]))
2364 {
2365 $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]);
2366 $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key];
2367 }
2368 else
2369 {
2370 $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key];
2371 }
2372 }
2373 elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY)
2374 {
2375 $this->data['links'][substr($key, 41)] =& $this->data['links'][$key];
2376 }
2377 $this->data['links'][$key] = array_unique($this->data['links'][$key]);
2378 }
2379 }
2380
2381 if (isset($this->data['links'][$rel]))
2382 {
2383 return $this->data['links'][$rel];
2384 }
2385 else
2386 {
2387 return null;
2388 }
2389 }
2390
2391 public function get_all_discovered_feeds()
2392 {
2393 return $this->all_discovered_feeds;
2394 }
2395
2396 public function get_description()
2397 {
2398 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle'))
2399 {
2400 return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0]));
2401 }
2402 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline'))
2403 {
2404 return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0]));
2405 }
2406 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description'))
2407 {
2408 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
2409 }
2410 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description'))
2411 {
2412 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
2413 }
2414 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description'))
2415 {
2416 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
2417 }
2418 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description'))
2419 {
2420 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2421 }
2422 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description'))
2423 {
2424 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2425 }
2426 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary'))
2427 {
2428 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
2429 }
2430 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle'))
2431 {
2432 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
2433 }
2434 else
2435 {
2436 return null;
2437 }
2438 }
2439
2440 public function get_copyright()
2441 {
2442 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights'))
2443 {
2444 return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0]));
2445 }
2446 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright'))
2447 {
2448 return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0]));
2449 }
2450 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright'))
2451 {
2452 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2453 }
2454 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights'))
2455 {
2456 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2457 }
2458 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights'))
2459 {
2460 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2461 }
2462 else
2463 {
2464 return null;
2465 }
2466 }
2467
2468 public function get_language()
2469 {
2470 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language'))
2471 {
2472 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2473 }
2474 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language'))
2475 {
2476 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2477 }
2478 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language'))
2479 {
2480 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2481 }
2482 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang']))
2483 {
2484 return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
2485 }
2486 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang']))
2487 {
2488 return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
2489 }
2490 elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang']))
2491 {
2492 return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
2493 }
2494 elseif (isset($this->data['headers']['content-language']))
2495 {
2496 return $this->sanitize($this->data['headers']['content-language'], SIMPLEPIE_CONSTRUCT_TEXT);
2497 }
2498 else
2499 {
2500 return null;
2501 }
2502 }
2503
2504 public function get_latitude()
2505 {
2506
2507 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat'))
2508 {
2509 return (float) $return[0]['data'];
2510 }
2511 elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
2512 {
2513 return (float) $match[1];
2514 }
2515 else
2516 {
2517 return null;
2518 }
2519 }
2520
2521 public function get_longitude()
2522 {
2523 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long'))
2524 {
2525 return (float) $return[0]['data'];
2526 }
2527 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon'))
2528 {
2529 return (float) $return[0]['data'];
2530 }
2531 elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
2532 {
2533 return (float) $match[2];
2534 }
2535 else
2536 {
2537 return null;
2538 }
2539 }
2540
2541 public function get_image_title()
2542 {
2543 if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
2544 {
2545 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2546 }
2547 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
2548 {
2549 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2550 }
2551 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
2552 {
2553 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2554 }
2555 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
2556 {
2557 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2558 }
2559 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
2560 {
2561 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2562 }
2563 else
2564 {
2565 return null;
2566 }
2567 }
2568
2569 public function get_image_url()
2570 {
2571 if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image'))
2572 {
2573 return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI);
2574 }
2575 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo'))
2576 {
2577 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2578 }
2579 elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon'))
2580 {
2581 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2582 }
2583 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'url'))
2584 {
2585 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2586 }
2587 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'url'))
2588 {
2589 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2590 }
2591 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url'))
2592 {
2593 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2594 }
2595 else
2596 {
2597 return null;
2598 }
2599 }
2600
2601 public function get_image_link()
2602 {
2603 if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
2604 {
2605 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2606 }
2607 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
2608 {
2609 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2610 }
2611 elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
2612 {
2613 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
2614 }
2615 else
2616 {
2617 return null;
2618 }
2619 }
2620
2621 public function get_image_width()
2622 {
2623 if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'width'))
2624 {
2625 return round($return[0]['data']);
2626 }
2627 elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url'))
2628 {
2629 return 88.0;
2630 }
2631 else
2632 {
2633 return null;
2634 }
2635 }
2636
2637 public function get_image_height()
2638 {
2639 if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'height'))
2640 {
2641 return round($return[0]['data']);
2642 }
2643 elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url'))
2644 {
2645 return 31.0;
2646 }
2647 else
2648 {
2649 return null;
2650 }
2651 }
2652
2653 public function get_item_quantity($max = 0)
2654 {
2655 $max = (int) $max;
2656 $qty = count($this->get_items());
2657 if ($max === 0)
2658 {
2659 return $qty;
2660 }
2661 else
2662 {
2663 return ($qty > $max) ? $max : $qty;
2664 }
2665 }
2666
2667 public function get_item($key = 0)
2668 {
2669 $items = $this->get_items();
2670 if (isset($items[$key]))
2671 {
2672 return $items[$key];
2673 }
2674 else
2675 {
2676 return null;
2677 }
2678 }
2679
2680 public function get_items($start = 0, $end = 0)
2681 {
2682 if (!isset($this->data['items']))
2683 {
2684 if (!empty($this->multifeed_objects))
2685 {
2686 $this->data['items'] = SimplePie::merge_items($this->multifeed_objects, $start, $end, $this->item_limit);
2687 }
2688 else
2689 {
2690 $this->data['items'] = array();
2691 if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'entry'))
2692 {
2693 $keys = array_keys($items);
2694 foreach ($keys as $key)
2695 {
2696 $this->data['items'][] = new $this->item_class($this, $items[$key]);
2697 }
2698 }
2699 if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'entry'))
2700 {
2701 $keys = array_keys($items);
2702 foreach ($keys as $key)
2703 {
2704 $this->data['items'][] = new $this->item_class($this, $items[$key]);
2705 }
2706 }
2707 if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'item'))
2708 {
2709 $keys = array_keys($items);
2710 foreach ($keys as $key)
2711 {
2712 $this->data['items'][] = new $this->item_class($this, $items[$key]);
2713 }
2714 }
2715 if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'item'))
2716 {
2717 $keys = array_keys($items);
2718 foreach ($keys as $key)
2719 {
2720 $this->data['items'][] = new $this->item_class($this, $items[$key]);
2721 }
2722 }
2723 if ($items = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'item'))
2724 {
2725 $keys = array_keys($items);
2726 foreach ($keys as $key)
2727 {
2728 $this->data['items'][] = new $this->item_class($this, $items[$key]);
2729 }
2730 }
2731 }
2732 }
2733
2734 if (!empty($this->data['items']))
2735 {
2736 // If we want to order it by date, check if all items have a date, and then sort it
2737 if ($this->order_by_date && empty($this->multifeed_objects))
2738 {
2739 if (!isset($this->data['ordered_items']))
2740 {
2741 $do_sort = true;
2742 foreach ($this->data['items'] as $item)
2743 {
2744 if (!$item->get_date('U'))
2745 {
2746 $do_sort = false;
2747 break;
2748 }
2749 }
2750 $item = null;
2751 $this->data['ordered_items'] = $this->data['items'];
2752 if ($do_sort)
2753 {
2754 usort($this->data['ordered_items'], array(&$this, 'sort_items'));
2755 }
2756 }
2757 $items = $this->data['ordered_items'];
2758 }
2759 else
2760 {
2761 $items = $this->data['items'];
2762 }
2763
2764 // Slice the data as desired
2765 if ($end === 0)
2766 {
2767 return array_slice($items, $start);
2768 }
2769 else
2770 {
2771 return array_slice($items, $start, $end);
2772 }
2773 }
2774 else
2775 {
2776 return array();
2777 }
2778 }
2779
2780 /**
2781 * @static
2782 */
2783 public function sort_items($a, $b)
2784 {
2785 return $a->get_date('U') <= $b->get_date('U');
2786 }
2787
2788 /**
2789 * @static
2790 */
2791 public function merge_items($urls, $start = 0, $end = 0, $limit = 0)
2792 {
2793 if (is_array($urls) && sizeof($urls) > 0)
2794 {
2795 $items = array();
2796 foreach ($urls as $arg)
2797 {
2798 if (is_a($arg, 'SimplePie'))
2799 {
2800 $items = array_merge($items, $arg->get_items(0, $limit));
2801 }
2802 else
2803 {
2804 trigger_error('Arguments must be SimplePie objects', E_USER_WARNING);
2805 }
2806 }
2807
2808 $do_sort = true;
2809 foreach ($items as $item)
2810 {
2811 if (!$item->get_date('U'))
2812 {
2813 $do_sort = false;
2814 break;
2815 }
2816 }
2817 $item = null;
2818 if ($do_sort)
2819 {
2820 usort($items, array('SimplePie', 'sort_items'));
2821 }
2822
2823 if ($end === 0)
2824 {
2825 return array_slice($items, $start);
2826 }
2827 else
2828 {
2829 return array_slice($items, $start, $end);
2830 }
2831 }
2832 else
2833 {
2834 trigger_error('Cannot merge zero SimplePie objects', E_USER_WARNING);
2835 return array();
2836 }
2837 }
2838}
diff --git a/inc/3rdparty/simplepie/SimplePie/Credit.php b/inc/3rdparty/simplepie/SimplePie/Credit.php
new file mode 100644
index 00000000..3894b30c
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Credit.php
@@ -0,0 +1,103 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47class SimplePie_Credit
48{
49 var $role;
50 var $scheme;
51 var $name;
52
53 // Constructor, used to input the data
54 public function __construct($role = null, $scheme = null, $name = null)
55 {
56 $this->role = $role;
57 $this->scheme = $scheme;
58 $this->name = $name;
59 }
60
61 public function __toString()
62 {
63 // There is no $this->data here
64 return md5(serialize($this));
65 }
66
67 public function get_role()
68 {
69 if ($this->role !== null)
70 {
71 return $this->role;
72 }
73 else
74 {
75 return null;
76 }
77 }
78
79 public function get_scheme()
80 {
81 if ($this->scheme !== null)
82 {
83 return $this->scheme;
84 }
85 else
86 {
87 return null;
88 }
89 }
90
91 public function get_name()
92 {
93 if ($this->name !== null)
94 {
95 return $this->name;
96 }
97 else
98 {
99 return null;
100 }
101 }
102}
103
diff --git a/inc/3rdparty/simplepie/SimplePie/Decode/HTML/Entities.php b/inc/3rdparty/simplepie/SimplePie/Decode/HTML/Entities.php
new file mode 100644
index 00000000..bf598579
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Decode/HTML/Entities.php
@@ -0,0 +1,250 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47/**
48 * Decode HTML Entities
49 *
50 * This implements HTML5 as of revision 967 (2007-06-28)
51 *
52 * @package SimplePie
53 */
54class SimplePie_Decode_HTML_Entities
55{
56 /**
57 * Data to be parsed
58 *
59 * @access private
60 * @var string
61 */
62 var $data = '';
63
64 /**
65 * Currently consumed bytes
66 *
67 * @access private
68 * @var string
69 */
70 var $consumed = '';
71
72 /**
73 * Position of the current byte being parsed
74 *
75 * @access private
76 * @var int
77 */
78 var $position = 0;
79
80 /**
81 * Create an instance of the class with the input data
82 *
83 * @access public
84 * @param string $data Input data
85 */
86 public function __construct($data)
87 {
88 $this->data = $data;
89 }
90
91 /**
92 * Parse the input data
93 *
94 * @access public
95 * @return string Output data
96 */
97 public function parse()
98 {
99 while (($this->position = strpos($this->data, '&', $this->position)) !== false)
100 {
101 $this->consume();
102 $this->entity();
103 $this->consumed = '';
104 }
105 return $this->data;
106 }
107
108 /**
109 * Consume the next byte
110 *
111 * @access private
112 * @return mixed The next byte, or false, if there is no more data
113 */
114 public function consume()
115 {
116 if (isset($this->data[$this->position]))
117 {
118 $this->consumed .= $this->data[$this->position];
119 return $this->data[$this->position++];
120 }
121 else
122 {
123 return false;
124 }
125 }
126
127 /**
128 * Consume a range of characters
129 *
130 * @access private
131 * @param string $chars Characters to consume
132 * @return mixed A series of characters that match the range, or false
133 */
134 public function consume_range($chars)
135 {
136 if ($len = strspn($this->data, $chars, $this->position))
137 {
138 $data = substr($this->data, $this->position, $len);
139 $this->consumed .= $data;
140 $this->position += $len;
141 return $data;
142 }
143 else
144 {
145 return false;
146 }
147 }
148
149 /**
150 * Unconsume one byte
151 *
152 * @access private
153 */
154 public function unconsume()
155 {
156 $this->consumed = substr($this->consumed, 0, -1);
157 $this->position--;
158 }
159
160 /**
161 * Decode an entity
162 *
163 * @access private
164 */
165 public function entity()
166 {
167 switch ($this->consume())
168 {
169 case "\x09":
170 case "\x0A":
171 case "\x0B":
172 case "\x0B":
173 case "\x0C":
174 case "\x20":
175 case "\x3C":
176 case "\x26":
177 case false:
178 break;
179
180 case "\x23":
181 switch ($this->consume())
182 {
183 case "\x78":
184 case "\x58":
185 $range = '0123456789ABCDEFabcdef';
186 $hex = true;
187 break;
188
189 default:
190 $range = '0123456789';
191 $hex = false;
192 $this->unconsume();
193 break;
194 }
195
196 if ($codepoint = $this->consume_range($range))
197 {
198 static $windows_1252_specials = array(0x0D => "\x0A", 0x80 => "\xE2\x82\xAC", 0x81 => "\xEF\xBF\xBD", 0x82 => "\xE2\x80\x9A", 0x83 => "\xC6\x92", 0x84 => "\xE2\x80\x9E", 0x85 => "\xE2\x80\xA6", 0x86 => "\xE2\x80\xA0", 0x87 => "\xE2\x80\xA1", 0x88 => "\xCB\x86", 0x89 => "\xE2\x80\xB0", 0x8A => "\xC5\xA0", 0x8B => "\xE2\x80\xB9", 0x8C => "\xC5\x92", 0x8D => "\xEF\xBF\xBD", 0x8E => "\xC5\xBD", 0x8F => "\xEF\xBF\xBD", 0x90 => "\xEF\xBF\xBD", 0x91 => "\xE2\x80\x98", 0x92 => "\xE2\x80\x99", 0x93 => "\xE2\x80\x9C", 0x94 => "\xE2\x80\x9D", 0x95 => "\xE2\x80\xA2", 0x96 => "\xE2\x80\x93", 0x97 => "\xE2\x80\x94", 0x98 => "\xCB\x9C", 0x99 => "\xE2\x84\xA2", 0x9A => "\xC5\xA1", 0x9B => "\xE2\x80\xBA", 0x9C => "\xC5\x93", 0x9D => "\xEF\xBF\xBD", 0x9E => "\xC5\xBE", 0x9F => "\xC5\xB8");
199
200 if ($hex)
201 {
202 $codepoint = hexdec($codepoint);
203 }
204 else
205 {
206 $codepoint = intval($codepoint);
207 }
208
209 if (isset($windows_1252_specials[$codepoint]))
210 {
211 $replacement = $windows_1252_specials[$codepoint];
212 }
213 else
214 {
215 $replacement = SimplePie_Misc::codepoint_to_utf8($codepoint);
216 }
217
218 if (!in_array($this->consume(), array(';', false), true))
219 {
220 $this->unconsume();
221 }
222
223 $consumed_length = strlen($this->consumed);
224 $this->data = substr_replace($this->data, $replacement, $this->position - $consumed_length, $consumed_length);
225 $this->position += strlen($replacement) - $consumed_length;
226 }
227 break;
228
229 default:
230 static $entities = array('Aacute' => "\xC3\x81", 'aacute' => "\xC3\xA1", 'Aacute;' => "\xC3\x81", 'aacute;' => "\xC3\xA1", 'Acirc' => "\xC3\x82", 'acirc' => "\xC3\xA2", 'Acirc;' => "\xC3\x82", 'acirc;' => "\xC3\xA2", 'acute' => "\xC2\xB4", 'acute;' => "\xC2\xB4", 'AElig' => "\xC3\x86", 'aelig' => "\xC3\xA6", 'AElig;' => "\xC3\x86", 'aelig;' => "\xC3\xA6", 'Agrave' => "\xC3\x80", 'agrave' => "\xC3\xA0", 'Agrave;' => "\xC3\x80", 'agrave;' => "\xC3\xA0", 'alefsym;' => "\xE2\x84\xB5", 'Alpha;' => "\xCE\x91", 'alpha;' => "\xCE\xB1", 'AMP' => "\x26", 'amp' => "\x26", 'AMP;' => "\x26", 'amp;' => "\x26", 'and;' => "\xE2\x88\xA7", 'ang;' => "\xE2\x88\xA0", 'apos;' => "\x27", 'Aring' => "\xC3\x85", 'aring' => "\xC3\xA5", 'Aring;' => "\xC3\x85", 'aring;' => "\xC3\xA5", 'asymp;' => "\xE2\x89\x88", 'Atilde' => "\xC3\x83", 'atilde' => "\xC3\xA3", 'Atilde;' => "\xC3\x83", 'atilde;' => "\xC3\xA3", 'Auml' => "\xC3\x84", 'auml' => "\xC3\xA4", 'Auml;' => "\xC3\x84", 'auml;' => "\xC3\xA4", 'bdquo;' => "\xE2\x80\x9E", 'Beta;' => "\xCE\x92", 'beta;' => "\xCE\xB2", 'brvbar' => "\xC2\xA6", 'brvbar;' => "\xC2\xA6", 'bull;' => "\xE2\x80\xA2", 'cap;' => "\xE2\x88\xA9", 'Ccedil' => "\xC3\x87", 'ccedil' => "\xC3\xA7", 'Ccedil;' => "\xC3\x87", 'ccedil;' => "\xC3\xA7", 'cedil' => "\xC2\xB8", 'cedil;' => "\xC2\xB8", 'cent' => "\xC2\xA2", 'cent;' => "\xC2\xA2", 'Chi;' => "\xCE\xA7", 'chi;' => "\xCF\x87", 'circ;' => "\xCB\x86", 'clubs;' => "\xE2\x99\xA3", 'cong;' => "\xE2\x89\x85", 'COPY' => "\xC2\xA9", 'copy' => "\xC2\xA9", 'COPY;' => "\xC2\xA9", 'copy;' => "\xC2\xA9", 'crarr;' => "\xE2\x86\xB5", 'cup;' => "\xE2\x88\xAA", 'curren' => "\xC2\xA4", 'curren;' => "\xC2\xA4", 'Dagger;' => "\xE2\x80\xA1", 'dagger;' => "\xE2\x80\xA0", 'dArr;' => "\xE2\x87\x93", 'darr;' => "\xE2\x86\x93", 'deg' => "\xC2\xB0", 'deg;' => "\xC2\xB0", 'Delta;' => "\xCE\x94", 'delta;' => "\xCE\xB4", 'diams;' => "\xE2\x99\xA6", 'divide' => "\xC3\xB7", 'divide;' => "\xC3\xB7", 'Eacute' => "\xC3\x89", 'eacute' => "\xC3\xA9", 'Eacute;' => "\xC3\x89", 'eacute;' => "\xC3\xA9", 'Ecirc' => "\xC3\x8A", 'ecirc' => "\xC3\xAA", 'Ecirc;' => "\xC3\x8A", 'ecirc;' => "\xC3\xAA", 'Egrave' => "\xC3\x88", 'egrave' => "\xC3\xA8", 'Egrave;' => "\xC3\x88", 'egrave;' => "\xC3\xA8", 'empty;' => "\xE2\x88\x85", 'emsp;' => "\xE2\x80\x83", 'ensp;' => "\xE2\x80\x82", 'Epsilon;' => "\xCE\x95", 'epsilon;' => "\xCE\xB5", 'equiv;' => "\xE2\x89\xA1", 'Eta;' => "\xCE\x97", 'eta;' => "\xCE\xB7", 'ETH' => "\xC3\x90", 'eth' => "\xC3\xB0", 'ETH;' => "\xC3\x90", 'eth;' => "\xC3\xB0", 'Euml' => "\xC3\x8B", 'euml' => "\xC3\xAB", 'Euml;' => "\xC3\x8B", 'euml;' => "\xC3\xAB", 'euro;' => "\xE2\x82\xAC", 'exist;' => "\xE2\x88\x83", 'fnof;' => "\xC6\x92", 'forall;' => "\xE2\x88\x80", 'frac12' => "\xC2\xBD", 'frac12;' => "\xC2\xBD", 'frac14' => "\xC2\xBC", 'frac14;' => "\xC2\xBC", 'frac34' => "\xC2\xBE", 'frac34;' => "\xC2\xBE", 'frasl;' => "\xE2\x81\x84", 'Gamma;' => "\xCE\x93", 'gamma;' => "\xCE\xB3", 'ge;' => "\xE2\x89\xA5", 'GT' => "\x3E", 'gt' => "\x3E", 'GT;' => "\x3E", 'gt;' => "\x3E", 'hArr;' => "\xE2\x87\x94", 'harr;' => "\xE2\x86\x94", 'hearts;' => "\xE2\x99\xA5", 'hellip;' => "\xE2\x80\xA6", 'Iacute' => "\xC3\x8D", 'iacute' => "\xC3\xAD", 'Iacute;' => "\xC3\x8D", 'iacute;' => "\xC3\xAD", 'Icirc' => "\xC3\x8E", 'icirc' => "\xC3\xAE", 'Icirc;' => "\xC3\x8E", 'icirc;' => "\xC3\xAE", 'iexcl' => "\xC2\xA1", 'iexcl;' => "\xC2\xA1", 'Igrave' => "\xC3\x8C", 'igrave' => "\xC3\xAC", 'Igrave;' => "\xC3\x8C", 'igrave;' => "\xC3\xAC", 'image;' => "\xE2\x84\x91", 'infin;' => "\xE2\x88\x9E", 'int;' => "\xE2\x88\xAB", 'Iota;' => "\xCE\x99", 'iota;' => "\xCE\xB9", 'iquest' => "\xC2\xBF", 'iquest;' => "\xC2\xBF", 'isin;' => "\xE2\x88\x88", 'Iuml' => "\xC3\x8F", 'iuml' => "\xC3\xAF", 'Iuml;' => "\xC3\x8F", 'iuml;' => "\xC3\xAF", 'Kappa;' => "\xCE\x9A", 'kappa;' => "\xCE\xBA", 'Lambda;' => "\xCE\x9B", 'lambda;' => "\xCE\xBB", 'lang;' => "\xE3\x80\x88", 'laquo' => "\xC2\xAB", 'laquo;' => "\xC2\xAB", 'lArr;' => "\xE2\x87\x90", 'larr;' => "\xE2\x86\x90", 'lceil;' => "\xE2\x8C\x88", 'ldquo;' => "\xE2\x80\x9C", 'le;' => "\xE2\x89\xA4", 'lfloor;' => "\xE2\x8C\x8A", 'lowast;' => "\xE2\x88\x97", 'loz;' => "\xE2\x97\x8A", 'lrm;' => "\xE2\x80\x8E", 'lsaquo;' => "\xE2\x80\xB9", 'lsquo;' => "\xE2\x80\x98", 'LT' => "\x3C", 'lt' => "\x3C", 'LT;' => "\x3C", 'lt;' => "\x3C", 'macr' => "\xC2\xAF", 'macr;' => "\xC2\xAF", 'mdash;' => "\xE2\x80\x94", 'micro' => "\xC2\xB5", 'micro;' => "\xC2\xB5", 'middot' => "\xC2\xB7", 'middot;' => "\xC2\xB7", 'minus;' => "\xE2\x88\x92", 'Mu;' => "\xCE\x9C", 'mu;' => "\xCE\xBC", 'nabla;' => "\xE2\x88\x87", 'nbsp' => "\xC2\xA0", 'nbsp;' => "\xC2\xA0", 'ndash;' => "\xE2\x80\x93", 'ne;' => "\xE2\x89\xA0", 'ni;' => "\xE2\x88\x8B", 'not' => "\xC2\xAC", 'not;' => "\xC2\xAC", 'notin;' => "\xE2\x88\x89", 'nsub;' => "\xE2\x8A\x84", 'Ntilde' => "\xC3\x91", 'ntilde' => "\xC3\xB1", 'Ntilde;' => "\xC3\x91", 'ntilde;' => "\xC3\xB1", 'Nu;' => "\xCE\x9D", 'nu;' => "\xCE\xBD", 'Oacute' => "\xC3\x93", 'oacute' => "\xC3\xB3", 'Oacute;' => "\xC3\x93", 'oacute;' => "\xC3\xB3", 'Ocirc' => "\xC3\x94", 'ocirc' => "\xC3\xB4", 'Ocirc;' => "\xC3\x94", 'ocirc;' => "\xC3\xB4", 'OElig;' => "\xC5\x92", 'oelig;' => "\xC5\x93", 'Ograve' => "\xC3\x92", 'ograve' => "\xC3\xB2", 'Ograve;' => "\xC3\x92", 'ograve;' => "\xC3\xB2", 'oline;' => "\xE2\x80\xBE", 'Omega;' => "\xCE\xA9", 'omega;' => "\xCF\x89", 'Omicron;' => "\xCE\x9F", 'omicron;' => "\xCE\xBF", 'oplus;' => "\xE2\x8A\x95", 'or;' => "\xE2\x88\xA8", 'ordf' => "\xC2\xAA", 'ordf;' => "\xC2\xAA", 'ordm' => "\xC2\xBA", 'ordm;' => "\xC2\xBA", 'Oslash' => "\xC3\x98", 'oslash' => "\xC3\xB8", 'Oslash;' => "\xC3\x98", 'oslash;' => "\xC3\xB8", 'Otilde' => "\xC3\x95", 'otilde' => "\xC3\xB5", 'Otilde;' => "\xC3\x95", 'otilde;' => "\xC3\xB5", 'otimes;' => "\xE2\x8A\x97", 'Ouml' => "\xC3\x96", 'ouml' => "\xC3\xB6", 'Ouml;' => "\xC3\x96", 'ouml;' => "\xC3\xB6", 'para' => "\xC2\xB6", 'para;' => "\xC2\xB6", 'part;' => "\xE2\x88\x82", 'permil;' => "\xE2\x80\xB0", 'perp;' => "\xE2\x8A\xA5", 'Phi;' => "\xCE\xA6", 'phi;' => "\xCF\x86", 'Pi;' => "\xCE\xA0", 'pi;' => "\xCF\x80", 'piv;' => "\xCF\x96", 'plusmn' => "\xC2\xB1", 'plusmn;' => "\xC2\xB1", 'pound' => "\xC2\xA3", 'pound;' => "\xC2\xA3", 'Prime;' => "\xE2\x80\xB3", 'prime;' => "\xE2\x80\xB2", 'prod;' => "\xE2\x88\x8F", 'prop;' => "\xE2\x88\x9D", 'Psi;' => "\xCE\xA8", 'psi;' => "\xCF\x88", 'QUOT' => "\x22", 'quot' => "\x22", 'QUOT;' => "\x22", 'quot;' => "\x22", 'radic;' => "\xE2\x88\x9A", 'rang;' => "\xE3\x80\x89", 'raquo' => "\xC2\xBB", 'raquo;' => "\xC2\xBB", 'rArr;' => "\xE2\x87\x92", 'rarr;' => "\xE2\x86\x92", 'rceil;' => "\xE2\x8C\x89", 'rdquo;' => "\xE2\x80\x9D", 'real;' => "\xE2\x84\x9C", 'REG' => "\xC2\xAE", 'reg' => "\xC2\xAE", 'REG;' => "\xC2\xAE", 'reg;' => "\xC2\xAE", 'rfloor;' => "\xE2\x8C\x8B", 'Rho;' => "\xCE\xA1", 'rho;' => "\xCF\x81", 'rlm;' => "\xE2\x80\x8F", 'rsaquo;' => "\xE2\x80\xBA", 'rsquo;' => "\xE2\x80\x99", 'sbquo;' => "\xE2\x80\x9A", 'Scaron;' => "\xC5\xA0", 'scaron;' => "\xC5\xA1", 'sdot;' => "\xE2\x8B\x85", 'sect' => "\xC2\xA7", 'sect;' => "\xC2\xA7", 'shy' => "\xC2\xAD", 'shy;' => "\xC2\xAD", 'Sigma;' => "\xCE\xA3", 'sigma;' => "\xCF\x83", 'sigmaf;' => "\xCF\x82", 'sim;' => "\xE2\x88\xBC", 'spades;' => "\xE2\x99\xA0", 'sub;' => "\xE2\x8A\x82", 'sube;' => "\xE2\x8A\x86", 'sum;' => "\xE2\x88\x91", 'sup;' => "\xE2\x8A\x83", 'sup1' => "\xC2\xB9", 'sup1;' => "\xC2\xB9", 'sup2' => "\xC2\xB2", 'sup2;' => "\xC2\xB2", 'sup3' => "\xC2\xB3", 'sup3;' => "\xC2\xB3", 'supe;' => "\xE2\x8A\x87", 'szlig' => "\xC3\x9F", 'szlig;' => "\xC3\x9F", 'Tau;' => "\xCE\xA4", 'tau;' => "\xCF\x84", 'there4;' => "\xE2\x88\xB4", 'Theta;' => "\xCE\x98", 'theta;' => "\xCE\xB8", 'thetasym;' => "\xCF\x91", 'thinsp;' => "\xE2\x80\x89", 'THORN' => "\xC3\x9E", 'thorn' => "\xC3\xBE", 'THORN;' => "\xC3\x9E", 'thorn;' => "\xC3\xBE", 'tilde;' => "\xCB\x9C", 'times' => "\xC3\x97", 'times;' => "\xC3\x97", 'TRADE;' => "\xE2\x84\xA2", 'trade;' => "\xE2\x84\xA2", 'Uacute' => "\xC3\x9A", 'uacute' => "\xC3\xBA", 'Uacute;' => "\xC3\x9A", 'uacute;' => "\xC3\xBA", 'uArr;' => "\xE2\x87\x91", 'uarr;' => "\xE2\x86\x91", 'Ucirc' => "\xC3\x9B", 'ucirc' => "\xC3\xBB", 'Ucirc;' => "\xC3\x9B", 'ucirc;' => "\xC3\xBB", 'Ugrave' => "\xC3\x99", 'ugrave' => "\xC3\xB9", 'Ugrave;' => "\xC3\x99", 'ugrave;' => "\xC3\xB9", 'uml' => "\xC2\xA8", 'uml;' => "\xC2\xA8", 'upsih;' => "\xCF\x92", 'Upsilon;' => "\xCE\xA5", 'upsilon;' => "\xCF\x85", 'Uuml' => "\xC3\x9C", 'uuml' => "\xC3\xBC", 'Uuml;' => "\xC3\x9C", 'uuml;' => "\xC3\xBC", 'weierp;' => "\xE2\x84\x98", 'Xi;' => "\xCE\x9E", 'xi;' => "\xCE\xBE", 'Yacute' => "\xC3\x9D", 'yacute' => "\xC3\xBD", 'Yacute;' => "\xC3\x9D", 'yacute;' => "\xC3\xBD", 'yen' => "\xC2\xA5", 'yen;' => "\xC2\xA5", 'yuml' => "\xC3\xBF", 'Yuml;' => "\xC5\xB8", 'yuml;' => "\xC3\xBF", 'Zeta;' => "\xCE\x96", 'zeta;' => "\xCE\xB6", 'zwj;' => "\xE2\x80\x8D", 'zwnj;' => "\xE2\x80\x8C");
231
232 for ($i = 0, $match = null; $i < 9 && $this->consume() !== false; $i++)
233 {
234 $consumed = substr($this->consumed, 1);
235 if (isset($entities[$consumed]))
236 {
237 $match = $consumed;
238 }
239 }
240
241 if ($match !== null)
242 {
243 $this->data = substr_replace($this->data, $entities[$match], $this->position - strlen($consumed) - 1, strlen($match) + 1);
244 $this->position += strlen($entities[$match]) - strlen($consumed) - 1;
245 }
246 break;
247 }
248 }
249}
250
diff --git a/inc/3rdparty/simplepie/SimplePie/Enclosure.php b/inc/3rdparty/simplepie/SimplePie/Enclosure.php
new file mode 100644
index 00000000..ca9a8b46
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Enclosure.php
@@ -0,0 +1,990 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47class SimplePie_Enclosure
48{
49 var $bitrate;
50 var $captions;
51 var $categories;
52 var $channels;
53 var $copyright;
54 var $credits;
55 var $description;
56 var $duration;
57 var $expression;
58 var $framerate;
59 var $handler;
60 var $hashes;
61 var $height;
62 var $javascript;
63 var $keywords;
64 var $lang;
65 var $length;
66 var $link;
67 var $medium;
68 var $player;
69 var $ratings;
70 var $restrictions;
71 var $samplingrate;
72 var $thumbnails;
73 var $title;
74 var $type;
75 var $width;
76
77 // Constructor, used to input the data
78 public function __construct($link = null, $type = null, $length = null, $javascript = null, $bitrate = null, $captions = null, $categories = null, $channels = null, $copyright = null, $credits = null, $description = null, $duration = null, $expression = null, $framerate = null, $hashes = null, $height = null, $keywords = null, $lang = null, $medium = null, $player = null, $ratings = null, $restrictions = null, $samplingrate = null, $thumbnails = null, $title = null, $width = null)
79 {
80 $this->bitrate = $bitrate;
81 $this->captions = $captions;
82 $this->categories = $categories;
83 $this->channels = $channels;
84 $this->copyright = $copyright;
85 $this->credits = $credits;
86 $this->description = $description;
87 $this->duration = $duration;
88 $this->expression = $expression;
89 $this->framerate = $framerate;
90 $this->hashes = $hashes;
91 $this->height = $height;
92 $this->keywords = $keywords;
93 $this->lang = $lang;
94 $this->length = $length;
95 $this->link = $link;
96 $this->medium = $medium;
97 $this->player = $player;
98 $this->ratings = $ratings;
99 $this->restrictions = $restrictions;
100 $this->samplingrate = $samplingrate;
101 $this->thumbnails = $thumbnails;
102 $this->title = $title;
103 $this->type = $type;
104 $this->width = $width;
105
106 if (class_exists('idna_convert'))
107 {
108 $idn = new idna_convert();
109 $parsed = SimplePie_Misc::parse_url($link);
110 $this->link = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']);
111 }
112 $this->handler = $this->get_handler(); // Needs to load last
113 }
114
115 public function __toString()
116 {
117 // There is no $this->data here
118 return md5(serialize($this));
119 }
120
121 public function get_bitrate()
122 {
123 if ($this->bitrate !== null)
124 {
125 return $this->bitrate;
126 }
127 else
128 {
129 return null;
130 }
131 }
132
133 public function get_caption($key = 0)
134 {
135 $captions = $this->get_captions();
136 if (isset($captions[$key]))
137 {
138 return $captions[$key];
139 }
140 else
141 {
142 return null;
143 }
144 }
145
146 public function get_captions()
147 {
148 if ($this->captions !== null)
149 {
150 return $this->captions;
151 }
152 else
153 {
154 return null;
155 }
156 }
157
158 public function get_category($key = 0)
159 {
160 $categories = $this->get_categories();
161 if (isset($categories[$key]))
162 {
163 return $categories[$key];
164 }
165 else
166 {
167 return null;
168 }
169 }
170
171 public function get_categories()
172 {
173 if ($this->categories !== null)
174 {
175 return $this->categories;
176 }
177 else
178 {
179 return null;
180 }
181 }
182
183 public function get_channels()
184 {
185 if ($this->channels !== null)
186 {
187 return $this->channels;
188 }
189 else
190 {
191 return null;
192 }
193 }
194
195 public function get_copyright()
196 {
197 if ($this->copyright !== null)
198 {
199 return $this->copyright;
200 }
201 else
202 {
203 return null;
204 }
205 }
206
207 public function get_credit($key = 0)
208 {
209 $credits = $this->get_credits();
210 if (isset($credits[$key]))
211 {
212 return $credits[$key];
213 }
214 else
215 {
216 return null;
217 }
218 }
219
220 public function get_credits()
221 {
222 if ($this->credits !== null)
223 {
224 return $this->credits;
225 }
226 else
227 {
228 return null;
229 }
230 }
231
232 public function get_description()
233 {
234 if ($this->description !== null)
235 {
236 return $this->description;
237 }
238 else
239 {
240 return null;
241 }
242 }
243
244 public function get_duration($convert = false)
245 {
246 if ($this->duration !== null)
247 {
248 if ($convert)
249 {
250 $time = SimplePie_Misc::time_hms($this->duration);
251 return $time;
252 }
253 else
254 {
255 return $this->duration;
256 }
257 }
258 else
259 {
260 return null;
261 }
262 }
263
264 public function get_expression()
265 {
266 if ($this->expression !== null)
267 {
268 return $this->expression;
269 }
270 else
271 {
272 return 'full';
273 }
274 }
275
276 public function get_extension()
277 {
278 if ($this->link !== null)
279 {
280 $url = SimplePie_Misc::parse_url($this->link);
281 if ($url['path'] !== '')
282 {
283 return pathinfo($url['path'], PATHINFO_EXTENSION);
284 }
285 }
286 return null;
287 }
288
289 public function get_framerate()
290 {
291 if ($this->framerate !== null)
292 {
293 return $this->framerate;
294 }
295 else
296 {
297 return null;
298 }
299 }
300
301 public function get_handler()
302 {
303 return $this->get_real_type(true);
304 }
305
306 public function get_hash($key = 0)
307 {
308 $hashes = $this->get_hashes();
309 if (isset($hashes[$key]))
310 {
311 return $hashes[$key];
312 }
313 else
314 {
315 return null;
316 }
317 }
318
319 public function get_hashes()
320 {
321 if ($this->hashes !== null)
322 {
323 return $this->hashes;
324 }
325 else
326 {
327 return null;
328 }
329 }
330
331 public function get_height()
332 {
333 if ($this->height !== null)
334 {
335 return $this->height;
336 }
337 else
338 {
339 return null;
340 }
341 }
342
343 public function get_language()
344 {
345 if ($this->lang !== null)
346 {
347 return $this->lang;
348 }
349 else
350 {
351 return null;
352 }
353 }
354
355 public function get_keyword($key = 0)
356 {
357 $keywords = $this->get_keywords();
358 if (isset($keywords[$key]))
359 {
360 return $keywords[$key];
361 }
362 else
363 {
364 return null;
365 }
366 }
367
368 public function get_keywords()
369 {
370 if ($this->keywords !== null)
371 {
372 return $this->keywords;
373 }
374 else
375 {
376 return null;
377 }
378 }
379
380 public function get_length()
381 {
382 if ($this->length !== null)
383 {
384 return $this->length;
385 }
386 else
387 {
388 return null;
389 }
390 }
391
392 public function get_link()
393 {
394 if ($this->link !== null)
395 {
396 return urldecode($this->link);
397 }
398 else
399 {
400 return null;
401 }
402 }
403
404 public function get_medium()
405 {
406 if ($this->medium !== null)
407 {
408 return $this->medium;
409 }
410 else
411 {
412 return null;
413 }
414 }
415
416 public function get_player()
417 {
418 if ($this->player !== null)
419 {
420 return $this->player;
421 }
422 else
423 {
424 return null;
425 }
426 }
427
428 public function get_rating($key = 0)
429 {
430 $ratings = $this->get_ratings();
431 if (isset($ratings[$key]))
432 {
433 return $ratings[$key];
434 }
435 else
436 {
437 return null;
438 }
439 }
440
441 public function get_ratings()
442 {
443 if ($this->ratings !== null)
444 {
445 return $this->ratings;
446 }
447 else
448 {
449 return null;
450 }
451 }
452
453 public function get_restriction($key = 0)
454 {
455 $restrictions = $this->get_restrictions();
456 if (isset($restrictions[$key]))
457 {
458 return $restrictions[$key];
459 }
460 else
461 {
462 return null;
463 }
464 }
465
466 public function get_restrictions()
467 {
468 if ($this->restrictions !== null)
469 {
470 return $this->restrictions;
471 }
472 else
473 {
474 return null;
475 }
476 }
477
478 public function get_sampling_rate()
479 {
480 if ($this->samplingrate !== null)
481 {
482 return $this->samplingrate;
483 }
484 else
485 {
486 return null;
487 }
488 }
489
490 public function get_size()
491 {
492 $length = $this->get_length();
493 if ($length !== null)
494 {
495 return round($length/1048576, 2);
496 }
497 else
498 {
499 return null;
500 }
501 }
502
503 public function get_thumbnail($key = 0)
504 {
505 $thumbnails = $this->get_thumbnails();
506 if (isset($thumbnails[$key]))
507 {
508 return $thumbnails[$key];
509 }
510 else
511 {
512 return null;
513 }
514 }
515
516 public function get_thumbnails()
517 {
518 if ($this->thumbnails !== null)
519 {
520 return $this->thumbnails;
521 }
522 else
523 {
524 return null;
525 }
526 }
527
528 public function get_title()
529 {
530 if ($this->title !== null)
531 {
532 return $this->title;
533 }
534 else
535 {
536 return null;
537 }
538 }
539
540 public function get_type()
541 {
542 if ($this->type !== null)
543 {
544 return $this->type;
545 }
546 else
547 {
548 return null;
549 }
550 }
551
552 public function get_width()
553 {
554 if ($this->width !== null)
555 {
556 return $this->width;
557 }
558 else
559 {
560 return null;
561 }
562 }
563
564 public function native_embed($options='')
565 {
566 return $this->embed($options, true);
567 }
568
569 /**
570 * @todo If the dimensions for media:content are defined, use them when width/height are set to 'auto'.
571 */
572 public function embed($options = '', $native = false)
573 {
574 // Set up defaults
575 $audio = '';
576 $video = '';
577 $alt = '';
578 $altclass = '';
579 $loop = 'false';
580 $width = 'auto';
581 $height = 'auto';
582 $bgcolor = '#ffffff';
583 $mediaplayer = '';
584 $widescreen = false;
585 $handler = $this->get_handler();
586 $type = $this->get_real_type();
587
588 // Process options and reassign values as necessary
589 if (is_array($options))
590 {
591 extract($options);
592 }
593 else
594 {
595 $options = explode(',', $options);
596 foreach($options as $option)
597 {
598 $opt = explode(':', $option, 2);
599 if (isset($opt[0], $opt[1]))
600 {
601 $opt[0] = trim($opt[0]);
602 $opt[1] = trim($opt[1]);
603 switch ($opt[0])
604 {
605 case 'audio':
606 $audio = $opt[1];
607 break;
608
609 case 'video':
610 $video = $opt[1];
611 break;
612
613 case 'alt':
614 $alt = $opt[1];
615 break;
616
617 case 'altclass':
618 $altclass = $opt[1];
619 break;
620
621 case 'loop':
622 $loop = $opt[1];
623 break;
624
625 case 'width':
626 $width = $opt[1];
627 break;
628
629 case 'height':
630 $height = $opt[1];
631 break;
632
633 case 'bgcolor':
634 $bgcolor = $opt[1];
635 break;
636
637 case 'mediaplayer':
638 $mediaplayer = $opt[1];
639 break;
640
641 case 'widescreen':
642 $widescreen = $opt[1];
643 break;
644 }
645 }
646 }
647 }
648
649 $mime = explode('/', $type, 2);
650 $mime = $mime[0];
651
652 // Process values for 'auto'
653 if ($width === 'auto')
654 {
655 if ($mime === 'video')
656 {
657 if ($height === 'auto')
658 {
659 $width = 480;
660 }
661 elseif ($widescreen)
662 {
663 $width = round((intval($height)/9)*16);
664 }
665 else
666 {
667 $width = round((intval($height)/3)*4);
668 }
669 }
670 else
671 {
672 $width = '100%';
673 }
674 }
675
676 if ($height === 'auto')
677 {
678 if ($mime === 'audio')
679 {
680 $height = 0;
681 }
682 elseif ($mime === 'video')
683 {
684 if ($width === 'auto')
685 {
686 if ($widescreen)
687 {
688 $height = 270;
689 }
690 else
691 {
692 $height = 360;
693 }
694 }
695 elseif ($widescreen)
696 {
697 $height = round((intval($width)/16)*9);
698 }
699 else
700 {
701 $height = round((intval($width)/4)*3);
702 }
703 }
704 else
705 {
706 $height = 376;
707 }
708 }
709 elseif ($mime === 'audio')
710 {
711 $height = 0;
712 }
713
714 // Set proper placeholder value
715 if ($mime === 'audio')
716 {
717 $placeholder = $audio;
718 }
719 elseif ($mime === 'video')
720 {
721 $placeholder = $video;
722 }
723
724 $embed = '';
725
726 // Odeo Feed MP3's
727 if ($handler === 'odeo')
728 {
729 if ($native)
730 {
731 $embed .= '<embed src="http://odeo.com/flash/audio_player_fullsize.swf" pluginspage="http://adobe.com/go/getflashplayer" type="application/x-shockwave-flash" quality="high" width="440" height="80" wmode="transparent" allowScriptAccess="any" flashvars="valid_sample_rate=true&external_url=' . $this->get_link() . '"></embed>';
732 }
733 else
734 {
735 $embed .= '<script type="text/javascript">embed_odeo("' . $this->get_link() . '");</script>';
736 }
737 }
738
739 // Flash
740 elseif ($handler === 'flash')
741 {
742 if ($native)
743 {
744 $embed .= "<embed src=\"" . $this->get_link() . "\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"$type\" quality=\"high\" width=\"$width\" height=\"$height\" bgcolor=\"$bgcolor\" loop=\"$loop\"></embed>";
745 }
746 else
747 {
748 $embed .= "<script type='text/javascript'>embed_flash('$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$loop', '$type');</script>";
749 }
750 }
751
752 // Flash Media Player file types.
753 // Preferred handler for MP3 file types.
754 elseif ($handler === 'fmedia' || ($handler === 'mp3' && $mediaplayer !== ''))
755 {
756 $height += 20;
757 if ($native)
758 {
759 $embed .= "<embed src=\"$mediaplayer\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"application/x-shockwave-flash\" quality=\"high\" width=\"$width\" height=\"$height\" wmode=\"transparent\" flashvars=\"file=" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "&autostart=false&repeat=$loop&showdigits=true&showfsbutton=false\"></embed>";
760 }
761 else
762 {
763 $embed .= "<script type='text/javascript'>embed_flv('$width', '$height', '" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "', '$placeholder', '$loop', '$mediaplayer');</script>";
764 }
765 }
766
767 // QuickTime 7 file types. Need to test with QuickTime 6.
768 // Only handle MP3's if the Flash Media Player is not present.
769 elseif ($handler === 'quicktime' || ($handler === 'mp3' && $mediaplayer === ''))
770 {
771 $height += 16;
772 if ($native)
773 {
774 if ($placeholder !== '')
775 {
776 $embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" href=\"" . $this->get_link() . "\" src=\"$placeholder\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"false\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\"></embed>";
777 }
778 else
779 {
780 $embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" src=\"" . $this->get_link() . "\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"true\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\"></embed>";
781 }
782 }
783 else
784 {
785 $embed .= "<script type='text/javascript'>embed_quicktime('$type', '$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$placeholder', '$loop');</script>";
786 }
787 }
788
789 // Windows Media
790 elseif ($handler === 'wmedia')
791 {
792 $height += 45;
793 if ($native)
794 {
795 $embed .= "<embed type=\"application/x-mplayer2\" src=\"" . $this->get_link() . "\" autosize=\"1\" width=\"$width\" height=\"$height\" showcontrols=\"1\" showstatusbar=\"0\" showdisplay=\"0\" autostart=\"0\"></embed>";
796 }
797 else
798 {
799 $embed .= "<script type='text/javascript'>embed_wmedia('$width', '$height', '" . $this->get_link() . "');</script>";
800 }
801 }
802
803 // Everything else
804 else $embed .= '<a href="' . $this->get_link() . '" class="' . $altclass . '">' . $alt . '</a>';
805
806 return $embed;
807 }
808
809 public function get_real_type($find_handler = false)
810 {
811 // If it's Odeo, let's get it out of the way.
812 if (substr(strtolower($this->get_link()), 0, 15) === 'http://odeo.com')
813 {
814 return 'odeo';
815 }
816
817 // Mime-types by handler.
818 $types_flash = array('application/x-shockwave-flash', 'application/futuresplash'); // Flash
819 $types_fmedia = array('video/flv', 'video/x-flv','flv-application/octet-stream'); // Flash Media Player
820 $types_quicktime = array('audio/3gpp', 'audio/3gpp2', 'audio/aac', 'audio/x-aac', 'audio/aiff', 'audio/x-aiff', 'audio/mid', 'audio/midi', 'audio/x-midi', 'audio/mp4', 'audio/m4a', 'audio/x-m4a', 'audio/wav', 'audio/x-wav', 'video/3gpp', 'video/3gpp2', 'video/m4v', 'video/x-m4v', 'video/mp4', 'video/mpeg', 'video/x-mpeg', 'video/quicktime', 'video/sd-video'); // QuickTime
821 $types_wmedia = array('application/asx', 'application/x-mplayer2', 'audio/x-ms-wma', 'audio/x-ms-wax', 'video/x-ms-asf-plugin', 'video/x-ms-asf', 'video/x-ms-wm', 'video/x-ms-wmv', 'video/x-ms-wvx'); // Windows Media
822 $types_mp3 = array('audio/mp3', 'audio/x-mp3', 'audio/mpeg', 'audio/x-mpeg'); // MP3
823
824 if ($this->get_type() !== null)
825 {
826 $type = strtolower($this->type);
827 }
828 else
829 {
830 $type = null;
831 }
832
833 // If we encounter an unsupported mime-type, check the file extension and guess intelligently.
834 if (!in_array($type, array_merge($types_flash, $types_fmedia, $types_quicktime, $types_wmedia, $types_mp3)))
835 {
836 switch (strtolower($this->get_extension()))
837 {
838 // Audio mime-types
839 case 'aac':
840 case 'adts':
841 $type = 'audio/acc';
842 break;
843
844 case 'aif':
845 case 'aifc':
846 case 'aiff':
847 case 'cdda':
848 $type = 'audio/aiff';
849 break;
850
851 case 'bwf':
852 $type = 'audio/wav';
853 break;
854
855 case 'kar':
856 case 'mid':
857 case 'midi':
858 case 'smf':
859 $type = 'audio/midi';
860 break;
861
862 case 'm4a':
863 $type = 'audio/x-m4a';
864 break;
865
866 case 'mp3':
867 case 'swa':
868 $type = 'audio/mp3';
869 break;
870
871 case 'wav':
872 $type = 'audio/wav';
873 break;
874
875 case 'wax':
876 $type = 'audio/x-ms-wax';
877 break;
878
879 case 'wma':
880 $type = 'audio/x-ms-wma';
881 break;
882
883 // Video mime-types
884 case '3gp':
885 case '3gpp':
886 $type = 'video/3gpp';
887 break;
888
889 case '3g2':
890 case '3gp2':
891 $type = 'video/3gpp2';
892 break;
893
894 case 'asf':
895 $type = 'video/x-ms-asf';
896 break;
897
898 case 'flv':
899 $type = 'video/x-flv';
900 break;
901
902 case 'm1a':
903 case 'm1s':
904 case 'm1v':
905 case 'm15':
906 case 'm75':
907 case 'mp2':
908 case 'mpa':
909 case 'mpeg':
910 case 'mpg':
911 case 'mpm':
912 case 'mpv':
913 $type = 'video/mpeg';
914 break;
915
916 case 'm4v':
917 $type = 'video/x-m4v';
918 break;
919
920 case 'mov':
921 case 'qt':
922 $type = 'video/quicktime';
923 break;
924
925 case 'mp4':
926 case 'mpg4':
927 $type = 'video/mp4';
928 break;
929
930 case 'sdv':
931 $type = 'video/sd-video';
932 break;
933
934 case 'wm':
935 $type = 'video/x-ms-wm';
936 break;
937
938 case 'wmv':
939 $type = 'video/x-ms-wmv';
940 break;
941
942 case 'wvx':
943 $type = 'video/x-ms-wvx';
944 break;
945
946 // Flash mime-types
947 case 'spl':
948 $type = 'application/futuresplash';
949 break;
950
951 case 'swf':
952 $type = 'application/x-shockwave-flash';
953 break;
954 }
955 }
956
957 if ($find_handler)
958 {
959 if (in_array($type, $types_flash))
960 {
961 return 'flash';
962 }
963 elseif (in_array($type, $types_fmedia))
964 {
965 return 'fmedia';
966 }
967 elseif (in_array($type, $types_quicktime))
968 {
969 return 'quicktime';
970 }
971 elseif (in_array($type, $types_wmedia))
972 {
973 return 'wmedia';
974 }
975 elseif (in_array($type, $types_mp3))
976 {
977 return 'mp3';
978 }
979 else
980 {
981 return null;
982 }
983 }
984 else
985 {
986 return $type;
987 }
988 }
989}
990
diff --git a/inc/3rdparty/simplepie/SimplePie/File.php b/inc/3rdparty/simplepie/SimplePie/File.php
new file mode 100644
index 00000000..55e74079
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/File.php
@@ -0,0 +1,278 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47/**
48 * @todo Move to properly supporting RFC2616 (HTTP/1.1)
49 */
50class SimplePie_File
51{
52 var $url;
53 var $useragent;
54 var $success = true;
55 var $headers = array();
56 var $body;
57 var $status_code;
58 var $redirects = 0;
59 var $error;
60 var $method = SIMPLEPIE_FILE_SOURCE_NONE;
61
62 public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false)
63 {
64 if (class_exists('idna_convert'))
65 {
66 $idn = new idna_convert();
67 $parsed = SimplePie_Misc::parse_url($url);
68 $url = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']);
69 }
70 $this->url = $url;
71 $this->useragent = $useragent;
72 if (preg_match('/^http(s)?:\/\//i', $url))
73 {
74 if ($useragent === null)
75 {
76 $useragent = ini_get('user_agent');
77 $this->useragent = $useragent;
78 }
79 if (!is_array($headers))
80 {
81 $headers = array();
82 }
83 if (!$force_fsockopen && function_exists('curl_exec'))
84 {
85 $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_CURL;
86 $fp = curl_init();
87 $headers2 = array();
88 foreach ($headers as $key => $value)
89 {
90 $headers2[] = "$key: $value";
91 }
92 if (version_compare(SimplePie_Misc::get_curl_version(), '7.10.5', '>='))
93 {
94 curl_setopt($fp, CURLOPT_ENCODING, '');
95 }
96 curl_setopt($fp, CURLOPT_URL, $url);
97 curl_setopt($fp, CURLOPT_HEADER, 1);
98 curl_setopt($fp, CURLOPT_RETURNTRANSFER, 1);
99 curl_setopt($fp, CURLOPT_TIMEOUT, $timeout);
100 curl_setopt($fp, CURLOPT_CONNECTTIMEOUT, $timeout);
101 curl_setopt($fp, CURLOPT_REFERER, $url);
102 curl_setopt($fp, CURLOPT_USERAGENT, $useragent);
103 curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2);
104 if (!ini_get('open_basedir') && !ini_get('safe_mode') && version_compare(SimplePie_Misc::get_curl_version(), '7.15.2', '>='))
105 {
106 curl_setopt($fp, CURLOPT_FOLLOWLOCATION, 1);
107 curl_setopt($fp, CURLOPT_MAXREDIRS, $redirects);
108 }
109
110 $this->headers = curl_exec($fp);
111 if (curl_errno($fp) === 23 || curl_errno($fp) === 61)
112 {
113 curl_setopt($fp, CURLOPT_ENCODING, 'none');
114 $this->headers = curl_exec($fp);
115 }
116 if (curl_errno($fp))
117 {
118 $this->error = 'cURL error ' . curl_errno($fp) . ': ' . curl_error($fp);
119 $this->success = false;
120 }
121 else
122 {
123 $info = curl_getinfo($fp);
124 curl_close($fp);
125 $this->headers = explode("\r\n\r\n", $this->headers, $info['redirect_count'] + 1);
126 $this->headers = array_pop($this->headers);
127 $parser = new SimplePie_HTTP_Parser($this->headers);
128 if ($parser->parse())
129 {
130 $this->headers = $parser->headers;
131 $this->body = $parser->body;
132 $this->status_code = $parser->status_code;
133 if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects)
134 {
135 $this->redirects++;
136 $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url);
137 return $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen);
138 }
139 }
140 }
141 }
142 else
143 {
144 $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_FSOCKOPEN;
145 $url_parts = parse_url($url);
146 $socket_host = $url_parts['host'];
147 if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https')
148 {
149 $socket_host = "ssl://$url_parts[host]";
150 $url_parts['port'] = 443;
151 }
152 if (!isset($url_parts['port']))
153 {
154 $url_parts['port'] = 80;
155 }
156 $fp = @fsockopen($socket_host, $url_parts['port'], $errno, $errstr, $timeout);
157 if (!$fp)
158 {
159 $this->error = 'fsockopen error: ' . $errstr;
160 $this->success = false;
161 }
162 else
163 {
164 stream_set_timeout($fp, $timeout);
165 if (isset($url_parts['path']))
166 {
167 if (isset($url_parts['query']))
168 {
169 $get = "$url_parts[path]?$url_parts[query]";
170 }
171 else
172 {
173 $get = $url_parts['path'];
174 }
175 }
176 else
177 {
178 $get = '/';
179 }
180 $out = "GET $get HTTP/1.1\r\n";
181 $out .= "Host: $url_parts[host]\r\n";
182 $out .= "User-Agent: $useragent\r\n";
183 if (extension_loaded('zlib'))
184 {
185 $out .= "Accept-Encoding: x-gzip,gzip,deflate\r\n";
186 }
187
188 if (isset($url_parts['user']) && isset($url_parts['pass']))
189 {
190 $out .= "Authorization: Basic " . base64_encode("$url_parts[user]:$url_parts[pass]") . "\r\n";
191 }
192 foreach ($headers as $key => $value)
193 {
194 $out .= "$key: $value\r\n";
195 }
196 $out .= "Connection: Close\r\n\r\n";
197 fwrite($fp, $out);
198
199 $info = stream_get_meta_data($fp);
200
201 $this->headers = '';
202 while (!$info['eof'] && !$info['timed_out'])
203 {
204 $this->headers .= fread($fp, 1160);
205 $info = stream_get_meta_data($fp);
206 }
207 if (!$info['timed_out'])
208 {
209 $parser = new SimplePie_HTTP_Parser($this->headers);
210 if ($parser->parse())
211 {
212 $this->headers = $parser->headers;
213 $this->body = $parser->body;
214 $this->status_code = $parser->status_code;
215 if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects)
216 {
217 $this->redirects++;
218 $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url);
219 return $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen);
220 }
221 if (isset($this->headers['content-encoding']))
222 {
223 // Hey, we act dumb elsewhere, so let's do that here too
224 switch (strtolower(trim($this->headers['content-encoding'], "\x09\x0A\x0D\x20")))
225 {
226 case 'gzip':
227 case 'x-gzip':
228 $decoder = new SimplePie_gzdecode($this->body);
229 if (!$decoder->parse())
230 {
231 $this->error = 'Unable to decode HTTP "gzip" stream';
232 $this->success = false;
233 }
234 else
235 {
236 $this->body = $decoder->data;
237 }
238 break;
239
240 case 'deflate':
241 if (($body = gzuncompress($this->body)) === false)
242 {
243 if (($body = gzinflate($this->body)) === false)
244 {
245 $this->error = 'Unable to decode HTTP "deflate" stream';
246 $this->success = false;
247 }
248 }
249 $this->body = $body;
250 break;
251
252 default:
253 $this->error = 'Unknown content coding';
254 $this->success = false;
255 }
256 }
257 }
258 }
259 else
260 {
261 $this->error = 'fsocket timed out';
262 $this->success = false;
263 }
264 fclose($fp);
265 }
266 }
267 }
268 else
269 {
270 $this->method = SIMPLEPIE_FILE_SOURCE_LOCAL | SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS;
271 if (!$this->body = file_get_contents($url))
272 {
273 $this->error = 'file_get_contents could not read the file';
274 $this->success = false;
275 }
276 }
277 }
278}
diff --git a/inc/3rdparty/simplepie/SimplePie/HTTP/Parser.php b/inc/3rdparty/simplepie/SimplePie/HTTP/Parser.php
new file mode 100644
index 00000000..cc9660c6
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/HTTP/Parser.php
@@ -0,0 +1,492 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47/**
48 * HTTP Response Parser
49 *
50 * @package SimplePie
51 */
52class SimplePie_HTTP_Parser
53{
54 /**
55 * HTTP Version
56 *
57 * @var float
58 */
59 public $http_version = 0.0;
60
61 /**
62 * Status code
63 *
64 * @var int
65 */
66 public $status_code = 0;
67
68 /**
69 * Reason phrase
70 *
71 * @var string
72 */
73 public $reason = '';
74
75 /**
76 * Key/value pairs of the headers
77 *
78 * @var array
79 */
80 public $headers = array();
81
82 /**
83 * Body of the response
84 *
85 * @var string
86 */
87 public $body = '';
88
89 /**
90 * Current state of the state machine
91 *
92 * @var string
93 */
94 protected $state = 'http_version';
95
96 /**
97 * Input data
98 *
99 * @var string
100 */
101 protected $data = '';
102
103 /**
104 * Input data length (to avoid calling strlen() everytime this is needed)
105 *
106 * @var int
107 */
108 protected $data_length = 0;
109
110 /**
111 * Current position of the pointer
112 *
113 * @var int
114 */
115 protected $position = 0;
116
117 /**
118 * Name of the hedaer currently being parsed
119 *
120 * @var string
121 */
122 protected $name = '';
123
124 /**
125 * Value of the hedaer currently being parsed
126 *
127 * @var string
128 */
129 protected $value = '';
130
131 /**
132 * Create an instance of the class with the input data
133 *
134 * @param string $data Input data
135 */
136 public function __construct($data)
137 {
138 $this->data = $data;
139 $this->data_length = strlen($this->data);
140 }
141
142 /**
143 * Parse the input data
144 *
145 * @return bool true on success, false on failure
146 */
147 public function parse()
148 {
149 while ($this->state && $this->state !== 'emit' && $this->has_data())
150 {
151 $state = $this->state;
152 $this->$state();
153 }
154 $this->data = '';
155 if ($this->state === 'emit' || $this->state === 'body')
156 {
157 return true;
158 }
159 else
160 {
161 $this->http_version = '';
162 $this->status_code = '';
163 $this->reason = '';
164 $this->headers = array();
165 $this->body = '';
166 return false;
167 }
168 }
169
170 /**
171 * Check whether there is data beyond the pointer
172 *
173 * @return bool true if there is further data, false if not
174 */
175 protected function has_data()
176 {
177 return (bool) ($this->position < $this->data_length);
178 }
179
180 /**
181 * See if the next character is LWS
182 *
183 * @return bool true if the next character is LWS, false if not
184 */
185 protected function is_linear_whitespace()
186 {
187 return (bool) ($this->data[$this->position] === "\x09"
188 || $this->data[$this->position] === "\x20"
189 || ($this->data[$this->position] === "\x0A"
190 && isset($this->data[$this->position + 1])
191 && ($this->data[$this->position + 1] === "\x09" || $this->data[$this->position + 1] === "\x20")));
192 }
193
194 /**
195 * Parse the HTTP version
196 */
197 protected function http_version()
198 {
199 if (strpos($this->data, "\x0A") !== false && strtoupper(substr($this->data, 0, 5)) === 'HTTP/')
200 {
201 $len = strspn($this->data, '0123456789.', 5);
202 $this->http_version = substr($this->data, 5, $len);
203 $this->position += 5 + $len;
204 if (substr_count($this->http_version, '.') <= 1)
205 {
206 $this->http_version = (float) $this->http_version;
207 $this->position += strspn($this->data, "\x09\x20", $this->position);
208 $this->state = 'status';
209 }
210 else
211 {
212 $this->state = false;
213 }
214 }
215 else
216 {
217 $this->state = false;
218 }
219 }
220
221 /**
222 * Parse the status code
223 */
224 protected function status()
225 {
226 if ($len = strspn($this->data, '0123456789', $this->position))
227 {
228 $this->status_code = (int) substr($this->data, $this->position, $len);
229 $this->position += $len;
230 $this->state = 'reason';
231 }
232 else
233 {
234 $this->state = false;
235 }
236 }
237
238 /**
239 * Parse the reason phrase
240 */
241 protected function reason()
242 {
243 $len = strcspn($this->data, "\x0A", $this->position);
244 $this->reason = trim(substr($this->data, $this->position, $len), "\x09\x0D\x20");
245 $this->position += $len + 1;
246 $this->state = 'new_line';
247 }
248
249 /**
250 * Deal with a new line, shifting data around as needed
251 */
252 protected function new_line()
253 {
254 $this->value = trim($this->value, "\x0D\x20");
255 if ($this->name !== '' && $this->value !== '')
256 {
257 $this->name = strtolower($this->name);
258 // We should only use the last Content-Type header. c.f. issue #1
259 if (isset($this->headers[$this->name]) && $this->name !== 'content-type')
260 {
261 $this->headers[$this->name] .= ', ' . $this->value;
262 }
263 else
264 {
265 $this->headers[$this->name] = $this->value;
266 }
267 }
268 $this->name = '';
269 $this->value = '';
270 if (substr($this->data[$this->position], 0, 2) === "\x0D\x0A")
271 {
272 $this->position += 2;
273 $this->state = 'body';
274 }
275 elseif ($this->data[$this->position] === "\x0A")
276 {
277 $this->position++;
278 $this->state = 'body';
279 }
280 else
281 {
282 $this->state = 'name';
283 }
284 }
285
286 /**
287 * Parse a header name
288 */
289 protected function name()
290 {
291 $len = strcspn($this->data, "\x0A:", $this->position);
292 if (isset($this->data[$this->position + $len]))
293 {
294 if ($this->data[$this->position + $len] === "\x0A")
295 {
296 $this->position += $len;
297 $this->state = 'new_line';
298 }
299 else
300 {
301 $this->name = substr($this->data, $this->position, $len);
302 $this->position += $len + 1;
303 $this->state = 'value';
304 }
305 }
306 else
307 {
308 $this->state = false;
309 }
310 }
311
312 /**
313 * Parse LWS, replacing consecutive LWS characters with a single space
314 */
315 protected function linear_whitespace()
316 {
317 do
318 {
319 if (substr($this->data, $this->position, 2) === "\x0D\x0A")
320 {
321 $this->position += 2;
322 }
323 elseif ($this->data[$this->position] === "\x0A")
324 {
325 $this->position++;
326 }
327 $this->position += strspn($this->data, "\x09\x20", $this->position);
328 } while ($this->has_data() && $this->is_linear_whitespace());
329 $this->value .= "\x20";
330 }
331
332 /**
333 * See what state to move to while within non-quoted header values
334 */
335 protected function value()
336 {
337 if ($this->is_linear_whitespace())
338 {
339 $this->linear_whitespace();
340 }
341 else
342 {
343 switch ($this->data[$this->position])
344 {
345 case '"':
346 // Workaround for ETags: we have to include the quotes as
347 // part of the tag.
348 if (strtolower($this->name) === 'etag')
349 {
350 $this->value .= '"';
351 $this->position++;
352 $this->state = 'value_char';
353 break;
354 }
355 $this->position++;
356 $this->state = 'quote';
357 break;
358
359 case "\x0A":
360 $this->position++;
361 $this->state = 'new_line';
362 break;
363
364 default:
365 $this->state = 'value_char';
366 break;
367 }
368 }
369 }
370
371 /**
372 * Parse a header value while outside quotes
373 */
374 protected function value_char()
375 {
376 $len = strcspn($this->data, "\x09\x20\x0A\"", $this->position);
377 $this->value .= substr($this->data, $this->position, $len);
378 $this->position += $len;
379 $this->state = 'value';
380 }
381
382 /**
383 * See what state to move to while within quoted header values
384 */
385 protected function quote()
386 {
387 if ($this->is_linear_whitespace())
388 {
389 $this->linear_whitespace();
390 }
391 else
392 {
393 switch ($this->data[$this->position])
394 {
395 case '"':
396 $this->position++;
397 $this->state = 'value';
398 break;
399
400 case "\x0A":
401 $this->position++;
402 $this->state = 'new_line';
403 break;
404
405 case '\\':
406 $this->position++;
407 $this->state = 'quote_escaped';
408 break;
409
410 default:
411 $this->state = 'quote_char';
412 break;
413 }
414 }
415 }
416
417 /**
418 * Parse a header value while within quotes
419 */
420 protected function quote_char()
421 {
422 $len = strcspn($this->data, "\x09\x20\x0A\"\\", $this->position);
423 $this->value .= substr($this->data, $this->position, $len);
424 $this->position += $len;
425 $this->state = 'value';
426 }
427
428 /**
429 * Parse an escaped character within quotes
430 */
431 protected function quote_escaped()
432 {
433 $this->value .= $this->data[$this->position];
434 $this->position++;
435 $this->state = 'quote';
436 }
437
438 /**
439 * Parse the body
440 */
441 protected function body()
442 {
443 $this->body = substr($this->data, $this->position);
444 if (!empty($this->headers['transfer-encoding']))
445 {
446 unset($this->headers['transfer-encoding']);
447 $this->state = 'chunked';
448 }
449 else
450 {
451 $this->state = 'emit';
452 }
453 }
454
455 /**
456 * Parsed a "Transfer-Encoding: chunked" body
457 */
458 protected function chunked()
459 {
460 if (!preg_match('/^[0-9a-f]+(\s|\r|\n)+/mi', trim($this->body)))
461 {
462 $this->state = 'emit';
463 return;
464 }
465
466 $decoded = '';
467 $encoded = $this->body;
468
469 while (true)
470 {
471 $is_chunked = (bool) preg_match( '/^([0-9a-f]+)(\s|\r|\n)+/mi', $encoded, $matches );
472 if (!$is_chunked)
473 {
474 // Looks like it's not chunked after all
475 $this->state = 'emit';
476 return;
477 }
478
479 $length = hexdec($matches[1]);
480 $chunk_length = strlen($matches[0]);
481 $decoded .= $part = substr($encoded, $chunk_length, $length);
482 $encoded = ltrim(substr($encoded, $chunk_length + $length), "\r\n");
483
484 if (trim($encoded) === '0')
485 {
486 $this->state = 'emit';
487 $this->body = $decoded;
488 return;
489 }
490 }
491 }
492}
diff --git a/inc/3rdparty/simplepie/SimplePie/IRI.php b/inc/3rdparty/simplepie/SimplePie/IRI.php
new file mode 100644
index 00000000..0fead324
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/IRI.php
@@ -0,0 +1,997 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46/**
47 * IRI parser/serialiser
48 *
49 * @package SimplePie
50 */
51class SimplePie_IRI
52{
53 /**
54 * Scheme
55 *
56 * @access private
57 * @var string
58 */
59 var $scheme;
60
61 /**
62 * User Information
63 *
64 * @access private
65 * @var string
66 */
67 var $userinfo;
68
69 /**
70 * Host
71 *
72 * @access private
73 * @var string
74 */
75 var $host;
76
77 /**
78 * Port
79 *
80 * @access private
81 * @var string
82 */
83 var $port;
84
85 /**
86 * Path
87 *
88 * @access private
89 * @var string
90 */
91 var $path;
92
93 /**
94 * Query
95 *
96 * @access private
97 * @var string
98 */
99 var $query;
100
101 /**
102 * Fragment
103 *
104 * @access private
105 * @var string
106 */
107 var $fragment;
108
109 /**
110 * Whether the object represents a valid IRI
111 *
112 * @access private
113 * @var array
114 */
115 var $valid = array();
116
117 /**
118 * Return the entire IRI when you try and read the object as a string
119 *
120 * @access public
121 * @return string
122 */
123 public function __toString()
124 {
125 return $this->get_iri();
126 }
127
128 /**
129 * Create a new IRI object, from a specified string
130 *
131 * @access public
132 * @param string $iri
133 * @return SimplePie_IRI
134 */
135 public function __construct($iri)
136 {
137 $iri = (string) $iri;
138 if ($iri !== '')
139 {
140 $parsed = $this->parse_iri($iri);
141 $this->set_scheme($parsed['scheme']);
142 $this->set_authority($parsed['authority']);
143 $this->set_path($parsed['path']);
144 $this->set_query($parsed['query']);
145 $this->set_fragment($parsed['fragment']);
146 }
147 }
148
149 /**
150 * Create a new IRI object by resolving a relative IRI
151 *
152 * @static
153 * @access public
154 * @param SimplePie_IRI $base Base IRI
155 * @param string $relative Relative IRI
156 * @return SimplePie_IRI
157 */
158 public static function absolutize($base, $relative)
159 {
160 $relative = (string) $relative;
161 if ($relative !== '')
162 {
163 $relative = new SimplePie_IRI($relative);
164 if ($relative->get_scheme() !== null)
165 {
166 $target = $relative;
167 }
168 elseif ($base->get_iri() !== null)
169 {
170 if ($relative->get_authority() !== null)
171 {
172 $target = $relative;
173 $target->set_scheme($base->get_scheme());
174 }
175 else
176 {
177 $target = new SimplePie_IRI('');
178 $target->set_scheme($base->get_scheme());
179 $target->set_userinfo($base->get_userinfo());
180 $target->set_host($base->get_host());
181 $target->set_port($base->get_port());
182 if ($relative->get_path() !== null)
183 {
184 if (strpos($relative->get_path(), '/') === 0)
185 {
186 $target->set_path($relative->get_path());
187 }
188 elseif (($base->get_userinfo() !== null || $base->get_host() !== null || $base->get_port() !== null) && $base->get_path() === null)
189 {
190 $target->set_path('/' . $relative->get_path());
191 }
192 elseif (($last_segment = strrpos($base->get_path(), '/')) !== false)
193 {
194 $target->set_path(substr($base->get_path(), 0, $last_segment + 1) . $relative->get_path());
195 }
196 else
197 {
198 $target->set_path($relative->get_path());
199 }
200 $target->set_query($relative->get_query());
201 }
202 else
203 {
204 $target->set_path($base->get_path());
205 if ($relative->get_query() !== null)
206 {
207 $target->set_query($relative->get_query());
208 }
209 elseif ($base->get_query() !== null)
210 {
211 $target->set_query($base->get_query());
212 }
213 }
214 }
215 $target->set_fragment($relative->get_fragment());
216 }
217 else
218 {
219 // No base URL, just return the relative URL
220 $target = $relative;
221 }
222 }
223 else
224 {
225 $target = $base;
226 }
227 return $target;
228 }
229
230 /**
231 * Parse an IRI into scheme/authority/path/query/fragment segments
232 *
233 * @access private
234 * @param string $iri
235 * @return array
236 */
237 public function parse_iri($iri)
238 {
239 preg_match('/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/', $iri, $match);
240 for ($i = count($match); $i <= 9; $i++)
241 {
242 $match[$i] = '';
243 }
244 return array('scheme' => $match[2], 'authority' => $match[4], 'path' => $match[5], 'query' => $match[7], 'fragment' => $match[9]);
245 }
246
247 /**
248 * Remove dot segments from a path
249 *
250 * @access private
251 * @param string $input
252 * @return string
253 */
254 public function remove_dot_segments($input)
255 {
256 $output = '';
257 while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..')
258 {
259 // A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise,
260 if (strpos($input, '../') === 0)
261 {
262 $input = substr($input, 3);
263 }
264 elseif (strpos($input, './') === 0)
265 {
266 $input = substr($input, 2);
267 }
268 // 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,
269 elseif (strpos($input, '/./') === 0)
270 {
271 $input = substr_replace($input, '/', 0, 3);
272 }
273 elseif ($input === '/.')
274 {
275 $input = '/';
276 }
277 // 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,
278 elseif (strpos($input, '/../') === 0)
279 {
280 $input = substr_replace($input, '/', 0, 4);
281 $output = substr_replace($output, '', strrpos($output, '/'));
282 }
283 elseif ($input === '/..')
284 {
285 $input = '/';
286 $output = substr_replace($output, '', strrpos($output, '/'));
287 }
288 // D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise,
289 elseif ($input === '.' || $input === '..')
290 {
291 $input = '';
292 }
293 // 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
294 elseif (($pos = strpos($input, '/', 1)) !== false)
295 {
296 $output .= substr($input, 0, $pos);
297 $input = substr_replace($input, '', 0, $pos);
298 }
299 else
300 {
301 $output .= $input;
302 $input = '';
303 }
304 }
305 return $output . $input;
306 }
307
308 /**
309 * Replace invalid character with percent encoding
310 *
311 * @param string $string Input string
312 * @param string $valid_chars Valid characters not in iunreserved or iprivate (this is ASCII-only)
313 * @param int $case Normalise case
314 * @param bool $iprivate Allow iprivate
315 * @return string
316 */
317 protected function replace_invalid_with_pct_encoding($string, $valid_chars, $case = SIMPLEPIE_SAME_CASE, $iprivate = false)
318 {
319 // Normalize as many pct-encoded sections as possible
320 $string = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', array(&$this, 'remove_iunreserved_percent_encoded'), $string);
321
322 // Replace invalid percent characters
323 $string = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $string);
324
325 // Add unreserved and % to $valid_chars (the latter is safe because all
326 // pct-encoded sections are now valid).
327 $valid_chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~%';
328
329 // Now replace any bytes that aren't allowed with their pct-encoded versions
330 $position = 0;
331 $strlen = strlen($string);
332 while (($position += strspn($string, $valid_chars, $position)) < $strlen)
333 {
334 $value = ord($string[$position]);
335
336 // Start position
337 $start = $position;
338
339 // By default we are valid
340 $valid = true;
341
342 // No one byte sequences are valid due to the while.
343 // Two byte sequence:
344 if (($value & 0xE0) === 0xC0)
345 {
346 $character = ($value & 0x1F) << 6;
347 $length = 2;
348 $remaining = 1;
349 }
350 // Three byte sequence:
351 elseif (($value & 0xF0) === 0xE0)
352 {
353 $character = ($value & 0x0F) << 12;
354 $length = 3;
355 $remaining = 2;
356 }
357 // Four byte sequence:
358 elseif (($value & 0xF8) === 0xF0)
359 {
360 $character = ($value & 0x07) << 18;
361 $length = 4;
362 $remaining = 3;
363 }
364 // Invalid byte:
365 else
366 {
367 $valid = false;
368 $length = 1;
369 $remaining = 0;
370 }
371
372 if ($remaining)
373 {
374 if ($position + $length <= $strlen)
375 {
376 for ($position++; $remaining; $position++)
377 {
378 $value = ord($string[$position]);
379
380 // Check that the byte is valid, then add it to the character:
381 if (($value & 0xC0) === 0x80)
382 {
383 $character |= ($value & 0x3F) << (--$remaining * 6);
384 }
385 // If it is invalid, count the sequence as invalid and reprocess the current byte:
386 else
387 {
388 $valid = false;
389 $position--;
390 break;
391 }
392 }
393 }
394 else
395 {
396 $position = $strlen - 1;
397 $valid = false;
398 }
399 }
400
401 // Percent encode anything invalid or not in ucschar
402 if (
403 // Invalid sequences
404 !$valid
405 // Non-shortest form sequences are invalid
406 || $length > 1 && $character <= 0x7F
407 || $length > 2 && $character <= 0x7FF
408 || $length > 3 && $character <= 0xFFFF
409 // Outside of range of ucschar codepoints
410 // Noncharacters
411 || ($character & 0xFFFE) === 0xFFFE
412 || $character >= 0xFDD0 && $character <= 0xFDEF
413 || (
414 // Everything else not in ucschar
415 $character > 0xD7FF && $character < 0xF900
416 || $character < 0xA0
417 || $character > 0xEFFFD
418 )
419 && (
420 // Everything not in iprivate, if it applies
421 !$iprivate
422 || $character < 0xE000
423 || $character > 0x10FFFD
424 )
425 )
426 {
427 // If we were a character, pretend we weren't, but rather an error.
428 if ($valid)
429 $position--;
430
431 for ($j = $start; $j <= $position; $j++)
432 {
433 $string = substr_replace($string, sprintf('%%%02X', ord($string[$j])), $j, 1);
434 $j += 2;
435 $position += 2;
436 $strlen += 2;
437 }
438 }
439 }
440
441 // Normalise case
442 if ($case & SIMPLEPIE_LOWERCASE)
443 {
444 $string = strtolower($string);
445 }
446 elseif ($case & SIMPLEPIE_UPPERCASE)
447 {
448 $string = strtoupper($string);
449 }
450
451 return $string;
452 }
453
454 /**
455 * Callback function for preg_replace_callback.
456 *
457 * Removes sequences of percent encoded bytes that represent UTF-8
458 * encoded characters in iunreserved
459 *
460 * @param array $match PCRE match
461 * @return string Replacement
462 */
463 protected function remove_iunreserved_percent_encoded($match)
464 {
465 // As we just have valid percent encoded sequences we can just explode
466 // and ignore the first member of the returned array (an empty string).
467 $bytes = explode('%', $match[0]);
468
469 // Initialize the new string (this is what will be returned) and that
470 // there are no bytes remaining in the current sequence (unsurprising
471 // at the first byte!).
472 $string = '';
473 $remaining = 0;
474
475 // Loop over each and every byte, and set $value to its value
476 for ($i = 1, $len = count($bytes); $i < $len; $i++)
477 {
478 $value = hexdec($bytes[$i]);
479
480 // If we're the first byte of sequence:
481 if (!$remaining)
482 {
483 // Start position
484 $start = $i;
485
486 // By default we are valid
487 $valid = true;
488
489 // One byte sequence:
490 if ($value <= 0x7F)
491 {
492 $character = $value;
493 $length = 1;
494 }
495 // Two byte sequence:
496 elseif (($value & 0xE0) === 0xC0)
497 {
498 $character = ($value & 0x1F) << 6;
499 $length = 2;
500 $remaining = 1;
501 }
502 // Three byte sequence:
503 elseif (($value & 0xF0) === 0xE0)
504 {
505 $character = ($value & 0x0F) << 12;
506 $length = 3;
507 $remaining = 2;
508 }
509 // Four byte sequence:
510 elseif (($value & 0xF8) === 0xF0)
511 {
512 $character = ($value & 0x07) << 18;
513 $length = 4;
514 $remaining = 3;
515 }
516 // Invalid byte:
517 else
518 {
519 $valid = false;
520 $remaining = 0;
521 }
522 }
523 // Continuation byte:
524 else
525 {
526 // Check that the byte is valid, then add it to the character:
527 if (($value & 0xC0) === 0x80)
528 {
529 $remaining--;
530 $character |= ($value & 0x3F) << ($remaining * 6);
531 }
532 // If it is invalid, count the sequence as invalid and reprocess the current byte as the start of a sequence:
533 else
534 {
535 $valid = false;
536 $remaining = 0;
537 $i--;
538 }
539 }
540
541 // If we've reached the end of the current byte sequence, append it to Unicode::$data
542 if (!$remaining)
543 {
544 // Percent encode anything invalid or not in iunreserved
545 if (
546 // Invalid sequences
547 !$valid
548 // Non-shortest form sequences are invalid
549 || $length > 1 && $character <= 0x7F
550 || $length > 2 && $character <= 0x7FF
551 || $length > 3 && $character <= 0xFFFF
552 // Outside of range of iunreserved codepoints
553 || $character < 0x2D
554 || $character > 0xEFFFD
555 // Noncharacters
556 || ($character & 0xFFFE) === 0xFFFE
557 || $character >= 0xFDD0 && $character <= 0xFDEF
558 // Everything else not in iunreserved (this is all BMP)
559 || $character === 0x2F
560 || $character > 0x39 && $character < 0x41
561 || $character > 0x5A && $character < 0x61
562 || $character > 0x7A && $character < 0x7E
563 || $character > 0x7E && $character < 0xA0
564 || $character > 0xD7FF && $character < 0xF900
565 )
566 {
567 for ($j = $start; $j <= $i; $j++)
568 {
569 $string .= '%' . strtoupper($bytes[$j]);
570 }
571 }
572 else
573 {
574 for ($j = $start; $j <= $i; $j++)
575 {
576 $string .= chr(hexdec($bytes[$j]));
577 }
578 }
579 }
580 }
581
582 // If we have any bytes left over they are invalid (i.e., we are
583 // mid-way through a multi-byte sequence)
584 if ($remaining)
585 {
586 for ($j = $start; $j < $len; $j++)
587 {
588 $string .= '%' . strtoupper($bytes[$j]);
589 }
590 }
591
592 return $string;
593 }
594
595 /**
596 * Check if the object represents a valid IRI
597 *
598 * @access public
599 * @return bool
600 */
601 public function is_valid()
602 {
603 return array_sum($this->valid) === count($this->valid);
604 }
605
606 /**
607 * Set the scheme. Returns true on success, false on failure (if there are
608 * any invalid characters).
609 *
610 * @access public
611 * @param string $scheme
612 * @return bool
613 */
614 public function set_scheme($scheme)
615 {
616 if ($scheme === null || $scheme === '')
617 {
618 $this->scheme = null;
619 }
620 else
621 {
622 $len = strlen($scheme);
623 switch (true)
624 {
625 case $len > 1:
626 if (!strspn($scheme, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-.', 1))
627 {
628 $this->scheme = null;
629 $this->valid[__FUNCTION__] = false;
630 return false;
631 }
632
633 case $len > 0:
634 if (!strspn($scheme, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', 0, 1))
635 {
636 $this->scheme = null;
637 $this->valid[__FUNCTION__] = false;
638 return false;
639 }
640 }
641 $this->scheme = strtolower($scheme);
642 }
643 $this->valid[__FUNCTION__] = true;
644 return true;
645 }
646
647 /**
648 * Set the authority. Returns true on success, false on failure (if there are
649 * any invalid characters).
650 *
651 * @access public
652 * @param string $authority
653 * @return bool
654 */
655 public function set_authority($authority)
656 {
657 if (($userinfo_end = strrpos($authority, '@')) !== false)
658 {
659 $userinfo = substr($authority, 0, $userinfo_end);
660 $authority = substr($authority, $userinfo_end + 1);
661 }
662 else
663 {
664 $userinfo = null;
665 }
666
667 if (($port_start = strpos($authority, ':')) !== false)
668 {
669 $port = substr($authority, $port_start + 1);
670 if ($port === false)
671 {
672 $port = null;
673 }
674 $authority = substr($authority, 0, $port_start);
675 }
676 else
677 {
678 $port = null;
679 }
680
681 return $this->set_userinfo($userinfo) && $this->set_host($authority) && $this->set_port($port);
682 }
683
684 /**
685 * Set the userinfo.
686 *
687 * @access public
688 * @param string $userinfo
689 * @return bool
690 */
691 public function set_userinfo($userinfo)
692 {
693 if ($userinfo === null || $userinfo === '')
694 {
695 $this->userinfo = null;
696 }
697 else
698 {
699 $this->userinfo = $this->replace_invalid_with_pct_encoding($userinfo, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$&\'()*+,;=:');
700 }
701 $this->valid[__FUNCTION__] = true;
702 return true;
703 }
704
705 /**
706 * Set the host. Returns true on success, false on failure (if there are
707 * any invalid characters).
708 *
709 * @access public
710 * @param string $host
711 * @return bool
712 */
713 public function set_host($host)
714 {
715 if ($host === null || $host === '')
716 {
717 $this->host = null;
718 $this->valid[__FUNCTION__] = true;
719 return true;
720 }
721 elseif ($host[0] === '[' && substr($host, -1) === ']')
722 {
723 if (SimplePie_Net_IPv6::checkIPv6(substr($host, 1, -1)))
724 {
725 $this->host = $host;
726 $this->valid[__FUNCTION__] = true;
727 return true;
728 }
729 else
730 {
731 $this->host = null;
732 $this->valid[__FUNCTION__] = false;
733 return false;
734 }
735 }
736 else
737 {
738 $this->host = $this->replace_invalid_with_pct_encoding($host, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$&\'()*+,;=', SIMPLEPIE_LOWERCASE);
739 $this->valid[__FUNCTION__] = true;
740 return true;
741 }
742 }
743
744 /**
745 * Set the port. Returns true on success, false on failure (if there are
746 * any invalid characters).
747 *
748 * @access public
749 * @param string $port
750 * @return bool
751 */
752 public function set_port($port)
753 {
754 if ($port === null || $port === '')
755 {
756 $this->port = null;
757 $this->valid[__FUNCTION__] = true;
758 return true;
759 }
760 elseif (strspn($port, '0123456789') === strlen($port))
761 {
762 $this->port = (int) $port;
763 $this->valid[__FUNCTION__] = true;
764 return true;
765 }
766 else
767 {
768 $this->port = null;
769 $this->valid[__FUNCTION__] = false;
770 return false;
771 }
772 }
773
774 /**
775 * Set the path.
776 *
777 * @access public
778 * @param string $path
779 * @return bool
780 */
781 public function set_path($path)
782 {
783 if ($path === null || $path === '')
784 {
785 $this->path = null;
786 $this->valid[__FUNCTION__] = true;
787 return true;
788 }
789 elseif (substr($path, 0, 2) === '//' && $this->userinfo === null && $this->host === null && $this->port === null)
790 {
791 $this->path = null;
792 $this->valid[__FUNCTION__] = false;
793 return false;
794 }
795 else
796 {
797 $this->path = $this->replace_invalid_with_pct_encoding($path, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$&\'()*+,;=@/');
798 if ($this->scheme !== null)
799 {
800 $this->path = $this->remove_dot_segments($this->path);
801 }
802 $this->valid[__FUNCTION__] = true;
803 return true;
804 }
805 }
806
807 /**
808 * Set the query.
809 *
810 * @access public
811 * @param string $query
812 * @return bool
813 */
814 public function set_query($query)
815 {
816 if ($query === null || $query === '')
817 {
818 $this->query = null;
819 }
820 else
821 {
822 $this->query = $this->replace_invalid_with_pct_encoding($query, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$\'()*+,;:@/?&=');
823 }
824 $this->valid[__FUNCTION__] = true;
825 return true;
826 }
827
828 /**
829 * Set the fragment.
830 *
831 * @access public
832 * @param string $fragment
833 * @return bool
834 */
835 public function set_fragment($fragment)
836 {
837 if ($fragment === null || $fragment === '')
838 {
839 $this->fragment = null;
840 }
841 else
842 {
843 $this->fragment = $this->replace_invalid_with_pct_encoding($fragment, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$&\'()*+,;=:@/?');
844 }
845 $this->valid[__FUNCTION__] = true;
846 return true;
847 }
848
849 /**
850 * Get the complete IRI
851 *
852 * @access public
853 * @return string
854 */
855 public function get_iri()
856 {
857 $iri = '';
858 if ($this->scheme !== null)
859 {
860 $iri .= $this->scheme . ':';
861 }
862 if (($authority = $this->get_authority()) !== null)
863 {
864 $iri .= '//' . $authority;
865 }
866 if ($this->path !== null)
867 {
868 $iri .= $this->path;
869 }
870 if ($this->query !== null)
871 {
872 $iri .= '?' . $this->query;
873 }
874 if ($this->fragment !== null)
875 {
876 $iri .= '#' . $this->fragment;
877 }
878
879 if ($iri !== '')
880 {
881 return $iri;
882 }
883 else
884 {
885 return null;
886 }
887 }
888
889 /**
890 * Get the scheme
891 *
892 * @access public
893 * @return string
894 */
895 public function get_scheme()
896 {
897 return $this->scheme;
898 }
899
900 /**
901 * Get the complete authority
902 *
903 * @access public
904 * @return string
905 */
906 public function get_authority()
907 {
908 $authority = '';
909 if ($this->userinfo !== null)
910 {
911 $authority .= $this->userinfo . '@';
912 }
913 if ($this->host !== null)
914 {
915 $authority .= $this->host;
916 }
917 if ($this->port !== null)
918 {
919 $authority .= ':' . $this->port;
920 }
921
922 if ($authority !== '')
923 {
924 return $authority;
925 }
926 else
927 {
928 return null;
929 }
930 }
931
932 /**
933 * Get the user information
934 *
935 * @access public
936 * @return string
937 */
938 public function get_userinfo()
939 {
940 return $this->userinfo;
941 }
942
943 /**
944 * Get the host
945 *
946 * @access public
947 * @return string
948 */
949 public function get_host()
950 {
951 return $this->host;
952 }
953
954 /**
955 * Get the port
956 *
957 * @access public
958 * @return string
959 */
960 public function get_port()
961 {
962 return $this->port;
963 }
964
965 /**
966 * Get the path
967 *
968 * @access public
969 * @return string
970 */
971 public function get_path()
972 {
973 return $this->path;
974 }
975
976 /**
977 * Get the query
978 *
979 * @access public
980 * @return string
981 */
982 public function get_query()
983 {
984 return $this->query;
985 }
986
987 /**
988 * Get the fragment
989 *
990 * @access public
991 * @return string
992 */
993 public function get_fragment()
994 {
995 return $this->fragment;
996 }
997}
diff --git a/inc/3rdparty/simplepie/SimplePie/Item.php b/inc/3rdparty/simplepie/SimplePie/Item.php
new file mode 100644
index 00000000..7538038a
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Item.php
@@ -0,0 +1,2576 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47class SimplePie_Item
48{
49 var $feed;
50 var $data = array();
51
52 public function __construct($feed, $data)
53 {
54 $this->feed = $feed;
55 $this->data = $data;
56 }
57
58 public function __toString()
59 {
60 return md5(serialize($this->data));
61 }
62
63 /**
64 * Remove items that link back to this before destroying this object
65 */
66 public function __destruct()
67 {
68 if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode'))
69 {
70 unset($this->feed);
71 }
72 }
73
74 public function get_item_tags($namespace, $tag)
75 {
76 if (isset($this->data['child'][$namespace][$tag]))
77 {
78 return $this->data['child'][$namespace][$tag];
79 }
80 else
81 {
82 return null;
83 }
84 }
85
86 public function get_base($element = array())
87 {
88 return $this->feed->get_base($element);
89 }
90
91 public function sanitize($data, $type, $base = '')
92 {
93 return $this->feed->sanitize($data, $type, $base);
94 }
95
96 public function get_feed()
97 {
98 return $this->feed;
99 }
100
101 public function get_id($hash = false)
102 {
103 if (!$hash)
104 {
105 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'id'))
106 {
107 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
108 }
109 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'id'))
110 {
111 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
112 }
113 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid'))
114 {
115 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
116 }
117 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'identifier'))
118 {
119 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
120 }
121 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'identifier'))
122 {
123 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
124 }
125 elseif (isset($this->data['attribs'][SIMPLEPIE_NAMESPACE_RDF]['about']))
126 {
127 return $this->sanitize($this->data['attribs'][SIMPLEPIE_NAMESPACE_RDF]['about'], SIMPLEPIE_CONSTRUCT_TEXT);
128 }
129 elseif (($return = $this->get_permalink()) !== null)
130 {
131 return $return;
132 }
133 elseif (($return = $this->get_title()) !== null)
134 {
135 return $return;
136 }
137 }
138 if ($this->get_permalink() !== null || $this->get_title() !== null)
139 {
140 return md5($this->get_permalink() . $this->get_title());
141 }
142 else
143 {
144 return md5(serialize($this->data));
145 }
146 }
147
148 public function get_title()
149 {
150 if (!isset($this->data['title']))
151 {
152 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title'))
153 {
154 $this->data['title'] = $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0]));
155 }
156 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title'))
157 {
158 $this->data['title'] = $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0]));
159 }
160 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
161 {
162 $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
163 }
164 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
165 {
166 $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
167 }
168 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
169 {
170 $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
171 }
172 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
173 {
174 $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
175 }
176 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
177 {
178 $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
179 }
180 else
181 {
182 $this->data['title'] = null;
183 }
184 }
185 return $this->data['title'];
186 }
187
188 public function get_description($description_only = false)
189 {
190 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'summary'))
191 {
192 return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0]));
193 }
194 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'summary'))
195 {
196 return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0]));
197 }
198 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description'))
199 {
200 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
201 }
202 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description'))
203 {
204 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
205 }
206 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description'))
207 {
208 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
209 }
210 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description'))
211 {
212 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
213 }
214 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary'))
215 {
216 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
217 }
218 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle'))
219 {
220 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
221 }
222 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description'))
223 {
224 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML);
225 }
226
227 elseif (!$description_only)
228 {
229 return $this->get_content(true);
230 }
231 else
232 {
233 return null;
234 }
235 }
236
237 public function get_content($content_only = false)
238 {
239 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'content'))
240 {
241 return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_content_construct_type($return[0]['attribs']), $this->get_base($return[0]));
242 }
243 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'content'))
244 {
245 return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0]));
246 }
247 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT, 'encoded'))
248 {
249 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
250 }
251 elseif (!$content_only)
252 {
253 return $this->get_description(true);
254 }
255 else
256 {
257 return null;
258 }
259 }
260
261 public function get_category($key = 0)
262 {
263 $categories = $this->get_categories();
264 if (isset($categories[$key]))
265 {
266 return $categories[$key];
267 }
268 else
269 {
270 return null;
271 }
272 }
273
274 public function get_categories()
275 {
276 $categories = array();
277
278 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category)
279 {
280 $term = null;
281 $scheme = null;
282 $label = null;
283 if (isset($category['attribs']['']['term']))
284 {
285 $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT);
286 }
287 if (isset($category['attribs']['']['scheme']))
288 {
289 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
290 }
291 if (isset($category['attribs']['']['label']))
292 {
293 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
294 }
295 $categories[] = new $this->feed->category_class($term, $scheme, $label);
296 }
297 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category)
298 {
299 // This is really the label, but keep this as the term also for BC.
300 // Label will also work on retrieving because that falls back to term.
301 $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
302 if (isset($category['attribs']['']['domain']))
303 {
304 $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT);
305 }
306 else
307 {
308 $scheme = null;
309 }
310 $categories[] = new $this->feed->category_class($term, $scheme, null);
311 }
312 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category)
313 {
314 $categories[] = new $this->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
315 }
316 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category)
317 {
318 $categories[] = new $this->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
319 }
320
321 if (!empty($categories))
322 {
323 return SimplePie_Misc::array_unique($categories);
324 }
325 else
326 {
327 return null;
328 }
329 }
330
331 public function get_author($key = 0)
332 {
333 $authors = $this->get_authors();
334 if (isset($authors[$key]))
335 {
336 return $authors[$key];
337 }
338 else
339 {
340 return null;
341 }
342 }
343
344 public function get_contributor($key = 0)
345 {
346 $contributors = $this->get_contributors();
347 if (isset($contributors[$key]))
348 {
349 return $contributors[$key];
350 }
351 else
352 {
353 return null;
354 }
355 }
356
357 public function get_contributors()
358 {
359 $contributors = array();
360 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor)
361 {
362 $name = null;
363 $uri = null;
364 $email = null;
365 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
366 {
367 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
368 }
369 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
370 {
371 $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
372 }
373 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
374 {
375 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
376 }
377 if ($name !== null || $email !== null || $uri !== null)
378 {
379 $contributors[] = new $this->feed->author_class($name, $uri, $email);
380 }
381 }
382 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor)
383 {
384 $name = null;
385 $url = null;
386 $email = null;
387 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
388 {
389 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
390 }
391 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
392 {
393 $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
394 }
395 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
396 {
397 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
398 }
399 if ($name !== null || $email !== null || $url !== null)
400 {
401 $contributors[] = new $this->feed->author_class($name, $url, $email);
402 }
403 }
404
405 if (!empty($contributors))
406 {
407 return SimplePie_Misc::array_unique($contributors);
408 }
409 else
410 {
411 return null;
412 }
413 }
414
415 public function get_authors()
416 {
417 $authors = array();
418 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author)
419 {
420 $name = null;
421 $uri = null;
422 $email = null;
423 if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
424 {
425 $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
426 }
427 if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
428 {
429 $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
430 }
431 if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
432 {
433 $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
434 }
435 if ($name !== null || $email !== null || $uri !== null)
436 {
437 $authors[] = new $this->feed->author_class($name, $uri, $email);
438 }
439 }
440 if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author'))
441 {
442 $name = null;
443 $url = null;
444 $email = null;
445 if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
446 {
447 $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
448 }
449 if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
450 {
451 $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
452 }
453 if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
454 {
455 $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
456 }
457 if ($name !== null || $email !== null || $url !== null)
458 {
459 $authors[] = new $this->feed->author_class($name, $url, $email);
460 }
461 }
462 if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'author'))
463 {
464 $authors[] = new $this->feed->author_class(null, null, $this->sanitize($author[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
465 }
466 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author)
467 {
468 $authors[] = new $this->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
469 }
470 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author)
471 {
472 $authors[] = new $this->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
473 }
474 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author)
475 {
476 $authors[] = new $this->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
477 }
478
479 if (!empty($authors))
480 {
481 return SimplePie_Misc::array_unique($authors);
482 }
483 elseif (($source = $this->get_source()) && ($authors = $source->get_authors()))
484 {
485 return $authors;
486 }
487 elseif ($authors = $this->feed->get_authors())
488 {
489 return $authors;
490 }
491 else
492 {
493 return null;
494 }
495 }
496
497 public function get_copyright()
498 {
499 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights'))
500 {
501 return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0]));
502 }
503 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights'))
504 {
505 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
506 }
507 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights'))
508 {
509 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
510 }
511 else
512 {
513 return null;
514 }
515 }
516
517 public function get_date($date_format = 'j F Y, g:i a')
518 {
519 if (!isset($this->data['date']))
520 {
521 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'published'))
522 {
523 $this->data['date']['raw'] = $return[0]['data'];
524 }
525 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated'))
526 {
527 $this->data['date']['raw'] = $return[0]['data'];
528 }
529 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'issued'))
530 {
531 $this->data['date']['raw'] = $return[0]['data'];
532 }
533 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'created'))
534 {
535 $this->data['date']['raw'] = $return[0]['data'];
536 }
537 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'modified'))
538 {
539 $this->data['date']['raw'] = $return[0]['data'];
540 }
541 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'pubDate'))
542 {
543 $this->data['date']['raw'] = $return[0]['data'];
544 }
545 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'date'))
546 {
547 $this->data['date']['raw'] = $return[0]['data'];
548 }
549 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'date'))
550 {
551 $this->data['date']['raw'] = $return[0]['data'];
552 }
553
554 if (!empty($this->data['date']['raw']))
555 {
556 $parser = SimplePie_Parse_Date::get();
557 $this->data['date']['parsed'] = $parser->parse($this->data['date']['raw']);
558 }
559 else
560 {
561 $this->data['date'] = null;
562 }
563 }
564 if ($this->data['date'])
565 {
566 $date_format = (string) $date_format;
567 switch ($date_format)
568 {
569 case '':
570 return $this->sanitize($this->data['date']['raw'], SIMPLEPIE_CONSTRUCT_TEXT);
571
572 case 'U':
573 return $this->data['date']['parsed'];
574
575 default:
576 return date($date_format, $this->data['date']['parsed']);
577 }
578 }
579 else
580 {
581 return null;
582 }
583 }
584
585 public function get_local_date($date_format = '%c')
586 {
587 if (!$date_format)
588 {
589 return $this->sanitize($this->get_date(''), SIMPLEPIE_CONSTRUCT_TEXT);
590 }
591 elseif (($date = $this->get_date('U')) !== null && $date !== false)
592 {
593 return strftime($date_format, $date);
594 }
595 else
596 {
597 return null;
598 }
599 }
600
601 public function get_permalink()
602 {
603 $link = $this->get_link();
604 $enclosure = $this->get_enclosure(0);
605 if ($link !== null)
606 {
607 return $link;
608 }
609 elseif ($enclosure !== null)
610 {
611 return $enclosure->get_link();
612 }
613 else
614 {
615 return null;
616 }
617 }
618
619 public function get_link($key = 0, $rel = 'alternate')
620 {
621 $links = $this->get_links($rel);
622 if ($links[$key] !== null)
623 {
624 return $links[$key];
625 }
626 else
627 {
628 return null;
629 }
630 }
631
632 public function get_links($rel = 'alternate')
633 {
634 if (!isset($this->data['links']))
635 {
636 $this->data['links'] = array();
637 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link)
638 {
639 if (isset($link['attribs']['']['href']))
640 {
641 $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
642 $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
643
644 }
645 }
646 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link)
647 {
648 if (isset($link['attribs']['']['href']))
649 {
650 $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
651 $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
652 }
653 }
654 if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
655 {
656 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
657 }
658 if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
659 {
660 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
661 }
662 if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
663 {
664 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
665 }
666 if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid'))
667 {
668 if (!isset($links[0]['attribs']['']['isPermaLink']) || strtolower(trim($links[0]['attribs']['']['isPermaLink'])) === 'true')
669 {
670 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
671 }
672 }
673
674 $keys = array_keys($this->data['links']);
675 foreach ($keys as $key)
676 {
677 if (SimplePie_Misc::is_isegment_nz_nc($key))
678 {
679 if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]))
680 {
681 $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]);
682 $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key];
683 }
684 else
685 {
686 $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key];
687 }
688 }
689 elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY)
690 {
691 $this->data['links'][substr($key, 41)] =& $this->data['links'][$key];
692 }
693 $this->data['links'][$key] = array_unique($this->data['links'][$key]);
694 }
695 }
696 if (isset($this->data['links'][$rel]))
697 {
698 return $this->data['links'][$rel];
699 }
700 else
701 {
702 return null;
703 }
704 }
705
706 /**
707 * @todo Add ability to prefer one type of content over another (in a media group).
708 */
709 public function get_enclosure($key = 0, $prefer = null)
710 {
711 $enclosures = $this->get_enclosures();
712 if (isset($enclosures[$key]))
713 {
714 return $enclosures[$key];
715 }
716 else
717 {
718 return null;
719 }
720 }
721
722 /**
723 * Grabs all available enclosures (podcasts, etc.)
724 *
725 * Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS.
726 *
727 * At this point, we're pretty much assuming that all enclosures for an item are the same content. Anything else is too complicated to properly support.
728 *
729 * @todo Add support for end-user defined sorting of enclosures by type/handler (so we can prefer the faster-loading FLV over MP4).
730 * @todo If an element exists at a level, but it's value is empty, we should fall back to the value from the parent (if it exists).
731 */
732 public function get_enclosures()
733 {
734 if (!isset($this->data['enclosures']))
735 {
736 $this->data['enclosures'] = array();
737
738 // Elements
739 $captions_parent = null;
740 $categories_parent = null;
741 $copyrights_parent = null;
742 $credits_parent = null;
743 $description_parent = null;
744 $duration_parent = null;
745 $hashes_parent = null;
746 $keywords_parent = null;
747 $player_parent = null;
748 $ratings_parent = null;
749 $restrictions_parent = null;
750 $thumbnails_parent = null;
751 $title_parent = null;
752
753 // Let's do the channel and item-level ones first, and just re-use them if we need to.
754 $parent = $this->get_feed();
755
756 // CAPTIONS
757 if ($captions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text'))
758 {
759 foreach ($captions as $caption)
760 {
761 $caption_type = null;
762 $caption_lang = null;
763 $caption_startTime = null;
764 $caption_endTime = null;
765 $caption_text = null;
766 if (isset($caption['attribs']['']['type']))
767 {
768 $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
769 }
770 if (isset($caption['attribs']['']['lang']))
771 {
772 $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
773 }
774 if (isset($caption['attribs']['']['start']))
775 {
776 $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
777 }
778 if (isset($caption['attribs']['']['end']))
779 {
780 $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
781 }
782 if (isset($caption['data']))
783 {
784 $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
785 }
786 $captions_parent[] = new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text);
787 }
788 }
789 elseif ($captions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text'))
790 {
791 foreach ($captions as $caption)
792 {
793 $caption_type = null;
794 $caption_lang = null;
795 $caption_startTime = null;
796 $caption_endTime = null;
797 $caption_text = null;
798 if (isset($caption['attribs']['']['type']))
799 {
800 $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
801 }
802 if (isset($caption['attribs']['']['lang']))
803 {
804 $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
805 }
806 if (isset($caption['attribs']['']['start']))
807 {
808 $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
809 }
810 if (isset($caption['attribs']['']['end']))
811 {
812 $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
813 }
814 if (isset($caption['data']))
815 {
816 $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
817 }
818 $captions_parent[] = new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text);
819 }
820 }
821 if (is_array($captions_parent))
822 {
823 $captions_parent = array_values(SimplePie_Misc::array_unique($captions_parent));
824 }
825
826 // CATEGORIES
827 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category)
828 {
829 $term = null;
830 $scheme = null;
831 $label = null;
832 if (isset($category['data']))
833 {
834 $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
835 }
836 if (isset($category['attribs']['']['scheme']))
837 {
838 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
839 }
840 else
841 {
842 $scheme = 'http://search.yahoo.com/mrss/category_schema';
843 }
844 if (isset($category['attribs']['']['label']))
845 {
846 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
847 }
848 $categories_parent[] = new $this->feed->category_class($term, $scheme, $label);
849 }
850 foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category)
851 {
852 $term = null;
853 $scheme = null;
854 $label = null;
855 if (isset($category['data']))
856 {
857 $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
858 }
859 if (isset($category['attribs']['']['scheme']))
860 {
861 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
862 }
863 else
864 {
865 $scheme = 'http://search.yahoo.com/mrss/category_schema';
866 }
867 if (isset($category['attribs']['']['label']))
868 {
869 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
870 }
871 $categories_parent[] = new $this->feed->category_class($term, $scheme, $label);
872 }
873 foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'category') as $category)
874 {
875 $term = null;
876 $scheme = 'http://www.itunes.com/dtds/podcast-1.0.dtd';
877 $label = null;
878 if (isset($category['attribs']['']['text']))
879 {
880 $label = $this->sanitize($category['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT);
881 }
882 $categories_parent[] = new $this->feed->category_class($term, $scheme, $label);
883
884 if (isset($category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category']))
885 {
886 foreach ((array) $category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'] as $subcategory)
887 {
888 if (isset($subcategory['attribs']['']['text']))
889 {
890 $label = $this->sanitize($subcategory['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT);
891 }
892 $categories_parent[] = new $this->feed->category_class($term, $scheme, $label);
893 }
894 }
895 }
896 if (is_array($categories_parent))
897 {
898 $categories_parent = array_values(SimplePie_Misc::array_unique($categories_parent));
899 }
900
901 // COPYRIGHT
902 if ($copyright = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright'))
903 {
904 $copyright_url = null;
905 $copyright_label = null;
906 if (isset($copyright[0]['attribs']['']['url']))
907 {
908 $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
909 }
910 if (isset($copyright[0]['data']))
911 {
912 $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
913 }
914 $copyrights_parent = new $this->feed->copyright_class($copyright_url, $copyright_label);
915 }
916 elseif ($copyright = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright'))
917 {
918 $copyright_url = null;
919 $copyright_label = null;
920 if (isset($copyright[0]['attribs']['']['url']))
921 {
922 $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
923 }
924 if (isset($copyright[0]['data']))
925 {
926 $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
927 }
928 $copyrights_parent = new $this->feed->copyright_class($copyright_url, $copyright_label);
929 }
930
931 // CREDITS
932 if ($credits = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit'))
933 {
934 foreach ($credits as $credit)
935 {
936 $credit_role = null;
937 $credit_scheme = null;
938 $credit_name = null;
939 if (isset($credit['attribs']['']['role']))
940 {
941 $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
942 }
943 if (isset($credit['attribs']['']['scheme']))
944 {
945 $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
946 }
947 else
948 {
949 $credit_scheme = 'urn:ebu';
950 }
951 if (isset($credit['data']))
952 {
953 $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
954 }
955 $credits_parent[] = new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name);
956 }
957 }
958 elseif ($credits = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit'))
959 {
960 foreach ($credits as $credit)
961 {
962 $credit_role = null;
963 $credit_scheme = null;
964 $credit_name = null;
965 if (isset($credit['attribs']['']['role']))
966 {
967 $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
968 }
969 if (isset($credit['attribs']['']['scheme']))
970 {
971 $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
972 }
973 else
974 {
975 $credit_scheme = 'urn:ebu';
976 }
977 if (isset($credit['data']))
978 {
979 $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
980 }
981 $credits_parent[] = new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name);
982 }
983 }
984 if (is_array($credits_parent))
985 {
986 $credits_parent = array_values(SimplePie_Misc::array_unique($credits_parent));
987 }
988
989 // DESCRIPTION
990 if ($description_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description'))
991 {
992 if (isset($description_parent[0]['data']))
993 {
994 $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
995 }
996 }
997 elseif ($description_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description'))
998 {
999 if (isset($description_parent[0]['data']))
1000 {
1001 $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1002 }
1003 }
1004
1005 // DURATION
1006 if ($duration_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'duration'))
1007 {
1008 $seconds = null;
1009 $minutes = null;
1010 $hours = null;
1011 if (isset($duration_parent[0]['data']))
1012 {
1013 $temp = explode(':', $this->sanitize($duration_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
1014 if (sizeof($temp) > 0)
1015 {
1016 $seconds = (int) array_pop($temp);
1017 }
1018 if (sizeof($temp) > 0)
1019 {
1020 $minutes = (int) array_pop($temp);
1021 $seconds += $minutes * 60;
1022 }
1023 if (sizeof($temp) > 0)
1024 {
1025 $hours = (int) array_pop($temp);
1026 $seconds += $hours * 3600;
1027 }
1028 unset($temp);
1029 $duration_parent = $seconds;
1030 }
1031 }
1032
1033 // HASHES
1034 if ($hashes_iterator = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash'))
1035 {
1036 foreach ($hashes_iterator as $hash)
1037 {
1038 $value = null;
1039 $algo = null;
1040 if (isset($hash['data']))
1041 {
1042 $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1043 }
1044 if (isset($hash['attribs']['']['algo']))
1045 {
1046 $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
1047 }
1048 else
1049 {
1050 $algo = 'md5';
1051 }
1052 $hashes_parent[] = $algo.':'.$value;
1053 }
1054 }
1055 elseif ($hashes_iterator = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash'))
1056 {
1057 foreach ($hashes_iterator as $hash)
1058 {
1059 $value = null;
1060 $algo = null;
1061 if (isset($hash['data']))
1062 {
1063 $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1064 }
1065 if (isset($hash['attribs']['']['algo']))
1066 {
1067 $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
1068 }
1069 else
1070 {
1071 $algo = 'md5';
1072 }
1073 $hashes_parent[] = $algo.':'.$value;
1074 }
1075 }
1076 if (is_array($hashes_parent))
1077 {
1078 $hashes_parent = array_values(SimplePie_Misc::array_unique($hashes_parent));
1079 }
1080
1081 // KEYWORDS
1082 if ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords'))
1083 {
1084 if (isset($keywords[0]['data']))
1085 {
1086 $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
1087 foreach ($temp as $word)
1088 {
1089 $keywords_parent[] = trim($word);
1090 }
1091 }
1092 unset($temp);
1093 }
1094 elseif ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords'))
1095 {
1096 if (isset($keywords[0]['data']))
1097 {
1098 $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
1099 foreach ($temp as $word)
1100 {
1101 $keywords_parent[] = trim($word);
1102 }
1103 }
1104 unset($temp);
1105 }
1106 elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords'))
1107 {
1108 if (isset($keywords[0]['data']))
1109 {
1110 $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
1111 foreach ($temp as $word)
1112 {
1113 $keywords_parent[] = trim($word);
1114 }
1115 }
1116 unset($temp);
1117 }
1118 elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords'))
1119 {
1120 if (isset($keywords[0]['data']))
1121 {
1122 $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
1123 foreach ($temp as $word)
1124 {
1125 $keywords_parent[] = trim($word);
1126 }
1127 }
1128 unset($temp);
1129 }
1130 if (is_array($keywords_parent))
1131 {
1132 $keywords_parent = array_values(SimplePie_Misc::array_unique($keywords_parent));
1133 }
1134
1135 // PLAYER
1136 if ($player_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player'))
1137 {
1138 if (isset($player_parent[0]['attribs']['']['url']))
1139 {
1140 $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
1141 }
1142 }
1143 elseif ($player_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player'))
1144 {
1145 if (isset($player_parent[0]['attribs']['']['url']))
1146 {
1147 $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
1148 }
1149 }
1150
1151 // RATINGS
1152 if ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating'))
1153 {
1154 foreach ($ratings as $rating)
1155 {
1156 $rating_scheme = null;
1157 $rating_value = null;
1158 if (isset($rating['attribs']['']['scheme']))
1159 {
1160 $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
1161 }
1162 else
1163 {
1164 $rating_scheme = 'urn:simple';
1165 }
1166 if (isset($rating['data']))
1167 {
1168 $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1169 }
1170 $ratings_parent[] = new $this->feed->rating_class($rating_scheme, $rating_value);
1171 }
1172 }
1173 elseif ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit'))
1174 {
1175 foreach ($ratings as $rating)
1176 {
1177 $rating_scheme = 'urn:itunes';
1178 $rating_value = null;
1179 if (isset($rating['data']))
1180 {
1181 $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1182 }
1183 $ratings_parent[] = new $this->feed->rating_class($rating_scheme, $rating_value);
1184 }
1185 }
1186 elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating'))
1187 {
1188 foreach ($ratings as $rating)
1189 {
1190 $rating_scheme = null;
1191 $rating_value = null;
1192 if (isset($rating['attribs']['']['scheme']))
1193 {
1194 $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
1195 }
1196 else
1197 {
1198 $rating_scheme = 'urn:simple';
1199 }
1200 if (isset($rating['data']))
1201 {
1202 $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1203 }
1204 $ratings_parent[] = new $this->feed->rating_class($rating_scheme, $rating_value);
1205 }
1206 }
1207 elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit'))
1208 {
1209 foreach ($ratings as $rating)
1210 {
1211 $rating_scheme = 'urn:itunes';
1212 $rating_value = null;
1213 if (isset($rating['data']))
1214 {
1215 $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1216 }
1217 $ratings_parent[] = new $this->feed->rating_class($rating_scheme, $rating_value);
1218 }
1219 }
1220 if (is_array($ratings_parent))
1221 {
1222 $ratings_parent = array_values(SimplePie_Misc::array_unique($ratings_parent));
1223 }
1224
1225 // RESTRICTIONS
1226 if ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction'))
1227 {
1228 foreach ($restrictions as $restriction)
1229 {
1230 $restriction_relationship = null;
1231 $restriction_type = null;
1232 $restriction_value = null;
1233 if (isset($restriction['attribs']['']['relationship']))
1234 {
1235 $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
1236 }
1237 if (isset($restriction['attribs']['']['type']))
1238 {
1239 $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
1240 }
1241 if (isset($restriction['data']))
1242 {
1243 $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1244 }
1245 $restrictions_parent[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value);
1246 }
1247 }
1248 elseif ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block'))
1249 {
1250 foreach ($restrictions as $restriction)
1251 {
1252 $restriction_relationship = 'allow';
1253 $restriction_type = null;
1254 $restriction_value = 'itunes';
1255 if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes')
1256 {
1257 $restriction_relationship = 'deny';
1258 }
1259 $restrictions_parent[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value);
1260 }
1261 }
1262 elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction'))
1263 {
1264 foreach ($restrictions as $restriction)
1265 {
1266 $restriction_relationship = null;
1267 $restriction_type = null;
1268 $restriction_value = null;
1269 if (isset($restriction['attribs']['']['relationship']))
1270 {
1271 $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
1272 }
1273 if (isset($restriction['attribs']['']['type']))
1274 {
1275 $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
1276 }
1277 if (isset($restriction['data']))
1278 {
1279 $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1280 }
1281 $restrictions_parent[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value);
1282 }
1283 }
1284 elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block'))
1285 {
1286 foreach ($restrictions as $restriction)
1287 {
1288 $restriction_relationship = 'allow';
1289 $restriction_type = null;
1290 $restriction_value = 'itunes';
1291 if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes')
1292 {
1293 $restriction_relationship = 'deny';
1294 }
1295 $restrictions_parent[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value);
1296 }
1297 }
1298 if (is_array($restrictions_parent))
1299 {
1300 $restrictions_parent = array_values(SimplePie_Misc::array_unique($restrictions_parent));
1301 }
1302
1303 // THUMBNAILS
1304 if ($thumbnails = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail'))
1305 {
1306 foreach ($thumbnails as $thumbnail)
1307 {
1308 if (isset($thumbnail['attribs']['']['url']))
1309 {
1310 $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
1311 }
1312 }
1313 }
1314 elseif ($thumbnails = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail'))
1315 {
1316 foreach ($thumbnails as $thumbnail)
1317 {
1318 if (isset($thumbnail['attribs']['']['url']))
1319 {
1320 $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
1321 }
1322 }
1323 }
1324
1325 // TITLES
1326 if ($title_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title'))
1327 {
1328 if (isset($title_parent[0]['data']))
1329 {
1330 $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1331 }
1332 }
1333 elseif ($title_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title'))
1334 {
1335 if (isset($title_parent[0]['data']))
1336 {
1337 $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1338 }
1339 }
1340
1341 // Clear the memory
1342 unset($parent);
1343
1344 // Attributes
1345 $bitrate = null;
1346 $channels = null;
1347 $duration = null;
1348 $expression = null;
1349 $framerate = null;
1350 $height = null;
1351 $javascript = null;
1352 $lang = null;
1353 $length = null;
1354 $medium = null;
1355 $samplingrate = null;
1356 $type = null;
1357 $url = null;
1358 $width = null;
1359
1360 // Elements
1361 $captions = null;
1362 $categories = null;
1363 $copyrights = null;
1364 $credits = null;
1365 $description = null;
1366 $hashes = null;
1367 $keywords = null;
1368 $player = null;
1369 $ratings = null;
1370 $restrictions = null;
1371 $thumbnails = null;
1372 $title = null;
1373
1374 // If we have media:group tags, loop through them.
1375 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group') as $group)
1376 {
1377 if(isset($group['child']) && isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content']))
1378 {
1379 // If we have media:content tags, loop through them.
1380 foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content)
1381 {
1382 if (isset($content['attribs']['']['url']))
1383 {
1384 // Attributes
1385 $bitrate = null;
1386 $channels = null;
1387 $duration = null;
1388 $expression = null;
1389 $framerate = null;
1390 $height = null;
1391 $javascript = null;
1392 $lang = null;
1393 $length = null;
1394 $medium = null;
1395 $samplingrate = null;
1396 $type = null;
1397 $url = null;
1398 $width = null;
1399
1400 // Elements
1401 $captions = null;
1402 $categories = null;
1403 $copyrights = null;
1404 $credits = null;
1405 $description = null;
1406 $hashes = null;
1407 $keywords = null;
1408 $player = null;
1409 $ratings = null;
1410 $restrictions = null;
1411 $thumbnails = null;
1412 $title = null;
1413
1414 // Start checking the attributes of media:content
1415 if (isset($content['attribs']['']['bitrate']))
1416 {
1417 $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT);
1418 }
1419 if (isset($content['attribs']['']['channels']))
1420 {
1421 $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT);
1422 }
1423 if (isset($content['attribs']['']['duration']))
1424 {
1425 $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT);
1426 }
1427 else
1428 {
1429 $duration = $duration_parent;
1430 }
1431 if (isset($content['attribs']['']['expression']))
1432 {
1433 $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT);
1434 }
1435 if (isset($content['attribs']['']['framerate']))
1436 {
1437 $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT);
1438 }
1439 if (isset($content['attribs']['']['height']))
1440 {
1441 $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT);
1442 }
1443 if (isset($content['attribs']['']['lang']))
1444 {
1445 $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
1446 }
1447 if (isset($content['attribs']['']['fileSize']))
1448 {
1449 $length = ceil($content['attribs']['']['fileSize']);
1450 }
1451 if (isset($content['attribs']['']['medium']))
1452 {
1453 $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT);
1454 }
1455 if (isset($content['attribs']['']['samplingrate']))
1456 {
1457 $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT);
1458 }
1459 if (isset($content['attribs']['']['type']))
1460 {
1461 $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
1462 }
1463 if (isset($content['attribs']['']['width']))
1464 {
1465 $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT);
1466 }
1467 $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
1468
1469 // Checking the other optional media: elements. Priority: media:content, media:group, item, channel
1470
1471 // CAPTIONS
1472 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text']))
1473 {
1474 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption)
1475 {
1476 $caption_type = null;
1477 $caption_lang = null;
1478 $caption_startTime = null;
1479 $caption_endTime = null;
1480 $caption_text = null;
1481 if (isset($caption['attribs']['']['type']))
1482 {
1483 $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
1484 }
1485 if (isset($caption['attribs']['']['lang']))
1486 {
1487 $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
1488 }
1489 if (isset($caption['attribs']['']['start']))
1490 {
1491 $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
1492 }
1493 if (isset($caption['attribs']['']['end']))
1494 {
1495 $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
1496 }
1497 if (isset($caption['data']))
1498 {
1499 $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1500 }
1501 $captions[] = new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text);
1502 }
1503 if (is_array($captions))
1504 {
1505 $captions = array_values(SimplePie_Misc::array_unique($captions));
1506 }
1507 }
1508 elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text']))
1509 {
1510 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption)
1511 {
1512 $caption_type = null;
1513 $caption_lang = null;
1514 $caption_startTime = null;
1515 $caption_endTime = null;
1516 $caption_text = null;
1517 if (isset($caption['attribs']['']['type']))
1518 {
1519 $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
1520 }
1521 if (isset($caption['attribs']['']['lang']))
1522 {
1523 $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
1524 }
1525 if (isset($caption['attribs']['']['start']))
1526 {
1527 $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
1528 }
1529 if (isset($caption['attribs']['']['end']))
1530 {
1531 $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
1532 }
1533 if (isset($caption['data']))
1534 {
1535 $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1536 }
1537 $captions[] = new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text);
1538 }
1539 if (is_array($captions))
1540 {
1541 $captions = array_values(SimplePie_Misc::array_unique($captions));
1542 }
1543 }
1544 else
1545 {
1546 $captions = $captions_parent;
1547 }
1548
1549 // CATEGORIES
1550 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category']))
1551 {
1552 foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category)
1553 {
1554 $term = null;
1555 $scheme = null;
1556 $label = null;
1557 if (isset($category['data']))
1558 {
1559 $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1560 }
1561 if (isset($category['attribs']['']['scheme']))
1562 {
1563 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
1564 }
1565 else
1566 {
1567 $scheme = 'http://search.yahoo.com/mrss/category_schema';
1568 }
1569 if (isset($category['attribs']['']['label']))
1570 {
1571 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
1572 }
1573 $categories[] = new $this->feed->category_class($term, $scheme, $label);
1574 }
1575 }
1576 if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category']))
1577 {
1578 foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category)
1579 {
1580 $term = null;
1581 $scheme = null;
1582 $label = null;
1583 if (isset($category['data']))
1584 {
1585 $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1586 }
1587 if (isset($category['attribs']['']['scheme']))
1588 {
1589 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
1590 }
1591 else
1592 {
1593 $scheme = 'http://search.yahoo.com/mrss/category_schema';
1594 }
1595 if (isset($category['attribs']['']['label']))
1596 {
1597 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
1598 }
1599 $categories[] = new $this->feed->category_class($term, $scheme, $label);
1600 }
1601 }
1602 if (is_array($categories) && is_array($categories_parent))
1603 {
1604 $categories = array_values(SimplePie_Misc::array_unique(array_merge($categories, $categories_parent)));
1605 }
1606 elseif (is_array($categories))
1607 {
1608 $categories = array_values(SimplePie_Misc::array_unique($categories));
1609 }
1610 elseif (is_array($categories_parent))
1611 {
1612 $categories = array_values(SimplePie_Misc::array_unique($categories_parent));
1613 }
1614
1615 // COPYRIGHTS
1616 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright']))
1617 {
1618 $copyright_url = null;
1619 $copyright_label = null;
1620 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url']))
1621 {
1622 $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
1623 }
1624 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data']))
1625 {
1626 $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1627 }
1628 $copyrights = new $this->feed->copyright_class($copyright_url, $copyright_label);
1629 }
1630 elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright']))
1631 {
1632 $copyright_url = null;
1633 $copyright_label = null;
1634 if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url']))
1635 {
1636 $copyright_url = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
1637 }
1638 if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data']))
1639 {
1640 $copyright_label = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1641 }
1642 $copyrights = new $this->feed->copyright_class($copyright_url, $copyright_label);
1643 }
1644 else
1645 {
1646 $copyrights = $copyrights_parent;
1647 }
1648
1649 // CREDITS
1650 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit']))
1651 {
1652 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit)
1653 {
1654 $credit_role = null;
1655 $credit_scheme = null;
1656 $credit_name = null;
1657 if (isset($credit['attribs']['']['role']))
1658 {
1659 $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
1660 }
1661 if (isset($credit['attribs']['']['scheme']))
1662 {
1663 $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
1664 }
1665 else
1666 {
1667 $credit_scheme = 'urn:ebu';
1668 }
1669 if (isset($credit['data']))
1670 {
1671 $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1672 }
1673 $credits[] = new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name);
1674 }
1675 if (is_array($credits))
1676 {
1677 $credits = array_values(SimplePie_Misc::array_unique($credits));
1678 }
1679 }
1680 elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit']))
1681 {
1682 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit)
1683 {
1684 $credit_role = null;
1685 $credit_scheme = null;
1686 $credit_name = null;
1687 if (isset($credit['attribs']['']['role']))
1688 {
1689 $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
1690 }
1691 if (isset($credit['attribs']['']['scheme']))
1692 {
1693 $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
1694 }
1695 else
1696 {
1697 $credit_scheme = 'urn:ebu';
1698 }
1699 if (isset($credit['data']))
1700 {
1701 $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1702 }
1703 $credits[] = new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name);
1704 }
1705 if (is_array($credits))
1706 {
1707 $credits = array_values(SimplePie_Misc::array_unique($credits));
1708 }
1709 }
1710 else
1711 {
1712 $credits = $credits_parent;
1713 }
1714
1715 // DESCRIPTION
1716 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description']))
1717 {
1718 $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1719 }
1720 elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description']))
1721 {
1722 $description = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1723 }
1724 else
1725 {
1726 $description = $description_parent;
1727 }
1728
1729 // HASHES
1730 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash']))
1731 {
1732 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash)
1733 {
1734 $value = null;
1735 $algo = null;
1736 if (isset($hash['data']))
1737 {
1738 $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1739 }
1740 if (isset($hash['attribs']['']['algo']))
1741 {
1742 $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
1743 }
1744 else
1745 {
1746 $algo = 'md5';
1747 }
1748 $hashes[] = $algo.':'.$value;
1749 }
1750 if (is_array($hashes))
1751 {
1752 $hashes = array_values(SimplePie_Misc::array_unique($hashes));
1753 }
1754 }
1755 elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash']))
1756 {
1757 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash)
1758 {
1759 $value = null;
1760 $algo = null;
1761 if (isset($hash['data']))
1762 {
1763 $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1764 }
1765 if (isset($hash['attribs']['']['algo']))
1766 {
1767 $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
1768 }
1769 else
1770 {
1771 $algo = 'md5';
1772 }
1773 $hashes[] = $algo.':'.$value;
1774 }
1775 if (is_array($hashes))
1776 {
1777 $hashes = array_values(SimplePie_Misc::array_unique($hashes));
1778 }
1779 }
1780 else
1781 {
1782 $hashes = $hashes_parent;
1783 }
1784
1785 // KEYWORDS
1786 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords']))
1787 {
1788 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data']))
1789 {
1790 $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
1791 foreach ($temp as $word)
1792 {
1793 $keywords[] = trim($word);
1794 }
1795 unset($temp);
1796 }
1797 if (is_array($keywords))
1798 {
1799 $keywords = array_values(SimplePie_Misc::array_unique($keywords));
1800 }
1801 }
1802 elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords']))
1803 {
1804 if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data']))
1805 {
1806 $temp = explode(',', $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
1807 foreach ($temp as $word)
1808 {
1809 $keywords[] = trim($word);
1810 }
1811 unset($temp);
1812 }
1813 if (is_array($keywords))
1814 {
1815 $keywords = array_values(SimplePie_Misc::array_unique($keywords));
1816 }
1817 }
1818 else
1819 {
1820 $keywords = $keywords_parent;
1821 }
1822
1823 // PLAYER
1824 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
1825 {
1826 $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
1827 }
1828 elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
1829 {
1830 $player = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
1831 }
1832 else
1833 {
1834 $player = $player_parent;
1835 }
1836
1837 // RATINGS
1838 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating']))
1839 {
1840 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating)
1841 {
1842 $rating_scheme = null;
1843 $rating_value = null;
1844 if (isset($rating['attribs']['']['scheme']))
1845 {
1846 $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
1847 }
1848 else
1849 {
1850 $rating_scheme = 'urn:simple';
1851 }
1852 if (isset($rating['data']))
1853 {
1854 $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1855 }
1856 $ratings[] = new $this->feed->rating_class($rating_scheme, $rating_value);
1857 }
1858 if (is_array($ratings))
1859 {
1860 $ratings = array_values(SimplePie_Misc::array_unique($ratings));
1861 }
1862 }
1863 elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating']))
1864 {
1865 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating)
1866 {
1867 $rating_scheme = null;
1868 $rating_value = null;
1869 if (isset($rating['attribs']['']['scheme']))
1870 {
1871 $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
1872 }
1873 else
1874 {
1875 $rating_scheme = 'urn:simple';
1876 }
1877 if (isset($rating['data']))
1878 {
1879 $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1880 }
1881 $ratings[] = new $this->feed->rating_class($rating_scheme, $rating_value);
1882 }
1883 if (is_array($ratings))
1884 {
1885 $ratings = array_values(SimplePie_Misc::array_unique($ratings));
1886 }
1887 }
1888 else
1889 {
1890 $ratings = $ratings_parent;
1891 }
1892
1893 // RESTRICTIONS
1894 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction']))
1895 {
1896 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction)
1897 {
1898 $restriction_relationship = null;
1899 $restriction_type = null;
1900 $restriction_value = null;
1901 if (isset($restriction['attribs']['']['relationship']))
1902 {
1903 $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
1904 }
1905 if (isset($restriction['attribs']['']['type']))
1906 {
1907 $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
1908 }
1909 if (isset($restriction['data']))
1910 {
1911 $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1912 }
1913 $restrictions[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value);
1914 }
1915 if (is_array($restrictions))
1916 {
1917 $restrictions = array_values(SimplePie_Misc::array_unique($restrictions));
1918 }
1919 }
1920 elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction']))
1921 {
1922 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction)
1923 {
1924 $restriction_relationship = null;
1925 $restriction_type = null;
1926 $restriction_value = null;
1927 if (isset($restriction['attribs']['']['relationship']))
1928 {
1929 $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
1930 }
1931 if (isset($restriction['attribs']['']['type']))
1932 {
1933 $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
1934 }
1935 if (isset($restriction['data']))
1936 {
1937 $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1938 }
1939 $restrictions[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value);
1940 }
1941 if (is_array($restrictions))
1942 {
1943 $restrictions = array_values(SimplePie_Misc::array_unique($restrictions));
1944 }
1945 }
1946 else
1947 {
1948 $restrictions = $restrictions_parent;
1949 }
1950
1951 // THUMBNAILS
1952 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail']))
1953 {
1954 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail)
1955 {
1956 $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
1957 }
1958 if (is_array($thumbnails))
1959 {
1960 $thumbnails = array_values(SimplePie_Misc::array_unique($thumbnails));
1961 }
1962 }
1963 elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail']))
1964 {
1965 foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail)
1966 {
1967 $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
1968 }
1969 if (is_array($thumbnails))
1970 {
1971 $thumbnails = array_values(SimplePie_Misc::array_unique($thumbnails));
1972 }
1973 }
1974 else
1975 {
1976 $thumbnails = $thumbnails_parent;
1977 }
1978
1979 // TITLES
1980 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title']))
1981 {
1982 $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1983 }
1984 elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title']))
1985 {
1986 $title = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
1987 }
1988 else
1989 {
1990 $title = $title_parent;
1991 }
1992
1993 $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, null, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width);
1994 }
1995 }
1996 }
1997 }
1998
1999 // If we have standalone media:content tags, loop through them.
2000 if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content']))
2001 {
2002 foreach ((array) $this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content)
2003 {
2004 if (isset($content['attribs']['']['url']) || isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
2005 {
2006 // Attributes
2007 $bitrate = null;
2008 $channels = null;
2009 $duration = null;
2010 $expression = null;
2011 $framerate = null;
2012 $height = null;
2013 $javascript = null;
2014 $lang = null;
2015 $length = null;
2016 $medium = null;
2017 $samplingrate = null;
2018 $type = null;
2019 $url = null;
2020 $width = null;
2021
2022 // Elements
2023 $captions = null;
2024 $categories = null;
2025 $copyrights = null;
2026 $credits = null;
2027 $description = null;
2028 $hashes = null;
2029 $keywords = null;
2030 $player = null;
2031 $ratings = null;
2032 $restrictions = null;
2033 $thumbnails = null;
2034 $title = null;
2035
2036 // Start checking the attributes of media:content
2037 if (isset($content['attribs']['']['bitrate']))
2038 {
2039 $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT);
2040 }
2041 if (isset($content['attribs']['']['channels']))
2042 {
2043 $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT);
2044 }
2045 if (isset($content['attribs']['']['duration']))
2046 {
2047 $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT);
2048 }
2049 else
2050 {
2051 $duration = $duration_parent;
2052 }
2053 if (isset($content['attribs']['']['expression']))
2054 {
2055 $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT);
2056 }
2057 if (isset($content['attribs']['']['framerate']))
2058 {
2059 $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT);
2060 }
2061 if (isset($content['attribs']['']['height']))
2062 {
2063 $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT);
2064 }
2065 if (isset($content['attribs']['']['lang']))
2066 {
2067 $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
2068 }
2069 if (isset($content['attribs']['']['fileSize']))
2070 {
2071 $length = ceil($content['attribs']['']['fileSize']);
2072 }
2073 if (isset($content['attribs']['']['medium']))
2074 {
2075 $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT);
2076 }
2077 if (isset($content['attribs']['']['samplingrate']))
2078 {
2079 $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT);
2080 }
2081 if (isset($content['attribs']['']['type']))
2082 {
2083 $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
2084 }
2085 if (isset($content['attribs']['']['width']))
2086 {
2087 $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT);
2088 }
2089 if (isset($content['attribs']['']['url']))
2090 {
2091 $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
2092 }
2093 // Checking the other optional media: elements. Priority: media:content, media:group, item, channel
2094
2095 // CAPTIONS
2096 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text']))
2097 {
2098 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption)
2099 {
2100 $caption_type = null;
2101 $caption_lang = null;
2102 $caption_startTime = null;
2103 $caption_endTime = null;
2104 $caption_text = null;
2105 if (isset($caption['attribs']['']['type']))
2106 {
2107 $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
2108 }
2109 if (isset($caption['attribs']['']['lang']))
2110 {
2111 $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT);
2112 }
2113 if (isset($caption['attribs']['']['start']))
2114 {
2115 $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT);
2116 }
2117 if (isset($caption['attribs']['']['end']))
2118 {
2119 $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT);
2120 }
2121 if (isset($caption['data']))
2122 {
2123 $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2124 }
2125 $captions[] = new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text);
2126 }
2127 if (is_array($captions))
2128 {
2129 $captions = array_values(SimplePie_Misc::array_unique($captions));
2130 }
2131 }
2132 else
2133 {
2134 $captions = $captions_parent;
2135 }
2136
2137 // CATEGORIES
2138 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category']))
2139 {
2140 foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category)
2141 {
2142 $term = null;
2143 $scheme = null;
2144 $label = null;
2145 if (isset($category['data']))
2146 {
2147 $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2148 }
2149 if (isset($category['attribs']['']['scheme']))
2150 {
2151 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
2152 }
2153 else
2154 {
2155 $scheme = 'http://search.yahoo.com/mrss/category_schema';
2156 }
2157 if (isset($category['attribs']['']['label']))
2158 {
2159 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
2160 }
2161 $categories[] = new $this->feed->category_class($term, $scheme, $label);
2162 }
2163 }
2164 if (is_array($categories) && is_array($categories_parent))
2165 {
2166 $categories = array_values(SimplePie_Misc::array_unique(array_merge($categories, $categories_parent)));
2167 }
2168 elseif (is_array($categories))
2169 {
2170 $categories = array_values(SimplePie_Misc::array_unique($categories));
2171 }
2172 elseif (is_array($categories_parent))
2173 {
2174 $categories = array_values(SimplePie_Misc::array_unique($categories_parent));
2175 }
2176 else
2177 {
2178 $categories = null;
2179 }
2180
2181 // COPYRIGHTS
2182 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright']))
2183 {
2184 $copyright_url = null;
2185 $copyright_label = null;
2186 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url']))
2187 {
2188 $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT);
2189 }
2190 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data']))
2191 {
2192 $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2193 }
2194 $copyrights = new $this->feed->copyright_class($copyright_url, $copyright_label);
2195 }
2196 else
2197 {
2198 $copyrights = $copyrights_parent;
2199 }
2200
2201 // CREDITS
2202 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit']))
2203 {
2204 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit)
2205 {
2206 $credit_role = null;
2207 $credit_scheme = null;
2208 $credit_name = null;
2209 if (isset($credit['attribs']['']['role']))
2210 {
2211 $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT);
2212 }
2213 if (isset($credit['attribs']['']['scheme']))
2214 {
2215 $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
2216 }
2217 else
2218 {
2219 $credit_scheme = 'urn:ebu';
2220 }
2221 if (isset($credit['data']))
2222 {
2223 $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2224 }
2225 $credits[] = new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name);
2226 }
2227 if (is_array($credits))
2228 {
2229 $credits = array_values(SimplePie_Misc::array_unique($credits));
2230 }
2231 }
2232 else
2233 {
2234 $credits = $credits_parent;
2235 }
2236
2237 // DESCRIPTION
2238 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description']))
2239 {
2240 $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2241 }
2242 else
2243 {
2244 $description = $description_parent;
2245 }
2246
2247 // HASHES
2248 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash']))
2249 {
2250 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash)
2251 {
2252 $value = null;
2253 $algo = null;
2254 if (isset($hash['data']))
2255 {
2256 $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2257 }
2258 if (isset($hash['attribs']['']['algo']))
2259 {
2260 $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT);
2261 }
2262 else
2263 {
2264 $algo = 'md5';
2265 }
2266 $hashes[] = $algo.':'.$value;
2267 }
2268 if (is_array($hashes))
2269 {
2270 $hashes = array_values(SimplePie_Misc::array_unique($hashes));
2271 }
2272 }
2273 else
2274 {
2275 $hashes = $hashes_parent;
2276 }
2277
2278 // KEYWORDS
2279 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords']))
2280 {
2281 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data']))
2282 {
2283 $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT));
2284 foreach ($temp as $word)
2285 {
2286 $keywords[] = trim($word);
2287 }
2288 unset($temp);
2289 }
2290 if (is_array($keywords))
2291 {
2292 $keywords = array_values(SimplePie_Misc::array_unique($keywords));
2293 }
2294 }
2295 else
2296 {
2297 $keywords = $keywords_parent;
2298 }
2299
2300 // PLAYER
2301 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player']))
2302 {
2303 $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
2304 }
2305 else
2306 {
2307 $player = $player_parent;
2308 }
2309
2310 // RATINGS
2311 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating']))
2312 {
2313 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating)
2314 {
2315 $rating_scheme = null;
2316 $rating_value = null;
2317 if (isset($rating['attribs']['']['scheme']))
2318 {
2319 $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
2320 }
2321 else
2322 {
2323 $rating_scheme = 'urn:simple';
2324 }
2325 if (isset($rating['data']))
2326 {
2327 $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2328 }
2329 $ratings[] = new $this->feed->rating_class($rating_scheme, $rating_value);
2330 }
2331 if (is_array($ratings))
2332 {
2333 $ratings = array_values(SimplePie_Misc::array_unique($ratings));
2334 }
2335 }
2336 else
2337 {
2338 $ratings = $ratings_parent;
2339 }
2340
2341 // RESTRICTIONS
2342 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction']))
2343 {
2344 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction)
2345 {
2346 $restriction_relationship = null;
2347 $restriction_type = null;
2348 $restriction_value = null;
2349 if (isset($restriction['attribs']['']['relationship']))
2350 {
2351 $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT);
2352 }
2353 if (isset($restriction['attribs']['']['type']))
2354 {
2355 $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
2356 }
2357 if (isset($restriction['data']))
2358 {
2359 $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2360 }
2361 $restrictions[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value);
2362 }
2363 if (is_array($restrictions))
2364 {
2365 $restrictions = array_values(SimplePie_Misc::array_unique($restrictions));
2366 }
2367 }
2368 else
2369 {
2370 $restrictions = $restrictions_parent;
2371 }
2372
2373 // THUMBNAILS
2374 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail']))
2375 {
2376 foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail)
2377 {
2378 $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI);
2379 }
2380 if (is_array($thumbnails))
2381 {
2382 $thumbnails = array_values(SimplePie_Misc::array_unique($thumbnails));
2383 }
2384 }
2385 else
2386 {
2387 $thumbnails = $thumbnails_parent;
2388 }
2389
2390 // TITLES
2391 if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title']))
2392 {
2393 $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
2394 }
2395 else
2396 {
2397 $title = $title_parent;
2398 }
2399
2400 $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, null, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width);
2401 }
2402 }
2403 }
2404
2405 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link)
2406 {
2407 if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure')
2408 {
2409 // Attributes
2410 $bitrate = null;
2411 $channels = null;
2412 $duration = null;
2413 $expression = null;
2414 $framerate = null;
2415 $height = null;
2416 $javascript = null;
2417 $lang = null;
2418 $length = null;
2419 $medium = null;
2420 $samplingrate = null;
2421 $type = null;
2422 $url = null;
2423 $width = null;
2424
2425 $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
2426 if (isset($link['attribs']['']['type']))
2427 {
2428 $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
2429 }
2430 if (isset($link['attribs']['']['length']))
2431 {
2432 $length = ceil($link['attribs']['']['length']);
2433 }
2434
2435 // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
2436 $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width);
2437 }
2438 }
2439
2440 foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link)
2441 {
2442 if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure')
2443 {
2444 // Attributes
2445 $bitrate = null;
2446 $channels = null;
2447 $duration = null;
2448 $expression = null;
2449 $framerate = null;
2450 $height = null;
2451 $javascript = null;
2452 $lang = null;
2453 $length = null;
2454 $medium = null;
2455 $samplingrate = null;
2456 $type = null;
2457 $url = null;
2458 $width = null;
2459
2460 $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
2461 if (isset($link['attribs']['']['type']))
2462 {
2463 $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
2464 }
2465 if (isset($link['attribs']['']['length']))
2466 {
2467 $length = ceil($link['attribs']['']['length']);
2468 }
2469
2470 // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
2471 $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width);
2472 }
2473 }
2474
2475 if ($enclosure = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'enclosure'))
2476 {
2477 if (isset($enclosure[0]['attribs']['']['url']))
2478 {
2479 // Attributes
2480 $bitrate = null;
2481 $channels = null;
2482 $duration = null;
2483 $expression = null;
2484 $framerate = null;
2485 $height = null;
2486 $javascript = null;
2487 $lang = null;
2488 $length = null;
2489 $medium = null;
2490 $samplingrate = null;
2491 $type = null;
2492 $url = null;
2493 $width = null;
2494
2495 $url = $this->sanitize($enclosure[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($enclosure[0]));
2496 if (isset($enclosure[0]['attribs']['']['type']))
2497 {
2498 $type = $this->sanitize($enclosure[0]['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT);
2499 }
2500 if (isset($enclosure[0]['attribs']['']['length']))
2501 {
2502 $length = ceil($enclosure[0]['attribs']['']['length']);
2503 }
2504
2505 // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
2506 $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width);
2507 }
2508 }
2509
2510 if (sizeof($this->data['enclosures']) === 0 && ($url || $type || $length || $bitrate || $captions_parent || $categories_parent || $channels || $copyrights_parent || $credits_parent || $description_parent || $duration_parent || $expression || $framerate || $hashes_parent || $height || $keywords_parent || $lang || $medium || $player_parent || $ratings_parent || $restrictions_parent || $samplingrate || $thumbnails_parent || $title_parent || $width))
2511 {
2512 // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor
2513 $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width);
2514 }
2515
2516 $this->data['enclosures'] = array_values(SimplePie_Misc::array_unique($this->data['enclosures']));
2517 }
2518 if (!empty($this->data['enclosures']))
2519 {
2520 return $this->data['enclosures'];
2521 }
2522 else
2523 {
2524 return null;
2525 }
2526 }
2527
2528 public function get_latitude()
2529 {
2530 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat'))
2531 {
2532 return (float) $return[0]['data'];
2533 }
2534 elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
2535 {
2536 return (float) $match[1];
2537 }
2538 else
2539 {
2540 return null;
2541 }
2542 }
2543
2544 public function get_longitude()
2545 {
2546 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long'))
2547 {
2548 return (float) $return[0]['data'];
2549 }
2550 elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon'))
2551 {
2552 return (float) $return[0]['data'];
2553 }
2554 elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
2555 {
2556 return (float) $match[2];
2557 }
2558 else
2559 {
2560 return null;
2561 }
2562 }
2563
2564 public function get_source()
2565 {
2566 if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'source'))
2567 {
2568 return new $this->feed->source_class($this, $return[0]);
2569 }
2570 else
2571 {
2572 return null;
2573 }
2574 }
2575}
2576
diff --git a/inc/3rdparty/simplepie/SimplePie/Locator.php b/inc/3rdparty/simplepie/SimplePie/Locator.php
new file mode 100644
index 00000000..f519b7ee
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Locator.php
@@ -0,0 +1,314 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47class SimplePie_Locator
48{
49 var $useragent;
50 var $timeout;
51 var $file;
52 var $local = array();
53 var $elsewhere = array();
54 var $file_class = 'SimplePie_File';
55 var $cached_entities = array();
56 var $http_base;
57 var $base;
58 var $base_location = 0;
59 var $checked_feeds = 0;
60 var $max_checked_feeds = 10;
61 var $content_type_sniffer_class = 'SimplePie_Content_Type_Sniffer';
62
63 public function __construct(&$file, $timeout = 10, $useragent = null, $file_class = 'SimplePie_File', $max_checked_feeds = 10, $content_type_sniffer_class = 'SimplePie_Content_Type_Sniffer')
64 {
65 $this->file =& $file;
66 $this->file_class = $file_class;
67 $this->useragent = $useragent;
68 $this->timeout = $timeout;
69 $this->max_checked_feeds = $max_checked_feeds;
70 $this->content_type_sniffer_class = $content_type_sniffer_class;
71 }
72
73 public function find($type = SIMPLEPIE_LOCATOR_ALL, &$working)
74 {
75 if ($this->is_feed($this->file))
76 {
77 return $this->file;
78 }
79
80 if ($this->file->method & SIMPLEPIE_FILE_SOURCE_REMOTE)
81 {
82 $sniffer = new $this->content_type_sniffer_class($this->file);
83 if ($sniffer->get_type() !== 'text/html')
84 {
85 return null;
86 }
87 }
88
89 if ($type & ~SIMPLEPIE_LOCATOR_NONE)
90 {
91 $this->get_base();
92 }
93
94 if ($type & SIMPLEPIE_LOCATOR_AUTODISCOVERY && $working = $this->autodiscovery())
95 {
96 return $working[0];
97 }
98
99 if ($type & (SIMPLEPIE_LOCATOR_LOCAL_EXTENSION | SIMPLEPIE_LOCATOR_LOCAL_BODY | SIMPLEPIE_LOCATOR_REMOTE_EXTENSION | SIMPLEPIE_LOCATOR_REMOTE_BODY) && $this->get_links())
100 {
101 if ($type & SIMPLEPIE_LOCATOR_LOCAL_EXTENSION && $working = $this->extension($this->local))
102 {
103 return $working;
104 }
105
106 if ($type & SIMPLEPIE_LOCATOR_LOCAL_BODY && $working = $this->body($this->local))
107 {
108 return $working;
109 }
110
111 if ($type & SIMPLEPIE_LOCATOR_REMOTE_EXTENSION && $working = $this->extension($this->elsewhere))
112 {
113 return $working;
114 }
115
116 if ($type & SIMPLEPIE_LOCATOR_REMOTE_BODY && $working = $this->body($this->elsewhere))
117 {
118 return $working;
119 }
120 }
121 return null;
122 }
123
124 public function is_feed(&$file)
125 {
126 if ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE)
127 {
128 $sniffer = new $this->content_type_sniffer_class($file);
129 $sniffed = $sniffer->get_type();
130 if (in_array($sniffed, array('application/rss+xml', 'application/rdf+xml', 'text/rdf', 'application/atom+xml', 'text/xml', 'application/xml')))
131 {
132 return true;
133 }
134 else
135 {
136 return false;
137 }
138 }
139 elseif ($file->method & SIMPLEPIE_FILE_SOURCE_LOCAL)
140 {
141 return true;
142 }
143 else
144 {
145 return false;
146 }
147 }
148
149 public function get_base()
150 {
151 $this->http_base = $this->file->url;
152 $this->base = $this->http_base;
153 $elements = SimplePie_Misc::get_element('base', $this->file->body);
154 foreach ($elements as $element)
155 {
156 if ($element['attribs']['href']['data'] !== '')
157 {
158 $this->base = SimplePie_Misc::absolutize_url(trim($element['attribs']['href']['data']), $this->http_base);
159 $this->base_location = $element['offset'];
160 break;
161 }
162 }
163 }
164
165 public function autodiscovery()
166 {
167 $links = array_merge(SimplePie_Misc::get_element('link', $this->file->body), SimplePie_Misc::get_element('a', $this->file->body), SimplePie_Misc::get_element('area', $this->file->body));
168 $done = array();
169 $feeds = array();
170 foreach ($links as $link)
171 {
172 if ($this->checked_feeds === $this->max_checked_feeds)
173 {
174 break;
175 }
176 if (isset($link['attribs']['href']['data']) && isset($link['attribs']['rel']['data']))
177 {
178 $rel = array_unique(SimplePie_Misc::space_seperated_tokens(strtolower($link['attribs']['rel']['data'])));
179
180 if ($this->base_location < $link['offset'])
181 {
182 $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->base);
183 }
184 else
185 {
186 $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->http_base);
187 }
188
189 if (!in_array($href, $done) && in_array('feed', $rel) || (in_array('alternate', $rel) && !in_array('stylesheet', $rel) && !empty($link['attribs']['type']['data']) && in_array(strtolower(SimplePie_Misc::parse_mime($link['attribs']['type']['data'])), array('application/rss+xml', 'application/atom+xml'))) && !isset($feeds[$href]))
190 {
191 $this->checked_feeds++;
192 $headers = array(
193 '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',
194 );
195 $feed = new $this->file_class($href, $this->timeout, 5, $headers, $this->useragent);
196 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))
197 {
198 $feeds[$href] = $feed;
199 }
200 }
201 $done[] = $href;
202 }
203 }
204
205 if (!empty($feeds))
206 {
207 return array_values($feeds);
208 }
209 else
210 {
211 return null;
212 }
213 }
214
215 public function get_links()
216 {
217 $links = SimplePie_Misc::get_element('a', $this->file->body);
218 foreach ($links as $link)
219 {
220 if (isset($link['attribs']['href']['data']))
221 {
222 $href = trim($link['attribs']['href']['data']);
223 $parsed = SimplePie_Misc::parse_url($href);
224 if ($parsed['scheme'] === '' || preg_match('/^(http(s)|feed)?$/i', $parsed['scheme']))
225 {
226 if ($this->base_location < $link['offset'])
227 {
228 $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->base);
229 }
230 else
231 {
232 $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->http_base);
233 }
234
235 $current = SimplePie_Misc::parse_url($this->file->url);
236
237 if ($parsed['authority'] === '' || $parsed['authority'] === $current['authority'])
238 {
239 $this->local[] = $href;
240 }
241 else
242 {
243 $this->elsewhere[] = $href;
244 }
245 }
246 }
247 }
248 $this->local = array_unique($this->local);
249 $this->elsewhere = array_unique($this->elsewhere);
250 if (!empty($this->local) || !empty($this->elsewhere))
251 {
252 return true;
253 }
254 return null;
255 }
256
257 public function extension(&$array)
258 {
259 foreach ($array as $key => $value)
260 {
261 if ($this->checked_feeds === $this->max_checked_feeds)
262 {
263 break;
264 }
265 if (in_array(strtolower(strrchr($value, '.')), array('.rss', '.rdf', '.atom', '.xml')))
266 {
267 $this->checked_feeds++;
268
269 $headers = array(
270 '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',
271 );
272 $feed = new $this->file_class($value, $this->timeout, 5, $headers, $this->useragent);
273 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))
274 {
275 return $feed;
276 }
277 else
278 {
279 unset($array[$key]);
280 }
281 }
282 }
283 return null;
284 }
285
286 public function body(&$array)
287 {
288 foreach ($array as $key => $value)
289 {
290 if ($this->checked_feeds === $this->max_checked_feeds)
291 {
292 break;
293 }
294 if (preg_match('/(rss|rdf|atom|xml)/i', $value))
295 {
296 $this->checked_feeds++;
297 $headers = array(
298 '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',
299 );
300 $feed = new $this->file_class($value, $this->timeout, 5, null, $this->useragent);
301 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))
302 {
303 return $feed;
304 }
305 else
306 {
307 unset($array[$key]);
308 }
309 }
310 }
311 return null;
312 }
313}
314
diff --git a/inc/3rdparty/simplepie/SimplePie/Misc.php b/inc/3rdparty/simplepie/SimplePie/Misc.php
new file mode 100644
index 00000000..eabf2738
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Misc.php
@@ -0,0 +1,2365 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47class SimplePie_Misc
48{
49 public static function time_hms($seconds)
50 {
51 $time = '';
52
53 $hours = floor($seconds / 3600);
54 $remainder = $seconds % 3600;
55 if ($hours > 0)
56 {
57 $time .= $hours.':';
58 }
59
60 $minutes = floor($remainder / 60);
61 $seconds = $remainder % 60;
62 if ($minutes < 10 && $hours > 0)
63 {
64 $minutes = '0' . $minutes;
65 }
66 if ($seconds < 10)
67 {
68 $seconds = '0' . $seconds;
69 }
70
71 $time .= $minutes.':';
72 $time .= $seconds;
73
74 return $time;
75 }
76
77 public static function absolutize_url($relative, $base)
78 {
79 $iri = SimplePie_IRI::absolutize(new SimplePie_IRI($base), $relative);
80 return $iri->get_iri();
81 }
82
83 public static function remove_dot_segments($input)
84 {
85 $output = '';
86 while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..')
87 {
88 // A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise,
89 if (strpos($input, '../') === 0)
90 {
91 $input = substr($input, 3);
92 }
93 elseif (strpos($input, './') === 0)
94 {
95 $input = substr($input, 2);
96 }
97 // 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,
98 elseif (strpos($input, '/./') === 0)
99 {
100 $input = substr_replace($input, '/', 0, 3);
101 }
102 elseif ($input === '/.')
103 {
104 $input = '/';
105 }
106 // 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,
107 elseif (strpos($input, '/../') === 0)
108 {
109 $input = substr_replace($input, '/', 0, 4);
110 $output = substr_replace($output, '', strrpos($output, '/'));
111 }
112 elseif ($input === '/..')
113 {
114 $input = '/';
115 $output = substr_replace($output, '', strrpos($output, '/'));
116 }
117 // D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise,
118 elseif ($input === '.' || $input === '..')
119 {
120 $input = '';
121 }
122 // 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
123 elseif (($pos = strpos($input, '/', 1)) !== false)
124 {
125 $output .= substr($input, 0, $pos);
126 $input = substr_replace($input, '', 0, $pos);
127 }
128 else
129 {
130 $output .= $input;
131 $input = '';
132 }
133 }
134 return $output . $input;
135 }
136
137 public static function get_element($realname, $string)
138 {
139 $return = array();
140 $name = preg_quote($realname, '/');
141 if (preg_match_all("/<($name)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$name>|(\/)?>)/siU", $string, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE))
142 {
143 for ($i = 0, $total_matches = count($matches); $i < $total_matches; $i++)
144 {
145 $return[$i]['tag'] = $realname;
146 $return[$i]['full'] = $matches[$i][0][0];
147 $return[$i]['offset'] = $matches[$i][0][1];
148 if (strlen($matches[$i][3][0]) <= 2)
149 {
150 $return[$i]['self_closing'] = true;
151 }
152 else
153 {
154 $return[$i]['self_closing'] = false;
155 $return[$i]['content'] = $matches[$i][4][0];
156 }
157 $return[$i]['attribs'] = array();
158 if (isset($matches[$i][2][0]) && preg_match_all('/[\x09\x0A\x0B\x0C\x0D\x20]+([^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*)(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"([^"]*)"|\'([^\']*)\'|([^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?/', ' ' . $matches[$i][2][0] . ' ', $attribs, PREG_SET_ORDER))
159 {
160 for ($j = 0, $total_attribs = count($attribs); $j < $total_attribs; $j++)
161 {
162 if (count($attribs[$j]) === 2)
163 {
164 $attribs[$j][2] = $attribs[$j][1];
165 }
166 $return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = SimplePie_Misc::entities_decode(end($attribs[$j]), 'UTF-8');
167 }
168 }
169 }
170 }
171 return $return;
172 }
173
174 public static function element_implode($element)
175 {
176 $full = "<$element[tag]";
177 foreach ($element['attribs'] as $key => $value)
178 {
179 $key = strtolower($key);
180 $full .= " $key=\"" . htmlspecialchars($value['data']) . '"';
181 }
182 if ($element['self_closing'])
183 {
184 $full .= ' />';
185 }
186 else
187 {
188 $full .= ">$element[content]</$element[tag]>";
189 }
190 return $full;
191 }
192
193 public static function error($message, $level, $file, $line)
194 {
195 if ((ini_get('error_reporting') & $level) > 0)
196 {
197 switch ($level)
198 {
199 case E_USER_ERROR:
200 $note = 'PHP Error';
201 break;
202 case E_USER_WARNING:
203 $note = 'PHP Warning';
204 break;
205 case E_USER_NOTICE:
206 $note = 'PHP Notice';
207 break;
208 default:
209 $note = 'Unknown Error';
210 break;
211 }
212
213 $log_error = true;
214 if (!function_exists('error_log'))
215 {
216 $log_error = false;
217 }
218
219 $log_file = @ini_get('error_log');
220 if (!empty($log_file) && ('syslog' !== $log_file) && !@is_writable($log_file))
221 {
222 $log_error = false;
223 }
224
225 if ($log_error)
226 {
227 @error_log("$note: $message in $file on line $line", 0);
228 }
229 }
230
231 return $message;
232 }
233
234 public static function fix_protocol($url, $http = 1)
235 {
236 $url = SimplePie_Misc::normalize_url($url);
237 $parsed = SimplePie_Misc::parse_url($url);
238 if ($parsed['scheme'] !== '' && $parsed['scheme'] !== 'http' && $parsed['scheme'] !== 'https')
239 {
240 return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['authority'], $parsed['path'], $parsed['query'], $parsed['fragment']), $http);
241 }
242
243 if ($parsed['scheme'] === '' && $parsed['authority'] === '' && !file_exists($url))
244 {
245 return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['path'], '', $parsed['query'], $parsed['fragment']), $http);
246 }
247
248 if ($http === 2 && $parsed['scheme'] !== '')
249 {
250 return "feed:$url";
251 }
252 elseif ($http === 3 && strtolower($parsed['scheme']) === 'http')
253 {
254 return substr_replace($url, 'podcast', 0, 4);
255 }
256 elseif ($http === 4 && strtolower($parsed['scheme']) === 'http')
257 {
258 return substr_replace($url, 'itpc', 0, 4);
259 }
260 else
261 {
262 return $url;
263 }
264 }
265
266 public static function parse_url($url)
267 {
268 $iri = new SimplePie_IRI($url);
269 return array(
270 'scheme' => (string) $iri->get_scheme(),
271 'authority' => (string) $iri->get_authority(),
272 'path' => (string) $iri->get_path(),
273 'query' => (string) $iri->get_query(),
274 'fragment' => (string) $iri->get_fragment()
275 );
276 }
277
278 public static function compress_parse_url($scheme = '', $authority = '', $path = '', $query = '', $fragment = '')
279 {
280 $iri = new SimplePie_IRI('');
281 $iri->set_scheme($scheme);
282 $iri->set_authority($authority);
283 $iri->set_path($path);
284 $iri->set_query($query);
285 $iri->set_fragment($fragment);
286 return $iri->get_iri();
287 }
288
289 public static function normalize_url($url)
290 {
291 $iri = new SimplePie_IRI($url);
292 return $iri->get_iri();
293 }
294
295 public static function percent_encoding_normalization($match)
296 {
297 $integer = hexdec($match[1]);
298 if ($integer >= 0x41 && $integer <= 0x5A || $integer >= 0x61 && $integer <= 0x7A || $integer >= 0x30 && $integer <= 0x39 || $integer === 0x2D || $integer === 0x2E || $integer === 0x5F || $integer === 0x7E)
299 {
300 return chr($integer);
301 }
302 else
303 {
304 return strtoupper($match[0]);
305 }
306 }
307
308 /**
309 * Converts a Windows-1252 encoded string to a UTF-8 encoded string
310 *
311 * @static
312 * @param string $string Windows-1252 encoded string
313 * @return string UTF-8 encoded string
314 */
315 public static function windows_1252_to_utf8($string)
316 {
317 static $convert_table = array("\x80" => "\xE2\x82\xAC", "\x81" => "\xEF\xBF\xBD", "\x82" => "\xE2\x80\x9A", "\x83" => "\xC6\x92", "\x84" => "\xE2\x80\x9E", "\x85" => "\xE2\x80\xA6", "\x86" => "\xE2\x80\xA0", "\x87" => "\xE2\x80\xA1", "\x88" => "\xCB\x86", "\x89" => "\xE2\x80\xB0", "\x8A" => "\xC5\xA0", "\x8B" => "\xE2\x80\xB9", "\x8C" => "\xC5\x92", "\x8D" => "\xEF\xBF\xBD", "\x8E" => "\xC5\xBD", "\x8F" => "\xEF\xBF\xBD", "\x90" => "\xEF\xBF\xBD", "\x91" => "\xE2\x80\x98", "\x92" => "\xE2\x80\x99", "\x93" => "\xE2\x80\x9C", "\x94" => "\xE2\x80\x9D", "\x95" => "\xE2\x80\xA2", "\x96" => "\xE2\x80\x93", "\x97" => "\xE2\x80\x94", "\x98" => "\xCB\x9C", "\x99" => "\xE2\x84\xA2", "\x9A" => "\xC5\xA1", "\x9B" => "\xE2\x80\xBA", "\x9C" => "\xC5\x93", "\x9D" => "\xEF\xBF\xBD", "\x9E" => "\xC5\xBE", "\x9F" => "\xC5\xB8", "\xA0" => "\xC2\xA0", "\xA1" => "\xC2\xA1", "\xA2" => "\xC2\xA2", "\xA3" => "\xC2\xA3", "\xA4" => "\xC2\xA4", "\xA5" => "\xC2\xA5", "\xA6" => "\xC2\xA6", "\xA7" => "\xC2\xA7", "\xA8" => "\xC2\xA8", "\xA9" => "\xC2\xA9", "\xAA" => "\xC2\xAA", "\xAB" => "\xC2\xAB", "\xAC" => "\xC2\xAC", "\xAD" => "\xC2\xAD", "\xAE" => "\xC2\xAE", "\xAF" => "\xC2\xAF", "\xB0" => "\xC2\xB0", "\xB1" => "\xC2\xB1", "\xB2" => "\xC2\xB2", "\xB3" => "\xC2\xB3", "\xB4" => "\xC2\xB4", "\xB5" => "\xC2\xB5", "\xB6" => "\xC2\xB6", "\xB7" => "\xC2\xB7", "\xB8" => "\xC2\xB8", "\xB9" => "\xC2\xB9", "\xBA" => "\xC2\xBA", "\xBB" => "\xC2\xBB", "\xBC" => "\xC2\xBC", "\xBD" => "\xC2\xBD", "\xBE" => "\xC2\xBE", "\xBF" => "\xC2\xBF", "\xC0" => "\xC3\x80", "\xC1" => "\xC3\x81", "\xC2" => "\xC3\x82", "\xC3" => "\xC3\x83", "\xC4" => "\xC3\x84", "\xC5" => "\xC3\x85", "\xC6" => "\xC3\x86", "\xC7" => "\xC3\x87", "\xC8" => "\xC3\x88", "\xC9" => "\xC3\x89", "\xCA" => "\xC3\x8A", "\xCB" => "\xC3\x8B", "\xCC" => "\xC3\x8C", "\xCD" => "\xC3\x8D", "\xCE" => "\xC3\x8E", "\xCF" => "\xC3\x8F", "\xD0" => "\xC3\x90", "\xD1" => "\xC3\x91", "\xD2" => "\xC3\x92", "\xD3" => "\xC3\x93", "\xD4" => "\xC3\x94", "\xD5" => "\xC3\x95", "\xD6" => "\xC3\x96", "\xD7" => "\xC3\x97", "\xD8" => "\xC3\x98", "\xD9" => "\xC3\x99", "\xDA" => "\xC3\x9A", "\xDB" => "\xC3\x9B", "\xDC" => "\xC3\x9C", "\xDD" => "\xC3\x9D", "\xDE" => "\xC3\x9E", "\xDF" => "\xC3\x9F", "\xE0" => "\xC3\xA0", "\xE1" => "\xC3\xA1", "\xE2" => "\xC3\xA2", "\xE3" => "\xC3\xA3", "\xE4" => "\xC3\xA4", "\xE5" => "\xC3\xA5", "\xE6" => "\xC3\xA6", "\xE7" => "\xC3\xA7", "\xE8" => "\xC3\xA8", "\xE9" => "\xC3\xA9", "\xEA" => "\xC3\xAA", "\xEB" => "\xC3\xAB", "\xEC" => "\xC3\xAC", "\xED" => "\xC3\xAD", "\xEE" => "\xC3\xAE", "\xEF" => "\xC3\xAF", "\xF0" => "\xC3\xB0", "\xF1" => "\xC3\xB1", "\xF2" => "\xC3\xB2", "\xF3" => "\xC3\xB3", "\xF4" => "\xC3\xB4", "\xF5" => "\xC3\xB5", "\xF6" => "\xC3\xB6", "\xF7" => "\xC3\xB7", "\xF8" => "\xC3\xB8", "\xF9" => "\xC3\xB9", "\xFA" => "\xC3\xBA", "\xFB" => "\xC3\xBB", "\xFC" => "\xC3\xBC", "\xFD" => "\xC3\xBD", "\xFE" => "\xC3\xBE", "\xFF" => "\xC3\xBF");
318
319 return strtr($string, $convert_table);
320 }
321
322 /**
323 * Change a string from one encoding to another
324 *
325 * @param string $data Raw data in $input encoding
326 * @param string $input Encoding of $data
327 * @param string $output Encoding you want
328 * @return string|boolean False if we can't convert it
329 */
330 public static function change_encoding($data, $input, $output)
331 {
332 $input = SimplePie_Misc::encoding($input);
333 $output = SimplePie_Misc::encoding($output);
334
335 // We fail to fail on non US-ASCII bytes
336 if ($input === 'US-ASCII')
337 {
338 static $non_ascii_octects = '';
339 if (!$non_ascii_octects)
340 {
341 for ($i = 0x80; $i <= 0xFF; $i++)
342 {
343 $non_ascii_octects .= chr($i);
344 }
345 }
346 $data = substr($data, 0, strcspn($data, $non_ascii_octects));
347 }
348
349 // This is first, as behaviour of this is completely predictable
350 if ($input === 'windows-1252' && $output === 'UTF-8')
351 {
352 return SimplePie_Misc::windows_1252_to_utf8($data);
353 }
354 // This is second, as behaviour of this varies only with PHP version (the middle part of this expression checks the encoding is supported).
355 elseif (function_exists('mb_convert_encoding') && ($return = SimplePie_Misc::change_encoding_mbstring($data, $input, $output)))
356 {
357 return $return;
358 }
359 // This is last, as behaviour of this varies with OS userland and PHP version
360 elseif (function_exists('iconv') && ($return = SimplePie_Misc::change_encoding_iconv($data, $input, $output)))
361 {
362 return $return;
363 }
364 // If we can't do anything, just fail
365 else
366 {
367 return false;
368 }
369 }
370
371 protected static function change_encoding_mbstring($data, $input, $output)
372 {
373 if ($input === 'windows-949')
374 {
375 $input = 'EUC-KR';
376 }
377 if ($output === 'windows-949')
378 {
379 $output = 'EUC-KR';
380 }
381
382 // Check that the encoding is supported
383 if (@mb_convert_encoding("\x80", 'UTF-16BE', $input) === "\x00\x80")
384 {
385 return false;
386 }
387 if (!in_array($input, mb_list_encodings()))
388 {
389 return false;
390 }
391
392 // Let's do some conversion
393 if ($return = @mb_convert_encoding($data, $output, $input))
394 {
395 return $return;
396 }
397
398 return false;
399 }
400
401 protected static function change_encoding_iconv($data, $input, $output)
402 {
403 return @iconv($input, $output, $data);
404 }
405
406 /**
407 * Normalize an encoding name
408 *
409 * This is automatically generated by create.php
410 *
411 * To generate it, run `php create.php` on the command line, and copy the
412 * output to replace this function.
413 *
414 * @param string $charset Character set to standardise
415 * @return string Standardised name
416 */
417 public static function encoding($charset)
418 {
419 // Normalization from UTS #22
420 switch (strtolower(preg_replace('/(?:[^a-zA-Z0-9]+|([^0-9])0+)/', '\1', $charset)))
421 {
422 case 'adobestandardencoding':
423 case 'csadobestandardencoding':
424 return 'Adobe-Standard-Encoding';
425
426 case 'adobesymbolencoding':
427 case 'cshppsmath':
428 return 'Adobe-Symbol-Encoding';
429
430 case 'ami1251':
431 case 'amiga1251':
432 return 'Amiga-1251';
433
434 case 'ansix31101983':
435 case 'csat5001983':
436 case 'csiso99naplps':
437 case 'isoir99':
438 case 'naplps':
439 return 'ANSI_X3.110-1983';
440
441 case 'arabic7':
442 case 'asmo449':
443 case 'csiso89asmo449':
444 case 'iso9036':
445 case 'isoir89':
446 return 'ASMO_449';
447
448 case 'big5':
449 case 'csbig5':
450 return 'Big5';
451
452 case 'big5hkscs':
453 return 'Big5-HKSCS';
454
455 case 'bocu1':
456 case 'csbocu1':
457 return 'BOCU-1';
458
459 case 'brf':
460 case 'csbrf':
461 return 'BRF';
462
463 case 'bs4730':
464 case 'csiso4unitedkingdom':
465 case 'gb':
466 case 'iso646gb':
467 case 'isoir4':
468 case 'uk':
469 return 'BS_4730';
470
471 case 'bsviewdata':
472 case 'csiso47bsviewdata':
473 case 'isoir47':
474 return 'BS_viewdata';
475
476 case 'cesu8':
477 case 'cscesu8':
478 return 'CESU-8';
479
480 case 'ca':
481 case 'csa71':
482 case 'csaz243419851':
483 case 'csiso121canadian1':
484 case 'iso646ca':
485 case 'isoir121':
486 return 'CSA_Z243.4-1985-1';
487
488 case 'csa72':
489 case 'csaz243419852':
490 case 'csiso122canadian2':
491 case 'iso646ca2':
492 case 'isoir122':
493 return 'CSA_Z243.4-1985-2';
494
495 case 'csaz24341985gr':
496 case 'csiso123csaz24341985gr':
497 case 'isoir123':
498 return 'CSA_Z243.4-1985-gr';
499
500 case 'csiso139csn369103':
501 case 'csn369103':
502 case 'isoir139':
503 return 'CSN_369103';
504
505 case 'csdecmcs':
506 case 'dec':
507 case 'decmcs':
508 return 'DEC-MCS';
509
510 case 'csiso21german':
511 case 'de':
512 case 'din66003':
513 case 'iso646de':
514 case 'isoir21':
515 return 'DIN_66003';
516
517 case 'csdkus':
518 case 'dkus':
519 return 'dk-us';
520
521 case 'csiso646danish':
522 case 'dk':
523 case 'ds2089':
524 case 'iso646dk':
525 return 'DS_2089';
526
527 case 'csibmebcdicatde':
528 case 'ebcdicatde':
529 return 'EBCDIC-AT-DE';
530
531 case 'csebcdicatdea':
532 case 'ebcdicatdea':
533 return 'EBCDIC-AT-DE-A';
534
535 case 'csebcdiccafr':
536 case 'ebcdiccafr':
537 return 'EBCDIC-CA-FR';
538
539 case 'csebcdicdkno':
540 case 'ebcdicdkno':
541 return 'EBCDIC-DK-NO';
542
543 case 'csebcdicdknoa':
544 case 'ebcdicdknoa':
545 return 'EBCDIC-DK-NO-A';
546
547 case 'csebcdices':
548 case 'ebcdices':
549 return 'EBCDIC-ES';
550
551 case 'csebcdicesa':
552 case 'ebcdicesa':
553 return 'EBCDIC-ES-A';
554
555 case 'csebcdicess':
556 case 'ebcdicess':
557 return 'EBCDIC-ES-S';
558
559 case 'csebcdicfise':
560 case 'ebcdicfise':
561 return 'EBCDIC-FI-SE';
562
563 case 'csebcdicfisea':
564 case 'ebcdicfisea':
565 return 'EBCDIC-FI-SE-A';
566
567 case 'csebcdicfr':
568 case 'ebcdicfr':
569 return 'EBCDIC-FR';
570
571 case 'csebcdicit':
572 case 'ebcdicit':
573 return 'EBCDIC-IT';
574
575 case 'csebcdicpt':
576 case 'ebcdicpt':
577 return 'EBCDIC-PT';
578
579 case 'csebcdicuk':
580 case 'ebcdicuk':
581 return 'EBCDIC-UK';
582
583 case 'csebcdicus':
584 case 'ebcdicus':
585 return 'EBCDIC-US';
586
587 case 'csiso111ecmacyrillic':
588 case 'ecmacyrillic':
589 case 'isoir111':
590 case 'koi8e':
591 return 'ECMA-cyrillic';
592
593 case 'csiso17spanish':
594 case 'es':
595 case 'iso646es':
596 case 'isoir17':
597 return 'ES';
598
599 case 'csiso85spanish2':
600 case 'es2':
601 case 'iso646es2':
602 case 'isoir85':
603 return 'ES2';
604
605 case 'cseucpkdfmtjapanese':
606 case 'eucjp':
607 case 'extendedunixcodepackedformatforjapanese':
608 return 'EUC-JP';
609
610 case 'cseucfixwidjapanese':
611 case 'extendedunixcodefixedwidthforjapanese':
612 return 'Extended_UNIX_Code_Fixed_Width_for_Japanese';
613
614 case 'gb18030':
615 return 'GB18030';
616
617 case 'chinese':
618 case 'cp936':
619 case 'csgb2312':
620 case 'csiso58gb231280':
621 case 'gb2312':
622 case 'gb231280':
623 case 'gbk':
624 case 'isoir58':
625 case 'ms936':
626 case 'windows936':
627 return 'GBK';
628
629 case 'cn':
630 case 'csiso57gb1988':
631 case 'gb198880':
632 case 'iso646cn':
633 case 'isoir57':
634 return 'GB_1988-80';
635
636 case 'csiso153gost1976874':
637 case 'gost1976874':
638 case 'isoir153':
639 case 'stsev35888':
640 return 'GOST_19768-74';
641
642 case 'csiso150':
643 case 'csiso150greekccitt':
644 case 'greekccitt':
645 case 'isoir150':
646 return 'greek-ccitt';
647
648 case 'csiso88greek7':
649 case 'greek7':
650 case 'isoir88':
651 return 'greek7';
652
653 case 'csiso18greek7old':
654 case 'greek7old':
655 case 'isoir18':
656 return 'greek7-old';
657
658 case 'cshpdesktop':
659 case 'hpdesktop':
660 return 'HP-DeskTop';
661
662 case 'cshplegal':
663 case 'hplegal':
664 return 'HP-Legal';
665
666 case 'cshpmath8':
667 case 'hpmath8':
668 return 'HP-Math8';
669
670 case 'cshppifont':
671 case 'hppifont':
672 return 'HP-Pi-font';
673
674 case 'cshproman8':
675 case 'hproman8':
676 case 'r8':
677 case 'roman8':
678 return 'hp-roman8';
679
680 case 'hzgb2312':
681 return 'HZ-GB-2312';
682
683 case 'csibmsymbols':
684 case 'ibmsymbols':
685 return 'IBM-Symbols';
686
687 case 'csibmthai':
688 case 'ibmthai':
689 return 'IBM-Thai';
690
691 case 'cp37':
692 case 'csibm37':
693 case 'ebcdiccpca':
694 case 'ebcdiccpnl':
695 case 'ebcdiccpus':
696 case 'ebcdiccpwt':
697 case 'ibm37':
698 return 'IBM037';
699
700 case 'cp38':
701 case 'csibm38':
702 case 'ebcdicint':
703 case 'ibm38':
704 return 'IBM038';
705
706 case 'cp273':
707 case 'csibm273':
708 case 'ibm273':
709 return 'IBM273';
710
711 case 'cp274':
712 case 'csibm274':
713 case 'ebcdicbe':
714 case 'ibm274':
715 return 'IBM274';
716
717 case 'cp275':
718 case 'csibm275':
719 case 'ebcdicbr':
720 case 'ibm275':
721 return 'IBM275';
722
723 case 'csibm277':
724 case 'ebcdiccpdk':
725 case 'ebcdiccpno':
726 case 'ibm277':
727 return 'IBM277';
728
729 case 'cp278':
730 case 'csibm278':
731 case 'ebcdiccpfi':
732 case 'ebcdiccpse':
733 case 'ibm278':
734 return 'IBM278';
735
736 case 'cp280':
737 case 'csibm280':
738 case 'ebcdiccpit':
739 case 'ibm280':
740 return 'IBM280';
741
742 case 'cp281':
743 case 'csibm281':
744 case 'ebcdicjpe':
745 case 'ibm281':
746 return 'IBM281';
747
748 case 'cp284':
749 case 'csibm284':
750 case 'ebcdiccpes':
751 case 'ibm284':
752 return 'IBM284';
753
754 case 'cp285':
755 case 'csibm285':
756 case 'ebcdiccpgb':
757 case 'ibm285':
758 return 'IBM285';
759
760 case 'cp290':
761 case 'csibm290':
762 case 'ebcdicjpkana':
763 case 'ibm290':
764 return 'IBM290';
765
766 case 'cp297':
767 case 'csibm297':
768 case 'ebcdiccpfr':
769 case 'ibm297':
770 return 'IBM297';
771
772 case 'cp420':
773 case 'csibm420':
774 case 'ebcdiccpar1':
775 case 'ibm420':
776 return 'IBM420';
777
778 case 'cp423':
779 case 'csibm423':
780 case 'ebcdiccpgr':
781 case 'ibm423':
782 return 'IBM423';
783
784 case 'cp424':
785 case 'csibm424':
786 case 'ebcdiccphe':
787 case 'ibm424':
788 return 'IBM424';
789
790 case '437':
791 case 'cp437':
792 case 'cspc8codepage437':
793 case 'ibm437':
794 return 'IBM437';
795
796 case 'cp500':
797 case 'csibm500':
798 case 'ebcdiccpbe':
799 case 'ebcdiccpch':
800 case 'ibm500':
801 return 'IBM500';
802
803 case 'cp775':
804 case 'cspc775baltic':
805 case 'ibm775':
806 return 'IBM775';
807
808 case '850':
809 case 'cp850':
810 case 'cspc850multilingual':
811 case 'ibm850':
812 return 'IBM850';
813
814 case '851':
815 case 'cp851':
816 case 'csibm851':
817 case 'ibm851':
818 return 'IBM851';
819
820 case '852':
821 case 'cp852':
822 case 'cspcp852':
823 case 'ibm852':
824 return 'IBM852';
825
826 case '855':
827 case 'cp855':
828 case 'csibm855':
829 case 'ibm855':
830 return 'IBM855';
831
832 case '857':
833 case 'cp857':
834 case 'csibm857':
835 case 'ibm857':
836 return 'IBM857';
837
838 case 'ccsid858':
839 case 'cp858':
840 case 'ibm858':
841 case 'pcmultilingual850euro':
842 return 'IBM00858';
843
844 case '860':
845 case 'cp860':
846 case 'csibm860':
847 case 'ibm860':
848 return 'IBM860';
849
850 case '861':
851 case 'cp861':
852 case 'cpis':
853 case 'csibm861':
854 case 'ibm861':
855 return 'IBM861';
856
857 case '862':
858 case 'cp862':
859 case 'cspc862latinhebrew':
860 case 'ibm862':
861 return 'IBM862';
862
863 case '863':
864 case 'cp863':
865 case 'csibm863':
866 case 'ibm863':
867 return 'IBM863';
868
869 case 'cp864':
870 case 'csibm864':
871 case 'ibm864':
872 return 'IBM864';
873
874 case '865':
875 case 'cp865':
876 case 'csibm865':
877 case 'ibm865':
878 return 'IBM865';
879
880 case '866':
881 case 'cp866':
882 case 'csibm866':
883 case 'ibm866':
884 return 'IBM866';
885
886 case 'cp868':
887 case 'cpar':
888 case 'csibm868':
889 case 'ibm868':
890 return 'IBM868';
891
892 case '869':
893 case 'cp869':
894 case 'cpgr':
895 case 'csibm869':
896 case 'ibm869':
897 return 'IBM869';
898
899 case 'cp870':
900 case 'csibm870':
901 case 'ebcdiccproece':
902 case 'ebcdiccpyu':
903 case 'ibm870':
904 return 'IBM870';
905
906 case 'cp871':
907 case 'csibm871':
908 case 'ebcdiccpis':
909 case 'ibm871':
910 return 'IBM871';
911
912 case 'cp880':
913 case 'csibm880':
914 case 'ebcdiccyrillic':
915 case 'ibm880':
916 return 'IBM880';
917
918 case 'cp891':
919 case 'csibm891':
920 case 'ibm891':
921 return 'IBM891';
922
923 case 'cp903':
924 case 'csibm903':
925 case 'ibm903':
926 return 'IBM903';
927
928 case '904':
929 case 'cp904':
930 case 'csibbm904':
931 case 'ibm904':
932 return 'IBM904';
933
934 case 'cp905':
935 case 'csibm905':
936 case 'ebcdiccptr':
937 case 'ibm905':
938 return 'IBM905';
939
940 case 'cp918':
941 case 'csibm918':
942 case 'ebcdiccpar2':
943 case 'ibm918':
944 return 'IBM918';
945
946 case 'ccsid924':
947 case 'cp924':
948 case 'ebcdiclatin9euro':
949 case 'ibm924':
950 return 'IBM00924';
951
952 case 'cp1026':
953 case 'csibm1026':
954 case 'ibm1026':
955 return 'IBM1026';
956
957 case 'ibm1047':
958 return 'IBM1047';
959
960 case 'ccsid1140':
961 case 'cp1140':
962 case 'ebcdicus37euro':
963 case 'ibm1140':
964 return 'IBM01140';
965
966 case 'ccsid1141':
967 case 'cp1141':
968 case 'ebcdicde273euro':
969 case 'ibm1141':
970 return 'IBM01141';
971
972 case 'ccsid1142':
973 case 'cp1142':
974 case 'ebcdicdk277euro':
975 case 'ebcdicno277euro':
976 case 'ibm1142':
977 return 'IBM01142';
978
979 case 'ccsid1143':
980 case 'cp1143':
981 case 'ebcdicfi278euro':
982 case 'ebcdicse278euro':
983 case 'ibm1143':
984 return 'IBM01143';
985
986 case 'ccsid1144':
987 case 'cp1144':
988 case 'ebcdicit280euro':
989 case 'ibm1144':
990 return 'IBM01144';
991
992 case 'ccsid1145':
993 case 'cp1145':
994 case 'ebcdices284euro':
995 case 'ibm1145':
996 return 'IBM01145';
997
998 case 'ccsid1146':
999 case 'cp1146':
1000 case 'ebcdicgb285euro':
1001 case 'ibm1146':
1002 return 'IBM01146';
1003
1004 case 'ccsid1147':
1005 case 'cp1147':
1006 case 'ebcdicfr297euro':
1007 case 'ibm1147':
1008 return 'IBM01147';
1009
1010 case 'ccsid1148':
1011 case 'cp1148':
1012 case 'ebcdicinternational500euro':
1013 case 'ibm1148':
1014 return 'IBM01148';
1015
1016 case 'ccsid1149':
1017 case 'cp1149':
1018 case 'ebcdicis871euro':
1019 case 'ibm1149':
1020 return 'IBM01149';
1021
1022 case 'csiso143iecp271':
1023 case 'iecp271':
1024 case 'isoir143':
1025 return 'IEC_P27-1';
1026
1027 case 'csiso49inis':
1028 case 'inis':
1029 case 'isoir49':
1030 return 'INIS';
1031
1032 case 'csiso50inis8':
1033 case 'inis8':
1034 case 'isoir50':
1035 return 'INIS-8';
1036
1037 case 'csiso51iniscyrillic':
1038 case 'iniscyrillic':
1039 case 'isoir51':
1040 return 'INIS-cyrillic';
1041
1042 case 'csinvariant':
1043 case 'invariant':
1044 return 'INVARIANT';
1045
1046 case 'iso2022cn':
1047 return 'ISO-2022-CN';
1048
1049 case 'iso2022cnext':
1050 return 'ISO-2022-CN-EXT';
1051
1052 case 'csiso2022jp':
1053 case 'iso2022jp':
1054 return 'ISO-2022-JP';
1055
1056 case 'csiso2022jp2':
1057 case 'iso2022jp2':
1058 return 'ISO-2022-JP-2';
1059
1060 case 'csiso2022kr':
1061 case 'iso2022kr':
1062 return 'ISO-2022-KR';
1063
1064 case 'cswindows30latin1':
1065 case 'iso88591windows30latin1':
1066 return 'ISO-8859-1-Windows-3.0-Latin-1';
1067
1068 case 'cswindows31latin1':
1069 case 'iso88591windows31latin1':
1070 return 'ISO-8859-1-Windows-3.1-Latin-1';
1071
1072 case 'csisolatin2':
1073 case 'iso88592':
1074 case 'iso885921987':
1075 case 'isoir101':
1076 case 'l2':
1077 case 'latin2':
1078 return 'ISO-8859-2';
1079
1080 case 'cswindows31latin2':
1081 case 'iso88592windowslatin2':
1082 return 'ISO-8859-2-Windows-Latin-2';
1083
1084 case 'csisolatin3':
1085 case 'iso88593':
1086 case 'iso885931988':
1087 case 'isoir109':
1088 case 'l3':
1089 case 'latin3':
1090 return 'ISO-8859-3';
1091
1092 case 'csisolatin4':
1093 case 'iso88594':
1094 case 'iso885941988':
1095 case 'isoir110':
1096 case 'l4':
1097 case 'latin4':
1098 return 'ISO-8859-4';
1099
1100 case 'csisolatincyrillic':
1101 case 'cyrillic':
1102 case 'iso88595':
1103 case 'iso885951988':
1104 case 'isoir144':
1105 return 'ISO-8859-5';
1106
1107 case 'arabic':
1108 case 'asmo708':
1109 case 'csisolatinarabic':
1110 case 'ecma114':
1111 case 'iso88596':
1112 case 'iso885961987':
1113 case 'isoir127':
1114 return 'ISO-8859-6';
1115
1116 case 'csiso88596e':
1117 case 'iso88596e':
1118 return 'ISO-8859-6-E';
1119
1120 case 'csiso88596i':
1121 case 'iso88596i':
1122 return 'ISO-8859-6-I';
1123
1124 case 'csisolatingreek':
1125 case 'ecma118':
1126 case 'elot928':
1127 case 'greek':
1128 case 'greek8':
1129 case 'iso88597':
1130 case 'iso885971987':
1131 case 'isoir126':
1132 return 'ISO-8859-7';
1133
1134 case 'csisolatinhebrew':
1135 case 'hebrew':
1136 case 'iso88598':
1137 case 'iso885981988':
1138 case 'isoir138':
1139 return 'ISO-8859-8';
1140
1141 case 'csiso88598e':
1142 case 'iso88598e':
1143 return 'ISO-8859-8-E';
1144
1145 case 'csiso88598i':
1146 case 'iso88598i':
1147 return 'ISO-8859-8-I';
1148
1149 case 'cswindows31latin5':
1150 case 'iso88599windowslatin5':
1151 return 'ISO-8859-9-Windows-Latin-5';
1152
1153 case 'csisolatin6':
1154 case 'iso885910':
1155 case 'iso8859101992':
1156 case 'isoir157':
1157 case 'l6':
1158 case 'latin6':
1159 return 'ISO-8859-10';
1160
1161 case 'iso885913':
1162 return 'ISO-8859-13';
1163
1164 case 'iso885914':
1165 case 'iso8859141998':
1166 case 'isoceltic':
1167 case 'isoir199':
1168 case 'l8':
1169 case 'latin8':
1170 return 'ISO-8859-14';
1171
1172 case 'iso885915':
1173 case 'latin9':
1174 return 'ISO-8859-15';
1175
1176 case 'iso885916':
1177 case 'iso8859162001':
1178 case 'isoir226':
1179 case 'l10':
1180 case 'latin10':
1181 return 'ISO-8859-16';
1182
1183 case 'iso10646j1':
1184 return 'ISO-10646-J-1';
1185
1186 case 'csunicode':
1187 case 'iso10646ucs2':
1188 return 'ISO-10646-UCS-2';
1189
1190 case 'csucs4':
1191 case 'iso10646ucs4':
1192 return 'ISO-10646-UCS-4';
1193
1194 case 'csunicodeascii':
1195 case 'iso10646ucsbasic':
1196 return 'ISO-10646-UCS-Basic';
1197
1198 case 'csunicodelatin1':
1199 case 'iso10646':
1200 case 'iso10646unicodelatin1':
1201 return 'ISO-10646-Unicode-Latin1';
1202
1203 case 'csiso10646utf1':
1204 case 'iso10646utf1':
1205 return 'ISO-10646-UTF-1';
1206
1207 case 'csiso115481':
1208 case 'iso115481':
1209 case 'isotr115481':
1210 return 'ISO-11548-1';
1211
1212 case 'csiso90':
1213 case 'isoir90':
1214 return 'iso-ir-90';
1215
1216 case 'csunicodeibm1261':
1217 case 'isounicodeibm1261':
1218 return 'ISO-Unicode-IBM-1261';
1219
1220 case 'csunicodeibm1264':
1221 case 'isounicodeibm1264':
1222 return 'ISO-Unicode-IBM-1264';
1223
1224 case 'csunicodeibm1265':
1225 case 'isounicodeibm1265':
1226 return 'ISO-Unicode-IBM-1265';
1227
1228 case 'csunicodeibm1268':
1229 case 'isounicodeibm1268':
1230 return 'ISO-Unicode-IBM-1268';
1231
1232 case 'csunicodeibm1276':
1233 case 'isounicodeibm1276':
1234 return 'ISO-Unicode-IBM-1276';
1235
1236 case 'csiso646basic1983':
1237 case 'iso646basic1983':
1238 case 'ref':
1239 return 'ISO_646.basic:1983';
1240
1241 case 'csiso2intlrefversion':
1242 case 'irv':
1243 case 'iso646irv1983':
1244 case 'isoir2':
1245 return 'ISO_646.irv:1983';
1246
1247 case 'csiso2033':
1248 case 'e13b':
1249 case 'iso20331983':
1250 case 'isoir98':
1251 return 'ISO_2033-1983';
1252
1253 case 'csiso5427cyrillic':
1254 case 'iso5427':
1255 case 'isoir37':
1256 return 'ISO_5427';
1257
1258 case 'iso5427cyrillic1981':
1259 case 'iso54271981':
1260 case 'isoir54':
1261 return 'ISO_5427:1981';
1262
1263 case 'csiso5428greek':
1264 case 'iso54281980':
1265 case 'isoir55':
1266 return 'ISO_5428:1980';
1267
1268 case 'csiso6937add':
1269 case 'iso6937225':
1270 case 'isoir152':
1271 return 'ISO_6937-2-25';
1272
1273 case 'csisotextcomm':
1274 case 'iso69372add':
1275 case 'isoir142':
1276 return 'ISO_6937-2-add';
1277
1278 case 'csiso8859supp':
1279 case 'iso8859supp':
1280 case 'isoir154':
1281 case 'latin125':
1282 return 'ISO_8859-supp';
1283
1284 case 'csiso10367box':
1285 case 'iso10367box':
1286 case 'isoir155':
1287 return 'ISO_10367-box';
1288
1289 case 'csiso15italian':
1290 case 'iso646it':
1291 case 'isoir15':
1292 case 'it':
1293 return 'IT';
1294
1295 case 'csiso13jisc6220jp':
1296 case 'isoir13':
1297 case 'jisc62201969':
1298 case 'jisc62201969jp':
1299 case 'katakana':
1300 case 'x2017':
1301 return 'JIS_C6220-1969-jp';
1302
1303 case 'csiso14jisc6220ro':
1304 case 'iso646jp':
1305 case 'isoir14':
1306 case 'jisc62201969ro':
1307 case 'jp':
1308 return 'JIS_C6220-1969-ro';
1309
1310 case 'csiso42jisc62261978':
1311 case 'isoir42':
1312 case 'jisc62261978':
1313 return 'JIS_C6226-1978';
1314
1315 case 'csiso87jisx208':
1316 case 'isoir87':
1317 case 'jisc62261983':
1318 case 'jisx2081983':
1319 case 'x208':
1320 return 'JIS_C6226-1983';
1321
1322 case 'csiso91jisc62291984a':
1323 case 'isoir91':
1324 case 'jisc62291984a':
1325 case 'jpocra':
1326 return 'JIS_C6229-1984-a';
1327
1328 case 'csiso92jisc62991984b':
1329 case 'iso646jpocrb':
1330 case 'isoir92':
1331 case 'jisc62291984b':
1332 case 'jpocrb':
1333 return 'JIS_C6229-1984-b';
1334
1335 case 'csiso93jis62291984badd':
1336 case 'isoir93':
1337 case 'jisc62291984badd':
1338 case 'jpocrbadd':
1339 return 'JIS_C6229-1984-b-add';
1340
1341 case 'csiso94jis62291984hand':
1342 case 'isoir94':
1343 case 'jisc62291984hand':
1344 case 'jpocrhand':
1345 return 'JIS_C6229-1984-hand';
1346
1347 case 'csiso95jis62291984handadd':
1348 case 'isoir95':
1349 case 'jisc62291984handadd':
1350 case 'jpocrhandadd':
1351 return 'JIS_C6229-1984-hand-add';
1352
1353 case 'csiso96jisc62291984kana':
1354 case 'isoir96':
1355 case 'jisc62291984kana':
1356 return 'JIS_C6229-1984-kana';
1357
1358 case 'csjisencoding':
1359 case 'jisencoding':
1360 return 'JIS_Encoding';
1361
1362 case 'cshalfwidthkatakana':
1363 case 'jisx201':
1364 case 'x201':
1365 return 'JIS_X0201';
1366
1367 case 'csiso159jisx2121990':
1368 case 'isoir159':
1369 case 'jisx2121990':
1370 case 'x212':
1371 return 'JIS_X0212-1990';
1372
1373 case 'csiso141jusib1002':
1374 case 'iso646yu':
1375 case 'isoir141':
1376 case 'js':
1377 case 'jusib1002':
1378 case 'yu':
1379 return 'JUS_I.B1.002';
1380
1381 case 'csiso147macedonian':
1382 case 'isoir147':
1383 case 'jusib1003mac':
1384 case 'macedonian':
1385 return 'JUS_I.B1.003-mac';
1386
1387 case 'csiso146serbian':
1388 case 'isoir146':
1389 case 'jusib1003serb':
1390 case 'serbian':
1391 return 'JUS_I.B1.003-serb';
1392
1393 case 'koi7switched':
1394 return 'KOI7-switched';
1395
1396 case 'cskoi8r':
1397 case 'koi8r':
1398 return 'KOI8-R';
1399
1400 case 'koi8u':
1401 return 'KOI8-U';
1402
1403 case 'csksc5636':
1404 case 'iso646kr':
1405 case 'ksc5636':
1406 return 'KSC5636';
1407
1408 case 'cskz1048':
1409 case 'kz1048':
1410 case 'rk1048':
1411 case 'strk10482002':
1412 return 'KZ-1048';
1413
1414 case 'csiso19latingreek':
1415 case 'isoir19':
1416 case 'latingreek':
1417 return 'latin-greek';
1418
1419 case 'csiso27latingreek1':
1420 case 'isoir27':
1421 case 'latingreek1':
1422 return 'Latin-greek-1';
1423
1424 case 'csiso158lap':
1425 case 'isoir158':
1426 case 'lap':
1427 case 'latinlap':
1428 return 'latin-lap';
1429
1430 case 'csmacintosh':
1431 case 'mac':
1432 case 'macintosh':
1433 return 'macintosh';
1434
1435 case 'csmicrosoftpublishing':
1436 case 'microsoftpublishing':
1437 return 'Microsoft-Publishing';
1438
1439 case 'csmnem':
1440 case 'mnem':
1441 return 'MNEM';
1442
1443 case 'csmnemonic':
1444 case 'mnemonic':
1445 return 'MNEMONIC';
1446
1447 case 'csiso86hungarian':
1448 case 'hu':
1449 case 'iso646hu':
1450 case 'isoir86':
1451 case 'msz77953':
1452 return 'MSZ_7795.3';
1453
1454 case 'csnatsdano':
1455 case 'isoir91':
1456 case 'natsdano':
1457 return 'NATS-DANO';
1458
1459 case 'csnatsdanoadd':
1460 case 'isoir92':
1461 case 'natsdanoadd':
1462 return 'NATS-DANO-ADD';
1463
1464 case 'csnatssefi':
1465 case 'isoir81':
1466 case 'natssefi':
1467 return 'NATS-SEFI';
1468
1469 case 'csnatssefiadd':
1470 case 'isoir82':
1471 case 'natssefiadd':
1472 return 'NATS-SEFI-ADD';
1473
1474 case 'csiso151cuba':
1475 case 'cuba':
1476 case 'iso646cu':
1477 case 'isoir151':
1478 case 'ncnc1081':
1479 return 'NC_NC00-10:81';
1480
1481 case 'csiso69french':
1482 case 'fr':
1483 case 'iso646fr':
1484 case 'isoir69':
1485 case 'nfz62010':
1486 return 'NF_Z_62-010';
1487
1488 case 'csiso25french':
1489 case 'iso646fr1':
1490 case 'isoir25':
1491 case 'nfz620101973':
1492 return 'NF_Z_62-010_(1973)';
1493
1494 case 'csiso60danishnorwegian':
1495 case 'csiso60norwegian1':
1496 case 'iso646no':
1497 case 'isoir60':
1498 case 'no':
1499 case 'ns45511':
1500 return 'NS_4551-1';
1501
1502 case 'csiso61norwegian2':
1503 case 'iso646no2':
1504 case 'isoir61':
1505 case 'no2':
1506 case 'ns45512':
1507 return 'NS_4551-2';
1508
1509 case 'osdebcdicdf3irv':
1510 return 'OSD_EBCDIC_DF03_IRV';
1511
1512 case 'osdebcdicdf41':
1513 return 'OSD_EBCDIC_DF04_1';
1514
1515 case 'osdebcdicdf415':
1516 return 'OSD_EBCDIC_DF04_15';
1517
1518 case 'cspc8danishnorwegian':
1519 case 'pc8danishnorwegian':
1520 return 'PC8-Danish-Norwegian';
1521
1522 case 'cspc8turkish':
1523 case 'pc8turkish':
1524 return 'PC8-Turkish';
1525
1526 case 'csiso16portuguese':
1527 case 'iso646pt':
1528 case 'isoir16':
1529 case 'pt':
1530 return 'PT';
1531
1532 case 'csiso84portuguese2':
1533 case 'iso646pt2':
1534 case 'isoir84':
1535 case 'pt2':
1536 return 'PT2';
1537
1538 case 'cp154':
1539 case 'csptcp154':
1540 case 'cyrillicasian':
1541 case 'pt154':
1542 case 'ptcp154':
1543 return 'PTCP154';
1544
1545 case 'scsu':
1546 return 'SCSU';
1547
1548 case 'csiso10swedish':
1549 case 'fi':
1550 case 'iso646fi':
1551 case 'iso646se':
1552 case 'isoir10':
1553 case 'se':
1554 case 'sen850200b':
1555 return 'SEN_850200_B';
1556
1557 case 'csiso11swedishfornames':
1558 case 'iso646se2':
1559 case 'isoir11':
1560 case 'se2':
1561 case 'sen850200c':
1562 return 'SEN_850200_C';
1563
1564 case 'csiso102t617bit':
1565 case 'isoir102':
1566 case 't617bit':
1567 return 'T.61-7bit';
1568
1569 case 'csiso103t618bit':
1570 case 'isoir103':
1571 case 't61':
1572 case 't618bit':
1573 return 'T.61-8bit';
1574
1575 case 'csiso128t101g2':
1576 case 'isoir128':
1577 case 't101g2':
1578 return 'T.101-G2';
1579
1580 case 'cstscii':
1581 case 'tscii':
1582 return 'TSCII';
1583
1584 case 'csunicode11':
1585 case 'unicode11':
1586 return 'UNICODE-1-1';
1587
1588 case 'csunicode11utf7':
1589 case 'unicode11utf7':
1590 return 'UNICODE-1-1-UTF-7';
1591
1592 case 'csunknown8bit':
1593 case 'unknown8bit':
1594 return 'UNKNOWN-8BIT';
1595
1596 case 'ansix341968':
1597 case 'ansix341986':
1598 case 'ascii':
1599 case 'cp367':
1600 case 'csascii':
1601 case 'ibm367':
1602 case 'iso646irv1991':
1603 case 'iso646us':
1604 case 'isoir6':
1605 case 'us':
1606 case 'usascii':
1607 return 'US-ASCII';
1608
1609 case 'csusdk':
1610 case 'usdk':
1611 return 'us-dk';
1612
1613 case 'utf7':
1614 return 'UTF-7';
1615
1616 case 'utf8':
1617 return 'UTF-8';
1618
1619 case 'utf16':
1620 return 'UTF-16';
1621
1622 case 'utf16be':
1623 return 'UTF-16BE';
1624
1625 case 'utf16le':
1626 return 'UTF-16LE';
1627
1628 case 'utf32':
1629 return 'UTF-32';
1630
1631 case 'utf32be':
1632 return 'UTF-32BE';
1633
1634 case 'utf32le':
1635 return 'UTF-32LE';
1636
1637 case 'csventurainternational':
1638 case 'venturainternational':
1639 return 'Ventura-International';
1640
1641 case 'csventuramath':
1642 case 'venturamath':
1643 return 'Ventura-Math';
1644
1645 case 'csventuraus':
1646 case 'venturaus':
1647 return 'Ventura-US';
1648
1649 case 'csiso70videotexsupp1':
1650 case 'isoir70':
1651 case 'videotexsuppl':
1652 return 'videotex-suppl';
1653
1654 case 'csviqr':
1655 case 'viqr':
1656 return 'VIQR';
1657
1658 case 'csviscii':
1659 case 'viscii':
1660 return 'VISCII';
1661
1662 case 'csshiftjis':
1663 case 'cswindows31j':
1664 case 'mskanji':
1665 case 'shiftjis':
1666 case 'windows31j':
1667 return 'SJIS';
1668 //return 'Windows-31J';
1669
1670 case 'iso885911':
1671 case 'tis620':
1672 return 'windows-874';
1673
1674 case 'cseuckr':
1675 case 'csksc56011987':
1676 case 'euckr':
1677 case 'isoir149':
1678 case 'korean':
1679 case 'ksc5601':
1680 case 'ksc56011987':
1681 case 'ksc56011989':
1682 case 'windows949':
1683 return 'windows-949';
1684
1685 case 'windows1250':
1686 return 'windows-1250';
1687
1688 case 'windows1251':
1689 return 'windows-1251';
1690
1691 case 'cp819':
1692 case 'csisolatin1':
1693 case 'ibm819':
1694 case 'iso88591':
1695 case 'iso885911987':
1696 case 'isoir100':
1697 case 'l1':
1698 case 'latin1':
1699 case 'windows1252':
1700 return 'windows-1252';
1701
1702 case 'windows1253':
1703 return 'windows-1253';
1704
1705 case 'csisolatin5':
1706 case 'iso88599':
1707 case 'iso885991989':
1708 case 'isoir148':
1709 case 'l5':
1710 case 'latin5':
1711 case 'windows1254':
1712 return 'windows-1254';
1713
1714 case 'windows1255':
1715 return 'windows-1255';
1716
1717 case 'windows1256':
1718 return 'windows-1256';
1719
1720 case 'windows1257':
1721 return 'windows-1257';
1722
1723 case 'windows1258':
1724 return 'windows-1258';
1725
1726 default:
1727 return $charset;
1728 }
1729 }
1730
1731 public static function get_curl_version()
1732 {
1733 if (is_array($curl = curl_version()))
1734 {
1735 $curl = $curl['version'];
1736 }
1737 elseif (substr($curl, 0, 5) === 'curl/')
1738 {
1739 $curl = substr($curl, 5, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 5));
1740 }
1741 elseif (substr($curl, 0, 8) === 'libcurl/')
1742 {
1743 $curl = substr($curl, 8, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 8));
1744 }
1745 else
1746 {
1747 $curl = 0;
1748 }
1749 return $curl;
1750 }
1751
1752 public static function is_subclass_of($class1, $class2)
1753 {
1754 if (func_num_args() !== 2)
1755 {
1756 trigger_error('Wrong parameter count for SimplePie_Misc::is_subclass_of()', E_USER_WARNING);
1757 }
1758 elseif (version_compare(PHP_VERSION, '5.0.3', '>=') || is_object($class1))
1759 {
1760 return is_subclass_of($class1, $class2);
1761 }
1762 elseif (is_string($class1) && is_string($class2))
1763 {
1764 if (class_exists($class1))
1765 {
1766 if (class_exists($class2))
1767 {
1768 $class2 = strtolower($class2);
1769 while ($class1 = strtolower(get_parent_class($class1)))
1770 {
1771 if ($class1 === $class2)
1772 {
1773 return true;
1774 }
1775 }
1776 }
1777 }
1778 else
1779 {
1780 trigger_error('Unknown class passed as parameter', E_USER_WARNNG);
1781 }
1782 }
1783 return false;
1784 }
1785
1786 /**
1787 * Strip HTML comments
1788 *
1789 * @param string $data Data to strip comments from
1790 * @return string Comment stripped string
1791 */
1792 public static function strip_comments($data)
1793 {
1794 $output = '';
1795 while (($start = strpos($data, '<!--')) !== false)
1796 {
1797 $output .= substr($data, 0, $start);
1798 if (($end = strpos($data, '-->', $start)) !== false)
1799 {
1800 $data = substr_replace($data, '', 0, $end + 3);
1801 }
1802 else
1803 {
1804 $data = '';
1805 }
1806 }
1807 return $output . $data;
1808 }
1809
1810 public static function parse_date($dt)
1811 {
1812 $parser = SimplePie_Parse_Date::get();
1813 return $parser->parse($dt);
1814 }
1815
1816 /**
1817 * Decode HTML entities
1818 *
1819 * @static
1820 * @param string $data Input data
1821 * @return string Output data
1822 */
1823 public static function entities_decode($data)
1824 {
1825 $decoder = new SimplePie_Decode_HTML_Entities($data);
1826 return $decoder->parse();
1827 }
1828
1829 /**
1830 * Remove RFC822 comments
1831 *
1832 * @param string $data Data to strip comments from
1833 * @return string Comment stripped string
1834 */
1835 public static function uncomment_rfc822($string)
1836 {
1837 $string = (string) $string;
1838 $position = 0;
1839 $length = strlen($string);
1840 $depth = 0;
1841
1842 $output = '';
1843
1844 while ($position < $length && ($pos = strpos($string, '(', $position)) !== false)
1845 {
1846 $output .= substr($string, $position, $pos - $position);
1847 $position = $pos + 1;
1848 if ($string[$pos - 1] !== '\\')
1849 {
1850 $depth++;
1851 while ($depth && $position < $length)
1852 {
1853 $position += strcspn($string, '()', $position);
1854 if ($string[$position - 1] === '\\')
1855 {
1856 $position++;
1857 continue;
1858 }
1859 elseif (isset($string[$position]))
1860 {
1861 switch ($string[$position])
1862 {
1863 case '(':
1864 $depth++;
1865 break;
1866
1867 case ')':
1868 $depth--;
1869 break;
1870 }
1871 $position++;
1872 }
1873 else
1874 {
1875 break;
1876 }
1877 }
1878 }
1879 else
1880 {
1881 $output .= '(';
1882 }
1883 }
1884 $output .= substr($string, $position);
1885
1886 return $output;
1887 }
1888
1889 public static function parse_mime($mime)
1890 {
1891 if (($pos = strpos($mime, ';')) === false)
1892 {
1893 return trim($mime);
1894 }
1895 else
1896 {
1897 return trim(substr($mime, 0, $pos));
1898 }
1899 }
1900
1901 public static function htmlspecialchars_decode($string, $quote_style)
1902 {
1903 if (function_exists('htmlspecialchars_decode'))
1904 {
1905 return htmlspecialchars_decode($string, $quote_style);
1906 }
1907 else
1908 {
1909 return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style)));
1910 }
1911 }
1912
1913 public static function atom_03_construct_type($attribs)
1914 {
1915 if (isset($attribs['']['mode']) && strtolower(trim($attribs['']['mode']) === 'base64'))
1916 {
1917 $mode = SIMPLEPIE_CONSTRUCT_BASE64;
1918 }
1919 else
1920 {
1921 $mode = SIMPLEPIE_CONSTRUCT_NONE;
1922 }
1923 if (isset($attribs['']['type']))
1924 {
1925 switch (strtolower(trim($attribs['']['type'])))
1926 {
1927 case 'text':
1928 case 'text/plain':
1929 return SIMPLEPIE_CONSTRUCT_TEXT | $mode;
1930
1931 case 'html':
1932 case 'text/html':
1933 return SIMPLEPIE_CONSTRUCT_HTML | $mode;
1934
1935 case 'xhtml':
1936 case 'application/xhtml+xml':
1937 return SIMPLEPIE_CONSTRUCT_XHTML | $mode;
1938
1939 default:
1940 return SIMPLEPIE_CONSTRUCT_NONE | $mode;
1941 }
1942 }
1943 else
1944 {
1945 return SIMPLEPIE_CONSTRUCT_TEXT | $mode;
1946 }
1947 }
1948
1949 public static function atom_10_construct_type($attribs)
1950 {
1951 if (isset($attribs['']['type']))
1952 {
1953 switch (strtolower(trim($attribs['']['type'])))
1954 {
1955 case 'text':
1956 return SIMPLEPIE_CONSTRUCT_TEXT;
1957
1958 case 'html':
1959 return SIMPLEPIE_CONSTRUCT_HTML;
1960
1961 case 'xhtml':
1962 return SIMPLEPIE_CONSTRUCT_XHTML;
1963
1964 default:
1965 return SIMPLEPIE_CONSTRUCT_NONE;
1966 }
1967 }
1968 return SIMPLEPIE_CONSTRUCT_TEXT;
1969 }
1970
1971 public static function atom_10_content_construct_type($attribs)
1972 {
1973 if (isset($attribs['']['type']))
1974 {
1975 $type = strtolower(trim($attribs['']['type']));
1976 switch ($type)
1977 {
1978 case 'text':
1979 return SIMPLEPIE_CONSTRUCT_TEXT;
1980
1981 case 'html':
1982 return SIMPLEPIE_CONSTRUCT_HTML;
1983
1984 case 'xhtml':
1985 return SIMPLEPIE_CONSTRUCT_XHTML;
1986 }
1987 if (in_array(substr($type, -4), array('+xml', '/xml')) || substr($type, 0, 5) === 'text/')
1988 {
1989 return SIMPLEPIE_CONSTRUCT_NONE;
1990 }
1991 else
1992 {
1993 return SIMPLEPIE_CONSTRUCT_BASE64;
1994 }
1995 }
1996 else
1997 {
1998 return SIMPLEPIE_CONSTRUCT_TEXT;
1999 }
2000 }
2001
2002 public static function is_isegment_nz_nc($string)
2003 {
2004 return (bool) preg_match('/^([A-Za-z0-9\-._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!$&\'()*+,;=@]|(%[0-9ABCDEF]{2}))+$/u', $string);
2005 }
2006
2007 public static function space_seperated_tokens($string)
2008 {
2009 $space_characters = "\x20\x09\x0A\x0B\x0C\x0D";
2010 $string_length = strlen($string);
2011
2012 $position = strspn($string, $space_characters);
2013 $tokens = array();
2014
2015 while ($position < $string_length)
2016 {
2017 $len = strcspn($string, $space_characters, $position);
2018 $tokens[] = substr($string, $position, $len);
2019 $position += $len;
2020 $position += strspn($string, $space_characters, $position);
2021 }
2022
2023 return $tokens;
2024 }
2025
2026 public static function array_unique($array)
2027 {
2028 if (version_compare(PHP_VERSION, '5.2', '>='))
2029 {
2030 return array_unique($array);
2031 }
2032 else
2033 {
2034 $array = (array) $array;
2035 $new_array = array();
2036 $new_array_strings = array();
2037 foreach ($array as $key => $value)
2038 {
2039 if (is_object($value))
2040 {
2041 if (method_exists($value, '__toString'))
2042 {
2043 $cmp = $value->__toString();
2044 }
2045 else
2046 {
2047 trigger_error('Object of class ' . get_class($value) . ' could not be converted to string', E_USER_ERROR);
2048 }
2049 }
2050 elseif (is_array($value))
2051 {
2052 $cmp = (string) reset($value);
2053 }
2054 else
2055 {
2056 $cmp = (string) $value;
2057 }
2058 if (!in_array($cmp, $new_array_strings))
2059 {
2060 $new_array[$key] = $value;
2061 $new_array_strings[] = $cmp;
2062 }
2063 }
2064 return $new_array;
2065 }
2066 }
2067
2068 /**
2069 * Converts a unicode codepoint to a UTF-8 character
2070 *
2071 * @static
2072 * @param int $codepoint Unicode codepoint
2073 * @return string UTF-8 character
2074 */
2075 public static function codepoint_to_utf8($codepoint)
2076 {
2077 $codepoint = (int) $codepoint;
2078 if ($codepoint < 0)
2079 {
2080 return false;
2081 }
2082 else if ($codepoint <= 0x7f)
2083 {
2084 return chr($codepoint);
2085 }
2086 else if ($codepoint <= 0x7ff)
2087 {
2088 return chr(0xc0 | ($codepoint >> 6)) . chr(0x80 | ($codepoint & 0x3f));
2089 }
2090 else if ($codepoint <= 0xffff)
2091 {
2092 return chr(0xe0 | ($codepoint >> 12)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f));
2093 }
2094 else if ($codepoint <= 0x10ffff)
2095 {
2096 return chr(0xf0 | ($codepoint >> 18)) . chr(0x80 | (($codepoint >> 12) & 0x3f)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f));
2097 }
2098 else
2099 {
2100 // U+FFFD REPLACEMENT CHARACTER
2101 return "\xEF\xBF\xBD";
2102 }
2103 }
2104
2105 /**
2106 * Similar to parse_str()
2107 *
2108 * Returns an associative array of name/value pairs, where the value is an
2109 * array of values that have used the same name
2110 *
2111 * @static
2112 * @param string $str The input string.
2113 * @return array
2114 */
2115 public static function parse_str($str)
2116 {
2117 $return = array();
2118 $str = explode('&', $str);
2119
2120 foreach ($str as $section)
2121 {
2122 if (strpos($section, '=') !== false)
2123 {
2124 list($name, $value) = explode('=', $section, 2);
2125 $return[urldecode($name)][] = urldecode($value);
2126 }
2127 else
2128 {
2129 $return[urldecode($section)][] = null;
2130 }
2131 }
2132
2133 return $return;
2134 }
2135
2136 /**
2137 * Detect XML encoding, as per XML 1.0 Appendix F.1
2138 *
2139 * @todo Add support for EBCDIC
2140 * @param string $data XML data
2141 * @return array Possible encodings
2142 */
2143 public static function xml_encoding($data)
2144 {
2145 // UTF-32 Big Endian BOM
2146 if (substr($data, 0, 4) === "\x00\x00\xFE\xFF")
2147 {
2148 $encoding[] = 'UTF-32BE';
2149 }
2150 // UTF-32 Little Endian BOM
2151 elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00")
2152 {
2153 $encoding[] = 'UTF-32LE';
2154 }
2155 // UTF-16 Big Endian BOM
2156 elseif (substr($data, 0, 2) === "\xFE\xFF")
2157 {
2158 $encoding[] = 'UTF-16BE';
2159 }
2160 // UTF-16 Little Endian BOM
2161 elseif (substr($data, 0, 2) === "\xFF\xFE")
2162 {
2163 $encoding[] = 'UTF-16LE';
2164 }
2165 // UTF-8 BOM
2166 elseif (substr($data, 0, 3) === "\xEF\xBB\xBF")
2167 {
2168 $encoding[] = 'UTF-8';
2169 }
2170 // UTF-32 Big Endian Without BOM
2171 elseif (substr($data, 0, 20) === "\x00\x00\x00\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C")
2172 {
2173 if ($pos = strpos($data, "\x00\x00\x00\x3F\x00\x00\x00\x3E"))
2174 {
2175 $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32BE', 'UTF-8'));
2176 if ($parser->parse())
2177 {
2178 $encoding[] = $parser->encoding;
2179 }
2180 }
2181 $encoding[] = 'UTF-32BE';
2182 }
2183 // UTF-32 Little Endian Without BOM
2184 elseif (substr($data, 0, 20) === "\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C\x00\x00\x00")
2185 {
2186 if ($pos = strpos($data, "\x3F\x00\x00\x00\x3E\x00\x00\x00"))
2187 {
2188 $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32LE', 'UTF-8'));
2189 if ($parser->parse())
2190 {
2191 $encoding[] = $parser->encoding;
2192 }
2193 }
2194 $encoding[] = 'UTF-32LE';
2195 }
2196 // UTF-16 Big Endian Without BOM
2197 elseif (substr($data, 0, 10) === "\x00\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C")
2198 {
2199 if ($pos = strpos($data, "\x00\x3F\x00\x3E"))
2200 {
2201 $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16BE', 'UTF-8'));
2202 if ($parser->parse())
2203 {
2204 $encoding[] = $parser->encoding;
2205 }
2206 }
2207 $encoding[] = 'UTF-16BE';
2208 }
2209 // UTF-16 Little Endian Without BOM
2210 elseif (substr($data, 0, 10) === "\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C\x00")
2211 {
2212 if ($pos = strpos($data, "\x3F\x00\x3E\x00"))
2213 {
2214 $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16LE', 'UTF-8'));
2215 if ($parser->parse())
2216 {
2217 $encoding[] = $parser->encoding;
2218 }
2219 }
2220 $encoding[] = 'UTF-16LE';
2221 }
2222 // US-ASCII (or superset)
2223 elseif (substr($data, 0, 5) === "\x3C\x3F\x78\x6D\x6C")
2224 {
2225 if ($pos = strpos($data, "\x3F\x3E"))
2226 {
2227 $parser = new SimplePie_XML_Declaration_Parser(substr($data, 5, $pos - 5));
2228 if ($parser->parse())
2229 {
2230 $encoding[] = $parser->encoding;
2231 }
2232 }
2233 $encoding[] = 'UTF-8';
2234 }
2235 // Fallback to UTF-8
2236 else
2237 {
2238 $encoding[] = 'UTF-8';
2239 }
2240 return $encoding;
2241 }
2242
2243 public static function output_javascript()
2244 {
2245 if (function_exists('ob_gzhandler'))
2246 {
2247 ob_start('ob_gzhandler');
2248 }
2249 header('Content-type: text/javascript; charset: UTF-8');
2250 header('Cache-Control: must-revalidate');
2251 header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days
2252 ?>
2253function embed_odeo(link) {
2254 document.writeln('<embed src="http://odeo.com/flash/audio_player_fullsize.swf" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" quality="high" width="440" height="80" wmode="transparent" allowScriptAccess="any" flashvars="valid_sample_rate=true&external_url='+link+'"></embed>');
2255}
2256
2257function embed_quicktime(type, bgcolor, width, height, link, placeholder, loop) {
2258 if (placeholder != '') {
2259 document.writeln('<embed type="'+type+'" style="cursor:hand; cursor:pointer;" href="'+link+'" src="'+placeholder+'" width="'+width+'" height="'+height+'" autoplay="false" target="myself" controller="false" loop="'+loop+'" scale="aspect" bgcolor="'+bgcolor+'" pluginspage="http://www.apple.com/quicktime/download/"></embed>');
2260 }
2261 else {
2262 document.writeln('<embed type="'+type+'" style="cursor:hand; cursor:pointer;" src="'+link+'" width="'+width+'" height="'+height+'" autoplay="false" target="myself" controller="true" loop="'+loop+'" scale="aspect" bgcolor="'+bgcolor+'" pluginspage="http://www.apple.com/quicktime/download/"></embed>');
2263 }
2264}
2265
2266function embed_flash(bgcolor, width, height, link, loop, type) {
2267 document.writeln('<embed src="'+link+'" pluginspage="http://www.macromedia.com/go/getflashplayer" type="'+type+'" quality="high" width="'+width+'" height="'+height+'" bgcolor="'+bgcolor+'" loop="'+loop+'"></embed>');
2268}
2269
2270function embed_flv(width, height, link, placeholder, loop, player) {
2271 document.writeln('<embed src="'+player+'" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" quality="high" width="'+width+'" height="'+height+'" wmode="transparent" flashvars="file='+link+'&autostart=false&repeat='+loop+'&showdigits=true&showfsbutton=false"></embed>');
2272}
2273
2274function embed_wmedia(width, height, link) {
2275 document.writeln('<embed type="application/x-mplayer2" src="'+link+'" autosize="1" width="'+width+'" height="'+height+'" showcontrols="1" showstatusbar="0" showdisplay="0" autostart="0"></embed>');
2276}
2277 <?php
2278 }
2279
2280 /**
2281 * Get the SimplePie build timestamp
2282 *
2283 * Uses the git index if it exists, otherwise uses the modification time
2284 * of the newest file.
2285 */
2286 public static function get_build()
2287 {
2288 $root = dirname(dirname(__FILE__));
2289 if (file_exists($root . '/.git/index'))
2290 {
2291 return filemtime($root . '/.git/index');
2292 }
2293 elseif (file_exists($root . '/SimplePie'))
2294 {
2295 $time = 0;
2296 foreach (glob($root . '/SimplePie/*.php') as $file)
2297 {
2298 if (($mtime = filemtime($file)) > $time)
2299 {
2300 $time = $mtime;
2301 }
2302 }
2303 return $time;
2304 }
2305 elseif (file_exists(dirname(__FILE__) . '/Core.php'))
2306 {
2307 return filemtime(dirname(__FILE__) . '/Core.php');
2308 }
2309 else
2310 {
2311 return filemtime(__FILE__);
2312 }
2313 }
2314
2315 /**
2316 * Format debugging information
2317 */
2318 public static function debug(&$sp)
2319 {
2320 $info = 'SimplePie ' . SIMPLEPIE_VERSION . ' Build ' . SIMPLEPIE_BUILD . "\n";
2321 $info .= 'PHP ' . PHP_VERSION . "\n";
2322 if ($sp->error() !== null)
2323 {
2324 $info .= 'Error occurred: ' . $sp->error() . "\n";
2325 }
2326 else
2327 {
2328 $info .= "No error found.\n";
2329 }
2330 $info .= "Extensions:\n";
2331 $extensions = array('pcre', 'curl', 'zlib', 'mbstring', 'iconv', 'xmlreader', 'xml');
2332 foreach ($extensions as $ext)
2333 {
2334 if (extension_loaded($ext))
2335 {
2336 $info .= " $ext loaded\n";
2337 switch ($ext)
2338 {
2339 case 'pcre':
2340 $info .= ' Version ' . PCRE_VERSION . "\n";
2341 break;
2342 case 'curl':
2343 $version = curl_version();
2344 $info .= ' Version ' . $version['version'] . "\n";
2345 break;
2346 case 'mbstring':
2347 $info .= ' Overloading: ' . mb_get_info('func_overload') . "\n";
2348 break;
2349 case 'iconv':
2350 $info .= ' Version ' . ICONV_VERSION . "\n";
2351 break;
2352 case 'xml':
2353 $info .= ' Version ' . LIBXML_DOTTED_VERSION . "\n";
2354 break;
2355 }
2356 }
2357 else
2358 {
2359 $info .= " $ext not loaded\n";
2360 }
2361 }
2362 return $info;
2363 }
2364}
2365
diff --git a/inc/3rdparty/simplepie/SimplePie/Net/IPv6.php b/inc/3rdparty/simplepie/SimplePie/Net/IPv6.php
new file mode 100644
index 00000000..7806d9dc
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Net/IPv6.php
@@ -0,0 +1,258 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47/**
48 * Class to validate and to work with IPv6 addresses.
49 *
50 * @package SimplePie
51 * @copyright 2003-2005 The PHP Group
52 * @license http://www.opensource.org/licenses/bsd-license.php
53 * @link http://pear.php.net/package/Net_IPv6
54 * @author Alexander Merz <alexander.merz@web.de>
55 * @author elfrink at introweb dot nl
56 * @author Josh Peck <jmp at joshpeck dot org>
57 * @author Geoffrey Sneddon <geoffers@gmail.com>
58 */
59class SimplePie_Net_IPv6
60{
61 /**
62 * Removes a possible existing netmask specification of an IP address.
63 *
64 * @param string $ip the (compressed) IP as Hex representation
65 * @return string the IP the without netmask
66 * @since 1.1.0
67 * @access public
68 * @static
69 */
70 public static function removeNetmaskSpec($ip)
71 {
72 if (strpos($ip, '/') !== false)
73 {
74 list($addr, $nm) = explode('/', $ip);
75 }
76 else
77 {
78 $addr = $ip;
79 }
80 return $addr;
81 }
82
83 /**
84 * Uncompresses an IPv6 address
85 *
86 * RFC 2373 allows you to compress zeros in an address to '::'. This
87 * function expects an valid IPv6 address and expands the '::' to
88 * the required zeros.
89 *
90 * Example: FF01::101 -> FF01:0:0:0:0:0:0:101
91 * ::1 -> 0:0:0:0:0:0:0:1
92 *
93 * @access public
94 * @static
95 * @param string $ip a valid IPv6-address (hex format)
96 * @return string the uncompressed IPv6-address (hex format)
97 */
98 public static function Uncompress($ip)
99 {
100 $uip = SimplePie_Net_IPv6::removeNetmaskSpec($ip);
101 $c1 = -1;
102 $c2 = -1;
103 if (strpos($ip, '::') !== false)
104 {
105 list($ip1, $ip2) = explode('::', $ip);
106 if ($ip1 === '')
107 {
108 $c1 = -1;
109 }
110 else
111 {
112 $pos = 0;
113 if (($pos = substr_count($ip1, ':')) > 0)
114 {
115 $c1 = $pos;
116 }
117 else
118 {
119 $c1 = 0;
120 }
121 }
122 if ($ip2 === '')
123 {
124 $c2 = -1;
125 }
126 else
127 {
128 $pos = 0;
129 if (($pos = substr_count($ip2, ':')) > 0)
130 {
131 $c2 = $pos;
132 }
133 else
134 {
135 $c2 = 0;
136 }
137 }
138 if (strstr($ip2, '.'))
139 {
140 $c2++;
141 }
142 // ::
143 if ($c1 === -1 && $c2 === -1)
144 {
145 $uip = '0:0:0:0:0:0:0:0';
146 }
147 // ::xxx
148 else if ($c1 === -1)
149 {
150 $fill = str_repeat('0:', 7 - $c2);
151 $uip = str_replace('::', $fill, $uip);
152 }
153 // xxx::
154 else if ($c2 === -1)
155 {
156 $fill = str_repeat(':0', 7 - $c1);
157 $uip = str_replace('::', $fill, $uip);
158 }
159 // xxx::xxx
160 else
161 {
162 $fill = str_repeat(':0:', 6 - $c2 - $c1);
163 $uip = str_replace('::', $fill, $uip);
164 $uip = str_replace('::', ':', $uip);
165 }
166 }
167 return $uip;
168 }
169
170 /**
171 * Splits an IPv6 address into the IPv6 and a possible IPv4 part
172 *
173 * RFC 2373 allows you to note the last two parts of an IPv6 address as
174 * an IPv4 compatible address
175 *
176 * Example: 0:0:0:0:0:0:13.1.68.3
177 * 0:0:0:0:0:FFFF:129.144.52.38
178 *
179 * @access public
180 * @static
181 * @param string $ip a valid IPv6-address (hex format)
182 * @return array [0] contains the IPv6 part, [1] the IPv4 part (hex format)
183 */
184 public static function SplitV64($ip)
185 {
186 $ip = SimplePie_Net_IPv6::Uncompress($ip);
187 if (strstr($ip, '.'))
188 {
189 $pos = strrpos($ip, ':');
190 $ip[$pos] = '_';
191 $ipPart = explode('_', $ip);
192 return $ipPart;
193 }
194 else
195 {
196 return array($ip, '');
197 }
198 }
199
200 /**
201 * Checks an IPv6 address
202 *
203 * Checks if the given IP is IPv6-compatible
204 *
205 * @access public
206 * @static
207 * @param string $ip a valid IPv6-address
208 * @return bool true if $ip is an IPv6 address
209 */
210 public static function checkIPv6($ip)
211 {
212 $ipPart = SimplePie_Net_IPv6::SplitV64($ip);
213 $count = 0;
214 if (!empty($ipPart[0]))
215 {
216 $ipv6 = explode(':', $ipPart[0]);
217 for ($i = 0; $i < count($ipv6); $i++)
218 {
219 $dec = hexdec($ipv6[$i]);
220 $hex = strtoupper(preg_replace('/^[0]{1,3}(.*[0-9a-fA-F])$/', '\\1', $ipv6[$i]));
221 if ($ipv6[$i] >= 0 && $dec <= 65535 && $hex === strtoupper(dechex($dec)))
222 {
223 $count++;
224 }
225 }
226 if ($count === 8)
227 {
228 return true;
229 }
230 elseif ($count === 6 && !empty($ipPart[1]))
231 {
232 $ipv4 = explode('.', $ipPart[1]);
233 $count = 0;
234 foreach ($ipv4 as $ipv4_part)
235 {
236 if ($ipv4_part >= 0 && $ipv4_part <= 255 && preg_match('/^\d{1,3}$/', $ipv4_part))
237 {
238 $count++;
239 }
240 }
241 if ($count === 4)
242 {
243 return true;
244 }
245 }
246 else
247 {
248 return false;
249 }
250
251 }
252 else
253 {
254 return false;
255 }
256 }
257}
258
diff --git a/inc/3rdparty/simplepie/SimplePie/Parse/Date.php b/inc/3rdparty/simplepie/SimplePie/Parse/Date.php
new file mode 100644
index 00000000..2694443c
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Parse/Date.php
@@ -0,0 +1,983 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47/**
48 * Date Parser
49 *
50 * @package SimplePie
51 */
52class SimplePie_Parse_Date
53{
54 /**
55 * Input data
56 *
57 * @access protected
58 * @var string
59 */
60 var $date;
61
62 /**
63 * List of days, calendar day name => ordinal day number in the week
64 *
65 * @access protected
66 * @var array
67 */
68 var $day = array(
69 // English
70 'mon' => 1,
71 'monday' => 1,
72 'tue' => 2,
73 'tuesday' => 2,
74 'wed' => 3,
75 'wednesday' => 3,
76 'thu' => 4,
77 'thursday' => 4,
78 'fri' => 5,
79 'friday' => 5,
80 'sat' => 6,
81 'saturday' => 6,
82 'sun' => 7,
83 'sunday' => 7,
84 // Dutch
85 'maandag' => 1,
86 'dinsdag' => 2,
87 'woensdag' => 3,
88 'donderdag' => 4,
89 'vrijdag' => 5,
90 'zaterdag' => 6,
91 'zondag' => 7,
92 // French
93 'lundi' => 1,
94 'mardi' => 2,
95 'mercredi' => 3,
96 'jeudi' => 4,
97 'vendredi' => 5,
98 'samedi' => 6,
99 'dimanche' => 7,
100 // German
101 'montag' => 1,
102 'dienstag' => 2,
103 'mittwoch' => 3,
104 'donnerstag' => 4,
105 'freitag' => 5,
106 'samstag' => 6,
107 'sonnabend' => 6,
108 'sonntag' => 7,
109 // Italian
110 'lunedì' => 1,
111 'martedì' => 2,
112 'mercoledì' => 3,
113 'giovedì' => 4,
114 'venerdì' => 5,
115 'sabato' => 6,
116 'domenica' => 7,
117 // Spanish
118 'lunes' => 1,
119 'martes' => 2,
120 'miércoles' => 3,
121 'jueves' => 4,
122 'viernes' => 5,
123 'sábado' => 6,
124 'domingo' => 7,
125 // Finnish
126 'maanantai' => 1,
127 'tiistai' => 2,
128 'keskiviikko' => 3,
129 'torstai' => 4,
130 'perjantai' => 5,
131 'lauantai' => 6,
132 'sunnuntai' => 7,
133 // Hungarian
134 'hétfő' => 1,
135 'kedd' => 2,
136 'szerda' => 3,
137 'csütörtok' => 4,
138 'péntek' => 5,
139 'szombat' => 6,
140 'vasárnap' => 7,
141 // Greek
142 'Δευ' => 1,
143 'ΤÏι' => 2,
144 'Τετ' => 3,
145 'Πεμ' => 4,
146 'ΠαÏ' => 5,
147 'Σαβ' => 6,
148 'ΚυÏ' => 7,
149 );
150
151 /**
152 * List of months, calendar month name => calendar month number
153 *
154 * @access protected
155 * @var array
156 */
157 var $month = array(
158 // English
159 'jan' => 1,
160 'january' => 1,
161 'feb' => 2,
162 'february' => 2,
163 'mar' => 3,
164 'march' => 3,
165 'apr' => 4,
166 'april' => 4,
167 'may' => 5,
168 // No long form of May
169 'jun' => 6,
170 'june' => 6,
171 'jul' => 7,
172 'july' => 7,
173 'aug' => 8,
174 'august' => 8,
175 'sep' => 9,
176 'september' => 8,
177 'oct' => 10,
178 'october' => 10,
179 'nov' => 11,
180 'november' => 11,
181 'dec' => 12,
182 'december' => 12,
183 // Dutch
184 'januari' => 1,
185 'februari' => 2,
186 'maart' => 3,
187 'april' => 4,
188 'mei' => 5,
189 'juni' => 6,
190 'juli' => 7,
191 'augustus' => 8,
192 'september' => 9,
193 'oktober' => 10,
194 'november' => 11,
195 'december' => 12,
196 // French
197 'janvier' => 1,
198 'février' => 2,
199 'mars' => 3,
200 'avril' => 4,
201 'mai' => 5,
202 'juin' => 6,
203 'juillet' => 7,
204 'août' => 8,
205 'septembre' => 9,
206 'octobre' => 10,
207 'novembre' => 11,
208 'décembre' => 12,
209 // German
210 'januar' => 1,
211 'februar' => 2,
212 'märz' => 3,
213 'april' => 4,
214 'mai' => 5,
215 'juni' => 6,
216 'juli' => 7,
217 'august' => 8,
218 'september' => 9,
219 'oktober' => 10,
220 'november' => 11,
221 'dezember' => 12,
222 // Italian
223 'gennaio' => 1,
224 'febbraio' => 2,
225 'marzo' => 3,
226 'aprile' => 4,
227 'maggio' => 5,
228 'giugno' => 6,
229 'luglio' => 7,
230 'agosto' => 8,
231 'settembre' => 9,
232 'ottobre' => 10,
233 'novembre' => 11,
234 'dicembre' => 12,
235 // Spanish
236 'enero' => 1,
237 'febrero' => 2,
238 'marzo' => 3,
239 'abril' => 4,
240 'mayo' => 5,
241 'junio' => 6,
242 'julio' => 7,
243 'agosto' => 8,
244 'septiembre' => 9,
245 'setiembre' => 9,
246 'octubre' => 10,
247 'noviembre' => 11,
248 'diciembre' => 12,
249 // Finnish
250 'tammikuu' => 1,
251 'helmikuu' => 2,
252 'maaliskuu' => 3,
253 'huhtikuu' => 4,
254 'toukokuu' => 5,
255 'kesäkuu' => 6,
256 'heinäkuu' => 7,
257 'elokuu' => 8,
258 'suuskuu' => 9,
259 'lokakuu' => 10,
260 'marras' => 11,
261 'joulukuu' => 12,
262 // Hungarian
263 'január' => 1,
264 'február' => 2,
265 'március' => 3,
266 'április' => 4,
267 'május' => 5,
268 'június' => 6,
269 'július' => 7,
270 'augusztus' => 8,
271 'szeptember' => 9,
272 'október' => 10,
273 'november' => 11,
274 'december' => 12,
275 // Greek
276 'Ιαν' => 1,
277 'Φεβ' => 2,
278 'Μάώ' => 3,
279 'Μαώ' => 3,
280 'ΑπÏ' => 4,
281 'Μάι' => 5,
282 'Μαϊ' => 5,
283 'Μαι' => 5,
284 'ΙοÏν' => 6,
285 'Ιον' => 6,
286 'ΙοÏλ' => 7,
287 'Ιολ' => 7,
288 'ΑÏγ' => 8,
289 'Αυγ' => 8,
290 'Σεπ' => 9,
291 'Οκτ' => 10,
292 'Îοέ' => 11,
293 'Δεκ' => 12,
294 );
295
296 /**
297 * List of timezones, abbreviation => offset from UTC
298 *
299 * @access protected
300 * @var array
301 */
302 var $timezone = array(
303 'ACDT' => 37800,
304 'ACIT' => 28800,
305 'ACST' => 34200,
306 'ACT' => -18000,
307 'ACWDT' => 35100,
308 'ACWST' => 31500,
309 'AEDT' => 39600,
310 'AEST' => 36000,
311 'AFT' => 16200,
312 'AKDT' => -28800,
313 'AKST' => -32400,
314 'AMDT' => 18000,
315 'AMT' => -14400,
316 'ANAST' => 46800,
317 'ANAT' => 43200,
318 'ART' => -10800,
319 'AZOST' => -3600,
320 'AZST' => 18000,
321 'AZT' => 14400,
322 'BIOT' => 21600,
323 'BIT' => -43200,
324 'BOT' => -14400,
325 'BRST' => -7200,
326 'BRT' => -10800,
327 'BST' => 3600,
328 'BTT' => 21600,
329 'CAST' => 18000,
330 'CAT' => 7200,
331 'CCT' => 23400,
332 'CDT' => -18000,
333 'CEDT' => 7200,
334 'CET' => 3600,
335 'CGST' => -7200,
336 'CGT' => -10800,
337 'CHADT' => 49500,
338 'CHAST' => 45900,
339 'CIST' => -28800,
340 'CKT' => -36000,
341 'CLDT' => -10800,
342 'CLST' => -14400,
343 'COT' => -18000,
344 'CST' => -21600,
345 'CVT' => -3600,
346 'CXT' => 25200,
347 'DAVT' => 25200,
348 'DTAT' => 36000,
349 'EADT' => -18000,
350 'EAST' => -21600,
351 'EAT' => 10800,
352 'ECT' => -18000,
353 'EDT' => -14400,
354 'EEST' => 10800,
355 'EET' => 7200,
356 'EGT' => -3600,
357 'EKST' => 21600,
358 'EST' => -18000,
359 'FJT' => 43200,
360 'FKDT' => -10800,
361 'FKST' => -14400,
362 'FNT' => -7200,
363 'GALT' => -21600,
364 'GEDT' => 14400,
365 'GEST' => 10800,
366 'GFT' => -10800,
367 'GILT' => 43200,
368 'GIT' => -32400,
369 'GST' => 14400,
370 'GST' => -7200,
371 'GYT' => -14400,
372 'HAA' => -10800,
373 'HAC' => -18000,
374 'HADT' => -32400,
375 'HAE' => -14400,
376 'HAP' => -25200,
377 'HAR' => -21600,
378 'HAST' => -36000,
379 'HAT' => -9000,
380 'HAY' => -28800,
381 'HKST' => 28800,
382 'HMT' => 18000,
383 'HNA' => -14400,
384 'HNC' => -21600,
385 'HNE' => -18000,
386 'HNP' => -28800,
387 'HNR' => -25200,
388 'HNT' => -12600,
389 'HNY' => -32400,
390 'IRDT' => 16200,
391 'IRKST' => 32400,
392 'IRKT' => 28800,
393 'IRST' => 12600,
394 'JFDT' => -10800,
395 'JFST' => -14400,
396 'JST' => 32400,
397 'KGST' => 21600,
398 'KGT' => 18000,
399 'KOST' => 39600,
400 'KOVST' => 28800,
401 'KOVT' => 25200,
402 'KRAST' => 28800,
403 'KRAT' => 25200,
404 'KST' => 32400,
405 'LHDT' => 39600,
406 'LHST' => 37800,
407 'LINT' => 50400,
408 'LKT' => 21600,
409 'MAGST' => 43200,
410 'MAGT' => 39600,
411 'MAWT' => 21600,
412 'MDT' => -21600,
413 'MESZ' => 7200,
414 'MEZ' => 3600,
415 'MHT' => 43200,
416 'MIT' => -34200,
417 'MNST' => 32400,
418 'MSDT' => 14400,
419 'MSST' => 10800,
420 'MST' => -25200,
421 'MUT' => 14400,
422 'MVT' => 18000,
423 'MYT' => 28800,
424 'NCT' => 39600,
425 'NDT' => -9000,
426 'NFT' => 41400,
427 'NMIT' => 36000,
428 'NOVST' => 25200,
429 'NOVT' => 21600,
430 'NPT' => 20700,
431 'NRT' => 43200,
432 'NST' => -12600,
433 'NUT' => -39600,
434 'NZDT' => 46800,
435 'NZST' => 43200,
436 'OMSST' => 25200,
437 'OMST' => 21600,
438 'PDT' => -25200,
439 'PET' => -18000,
440 'PETST' => 46800,
441 'PETT' => 43200,
442 'PGT' => 36000,
443 'PHOT' => 46800,
444 'PHT' => 28800,
445 'PKT' => 18000,
446 'PMDT' => -7200,
447 'PMST' => -10800,
448 'PONT' => 39600,
449 'PST' => -28800,
450 'PWT' => 32400,
451 'PYST' => -10800,
452 'PYT' => -14400,
453 'RET' => 14400,
454 'ROTT' => -10800,
455 'SAMST' => 18000,
456 'SAMT' => 14400,
457 'SAST' => 7200,
458 'SBT' => 39600,
459 'SCDT' => 46800,
460 'SCST' => 43200,
461 'SCT' => 14400,
462 'SEST' => 3600,
463 'SGT' => 28800,
464 'SIT' => 28800,
465 'SRT' => -10800,
466 'SST' => -39600,
467 'SYST' => 10800,
468 'SYT' => 7200,
469 'TFT' => 18000,
470 'THAT' => -36000,
471 'TJT' => 18000,
472 'TKT' => -36000,
473 'TMT' => 18000,
474 'TOT' => 46800,
475 'TPT' => 32400,
476 'TRUT' => 36000,
477 'TVT' => 43200,
478 'TWT' => 28800,
479 'UYST' => -7200,
480 'UYT' => -10800,
481 'UZT' => 18000,
482 'VET' => -14400,
483 'VLAST' => 39600,
484 'VLAT' => 36000,
485 'VOST' => 21600,
486 'VUT' => 39600,
487 'WAST' => 7200,
488 'WAT' => 3600,
489 'WDT' => 32400,
490 'WEST' => 3600,
491 'WFT' => 43200,
492 'WIB' => 25200,
493 'WIT' => 32400,
494 'WITA' => 28800,
495 'WKST' => 18000,
496 'WST' => 28800,
497 'YAKST' => 36000,
498 'YAKT' => 32400,
499 'YAPT' => 36000,
500 'YEKST' => 21600,
501 'YEKT' => 18000,
502 );
503
504 /**
505 * Cached PCRE for SimplePie_Parse_Date::$day
506 *
507 * @access protected
508 * @var string
509 */
510 var $day_pcre;
511
512 /**
513 * Cached PCRE for SimplePie_Parse_Date::$month
514 *
515 * @access protected
516 * @var string
517 */
518 var $month_pcre;
519
520 /**
521 * Array of user-added callback methods
522 *
523 * @access private
524 * @var array
525 */
526 var $built_in = array();
527
528 /**
529 * Array of user-added callback methods
530 *
531 * @access private
532 * @var array
533 */
534 var $user = array();
535
536 /**
537 * Create new SimplePie_Parse_Date object, and set self::day_pcre,
538 * self::month_pcre, and self::built_in
539 *
540 * @access private
541 */
542 public function __construct()
543 {
544 $this->day_pcre = '(' . implode(array_keys($this->day), '|') . ')';
545 $this->month_pcre = '(' . implode(array_keys($this->month), '|') . ')';
546
547 static $cache;
548 if (!isset($cache[get_class($this)]))
549 {
550 $all_methods = get_class_methods($this);
551
552 foreach ($all_methods as $method)
553 {
554 if (strtolower(substr($method, 0, 5)) === 'date_')
555 {
556 $cache[get_class($this)][] = $method;
557 }
558 }
559 }
560
561 foreach ($cache[get_class($this)] as $method)
562 {
563 $this->built_in[] = $method;
564 }
565 }
566
567 /**
568 * Get the object
569 *
570 * @access public
571 */
572 public static function get()
573 {
574 static $object;
575 if (!$object)
576 {
577 $object = new SimplePie_Parse_Date;
578 }
579 return $object;
580 }
581
582 /**
583 * Parse a date
584 *
585 * @final
586 * @access public
587 * @param string $date Date to parse
588 * @return int Timestamp corresponding to date string, or false on failure
589 */
590 public function parse($date)
591 {
592 foreach ($this->user as $method)
593 {
594 if (($returned = call_user_func($method, $date)) !== false)
595 {
596 return $returned;
597 }
598 }
599
600 foreach ($this->built_in as $method)
601 {
602 if (($returned = call_user_func(array(&$this, $method), $date)) !== false)
603 {
604 return $returned;
605 }
606 }
607
608 return false;
609 }
610
611 /**
612 * Add a callback method to parse a date
613 *
614 * @final
615 * @access public
616 * @param callback $callback
617 */
618 public function add_callback($callback)
619 {
620 if (is_callable($callback))
621 {
622 $this->user[] = $callback;
623 }
624 else
625 {
626 trigger_error('User-supplied function must be a valid callback', E_USER_WARNING);
627 }
628 }
629
630 /**
631 * Parse a superset of W3C-DTF (allows hyphens and colons to be omitted, as
632 * well as allowing any of upper or lower case "T", horizontal tabs, or
633 * spaces to be used as the time seperator (including more than one))
634 *
635 * @access protected
636 * @return int Timestamp
637 */
638 public function date_w3cdtf($date)
639 {
640 static $pcre;
641 if (!$pcre)
642 {
643 $year = '([0-9]{4})';
644 $month = $day = $hour = $minute = $second = '([0-9]{2})';
645 $decimal = '([0-9]*)';
646 $zone = '(?:(Z)|([+\-])([0-9]{1,2}):?([0-9]{1,2}))';
647 $pcre = '/^' . $year . '(?:-?' . $month . '(?:-?' . $day . '(?:[Tt\x09\x20]+' . $hour . '(?::?' . $minute . '(?::?' . $second . '(?:.' . $decimal . ')?)?)?' . $zone . ')?)?)?$/';
648 }
649 if (preg_match($pcre, $date, $match))
650 {
651 /*
652 Capturing subpatterns:
653 1: Year
654 2: Month
655 3: Day
656 4: Hour
657 5: Minute
658 6: Second
659 7: Decimal fraction of a second
660 8: Zulu
661 9: Timezone ±
662 10: Timezone hours
663 11: Timezone minutes
664 */
665
666 // Fill in empty matches
667 for ($i = count($match); $i <= 3; $i++)
668 {
669 $match[$i] = '1';
670 }
671
672 for ($i = count($match); $i <= 7; $i++)
673 {
674 $match[$i] = '0';
675 }
676
677 // Numeric timezone
678 if (isset($match[9]) && $match[9] !== '')
679 {
680 $timezone = $match[10] * 3600;
681 $timezone += $match[11] * 60;
682 if ($match[9] === '-')
683 {
684 $timezone = 0 - $timezone;
685 }
686 }
687 else
688 {
689 $timezone = 0;
690 }
691
692 // Convert the number of seconds to an integer, taking decimals into account
693 $second = round($match[6] + $match[7] / pow(10, strlen($match[7])));
694
695 return gmmktime($match[4], $match[5], $second, $match[2], $match[3], $match[1]) - $timezone;
696 }
697 else
698 {
699 return false;
700 }
701 }
702
703 /**
704 * Remove RFC822 comments
705 *
706 * @access protected
707 * @param string $data Data to strip comments from
708 * @return string Comment stripped string
709 */
710 public function remove_rfc2822_comments($string)
711 {
712 $string = (string) $string;
713 $position = 0;
714 $length = strlen($string);
715 $depth = 0;
716
717 $output = '';
718
719 while ($position < $length && ($pos = strpos($string, '(', $position)) !== false)
720 {
721 $output .= substr($string, $position, $pos - $position);
722 $position = $pos + 1;
723 if ($string[$pos - 1] !== '\\')
724 {
725 $depth++;
726 while ($depth && $position < $length)
727 {
728 $position += strcspn($string, '()', $position);
729 if ($string[$position - 1] === '\\')
730 {
731 $position++;
732 continue;
733 }
734 elseif (isset($string[$position]))
735 {
736 switch ($string[$position])
737 {
738 case '(':
739 $depth++;
740 break;
741
742 case ')':
743 $depth--;
744 break;
745 }
746 $position++;
747 }
748 else
749 {
750 break;
751 }
752 }
753 }
754 else
755 {
756 $output .= '(';
757 }
758 }
759 $output .= substr($string, $position);
760
761 return $output;
762 }
763
764 /**
765 * Parse RFC2822's date format
766 *
767 * @access protected
768 * @return int Timestamp
769 */
770 public function date_rfc2822($date)
771 {
772 static $pcre;
773 if (!$pcre)
774 {
775 $wsp = '[\x09\x20]';
776 $fws = '(?:' . $wsp . '+|' . $wsp . '*(?:\x0D\x0A' . $wsp . '+)+)';
777 $optional_fws = $fws . '?';
778 $day_name = $this->day_pcre;
779 $month = $this->month_pcre;
780 $day = '([0-9]{1,2})';
781 $hour = $minute = $second = '([0-9]{2})';
782 $year = '([0-9]{2,4})';
783 $num_zone = '([+\-])([0-9]{2})([0-9]{2})';
784 $character_zone = '([A-Z]{1,5})';
785 $zone = '(?:' . $num_zone . '|' . $character_zone . ')';
786 $pcre = '/(?:' . $optional_fws . $day_name . $optional_fws . ',)?' . $optional_fws . $day . $fws . $month . $fws . $year . $fws . $hour . $optional_fws . ':' . $optional_fws . $minute . '(?:' . $optional_fws . ':' . $optional_fws . $second . ')?' . $fws . $zone . '/i';
787 }
788 if (preg_match($pcre, $this->remove_rfc2822_comments($date), $match))
789 {
790 /*
791 Capturing subpatterns:
792 1: Day name
793 2: Day
794 3: Month
795 4: Year
796 5: Hour
797 6: Minute
798 7: Second
799 8: Timezone ±
800 9: Timezone hours
801 10: Timezone minutes
802 11: Alphabetic timezone
803 */
804
805 // Find the month number
806 $month = $this->month[strtolower($match[3])];
807
808 // Numeric timezone
809 if ($match[8] !== '')
810 {
811 $timezone = $match[9] * 3600;
812 $timezone += $match[10] * 60;
813 if ($match[8] === '-')
814 {
815 $timezone = 0 - $timezone;
816 }
817 }
818 // Character timezone
819 elseif (isset($this->timezone[strtoupper($match[11])]))
820 {
821 $timezone = $this->timezone[strtoupper($match[11])];
822 }
823 // Assume everything else to be -0000
824 else
825 {
826 $timezone = 0;
827 }
828
829 // Deal with 2/3 digit years
830 if ($match[4] < 50)
831 {
832 $match[4] += 2000;
833 }
834 elseif ($match[4] < 1000)
835 {
836 $match[4] += 1900;
837 }
838
839 // Second is optional, if it is empty set it to zero
840 if ($match[7] !== '')
841 {
842 $second = $match[7];
843 }
844 else
845 {
846 $second = 0;
847 }
848
849 return gmmktime($match[5], $match[6], $second, $month, $match[2], $match[4]) - $timezone;
850 }
851 else
852 {
853 return false;
854 }
855 }
856
857 /**
858 * Parse RFC850's date format
859 *
860 * @access protected
861 * @return int Timestamp
862 */
863 public function date_rfc850($date)
864 {
865 static $pcre;
866 if (!$pcre)
867 {
868 $space = '[\x09\x20]+';
869 $day_name = $this->day_pcre;
870 $month = $this->month_pcre;
871 $day = '([0-9]{1,2})';
872 $year = $hour = $minute = $second = '([0-9]{2})';
873 $zone = '([A-Z]{1,5})';
874 $pcre = '/^' . $day_name . ',' . $space . $day . '-' . $month . '-' . $year . $space . $hour . ':' . $minute . ':' . $second . $space . $zone . '$/i';
875 }
876 if (preg_match($pcre, $date, $match))
877 {
878 /*
879 Capturing subpatterns:
880 1: Day name
881 2: Day
882 3: Month
883 4: Year
884 5: Hour
885 6: Minute
886 7: Second
887 8: Timezone
888 */
889
890 // Month
891 $month = $this->month[strtolower($match[3])];
892
893 // Character timezone
894 if (isset($this->timezone[strtoupper($match[8])]))
895 {
896 $timezone = $this->timezone[strtoupper($match[8])];
897 }
898 // Assume everything else to be -0000
899 else
900 {
901 $timezone = 0;
902 }
903
904 // Deal with 2 digit year
905 if ($match[4] < 50)
906 {
907 $match[4] += 2000;
908 }
909 else
910 {
911 $match[4] += 1900;
912 }
913
914 return gmmktime($match[5], $match[6], $match[7], $month, $match[2], $match[4]) - $timezone;
915 }
916 else
917 {
918 return false;
919 }
920 }
921
922 /**
923 * Parse C99's asctime()'s date format
924 *
925 * @access protected
926 * @return int Timestamp
927 */
928 public function date_asctime($date)
929 {
930 static $pcre;
931 if (!$pcre)
932 {
933 $space = '[\x09\x20]+';
934 $wday_name = $this->day_pcre;
935 $mon_name = $this->month_pcre;
936 $day = '([0-9]{1,2})';
937 $hour = $sec = $min = '([0-9]{2})';
938 $year = '([0-9]{4})';
939 $terminator = '\x0A?\x00?';
940 $pcre = '/^' . $wday_name . $space . $mon_name . $space . $day . $space . $hour . ':' . $min . ':' . $sec . $space . $year . $terminator . '$/i';
941 }
942 if (preg_match($pcre, $date, $match))
943 {
944 /*
945 Capturing subpatterns:
946 1: Day name
947 2: Month
948 3: Day
949 4: Hour
950 5: Minute
951 6: Second
952 7: Year
953 */
954
955 $month = $this->month[strtolower($match[2])];
956 return gmmktime($match[4], $match[5], $match[6], $month, $match[3], $match[7]);
957 }
958 else
959 {
960 return false;
961 }
962 }
963
964 /**
965 * Parse dates using strtotime()
966 *
967 * @access protected
968 * @return int Timestamp
969 */
970 public function date_strtotime($date)
971 {
972 $strtotime = strtotime($date);
973 if ($strtotime === -1 || $strtotime === false)
974 {
975 return false;
976 }
977 else
978 {
979 return $strtotime;
980 }
981 }
982}
983
diff --git a/inc/3rdparty/simplepie/SimplePie/Parser.php b/inc/3rdparty/simplepie/SimplePie/Parser.php
new file mode 100644
index 00000000..182bf861
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Parser.php
@@ -0,0 +1,387 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47class SimplePie_Parser
48{
49 var $error_code;
50 var $error_string;
51 var $current_line;
52 var $current_column;
53 var $current_byte;
54 var $separator = ' ';
55 var $namespace = array('');
56 var $element = array('');
57 var $xml_base = array('');
58 var $xml_base_explicit = array(false);
59 var $xml_lang = array('');
60 var $data = array();
61 var $datas = array(array());
62 var $current_xhtml_construct = -1;
63 var $encoding;
64
65 public function parse(&$data, $encoding)
66 {
67 // Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character
68 if (strtoupper($encoding) === 'US-ASCII')
69 {
70 $this->encoding = 'UTF-8';
71 }
72 else
73 {
74 $this->encoding = $encoding;
75 }
76
77 // Strip BOM:
78 // UTF-32 Big Endian BOM
79 if (substr($data, 0, 4) === "\x00\x00\xFE\xFF")
80 {
81 $data = substr($data, 4);
82 }
83 // UTF-32 Little Endian BOM
84 elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00")
85 {
86 $data = substr($data, 4);
87 }
88 // UTF-16 Big Endian BOM
89 elseif (substr($data, 0, 2) === "\xFE\xFF")
90 {
91 $data = substr($data, 2);
92 }
93 // UTF-16 Little Endian BOM
94 elseif (substr($data, 0, 2) === "\xFF\xFE")
95 {
96 $data = substr($data, 2);
97 }
98 // UTF-8 BOM
99 elseif (substr($data, 0, 3) === "\xEF\xBB\xBF")
100 {
101 $data = substr($data, 3);
102 }
103
104 if (substr($data, 0, 5) === '<?xml' && strspn(substr($data, 5, 1), "\x09\x0A\x0D\x20") && ($pos = strpos($data, '?>')) !== false)
105 {
106 $declaration = new SimplePie_XML_Declaration_Parser(substr($data, 5, $pos - 5));
107 if ($declaration->parse())
108 {
109 $data = substr($data, $pos + 2);
110 $data = '<?xml version="' . $declaration->version . '" encoding="' . $encoding . '" standalone="' . (($declaration->standalone) ? 'yes' : 'no') . '"?>' . $data;
111 }
112 else
113 {
114 $this->error_string = 'SimplePie bug! Please report this!';
115 return false;
116 }
117 }
118
119 $return = true;
120
121 static $xml_is_sane = null;
122 if ($xml_is_sane === null)
123 {
124 $parser_check = xml_parser_create();
125 xml_parse_into_struct($parser_check, '<foo>&amp;</foo>', $values);
126 xml_parser_free($parser_check);
127 $xml_is_sane = isset($values[0]['value']);
128 }
129
130 // Create the parser
131 if ($xml_is_sane)
132 {
133 $xml = xml_parser_create_ns($this->encoding, $this->separator);
134 xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1);
135 xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0);
136 xml_set_object($xml, $this);
137 xml_set_character_data_handler($xml, 'cdata');
138 xml_set_element_handler($xml, 'tag_open', 'tag_close');
139
140 // Parse!
141 if (!xml_parse($xml, $data, true))
142 {
143 $this->error_code = xml_get_error_code($xml);
144 $this->error_string = xml_error_string($this->error_code);
145 $return = false;
146 }
147 $this->current_line = xml_get_current_line_number($xml);
148 $this->current_column = xml_get_current_column_number($xml);
149 $this->current_byte = xml_get_current_byte_index($xml);
150 xml_parser_free($xml);
151 return $return;
152 }
153 else
154 {
155 libxml_clear_errors();
156 $xml = new XMLReader();
157 $xml->xml($data);
158 while (@$xml->read())
159 {
160 switch ($xml->nodeType)
161 {
162
163 case constant('XMLReader::END_ELEMENT'):
164 if ($xml->namespaceURI !== '')
165 {
166 $tagName = $xml->namespaceURI . $this->separator . $xml->localName;
167 }
168 else
169 {
170 $tagName = $xml->localName;
171 }
172 $this->tag_close(null, $tagName);
173 break;
174 case constant('XMLReader::ELEMENT'):
175 $empty = $xml->isEmptyElement;
176 if ($xml->namespaceURI !== '')
177 {
178 $tagName = $xml->namespaceURI . $this->separator . $xml->localName;
179 }
180 else
181 {
182 $tagName = $xml->localName;
183 }
184 $attributes = array();
185 while ($xml->moveToNextAttribute())
186 {
187 if ($xml->namespaceURI !== '')
188 {
189 $attrName = $xml->namespaceURI . $this->separator . $xml->localName;
190 }
191 else
192 {
193 $attrName = $xml->localName;
194 }
195 $attributes[$attrName] = $xml->value;
196 }
197 $this->tag_open(null, $tagName, $attributes);
198 if ($empty)
199 {
200 $this->tag_close(null, $tagName);
201 }
202 break;
203 case constant('XMLReader::TEXT'):
204
205 case constant('XMLReader::CDATA'):
206 $this->cdata(null, $xml->value);
207 break;
208 }
209 }
210 if ($error = libxml_get_last_error())
211 {
212 $this->error_code = $error->code;
213 $this->error_string = $error->message;
214 $this->current_line = $error->line;
215 $this->current_column = $error->column;
216 return false;
217 }
218 else
219 {
220 return true;
221 }
222 }
223 }
224
225 public function get_error_code()
226 {
227 return $this->error_code;
228 }
229
230 public function get_error_string()
231 {
232 return $this->error_string;
233 }
234
235 public function get_current_line()
236 {
237 return $this->current_line;
238 }
239
240 public function get_current_column()
241 {
242 return $this->current_column;
243 }
244
245 public function get_current_byte()
246 {
247 return $this->current_byte;
248 }
249
250 public function get_data()
251 {
252 return $this->data;
253 }
254
255 public function tag_open($parser, $tag, $attributes)
256 {
257 list($this->namespace[], $this->element[]) = $this->split_ns($tag);
258
259 $attribs = array();
260 foreach ($attributes as $name => $value)
261 {
262 list($attrib_namespace, $attribute) = $this->split_ns($name);
263 $attribs[$attrib_namespace][$attribute] = $value;
264 }
265
266 if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['base']))
267 {
268 $this->xml_base[] = SimplePie_Misc::absolutize_url($attribs[SIMPLEPIE_NAMESPACE_XML]['base'], end($this->xml_base));
269 $this->xml_base_explicit[] = true;
270 }
271 else
272 {
273 $this->xml_base[] = end($this->xml_base);
274 $this->xml_base_explicit[] = end($this->xml_base_explicit);
275 }
276
277 if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['lang']))
278 {
279 $this->xml_lang[] = $attribs[SIMPLEPIE_NAMESPACE_XML]['lang'];
280 }
281 else
282 {
283 $this->xml_lang[] = end($this->xml_lang);
284 }
285
286 if ($this->current_xhtml_construct >= 0)
287 {
288 $this->current_xhtml_construct++;
289 if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML)
290 {
291 $this->data['data'] .= '<' . end($this->element);
292 if (isset($attribs['']))
293 {
294 foreach ($attribs[''] as $name => $value)
295 {
296 $this->data['data'] .= ' ' . $name . '="' . htmlspecialchars($value, ENT_COMPAT, $this->encoding) . '"';
297 }
298 }
299 $this->data['data'] .= '>';
300 }
301 }
302 else
303 {
304 $this->datas[] =& $this->data;
305 $this->data =& $this->data['child'][end($this->namespace)][end($this->element)][];
306 $this->data = array('data' => '', 'attribs' => $attribs, 'xml_base' => end($this->xml_base), 'xml_base_explicit' => end($this->xml_base_explicit), 'xml_lang' => end($this->xml_lang));
307 if ((end($this->namespace) === SIMPLEPIE_NAMESPACE_ATOM_03 && in_array(end($this->element), array('title', 'tagline', 'copyright', 'info', 'summary', 'content')) && isset($attribs['']['mode']) && $attribs['']['mode'] === 'xml')
308 || (end($this->namespace) === SIMPLEPIE_NAMESPACE_ATOM_10 && in_array(end($this->element), array('rights', 'subtitle', 'summary', 'info', 'title', 'content')) && isset($attribs['']['type']) && $attribs['']['type'] === 'xhtml'))
309 {
310 $this->current_xhtml_construct = 0;
311 }
312 }
313 }
314
315 public function cdata($parser, $cdata)
316 {
317 if ($this->current_xhtml_construct >= 0)
318 {
319 $this->data['data'] .= htmlspecialchars($cdata, ENT_QUOTES, $this->encoding);
320 }
321 else
322 {
323 $this->data['data'] .= $cdata;
324 }
325 }
326
327 public function tag_close($parser, $tag)
328 {
329 if ($this->current_xhtml_construct >= 0)
330 {
331 $this->current_xhtml_construct--;
332 if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML && !in_array(end($this->element), array('area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param')))
333 {
334 $this->data['data'] .= '</' . end($this->element) . '>';
335 }
336 }
337 if ($this->current_xhtml_construct === -1)
338 {
339 $this->data =& $this->datas[count($this->datas) - 1];
340 array_pop($this->datas);
341 }
342
343 array_pop($this->element);
344 array_pop($this->namespace);
345 array_pop($this->xml_base);
346 array_pop($this->xml_base_explicit);
347 array_pop($this->xml_lang);
348 }
349
350 public function split_ns($string)
351 {
352 static $cache = array();
353 if (!isset($cache[$string]))
354 {
355 if ($pos = strpos($string, $this->separator))
356 {
357 static $separator_length;
358 if (!$separator_length)
359 {
360 $separator_length = strlen($this->separator);
361 }
362 $namespace = substr($string, 0, $pos);
363 $local_name = substr($string, $pos + $separator_length);
364 if (strtolower($namespace) === SIMPLEPIE_NAMESPACE_ITUNES)
365 {
366 $namespace = SIMPLEPIE_NAMESPACE_ITUNES;
367 }
368
369 // Normalize the Media RSS namespaces
370 if ($namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG ||
371 $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG2 ||
372 $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG3 ||
373 $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG4 ||
374 $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG5 )
375 {
376 $namespace = SIMPLEPIE_NAMESPACE_MEDIARSS;
377 }
378 $cache[$string] = array($namespace, $local_name);
379 }
380 else
381 {
382 $cache[$string] = array('', $string);
383 }
384 }
385 return $cache[$string];
386 }
387}
diff --git a/inc/3rdparty/simplepie/SimplePie/Rating.php b/inc/3rdparty/simplepie/SimplePie/Rating.php
new file mode 100644
index 00000000..bedc701c
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Rating.php
@@ -0,0 +1,88 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47class SimplePie_Rating
48{
49 var $scheme;
50 var $value;
51
52 // Constructor, used to input the data
53 public function __construct($scheme = null, $value = null)
54 {
55 $this->scheme = $scheme;
56 $this->value = $value;
57 }
58
59 public function __toString()
60 {
61 // There is no $this->data here
62 return md5(serialize($this));
63 }
64
65 public function get_scheme()
66 {
67 if ($this->scheme !== null)
68 {
69 return $this->scheme;
70 }
71 else
72 {
73 return null;
74 }
75 }
76
77 public function get_value()
78 {
79 if ($this->value !== null)
80 {
81 return $this->value;
82 }
83 else
84 {
85 return null;
86 }
87 }
88}
diff --git a/inc/3rdparty/simplepie/SimplePie/Restriction.php b/inc/3rdparty/simplepie/SimplePie/Restriction.php
new file mode 100644
index 00000000..b0e7667a
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Restriction.php
@@ -0,0 +1,102 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47class SimplePie_Restriction
48{
49 var $relationship;
50 var $type;
51 var $value;
52
53 // Constructor, used to input the data
54 public function __construct($relationship = null, $type = null, $value = null)
55 {
56 $this->relationship = $relationship;
57 $this->type = $type;
58 $this->value = $value;
59 }
60
61 public function __toString()
62 {
63 // There is no $this->data here
64 return md5(serialize($this));
65 }
66
67 public function get_relationship()
68 {
69 if ($this->relationship !== null)
70 {
71 return $this->relationship;
72 }
73 else
74 {
75 return null;
76 }
77 }
78
79 public function get_type()
80 {
81 if ($this->type !== null)
82 {
83 return $this->type;
84 }
85 else
86 {
87 return null;
88 }
89 }
90
91 public function get_value()
92 {
93 if ($this->value !== null)
94 {
95 return $this->value;
96 }
97 else
98 {
99 return null;
100 }
101 }
102}
diff --git a/inc/3rdparty/simplepie/SimplePie/Sanitize.php b/inc/3rdparty/simplepie/SimplePie/Sanitize.php
new file mode 100644
index 00000000..73705c0d
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Sanitize.php
@@ -0,0 +1,400 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47/**
48 * @todo Move to using an actual HTML parser (this will allow tags to be properly stripped, and to switch between HTML and XHTML), this will also make it easier to shorten a string while preserving HTML tags
49 */
50class SimplePie_Sanitize
51{
52 // Private vars
53 var $base;
54
55 // Options
56 var $remove_div = true;
57 var $image_handler = '';
58 var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style');
59 var $encode_instead_of_strip = false;
60 var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc');
61 var $strip_comments = false;
62 var $output_encoding = 'UTF-8';
63 var $enable_cache = true;
64 var $cache_location = './cache';
65 var $cache_name_function = 'md5';
66 var $cache_class = 'SimplePie_Cache';
67 var $file_class = 'SimplePie_File';
68 var $timeout = 10;
69 var $useragent = '';
70 var $force_fsockopen = false;
71
72 var $replace_url_attributes = array(
73 'a' => 'href',
74 'area' => 'href',
75 'blockquote' => 'cite',
76 'del' => 'cite',
77 'form' => 'action',
78 'img' => array('longdesc', 'src'),
79 'input' => 'src',
80 'ins' => 'cite',
81 'q' => 'cite'
82 );
83
84 public function remove_div($enable = true)
85 {
86 $this->remove_div = (bool) $enable;
87 }
88
89 public function set_image_handler($page = false)
90 {
91 if ($page)
92 {
93 $this->image_handler = (string) $page;
94 }
95 else
96 {
97 $this->image_handler = false;
98 }
99 }
100
101 public function pass_cache_data($enable_cache = true, $cache_location = './cache', $cache_name_function = 'md5', $cache_class = 'SimplePie_Cache')
102 {
103 if (isset($enable_cache))
104 {
105 $this->enable_cache = (bool) $enable_cache;
106 }
107
108 if ($cache_location)
109 {
110 $this->cache_location = (string) $cache_location;
111 }
112
113 if ($cache_name_function)
114 {
115 $this->cache_name_function = (string) $cache_name_function;
116 }
117
118 if ($cache_class)
119 {
120 $this->cache_class = (string) $cache_class;
121 }
122 }
123
124 public function pass_file_data($file_class = 'SimplePie_File', $timeout = 10, $useragent = '', $force_fsockopen = false)
125 {
126 if ($file_class)
127 {
128 $this->file_class = (string) $file_class;
129 }
130
131 if ($timeout)
132 {
133 $this->timeout = (string) $timeout;
134 }
135
136 if ($useragent)
137 {
138 $this->useragent = (string) $useragent;
139 }
140
141 if ($force_fsockopen)
142 {
143 $this->force_fsockopen = (string) $force_fsockopen;
144 }
145 }
146
147 public function strip_htmltags($tags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'))
148 {
149 if ($tags)
150 {
151 if (is_array($tags))
152 {
153 $this->strip_htmltags = $tags;
154 }
155 else
156 {
157 $this->strip_htmltags = explode(',', $tags);
158 }
159 }
160 else
161 {
162 $this->strip_htmltags = false;
163 }
164 }
165
166 public function encode_instead_of_strip($encode = false)
167 {
168 $this->encode_instead_of_strip = (bool) $encode;
169 }
170
171 public function strip_attributes($attribs = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'))
172 {
173 if ($attribs)
174 {
175 if (is_array($attribs))
176 {
177 $this->strip_attributes = $attribs;
178 }
179 else
180 {
181 $this->strip_attributes = explode(',', $attribs);
182 }
183 }
184 else
185 {
186 $this->strip_attributes = false;
187 }
188 }
189
190 public function strip_comments($strip = false)
191 {
192 $this->strip_comments = (bool) $strip;
193 }
194
195 public function set_output_encoding($encoding = 'UTF-8')
196 {
197 $this->output_encoding = (string) $encoding;
198 }
199
200 /**
201 * Set element/attribute key/value pairs of HTML attributes
202 * containing URLs that need to be resolved relative to the feed
203 *
204 * @access public
205 * @since 1.0
206 * @param array $element_attribute Element/attribute key/value pairs
207 */
208 public function set_url_replacements($element_attribute = array('a' => 'href', 'area' => 'href', 'blockquote' => 'cite', 'del' => 'cite', 'form' => 'action', 'img' => array('longdesc', 'src'), 'input' => 'src', 'ins' => 'cite', 'q' => 'cite'))
209 {
210 $this->replace_url_attributes = (array) $element_attribute;
211 }
212
213 public function sanitize($data, $type, $base = '')
214 {
215 $data = trim($data);
216 if ($data !== '' || $type & SIMPLEPIE_CONSTRUCT_IRI)
217 {
218 if ($type & SIMPLEPIE_CONSTRUCT_MAYBE_HTML)
219 {
220 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))
221 {
222 $type |= SIMPLEPIE_CONSTRUCT_HTML;
223 }
224 else
225 {
226 $type |= SIMPLEPIE_CONSTRUCT_TEXT;
227 }
228 }
229
230 if ($type & SIMPLEPIE_CONSTRUCT_BASE64)
231 {
232 $data = base64_decode($data);
233 }
234
235 if ($type & SIMPLEPIE_CONSTRUCT_XHTML)
236 {
237 if ($this->remove_div)
238 {
239 $data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '', $data);
240 $data = preg_replace('/<\/div>$/', '', $data);
241 }
242 else
243 {
244 $data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '<div>', $data);
245 }
246 }
247
248 if ($type & (SIMPLEPIE_CONSTRUCT_HTML | SIMPLEPIE_CONSTRUCT_XHTML))
249 {
250 // Strip comments
251 if ($this->strip_comments)
252 {
253 $data = SimplePie_Misc::strip_comments($data);
254 }
255
256 // Strip out HTML tags and attributes that might cause various security problems.
257 // Based on recommendations by Mark Pilgrim at:
258 // http://diveintomark.org/archives/2003/06/12/how_to_consume_rss_safely
259 if ($this->strip_htmltags)
260 {
261 foreach ($this->strip_htmltags as $tag)
262 {
263 $pcre = "/<($tag)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$tag" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>|(\/)?>)/siU';
264 while (preg_match($pcre, $data))
265 {
266 $data = preg_replace_callback($pcre, array(&$this, 'do_strip_htmltags'), $data);
267 }
268 }
269 }
270
271 if ($this->strip_attributes)
272 {
273 foreach ($this->strip_attributes as $attrib)
274 {
275 $data = preg_replace('/(<[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*)' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . trim($attrib) . '(?:\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>/', '\1\2\3>', $data);
276 }
277 }
278
279 // Replace relative URLs
280 $this->base = $base;
281 foreach ($this->replace_url_attributes as $element => $attributes)
282 {
283 $data = $this->replace_urls($data, $element, $attributes);
284 }
285
286 // If image handling (caching, etc.) is enabled, cache and rewrite all the image tags.
287 if (isset($this->image_handler) && ((string) $this->image_handler) !== '' && $this->enable_cache)
288 {
289 $images = SimplePie_Misc::get_element('img', $data);
290 foreach ($images as $img)
291 {
292 if (isset($img['attribs']['src']['data']))
293 {
294 $image_url = call_user_func($this->cache_name_function, $img['attribs']['src']['data']);
295 $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, $image_url, 'spi');
296
297 if ($cache->load())
298 {
299 $img['attribs']['src']['data'] = $this->image_handler . $image_url;
300 $data = str_replace($img['full'], SimplePie_Misc::element_implode($img), $data);
301 }
302 else
303 {
304 $file = new $this->file_class($img['attribs']['src']['data'], $this->timeout, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $this->useragent, $this->force_fsockopen);
305 $headers = $file->headers;
306
307 if ($file->success && ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300)))
308 {
309 if ($cache->save(array('headers' => $file->headers, 'body' => $file->body)))
310 {
311 $img['attribs']['src']['data'] = $this->image_handler . $image_url;
312 $data = str_replace($img['full'], SimplePie_Misc::element_implode($img), $data);
313 }
314 else
315 {
316 trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING);
317 }
318 }
319 }
320 }
321 }
322 }
323
324 // Having (possibly) taken stuff out, there may now be whitespace at the beginning/end of the data
325 $data = trim($data);
326 }
327
328 if ($type & SIMPLEPIE_CONSTRUCT_IRI)
329 {
330 $data = SimplePie_Misc::absolutize_url($data, $base);
331 }
332
333 if ($type & (SIMPLEPIE_CONSTRUCT_TEXT | SIMPLEPIE_CONSTRUCT_IRI))
334 {
335 $data = htmlspecialchars($data, ENT_COMPAT, 'UTF-8');
336 }
337
338 if ($this->output_encoding !== 'UTF-8')
339 {
340 $data = SimplePie_Misc::change_encoding($data, 'UTF-8', $this->output_encoding);
341 }
342 }
343 return $data;
344 }
345
346 public function replace_urls($data, $tag, $attributes)
347 {
348 if (!is_array($this->strip_htmltags) || !in_array($tag, $this->strip_htmltags))
349 {
350 $elements = SimplePie_Misc::get_element($tag, $data);
351 foreach ($elements as $element)
352 {
353 if (is_array($attributes))
354 {
355 foreach ($attributes as $attribute)
356 {
357 if (isset($element['attribs'][$attribute]['data']))
358 {
359 $element['attribs'][$attribute]['data'] = SimplePie_Misc::absolutize_url($element['attribs'][$attribute]['data'], $this->base);
360 $new_element = SimplePie_Misc::element_implode($element);
361 $data = str_replace($element['full'], $new_element, $data);
362 $element['full'] = $new_element;
363 }
364 }
365 }
366 elseif (isset($element['attribs'][$attributes]['data']))
367 {
368 $element['attribs'][$attributes]['data'] = SimplePie_Misc::absolutize_url($element['attribs'][$attributes]['data'], $this->base);
369 $data = str_replace($element['full'], SimplePie_Misc::element_implode($element), $data);
370 }
371 }
372 }
373 return $data;
374 }
375
376 public function do_strip_htmltags($match)
377 {
378 if ($this->encode_instead_of_strip)
379 {
380 if (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style')))
381 {
382 $match[1] = htmlspecialchars($match[1], ENT_COMPAT, 'UTF-8');
383 $match[2] = htmlspecialchars($match[2], ENT_COMPAT, 'UTF-8');
384 return "&lt;$match[1]$match[2]&gt;$match[3]&lt;/$match[1]&gt;";
385 }
386 else
387 {
388 return htmlspecialchars($match[0], ENT_COMPAT, 'UTF-8');
389 }
390 }
391 elseif (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style')))
392 {
393 return $match[4];
394 }
395 else
396 {
397 return '';
398 }
399 }
400}
diff --git a/inc/3rdparty/simplepie/SimplePie/Source.php b/inc/3rdparty/simplepie/SimplePie/Source.php
new file mode 100644
index 00000000..a6605c18
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/Source.php
@@ -0,0 +1,597 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47class SimplePie_Source
48{
49 var $item;
50 var $data = array();
51
52 public function __construct($item, $data)
53 {
54 $this->item = $item;
55 $this->data = $data;
56 }
57
58 public function __toString()
59 {
60 return md5(serialize($this->data));
61 }
62
63 public function get_source_tags($namespace, $tag)
64 {
65 if (isset($this->data['child'][$namespace][$tag]))
66 {
67 return $this->data['child'][$namespace][$tag];
68 }
69 else
70 {
71 return null;
72 }
73 }
74
75 public function get_base($element = array())
76 {
77 return $this->item->get_base($element);
78 }
79
80 public function sanitize($data, $type, $base = '')
81 {
82 return $this->item->sanitize($data, $type, $base);
83 }
84
85 public function get_item()
86 {
87 return $this->item;
88 }
89
90 public function get_title()
91 {
92 if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title'))
93 {
94 return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0]));
95 }
96 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title'))
97 {
98 return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0]));
99 }
100 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title'))
101 {
102 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
103 }
104 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title'))
105 {
106 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
107 }
108 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title'))
109 {
110 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
111 }
112 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title'))
113 {
114 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
115 }
116 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title'))
117 {
118 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
119 }
120 else
121 {
122 return null;
123 }
124 }
125
126 public function get_category($key = 0)
127 {
128 $categories = $this->get_categories();
129 if (isset($categories[$key]))
130 {
131 return $categories[$key];
132 }
133 else
134 {
135 return null;
136 }
137 }
138
139 public function get_categories()
140 {
141 $categories = array();
142
143 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category)
144 {
145 $term = null;
146 $scheme = null;
147 $label = null;
148 if (isset($category['attribs']['']['term']))
149 {
150 $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT);
151 }
152 if (isset($category['attribs']['']['scheme']))
153 {
154 $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT);
155 }
156 if (isset($category['attribs']['']['label']))
157 {
158 $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT);
159 }
160 $categories[] = new $this->item->feed->category_class($term, $scheme, $label);
161 }
162 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category)
163 {
164 // This is really the label, but keep this as the term also for BC.
165 // Label will also work on retrieving because that falls back to term.
166 $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT);
167 if (isset($category['attribs']['']['domain']))
168 {
169 $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT);
170 }
171 else
172 {
173 $scheme = null;
174 }
175 $categories[] = new $this->item->feed->category_class($term, $scheme, null);
176 }
177 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category)
178 {
179 $categories[] = new $this->item->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
180 }
181 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category)
182 {
183 $categories[] = new $this->item->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
184 }
185
186 if (!empty($categories))
187 {
188 return SimplePie_Misc::array_unique($categories);
189 }
190 else
191 {
192 return null;
193 }
194 }
195
196 public function get_author($key = 0)
197 {
198 $authors = $this->get_authors();
199 if (isset($authors[$key]))
200 {
201 return $authors[$key];
202 }
203 else
204 {
205 return null;
206 }
207 }
208
209 public function get_authors()
210 {
211 $authors = array();
212 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author)
213 {
214 $name = null;
215 $uri = null;
216 $email = null;
217 if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
218 {
219 $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
220 }
221 if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
222 {
223 $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
224 }
225 if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
226 {
227 $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
228 }
229 if ($name !== null || $email !== null || $uri !== null)
230 {
231 $authors[] = new $this->item->feed->author_class($name, $uri, $email);
232 }
233 }
234 if ($author = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author'))
235 {
236 $name = null;
237 $url = null;
238 $email = null;
239 if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
240 {
241 $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
242 }
243 if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
244 {
245 $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
246 }
247 if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
248 {
249 $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
250 }
251 if ($name !== null || $email !== null || $url !== null)
252 {
253 $authors[] = new $this->item->feed->author_class($name, $url, $email);
254 }
255 }
256 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author)
257 {
258 $authors[] = new $this->item->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
259 }
260 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author)
261 {
262 $authors[] = new $this->item->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
263 }
264 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author)
265 {
266 $authors[] = new $this->item->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null);
267 }
268
269 if (!empty($authors))
270 {
271 return SimplePie_Misc::array_unique($authors);
272 }
273 else
274 {
275 return null;
276 }
277 }
278
279 public function get_contributor($key = 0)
280 {
281 $contributors = $this->get_contributors();
282 if (isset($contributors[$key]))
283 {
284 return $contributors[$key];
285 }
286 else
287 {
288 return null;
289 }
290 }
291
292 public function get_contributors()
293 {
294 $contributors = array();
295 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor)
296 {
297 $name = null;
298 $uri = null;
299 $email = null;
300 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']))
301 {
302 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
303 }
304 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']))
305 {
306 $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]));
307 }
308 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data']))
309 {
310 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
311 }
312 if ($name !== null || $email !== null || $uri !== null)
313 {
314 $contributors[] = new $this->item->feed->author_class($name, $uri, $email);
315 }
316 }
317 foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor)
318 {
319 $name = null;
320 $url = null;
321 $email = null;
322 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data']))
323 {
324 $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
325 }
326 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data']))
327 {
328 $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]));
329 }
330 if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data']))
331 {
332 $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
333 }
334 if ($name !== null || $email !== null || $url !== null)
335 {
336 $contributors[] = new $this->item->feed->author_class($name, $url, $email);
337 }
338 }
339
340 if (!empty($contributors))
341 {
342 return SimplePie_Misc::array_unique($contributors);
343 }
344 else
345 {
346 return null;
347 }
348 }
349
350 public function get_link($key = 0, $rel = 'alternate')
351 {
352 $links = $this->get_links($rel);
353 if (isset($links[$key]))
354 {
355 return $links[$key];
356 }
357 else
358 {
359 return null;
360 }
361 }
362
363 /**
364 * Added for parity between the parent-level and the item/entry-level.
365 */
366 public function get_permalink()
367 {
368 return $this->get_link(0);
369 }
370
371 public function get_links($rel = 'alternate')
372 {
373 if (!isset($this->data['links']))
374 {
375 $this->data['links'] = array();
376 if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link'))
377 {
378 foreach ($links as $link)
379 {
380 if (isset($link['attribs']['']['href']))
381 {
382 $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
383 $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
384 }
385 }
386 }
387 if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link'))
388 {
389 foreach ($links as $link)
390 {
391 if (isset($link['attribs']['']['href']))
392 {
393 $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate';
394 $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link));
395
396 }
397 }
398 }
399 if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link'))
400 {
401 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
402 }
403 if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link'))
404 {
405 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
406 }
407 if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link'))
408 {
409 $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0]));
410 }
411
412 $keys = array_keys($this->data['links']);
413 foreach ($keys as $key)
414 {
415 if (SimplePie_Misc::is_isegment_nz_nc($key))
416 {
417 if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]))
418 {
419 $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]);
420 $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key];
421 }
422 else
423 {
424 $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key];
425 }
426 }
427 elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY)
428 {
429 $this->data['links'][substr($key, 41)] =& $this->data['links'][$key];
430 }
431 $this->data['links'][$key] = array_unique($this->data['links'][$key]);
432 }
433 }
434
435 if (isset($this->data['links'][$rel]))
436 {
437 return $this->data['links'][$rel];
438 }
439 else
440 {
441 return null;
442 }
443 }
444
445 public function get_description()
446 {
447 if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle'))
448 {
449 return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0]));
450 }
451 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline'))
452 {
453 return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0]));
454 }
455 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description'))
456 {
457 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
458 }
459 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description'))
460 {
461 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
462 }
463 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description'))
464 {
465 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0]));
466 }
467 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description'))
468 {
469 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
470 }
471 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description'))
472 {
473 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
474 }
475 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary'))
476 {
477 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
478 }
479 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle'))
480 {
481 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0]));
482 }
483 else
484 {
485 return null;
486 }
487 }
488
489 public function get_copyright()
490 {
491 if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights'))
492 {
493 return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0]));
494 }
495 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright'))
496 {
497 return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0]));
498 }
499 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright'))
500 {
501 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
502 }
503 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights'))
504 {
505 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
506 }
507 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights'))
508 {
509 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
510 }
511 else
512 {
513 return null;
514 }
515 }
516
517 public function get_language()
518 {
519 if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language'))
520 {
521 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
522 }
523 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language'))
524 {
525 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
526 }
527 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language'))
528 {
529 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT);
530 }
531 elseif (isset($this->data['xml_lang']))
532 {
533 return $this->sanitize($this->data['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT);
534 }
535 else
536 {
537 return null;
538 }
539 }
540
541 public function get_latitude()
542 {
543 if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat'))
544 {
545 return (float) $return[0]['data'];
546 }
547 elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
548 {
549 return (float) $match[1];
550 }
551 else
552 {
553 return null;
554 }
555 }
556
557 public function get_longitude()
558 {
559 if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long'))
560 {
561 return (float) $return[0]['data'];
562 }
563 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon'))
564 {
565 return (float) $return[0]['data'];
566 }
567 elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match))
568 {
569 return (float) $match[2];
570 }
571 else
572 {
573 return null;
574 }
575 }
576
577 public function get_image_url()
578 {
579 if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image'))
580 {
581 return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI);
582 }
583 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo'))
584 {
585 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
586 }
587 elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon'))
588 {
589 return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0]));
590 }
591 else
592 {
593 return null;
594 }
595 }
596}
597
diff --git a/inc/3rdparty/simplepie/SimplePie/XML/Declaration/Parser.php b/inc/3rdparty/simplepie/SimplePie/XML/Declaration/Parser.php
new file mode 100644
index 00000000..b7ebc6f5
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/XML/Declaration/Parser.php
@@ -0,0 +1,362 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47/**
48 * Parses the XML Declaration
49 *
50 * @package SimplePie
51 */
52class SimplePie_XML_Declaration_Parser
53{
54 /**
55 * XML Version
56 *
57 * @access public
58 * @var string
59 */
60 var $version = '1.0';
61
62 /**
63 * Encoding
64 *
65 * @access public
66 * @var string
67 */
68 var $encoding = 'UTF-8';
69
70 /**
71 * Standalone
72 *
73 * @access public
74 * @var bool
75 */
76 var $standalone = false;
77
78 /**
79 * Current state of the state machine
80 *
81 * @access private
82 * @var string
83 */
84 var $state = 'before_version_name';
85
86 /**
87 * Input data
88 *
89 * @access private
90 * @var string
91 */
92 var $data = '';
93
94 /**
95 * Input data length (to avoid calling strlen() everytime this is needed)
96 *
97 * @access private
98 * @var int
99 */
100 var $data_length = 0;
101
102 /**
103 * Current position of the pointer
104 *
105 * @var int
106 * @access private
107 */
108 var $position = 0;
109
110 /**
111 * Create an instance of the class with the input data
112 *
113 * @access public
114 * @param string $data Input data
115 */
116 public function __construct($data)
117 {
118 $this->data = $data;
119 $this->data_length = strlen($this->data);
120 }
121
122 /**
123 * Parse the input data
124 *
125 * @access public
126 * @return bool true on success, false on failure
127 */
128 public function parse()
129 {
130 while ($this->state && $this->state !== 'emit' && $this->has_data())
131 {
132 $state = $this->state;
133 $this->$state();
134 }
135 $this->data = '';
136 if ($this->state === 'emit')
137 {
138 return true;
139 }
140 else
141 {
142 $this->version = '';
143 $this->encoding = '';
144 $this->standalone = '';
145 return false;
146 }
147 }
148
149 /**
150 * Check whether there is data beyond the pointer
151 *
152 * @access private
153 * @return bool true if there is further data, false if not
154 */
155 public function has_data()
156 {
157 return (bool) ($this->position < $this->data_length);
158 }
159
160 /**
161 * Advance past any whitespace
162 *
163 * @return int Number of whitespace characters passed
164 */
165 public function skip_whitespace()
166 {
167 $whitespace = strspn($this->data, "\x09\x0A\x0D\x20", $this->position);
168 $this->position += $whitespace;
169 return $whitespace;
170 }
171
172 /**
173 * Read value
174 */
175 public function get_value()
176 {
177 $quote = substr($this->data, $this->position, 1);
178 if ($quote === '"' || $quote === "'")
179 {
180 $this->position++;
181 $len = strcspn($this->data, $quote, $this->position);
182 if ($this->has_data())
183 {
184 $value = substr($this->data, $this->position, $len);
185 $this->position += $len + 1;
186 return $value;
187 }
188 }
189 return false;
190 }
191
192 public function before_version_name()
193 {
194 if ($this->skip_whitespace())
195 {
196 $this->state = 'version_name';
197 }
198 else
199 {
200 $this->state = false;
201 }
202 }
203
204 public function version_name()
205 {
206 if (substr($this->data, $this->position, 7) === 'version')
207 {
208 $this->position += 7;
209 $this->skip_whitespace();
210 $this->state = 'version_equals';
211 }
212 else
213 {
214 $this->state = false;
215 }
216 }
217
218 public function version_equals()
219 {
220 if (substr($this->data, $this->position, 1) === '=')
221 {
222 $this->position++;
223 $this->skip_whitespace();
224 $this->state = 'version_value';
225 }
226 else
227 {
228 $this->state = false;
229 }
230 }
231
232 public function version_value()
233 {
234 if ($this->version = $this->get_value())
235 {
236 $this->skip_whitespace();
237 if ($this->has_data())
238 {
239 $this->state = 'encoding_name';
240 }
241 else
242 {
243 $this->state = 'emit';
244 }
245 }
246 else
247 {
248 $this->state = false;
249 }
250 }
251
252 public function encoding_name()
253 {
254 if (substr($this->data, $this->position, 8) === 'encoding')
255 {
256 $this->position += 8;
257 $this->skip_whitespace();
258 $this->state = 'encoding_equals';
259 }
260 else
261 {
262 $this->state = 'standalone_name';
263 }
264 }
265
266 public function encoding_equals()
267 {
268 if (substr($this->data, $this->position, 1) === '=')
269 {
270 $this->position++;
271 $this->skip_whitespace();
272 $this->state = 'encoding_value';
273 }
274 else
275 {
276 $this->state = false;
277 }
278 }
279
280 public function encoding_value()
281 {
282 if ($this->encoding = $this->get_value())
283 {
284 $this->skip_whitespace();
285 if ($this->has_data())
286 {
287 $this->state = 'standalone_name';
288 }
289 else
290 {
291 $this->state = 'emit';
292 }
293 }
294 else
295 {
296 $this->state = false;
297 }
298 }
299
300 public function standalone_name()
301 {
302 if (substr($this->data, $this->position, 10) === 'standalone')
303 {
304 $this->position += 10;
305 $this->skip_whitespace();
306 $this->state = 'standalone_equals';
307 }
308 else
309 {
310 $this->state = false;
311 }
312 }
313
314 public function standalone_equals()
315 {
316 if (substr($this->data, $this->position, 1) === '=')
317 {
318 $this->position++;
319 $this->skip_whitespace();
320 $this->state = 'standalone_value';
321 }
322 else
323 {
324 $this->state = false;
325 }
326 }
327
328 public function standalone_value()
329 {
330 if ($standalone = $this->get_value())
331 {
332 switch ($standalone)
333 {
334 case 'yes':
335 $this->standalone = true;
336 break;
337
338 case 'no':
339 $this->standalone = false;
340 break;
341
342 default:
343 $this->state = false;
344 return;
345 }
346
347 $this->skip_whitespace();
348 if ($this->has_data())
349 {
350 $this->state = false;
351 }
352 else
353 {
354 $this->state = 'emit';
355 }
356 }
357 else
358 {
359 $this->state = false;
360 }
361 }
362}
diff --git a/inc/3rdparty/simplepie/SimplePie/gzdecode.php b/inc/3rdparty/simplepie/SimplePie/gzdecode.php
new file mode 100644
index 00000000..ddbd517e
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePie/gzdecode.php
@@ -0,0 +1,355 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47/**
48 * gzdecode
49 *
50 * @package SimplePie
51 */
52class SimplePie_gzdecode
53{
54 /**
55 * Compressed data
56 *
57 * @access private
58 * @see gzdecode::$data
59 */
60 var $compressed_data;
61
62 /**
63 * Size of compressed data
64 *
65 * @access private
66 */
67 var $compressed_size;
68
69 /**
70 * Minimum size of a valid gzip string
71 *
72 * @access private
73 */
74 var $min_compressed_size = 18;
75
76 /**
77 * Current position of pointer
78 *
79 * @access private
80 */
81 var $position = 0;
82
83 /**
84 * Flags (FLG)
85 *
86 * @access private
87 */
88 var $flags;
89
90 /**
91 * Uncompressed data
92 *
93 * @access public
94 * @see gzdecode::$compressed_data
95 */
96 var $data;
97
98 /**
99 * Modified time
100 *
101 * @access public
102 */
103 var $MTIME;
104
105 /**
106 * Extra Flags
107 *
108 * @access public
109 */
110 var $XFL;
111
112 /**
113 * Operating System
114 *
115 * @access public
116 */
117 var $OS;
118
119 /**
120 * Subfield ID 1
121 *
122 * @access public
123 * @see gzdecode::$extra_field
124 * @see gzdecode::$SI2
125 */
126 var $SI1;
127
128 /**
129 * Subfield ID 2
130 *
131 * @access public
132 * @see gzdecode::$extra_field
133 * @see gzdecode::$SI1
134 */
135 var $SI2;
136
137 /**
138 * Extra field content
139 *
140 * @access public
141 * @see gzdecode::$SI1
142 * @see gzdecode::$SI2
143 */
144 var $extra_field;
145
146 /**
147 * Original filename
148 *
149 * @access public
150 */
151 var $filename;
152
153 /**
154 * Human readable comment
155 *
156 * @access public
157 */
158 var $comment;
159
160 /**
161 * Don't allow anything to be set
162 *
163 * @access public
164 */
165 public function __set($name, $value)
166 {
167 trigger_error("Cannot write property $name", E_USER_ERROR);
168 }
169
170 /**
171 * Set the compressed string and related properties
172 *
173 * @access public
174 */
175 public function __construct($data)
176 {
177 $this->compressed_data = $data;
178 $this->compressed_size = strlen($data);
179 }
180
181 /**
182 * Decode the GZIP stream
183 *
184 * @access public
185 */
186 public function parse()
187 {
188 if ($this->compressed_size >= $this->min_compressed_size)
189 {
190 // Check ID1, ID2, and CM
191 if (substr($this->compressed_data, 0, 3) !== "\x1F\x8B\x08")
192 {
193 return false;
194 }
195
196 // Get the FLG (FLaGs)
197 $this->flags = ord($this->compressed_data[3]);
198
199 // FLG bits above (1 << 4) are reserved
200 if ($this->flags > 0x1F)
201 {
202 return false;
203 }
204
205 // Advance the pointer after the above
206 $this->position += 4;
207
208 // MTIME
209 $mtime = substr($this->compressed_data, $this->position, 4);
210 // Reverse the string if we're on a big-endian arch because l is the only signed long and is machine endianness
211 if (current(unpack('S', "\x00\x01")) === 1)
212 {
213 $mtime = strrev($mtime);
214 }
215 $this->MTIME = current(unpack('l', $mtime));
216 $this->position += 4;
217
218 // Get the XFL (eXtra FLags)
219 $this->XFL = ord($this->compressed_data[$this->position++]);
220
221 // Get the OS (Operating System)
222 $this->OS = ord($this->compressed_data[$this->position++]);
223
224 // Parse the FEXTRA
225 if ($this->flags & 4)
226 {
227 // Read subfield IDs
228 $this->SI1 = $this->compressed_data[$this->position++];
229 $this->SI2 = $this->compressed_data[$this->position++];
230
231 // SI2 set to zero is reserved for future use
232 if ($this->SI2 === "\x00")
233 {
234 return false;
235 }
236
237 // Get the length of the extra field
238 $len = current(unpack('v', substr($this->compressed_data, $this->position, 2)));
239 $this->position += 2;
240
241 // Check the length of the string is still valid
242 $this->min_compressed_size += $len + 4;
243 if ($this->compressed_size >= $this->min_compressed_size)
244 {
245 // Set the extra field to the given data
246 $this->extra_field = substr($this->compressed_data, $this->position, $len);
247 $this->position += $len;
248 }
249 else
250 {
251 return false;
252 }
253 }
254
255 // Parse the FNAME
256 if ($this->flags & 8)
257 {
258 // Get the length of the filename
259 $len = strcspn($this->compressed_data, "\x00", $this->position);
260
261 // Check the length of the string is still valid
262 $this->min_compressed_size += $len + 1;
263 if ($this->compressed_size >= $this->min_compressed_size)
264 {
265 // Set the original filename to the given string
266 $this->filename = substr($this->compressed_data, $this->position, $len);
267 $this->position += $len + 1;
268 }
269 else
270 {
271 return false;
272 }
273 }
274
275 // Parse the FCOMMENT
276 if ($this->flags & 16)
277 {
278 // Get the length of the comment
279 $len = strcspn($this->compressed_data, "\x00", $this->position);
280
281 // Check the length of the string is still valid
282 $this->min_compressed_size += $len + 1;
283 if ($this->compressed_size >= $this->min_compressed_size)
284 {
285 // Set the original comment to the given string
286 $this->comment = substr($this->compressed_data, $this->position, $len);
287 $this->position += $len + 1;
288 }
289 else
290 {
291 return false;
292 }
293 }
294
295 // Parse the FHCRC
296 if ($this->flags & 2)
297 {
298 // Check the length of the string is still valid
299 $this->min_compressed_size += $len + 2;
300 if ($this->compressed_size >= $this->min_compressed_size)
301 {
302 // Read the CRC
303 $crc = current(unpack('v', substr($this->compressed_data, $this->position, 2)));
304
305 // Check the CRC matches
306 if ((crc32(substr($this->compressed_data, 0, $this->position)) & 0xFFFF) === $crc)
307 {
308 $this->position += 2;
309 }
310 else
311 {
312 return false;
313 }
314 }
315 else
316 {
317 return false;
318 }
319 }
320
321 // Decompress the actual data
322 if (($this->data = gzinflate(substr($this->compressed_data, $this->position, -8))) === false)
323 {
324 return false;
325 }
326 else
327 {
328 $this->position = $this->compressed_size - 8;
329 }
330
331 // Check CRC of data
332 $crc = current(unpack('V', substr($this->compressed_data, $this->position, 4)));
333 $this->position += 4;
334 /*if (extension_loaded('hash') && sprintf('%u', current(unpack('V', hash('crc32b', $this->data)))) !== sprintf('%u', $crc))
335 {
336 return false;
337 }*/
338
339 // Check ISIZE of data
340 $isize = current(unpack('V', substr($this->compressed_data, $this->position, 4)));
341 $this->position += 4;
342 if (sprintf('%u', strlen($this->data) & 0xFFFFFFFF) !== sprintf('%u', $isize))
343 {
344 return false;
345 }
346
347 // Wow, against all odds, we've actually got a valid gzip string
348 return true;
349 }
350 else
351 {
352 return false;
353 }
354 }
355}
diff --git a/inc/3rdparty/simplepie/SimplePieAutoloader.php b/inc/3rdparty/simplepie/SimplePieAutoloader.php
new file mode 100644
index 00000000..b2654dc5
--- /dev/null
+++ b/inc/3rdparty/simplepie/SimplePieAutoloader.php
@@ -0,0 +1,80 @@
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-2009, 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 * @version 1.3-dev
37 * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue
38 * @author Ryan Parman
39 * @author Geoffrey Sneddon
40 * @author Ryan McCue
41 * @link http://simplepie.org/ SimplePie
42 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @todo phpDoc comments
44 */
45
46
47// autoloader
48spl_autoload_register(array(new SimplePie_Autoloader(), 'autoload'));
49
50/**
51 * SimplePie Autoloader class.
52 *
53 * @package SimplePie
54 */
55class SimplePie_Autoloader
56{
57 /**
58 * Constructor.
59 */
60 public function __construct()
61 {
62 $this->path = dirname(__FILE__);
63 }
64
65 /**
66 * Autoloader.
67 *
68 * @param string $class The name of the class to attempt to load.
69 */
70 public function autoload($class)
71 {
72 // see if this request should be handled by this autoloader
73 if (strpos($class, 'SimplePie') !== 0) {
74 return;
75 }
76
77 $filename = $this->path . DIRECTORY_SEPARATOR . str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php';
78 include $filename;
79 }
80} \ No newline at end of file
diff --git a/inc/3rdparty/site_config/README.txt b/inc/3rdparty/site_config/README.txt
new file mode 100644
index 00000000..0aff456b
--- /dev/null
+++ b/inc/3rdparty/site_config/README.txt
@@ -0,0 +1,6 @@
1Full-Text RSS Site Patterns
2---------------------------
3
4Site patterns allow you to specify what should be extracted from specific sites.
5
6Please see http://help.fivefilters.org/customer/portal/articles/223153-site-patterns for more information. \ No newline at end of file
diff --git a/inc/3rdparty/site_config/custom/inthepoche.com.txt b/inc/3rdparty/site_config/custom/inthepoche.com.txt
new file mode 100644
index 00000000..ede74b97
--- /dev/null
+++ b/inc/3rdparty/site_config/custom/inthepoche.com.txt
@@ -0,0 +1,7 @@
1title: //title
2body: //div[@class='post-content']
3
4prune: no
5tidy: no
6
7test_url: http://www.inthepoche.com/?post/poche-hosting \ No newline at end of file
diff --git a/inc/3rdparty/site_config/index.php b/inc/3rdparty/site_config/index.php
new file mode 100644
index 00000000..a3d5f739
--- /dev/null
+++ b/inc/3rdparty/site_config/index.php
@@ -0,0 +1,3 @@
1<?php
2// this is here to prevent directory listing over the web
3?> \ No newline at end of file
diff --git a/inc/3rdparty/site_config/standard/.wikipedia.org.txt b/inc/3rdparty/site_config/standard/.wikipedia.org.txt
new file mode 100644
index 00000000..8b98ae4b
--- /dev/null
+++ b/inc/3rdparty/site_config/standard/.wikipedia.org.txt
@@ -0,0 +1,19 @@
1title: //h1[@id='firstHeading']
2body: //div[@id = 'bodyContent']
3strip_id_or_class: editsection
4#strip_id_or_class: toc
5strip_id_or_class: vertical-navbox
6strip: //table[@id='toc']
7strip: //div[@id='catlinks']
8strip: //div[@id='jump-to-nav']
9strip: //div[@class='thumbcaption']//div[@class='magnify']
10strip: //table[@class='navbox']
11strip: //table[contains(@class, 'infobox')]
12strip: //div[@class='dablink']
13strip: //div[@id='contentSub']
14strip: //table[contains(@class, 'metadata')]
15strip: //*[contains(@class, 'noprint')]
16strip: //span[@title='pronunciation:']
17prune: no
18tidy: no
19test_url: http://en.wikipedia.org/wiki/Christopher_Lloyd \ No newline at end of file
diff --git a/inc/3rdparty/site_config/standard/index.php b/inc/3rdparty/site_config/standard/index.php
new file mode 100644
index 00000000..a3d5f739
--- /dev/null
+++ b/inc/3rdparty/site_config/standard/index.php
@@ -0,0 +1,3 @@
1<?php
2// this is here to prevent directory listing over the web
3?> \ No newline at end of file
diff --git a/inc/3rdparty/site_config/standard/version.php b/inc/3rdparty/site_config/standard/version.php
new file mode 100644
index 00000000..e61807ed
--- /dev/null
+++ b/inc/3rdparty/site_config/standard/version.php
@@ -0,0 +1,2 @@
1<?php
2return 1; \ No newline at end of file
diff --git a/inc/poche/Poche.class.php b/inc/poche/Poche.class.php
index e0dc0d20..646193f7 100644
--- a/inc/poche/Poche.class.php
+++ b/inc/poche/Poche.class.php
@@ -47,16 +47,16 @@ class Poche
47 die('You don\'t have write access on cache directory.'); 47 die('You don\'t have write access on cache directory.');
48 } 48 }
49 else if (file_exists('./install/update.php') && !DEBUG_POCHE) { 49 else if (file_exists('./install/update.php') && !DEBUG_POCHE) {
50 $msg = 'A poche update is needed. Please execute this update <a href="install/update.php">by clicking here</a>. If you have already do the update, please delete /install folder.'; 50 $msg = '<h1>setup</h1><p><strong>It\'s your first time here?</strong> Please copy /install/poche.sqlite in db folder. Then, delete install folder.<br /><strong>If you have already installed poche</strong>, an update is needed <a href="install/update.php">by clicking here</a>.</p>';
51 $allIsGood = FALSE; 51 $allIsGood = FALSE;
52 } 52 }
53 else if (file_exists('./install') && !DEBUG_POCHE) { 53 else if (file_exists('./install') && !DEBUG_POCHE) {
54 $msg = 'If you want to update your poche, you just have to delete /install folder. <br />To install your poche with sqlite, copy /install/poche.sqlite in /db and delete the folder /install. you have to delete the /install folder before using poche.'; 54 $msg = '<h1>setup</h1><p><strong>If you want to update your poche</strong>, you just have to delete /install folder. <br /><strong>To install your poche with sqlite</strong>, copy /install/poche.sqlite in /db and delete the folder /install. you have to delete the /install folder before using poche.</p>';
55 $allIsGood = FALSE; 55 $allIsGood = FALSE;
56 } 56 }
57 else if (STORAGE == 'sqlite' && !is_writable(STORAGE_SQLITE)) { 57 else if (STORAGE == 'sqlite' && !is_writable(STORAGE_SQLITE)) {
58 Tools::logm('you don\'t have write access on sqlite file'); 58 Tools::logm('you don\'t have write access on sqlite file');
59 $msg = 'You don\'t have write access on sqlite file.'; 59 $msg = '<h1>error</h1><p>You don\'t have write access on sqlite file.</p>';
60 $allIsGood = FALSE; 60 $allIsGood = FALSE;
61 } 61 }
62 62
@@ -156,36 +156,31 @@ class Poche
156 switch ($action) 156 switch ($action)
157 { 157 {
158 case 'add': 158 case 'add':
159 if($parametres_url = $url->fetchContent()) { 159 $content = $url->extract();
160 if ($this->store->add($url->getUrl(), $parametres_url['title'], $parametres_url['content'], $this->user->getId())) { 160
161 Tools::logm('add link ' . $url->getUrl()); 161 if ($this->store->add($url->getUrl(), $content['title'], $content['body'], $this->user->getId())) {
162 $sequence = ''; 162 Tools::logm('add link ' . $url->getUrl());
163 if (STORAGE == 'postgres') { 163 $sequence = '';
164 $sequence = 'entries_id_seq'; 164 if (STORAGE == 'postgres') {
165 } 165 $sequence = 'entries_id_seq';
166 $last_id = $this->store->getLastId($sequence);
167 if (DOWNLOAD_PICTURES) {
168 $content = filtre_picture($parametres_url['content'], $url->getUrl(), $last_id);
169 Tools::logm('updating content article');
170 $this->store->updateContent($last_id, $content, $this->user->getId());
171 }
172 if (!$import) {
173 $this->messages->add('s', _('the link has been added successfully'));
174 }
175 } 166 }
176 else { 167 $last_id = $this->store->getLastId($sequence);
177 if (!$import) { 168 if (DOWNLOAD_PICTURES) {
178 $this->messages->add('e', _('error during insertion : the link wasn\'t added')); 169 $content = filtre_picture($parametres_url['body'], $url->getUrl(), $last_id);
179 Tools::logm('error during insertion : the link wasn\'t added ' . $url->getUrl()); 170 Tools::logm('updating content article');
180 } 171 $this->store->updateContent($last_id, $content, $this->user->getId());
172 }
173 if (!$import) {
174 $this->messages->add('s', _('the link has been added successfully'));
181 } 175 }
182 } 176 }
183 else { 177 else {
184 if (!$import) { 178 if (!$import) {
185 $this->messages->add('e', _('error during fetching content : the link wasn\'t added')); 179 $this->messages->add('e', _('error during insertion : the link wasn\'t added'));
186 Tools::logm('error during content fetch ' . $url->getUrl()); 180 Tools::logm('error during insertion : the link wasn\'t added ' . $url->getUrl());
187 } 181 }
188 } 182 }
183
189 if (!$import) { 184 if (!$import) {
190 Tools::redirect(); 185 Tools::redirect();
191 } 186 }
@@ -220,7 +215,6 @@ class Poche
220 } 215 }
221 break; 216 break;
222 default: 217 default:
223 Tools::logm('action ' . $action . 'doesn\'t exist');
224 break; 218 break;
225 } 219 }
226 } 220 }
@@ -364,13 +358,14 @@ class Poche
364 /** 358 /**
365 * import from Instapaper. poche needs a ./instapaper-export.html file 359 * import from Instapaper. poche needs a ./instapaper-export.html file
366 * @todo add the return value 360 * @todo add the return value
361 * @param string $targetFile the file used for importing
367 * @return boolean 362 * @return boolean
368 */ 363 */
369 private function importFromInstapaper() 364 private function importFromInstapaper($targetFile)
370 { 365 {
371 # TODO gestion des articles favs 366 # TODO gestion des articles favs
372 $html = new simple_html_dom(); 367 $html = new simple_html_dom();
373 $html->load_file('./instapaper-export.html'); 368 $html->load_file($targetFile);
374 Tools::logm('starting import from instapaper'); 369 Tools::logm('starting import from instapaper');
375 370
376 $read = 0; 371 $read = 0;
@@ -403,13 +398,14 @@ class Poche
403 /** 398 /**
404 * import from Pocket. poche needs a ./ril_export.html file 399 * import from Pocket. poche needs a ./ril_export.html file
405 * @todo add the return value 400 * @todo add the return value
401 * @param string $targetFile the file used for importing
406 * @return boolean 402 * @return boolean
407 */ 403 */
408 private function importFromPocket() 404 private function importFromPocket($targetFile)
409 { 405 {
410 # TODO gestion des articles favs 406 # TODO gestion des articles favs
411 $html = new simple_html_dom(); 407 $html = new simple_html_dom();
412 $html->load_file('./ril_export.html'); 408 $html->load_file($targetFile);
413 Tools::logm('starting import from pocket'); 409 Tools::logm('starting import from pocket');
414 410
415 $read = 0; 411 $read = 0;
@@ -442,12 +438,13 @@ class Poche
442 /** 438 /**
443 * import from Readability. poche needs a ./readability file 439 * import from Readability. poche needs a ./readability file
444 * @todo add the return value 440 * @todo add the return value
441 * @param string $targetFile the file used for importing
445 * @return boolean 442 * @return boolean
446 */ 443 */
447 private function importFromReadability() 444 private function importFromReadability($targetFile)
448 { 445 {
449 # TODO gestion des articles lus / favs 446 # TODO gestion des articles lus / favs
450 $str_data = file_get_contents("./readability"); 447 $str_data = file_get_contents($targetFile);
451 $data = json_decode($str_data,true); 448 $data = json_decode($str_data,true);
452 Tools::logm('starting import from Readability'); 449 Tools::logm('starting import from Readability');
453 $count = 0; 450 $count = 0;
@@ -499,15 +496,31 @@ class Poche
499 */ 496 */
500 public function import($from) 497 public function import($from)
501 { 498 {
502 if ($from == 'pocket') { 499 $providers = array(
503 return $this->importFromPocket(); 500 'pocket' => 'importFromPocket',
501 'readability' => 'importFromReadability',
502 'instapaper' => 'importFromInstapaper'
503 );
504
505 if (! isset($providers[$from])) {
506 $this->messages->add('e', _('Unknown import provider.'));
507 Tools::redirect();
504 } 508 }
505 else if ($from == 'readability') { 509
506 return $this->importFromReadability(); 510 $targetDefinition = 'IMPORT_' . strtoupper($from) . '_FILE';
511 $targetFile = constant($targetDefinition);
512
513 if (! defined($targetDefinition)) {
514 $this->messages->add('e', _('Incomplete inc/poche/define.inc.php file, please define "' . $targetDefinition . '".'));
515 Tools::redirect();
507 } 516 }
508 else if ($from == 'instapaper') { 517
509 return $this->importFromInstapaper(); 518 if (! file_exists($targetFile)) {
519 $this->messages->add('e', _('Could not find required "' . $targetFile . '" import file.'));
520 Tools::redirect();
510 } 521 }
522
523 $this->$providers[$from]($targetFile);
511 } 524 }
512 525
513 /** 526 /**
diff --git a/inc/poche/Tools.class.php b/inc/poche/Tools.class.php
index 1baf745d..3a792d43 100644
--- a/inc/poche/Tools.class.php
+++ b/inc/poche/Tools.class.php
@@ -216,13 +216,7 @@ class Tools
216 216
217 public static function getDomain($url) 217 public static function getDomain($url)
218 { 218 {
219 $pieces = parse_url($url); 219 return parse_url($url, PHP_URL_HOST);
220 $domain = isset($pieces['host']) ? $pieces['host'] : '';
221 if (preg_match('/(?P<domain>[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i', $domain, $regs)) {
222 return $regs['domain'];
223 }
224
225 return FALSE;
226 } 220 }
227 221
228 public static function getReadingTime($text) { 222 public static function getReadingTime($text) {
@@ -239,24 +233,19 @@ class Tools
239 { 233 {
240 $myconfig_file = './inc/poche/myconfig.inc.php'; 234 $myconfig_file = './inc/poche/myconfig.inc.php';
241 235
242 if (version_compare(POCHE_VERSION, '1.0-beta3') == 1) { 236 if (!is_writable('./inc/poche/')) {
243 # $myconfig_file is only created with poche > 1.0-beta3 237 self::logm('you don\'t have write access to create ./inc/poche/myconfig.inc.php');
244 # in 1.0-beta3, the update script creates $myconfig_file 238 die('You don\'t have write access to create ./inc/poche/myconfig.inc.php.');
245 239 }
246 if (!is_writable('./inc/poche/')) {
247 self::logm('you don\'t have write access to create ./inc/poche/myconfig.inc.php');
248 die('You don\'t have write access to create ./inc/poche/myconfig.inc.php.');
249 }
250 240
251 if (!file_exists($myconfig_file)) 241 if (!file_exists($myconfig_file))
252 { 242 {
253 $fp = fopen($myconfig_file, 'w'); 243 $fp = fopen($myconfig_file, 'w');
254 fwrite($fp, '<?php'."\r\n"); 244 fwrite($fp, '<?php'."\r\n");
255 fwrite($fp, "define ('POCHE_VERSION', '1.0-beta3');" . "\r\n"); 245 fwrite($fp, "define ('POCHE_VERSION', '1.0-beta4');" . "\r\n");
256 fwrite($fp, "define ('SALT', '" . md5(time() . $_SERVER['SCRIPT_FILENAME'] . rand()) . "');" . "\r\n"); 246 fwrite($fp, "define ('SALT', '" . md5(time() . $_SERVER['SCRIPT_FILENAME'] . rand()) . "');" . "\r\n");
257 fwrite($fp, "define ('LANG', 'en_EN.utf8');" . "\r\n"); 247 fwrite($fp, "define ('LANG', 'en_EN.utf8');" . "\r\n");
258 fclose($fp); 248 fclose($fp);
259 }
260 } 249 }
261 } 250 }
262} \ No newline at end of file 251} \ No newline at end of file
diff --git a/inc/poche/Url.class.php b/inc/poche/Url.class.php
index 00b0b257..5a893014 100644
--- a/inc/poche/Url.class.php
+++ b/inc/poche/Url.class.php
@@ -12,6 +12,45 @@ class Url
12{ 12{
13 public $url; 13 public $url;
14 14
15 private $fingerprints = array(
16 // Posterous
17 '<meta name="generator" content="Posterous"' => array('hostname'=>'fingerprint.posterous.com', 'head'=>true),
18 // Blogger
19 '<meta content=\'blogger\' name=\'generator\'' => array('hostname'=>'fingerprint.blogspot.com', 'head'=>true),
20 '<meta name="generator" content="Blogger"' => array('hostname'=>'fingerprint.blogspot.com', 'head'=>true),
21 // WordPress (self-hosted and hosted)
22 '<meta name="generator" content="WordPress' => array('hostname'=>'fingerprint.wordpress.com', 'head'=>true)
23 );
24
25 private $user_agents = array( 'lifehacker.com' => 'PHP/5.2',
26 'gawker.com' => 'PHP/5.2',
27 'deadspin.com' => 'PHP/5.2',
28 'kotaku.com' => 'PHP/5.2',
29 'jezebel.com' => 'PHP/5.2',
30 'io9.com' => 'PHP/5.2',
31 'jalopnik.com' => 'PHP/5.2',
32 'gizmodo.com' => 'PHP/5.2',
33 '.wikipedia.org' => 'Mozilla/5.2'
34 );
35
36 private $content_type_exc = array(
37 'application/pdf' => array('action'=>'link', 'name'=>'PDF'),
38 'image' => array('action'=>'link', 'name'=>'Image'),
39 'audio' => array('action'=>'link', 'name'=>'Audio'),
40 'video' => array('action'=>'link', 'name'=>'Video')
41 );
42
43 private $rewrite_url = array(
44 // Rewrite public Google Docs URLs to point to HTML view:
45 // if a URL contains docs.google.com, replace /Doc? with /View?
46 'docs.google.com' => array('/Doc?' => '/View?'),
47 'tnr.com' => array('tnr.com/article/' => 'tnr.com/print/article/'),
48 '.m.wikipedia.org' => array('.m.wikipedia.org' => '.wikipedia.org')
49 );
50
51 private $rewrite_relative_urls = true;
52 private $error_message = '[unable to retrieve full-text content]';
53
15 function __construct($url) 54 function __construct($url)
16 { 55 {
17 $this->url = base64_decode($url); 56 $this->url = base64_decode($url);
@@ -25,70 +64,332 @@ class Url
25 $this->url = $url; 64 $this->url = $url;
26 } 65 }
27 66
28 public function isCorrect() 67 public function isCorrect() {
29 { 68 return filter_var($this->url, FILTER_VALIDATE_URL) !== FALSE;
30 $pattern = '|^(.*:)//([a-z\-.]+)(:[0-9]+)?(.*)$|i';
31
32 return preg_match($pattern, $this->url);
33 } 69 }
34 70
35 public function clean() 71 public function extract() {
36 { 72 global $http, $extractor;
37 $url = html_entity_decode(trim($this->url)); 73 $extractor = new ContentExtractor(dirname(__FILE__).'/../3rdparty/site_config/custom', dirname(__FILE__).'/../3rdparty/site_config/standard');
38 74 $extractor->fingerprints = $this->fingerprints;
39 $stuff = strpos($url,'&utm_source=');
40 if ($stuff !== FALSE)
41 $url = substr($url, 0, $stuff);
42 $stuff = strpos($url,'?utm_source=');
43 if ($stuff !== FALSE)
44 $url = substr($url, 0, $stuff);
45 $stuff = strpos($url,'#xtor=RSS-');
46 if ($stuff !== FALSE)
47 $url = substr($url, 0, $stuff);
48 75
49 $this->url = $url; 76 $http = new HumbleHttpAgent();
50 } 77 $http->userAgentMap = $this->user_agents;
78 $http->headerOnlyTypes = array_keys($this->content_type_exc);
79 $http->rewriteUrls = $this->rewrite_url;
80 $http->userAgentDefault = HumbleHttpAgent::UA_PHP;
81 // configure SimplePie HTTP extension class to use our HumbleHttpAgent instance
82 SimplePie_HumbleHttpAgent::set_agent($http);
83 $feed = new SimplePie();
84 // some feeds use the text/html content type - force_feed tells SimplePie to process anyway
85 $feed->force_feed(true);
86 $feed->set_file_class('SimplePie_HumbleHttpAgent');
87 $feed->feed_url = $this->url;
88 $feed->set_autodiscovery_level(SIMPLEPIE_LOCATOR_NONE);
89 $feed->set_timeout(20);
90 $feed->enable_cache(false);
91 $feed->set_stupidly_fast(true);
92 $feed->enable_order_by_date(false); // we don't want to do anything to the feed
93 $feed->set_url_replacements(array());
94 // initialise the feed
95 // the @ suppresses notices which on some servers causes a 500 internal server error
96 $result = @$feed->init();
97 if ($result && (!is_array($feed->data) || count($feed->data) == 0)) {
98 die('Sorry, no feed items found');
99 }
100 // from now on, we'll identify ourselves as a browser
101 $http->userAgentDefault = HumbleHttpAgent::UA_BROWSER;
102 unset($feed, $result);
51 103
52 public function fetchContent() 104 $feed = new DummySingleItemFeed($this->url);
53 { 105
54 if ($this->isCorrect()) { 106 $items = $feed->get_items(0, 1);
55 $this->clean(); 107 // Request all feed items in parallel (if supported)
56 $html = Encoding::toUTF8(Tools::getFile($this->getUrl())); 108 $urls_sanitized = array();
57 109 $urls = array();
58 # if Tools::getFile() if not able to retrieve HTTPS content, try the same URL with HTTP protocol 110 foreach ($items as $key => $item) {
59 if (!preg_match('!^https?://!i', $this->getUrl()) && (!isset($html) || strlen($html) <= 0)) { 111 $permalink = htmlspecialchars_decode($item->get_permalink());
60 $this->setUrl('http://' . $this->getUrl()); 112 // Colons in URL path segments get encoded by SimplePie, yet some sites expect them unencoded
61 $html = Encoding::toUTF8(Tools::getFile($this->getUrl())); 113 $permalink = str_replace('%3A', ':', $permalink);
114 if ($permalink) {
115 $urls_sanitized[] = $permalink;
62 } 116 }
117 $urls[$key] = $permalink;
118 }
119 $http->fetchAll($urls_sanitized);
120
121 foreach ($items as $key => $item) {
122 $do_content_extraction = true;
123 $extract_result = false;
124 $permalink = $urls[$key];
125
126 // TODO: Allow error codes - some sites return correct content with error status
127 // e.g. prospectmagazine.co.uk returns 403
128
129 if ($permalink && ($response = $http->get($permalink, true)) && ($response['status_code'] < 300 || $response['status_code'] > 400)) {
130 $effective_url = $response['effective_url'];
131 // check if action defined for returned Content-Type
132 $type = null;
133 if (preg_match('!^Content-Type:\s*(([-\w]+)/([-\w\+]+))!im', $response['headers'], $match)) {
134 // look for full mime type (e.g. image/jpeg) or just type (e.g. image)
135 $match[1] = strtolower(trim($match[1]));
136 $match[2] = strtolower(trim($match[2]));
137 foreach (array($match[1], $match[2]) as $_mime) {
138 if (isset($this->content_type_exc[$_mime])) {
139 $type = $match[1];
140 $_act = $this->content_type_exc[$_mime]['action'];
141 $_name = $this->content_type_exc[$_mime]['name'];
142 if ($_act == 'exclude') {
143 continue 2; // skip this feed item entry
144 } elseif ($_act == 'link') {
145 if ($match[2] == 'image') {
146 $html = "<a href=\"$effective_url\"><img src=\"$effective_url\" alt=\"$_name\" /></a>";
147 } else {
148 $html = "<a href=\"$effective_url\">Download $_name</a>";
149 }
150 $title = $_name;
151 $do_content_extraction = false;
152 break;
153 }
154 }
155 }
156 unset($_mime, $_act, $_name, $match);
157 }
158 if ($do_content_extraction) {
159 $html = $response['body'];
160 // remove strange things
161 $html = str_replace('</[>', '', $html);
162 $html = $this->convert_to_utf8($html, $response['headers']);
63 163
64 if (function_exists('tidy_parse_string')) { 164 // check site config for single page URL - fetch it if found
65 $tidy = tidy_parse_string($html, array(), 'UTF8'); 165 if ($single_page_response = $this->getSinglePage($item, $html, $effective_url)) {
66 $tidy->cleanRepair(); 166 $html = $single_page_response['body'];
67 $html = $tidy->value; 167 // remove strange things
168 $html = str_replace('</[>', '', $html);
169 $html = $this->convert_to_utf8($html, $single_page_response['headers']);
170 $effective_url = $single_page_response['effective_url'];
171 unset($single_page_response);
172 }
173 $extract_result = $extractor->process($html, $effective_url);
174 $readability = $extractor->readability;
175 $content_block = ($extract_result) ? $extractor->getContent() : null;
176 }
177 }
178 if ($do_content_extraction) {
179 // if we failed to extract content...
180 if (!$extract_result) {
181 $html = $this->error_message;
182 // keep the original item description
183 $html .= $item->get_description();
184 } else {
185 $readability->clean($content_block, 'select');
186 if ($this->rewrite_relative_urls) $this->makeAbsolute($effective_url, $content_block);
187 if ($content_block->childNodes->length == 1 && $content_block->firstChild->nodeType === XML_ELEMENT_NODE) {
188 $html = $content_block->firstChild->innerHTML;
189 } else {
190 $html = $content_block->innerHTML;
191 }
192 // post-processing cleanup
193 $html = preg_replace('!<p>[\s\h\v]*</p>!u', '', $html);
194 }
68 } 195 }
196 }
197
198 $title = ($extractor->getTitle() != '' ? $extractor->getTitle() : _('Untitled'));
199 $content = array ('title' => $title, 'body' => $html);
200
201 return $content;
202 }
69 203
70 $parameters = array(); 204 private function convert_to_utf8($html, $header=null)
71 if (isset($html) and strlen($html) > 0) 205 {
72 { 206 $encoding = null;
73 $readability = new Readability($html, $this->getUrl()); 207 if ($html || $header) {
74 $readability->convertLinksToFootnotes = CONVERT_LINKS_FOOTNOTES; 208 if (is_array($header)) $header = implode("\n", $header);
75 $readability->revertForcedParagraphElements = REVERT_FORCED_PARAGRAPH_ELEMENTS; 209 if (!$header || !preg_match_all('/^Content-Type:\s+([^;]+)(?:;\s*charset=["\']?([^;"\'\n]*))?/im', $header, $match, PREG_SET_ORDER)) {
210 // error parsing the response
211 } else {
212 $match = end($match); // get last matched element (in case of redirects)
213 if (isset($match[2])) $encoding = trim($match[2], "\"' \r\n\0\x0B\t");
214 }
215 // TODO: check to see if encoding is supported (can we convert it?)
216 // If it's not, result will be empty string.
217 // For now we'll check for invalid encoding types returned by some sites, e.g. 'none'
218 // Problem URL: http://facta.co.jp/blog/archives/20111026001026.html
219 if (!$encoding || $encoding == 'none') {
220 // search for encoding in HTML - only look at the first 35000 characters
221 $html_head = substr($html, 0, 40000);
222 if (preg_match('/^<\?xml\s+version=(?:"[^"]*"|\'[^\']*\')\s+encoding=("[^"]*"|\'[^\']*\')/s', $html_head, $match)) {
223 $encoding = trim($match[1], '"\'');
224 } elseif (preg_match('/<meta\s+http-equiv=["\']?Content-Type["\']? content=["\'][^;]+;\s*charset=["\']?([^;"\'>]+)/i', $html_head, $match)) {
225 $encoding = trim($match[1]);
226 } elseif (preg_match_all('/<meta\s+([^>]+)>/i', $html_head, $match)) {
227 foreach ($match[1] as $_test) {
228 if (preg_match('/charset=["\']?([^"\']+)/i', $_test, $_m)) {
229 $encoding = trim($_m[1]);
230 break;
231 }
232 }
233 }
234 }
235 if (isset($encoding)) $encoding = trim($encoding);
236 // trim is important here!
237 if (!$encoding || (strtolower($encoding) == 'iso-8859-1')) {
238 // replace MS Word smart qutoes
239 $trans = array();
240 $trans[chr(130)] = '&sbquo;'; // Single Low-9 Quotation Mark
241 $trans[chr(131)] = '&fnof;'; // Latin Small Letter F With Hook
242 $trans[chr(132)] = '&bdquo;'; // Double Low-9 Quotation Mark
243 $trans[chr(133)] = '&hellip;'; // Horizontal Ellipsis
244 $trans[chr(134)] = '&dagger;'; // Dagger
245 $trans[chr(135)] = '&Dagger;'; // Double Dagger
246 $trans[chr(136)] = '&circ;'; // Modifier Letter Circumflex Accent
247 $trans[chr(137)] = '&permil;'; // Per Mille Sign
248 $trans[chr(138)] = '&Scaron;'; // Latin Capital Letter S With Caron
249 $trans[chr(139)] = '&lsaquo;'; // Single Left-Pointing Angle Quotation Mark
250 $trans[chr(140)] = '&OElig;'; // Latin Capital Ligature OE
251 $trans[chr(145)] = '&lsquo;'; // Left Single Quotation Mark
252 $trans[chr(146)] = '&rsquo;'; // Right Single Quotation Mark
253 $trans[chr(147)] = '&ldquo;'; // Left Double Quotation Mark
254 $trans[chr(148)] = '&rdquo;'; // Right Double Quotation Mark
255 $trans[chr(149)] = '&bull;'; // Bullet
256 $trans[chr(150)] = '&ndash;'; // En Dash
257 $trans[chr(151)] = '&mdash;'; // Em Dash
258 $trans[chr(152)] = '&tilde;'; // Small Tilde
259 $trans[chr(153)] = '&trade;'; // Trade Mark Sign
260 $trans[chr(154)] = '&scaron;'; // Latin Small Letter S With Caron
261 $trans[chr(155)] = '&rsaquo;'; // Single Right-Pointing Angle Quotation Mark
262 $trans[chr(156)] = '&oelig;'; // Latin Small Ligature OE
263 $trans[chr(159)] = '&Yuml;'; // Latin Capital Letter Y With Diaeresis
264 $html = strtr($html, $trans);
265 }
266 if (!$encoding) {
267 $encoding = 'utf-8';
268 } else {
269 if (strtolower($encoding) != 'utf-8') {
270 $html = SimplePie_Misc::change_encoding($html, $encoding, 'utf-8');
271 /*
272 if (function_exists('iconv')) {
273 // iconv appears to handle certain character encodings better than mb_convert_encoding
274 $html = iconv($encoding, 'utf-8', $html);
275 } else {
276 $html = mb_convert_encoding($html, 'utf-8', $encoding);
277 }
278 */
279 }
280 }
281 }
282 return $html;
283 }
76 284
77 if($readability->init()) 285 private function makeAbsolute($base, $elem) {
78 { 286 $base = new SimplePie_IRI($base);
79 $content = $readability->articleContent->innerHTML; 287 // remove '//' in URL path (used to prevent URLs from resolving properly)
80 $parameters['title'] = $readability->articleTitle->innerHTML; 288 // TODO: check if this is still the case
81 $parameters['content'] = $content; 289 if (isset($base->path)) $base->path = preg_replace('!//+!', '/', $base->path);
290 foreach(array('a'=>'href', 'img'=>'src') as $tag => $attr) {
291 $elems = $elem->getElementsByTagName($tag);
292 for ($i = $elems->length-1; $i >= 0; $i--) {
293 $e = $elems->item($i);
294 //$e->parentNode->replaceChild($articleContent->ownerDocument->createTextNode($e->textContent), $e);
295 $this->makeAbsoluteAttr($base, $e, $attr);
296 }
297 if (strtolower($elem->tagName) == $tag) $this->makeAbsoluteAttr($base, $elem, $attr);
298 }
299 }
82 300
83 return $parameters; 301 private function makeAbsoluteAttr($base, $e, $attr) {
302 if ($e->hasAttribute($attr)) {
303 // Trim leading and trailing white space. I don't really like this but
304 // unfortunately it does appear on some sites. e.g. <img src=" /path/to/image.jpg" />
305 $url = trim(str_replace('%20', ' ', $e->getAttribute($attr)));
306 $url = str_replace(' ', '%20', $url);
307 if (!preg_match('!https?://!i', $url)) {
308 if ($absolute = SimplePie_IRI::absolutize($base, $url)) {
309 $e->setAttribute($attr, $absolute);
84 } 310 }
85 } 311 }
86 } 312 }
87 else { 313 }
88 #$msg->add('e', _('error during url preparation : the link is not valid')); 314
89 Tools::logm($this->getUrl() . ' is not a valid url'); 315 private function makeAbsoluteStr($base, $url) {
316 $base = new SimplePie_IRI($base);
317 // remove '//' in URL path (causes URLs not to resolve properly)
318 if (isset($base->path)) $base->path = preg_replace('!//+!', '/', $base->path);
319 if (preg_match('!^https?://!i', $url)) {
320 // already absolute
321 return $url;
322 } else {
323 if ($absolute = SimplePie_IRI::absolutize($base, $url)) {
324 return $absolute;
325 }
326 return false;
90 } 327 }
328 }
91 329
92 return FALSE; 330 // returns single page response, or false if not found
331 private function getSinglePage($item, $html, $url) {
332 global $http, $extractor;
333 $host = @parse_url($url, PHP_URL_HOST);
334 $site_config = SiteConfig::build($host);
335 if ($site_config === false) {
336 // check for fingerprints
337 if (!empty($extractor->fingerprints) && ($_fphost = $extractor->findHostUsingFingerprints($html))) {
338 $site_config = SiteConfig::build($_fphost);
339 }
340 if ($site_config === false) $site_config = new SiteConfig();
341 SiteConfig::add_to_cache($host, $site_config);
342 return false;
343 } else {
344 SiteConfig::add_to_cache($host, $site_config);
345 }
346 $splink = null;
347 if (!empty($site_config->single_page_link)) {
348 $splink = $site_config->single_page_link;
349 } elseif (!empty($site_config->single_page_link_in_feed)) {
350 // single page link xpath is targeted at feed
351 $splink = $site_config->single_page_link_in_feed;
352 // so let's replace HTML with feed item description
353 $html = $item->get_description();
354 }
355 if (isset($splink)) {
356 // Build DOM tree from HTML
357 $readability = new Readability($html, $url);
358 $xpath = new DOMXPath($readability->dom);
359 // Loop through single_page_link xpath expressions
360 $single_page_url = null;
361 foreach ($splink as $pattern) {
362 $elems = @$xpath->evaluate($pattern, $readability->dom);
363 if (is_string($elems)) {
364 $single_page_url = trim($elems);
365 break;
366 } elseif ($elems instanceof DOMNodeList && $elems->length > 0) {
367 foreach ($elems as $item) {
368 if ($item instanceof DOMElement && $item->hasAttribute('href')) {
369 $single_page_url = $item->getAttribute('href');
370 break;
371 } elseif ($item instanceof DOMAttr && $item->value) {
372 $single_page_url = $item->value;
373 break;
374 }
375 }
376 }
377 }
378 // If we've got URL, resolve against $url
379 if (isset($single_page_url) && ($single_page_url = $this->makeAbsoluteStr($url, $single_page_url))) {
380 // check it's not what we have already!
381 if ($single_page_url != $url) {
382 // it's not, so let's try to fetch it...
383 $_prev_ref = $http->referer;
384 $http->referer = $single_page_url;
385 if (($response = $http->get($single_page_url, true)) && $response['status_code'] < 300) {
386 $http->referer = $_prev_ref;
387 return $response;
388 }
389 $http->referer = $_prev_ref;
390 }
391 }
392 }
393 return false;
93 } 394 }
94} \ No newline at end of file 395} \ No newline at end of file
diff --git a/inc/poche/config.inc.php b/inc/poche/config.inc.php
index 4122ff10..a1917295 100755
--- a/inc/poche/config.inc.php
+++ b/inc/poche/config.inc.php
@@ -15,9 +15,9 @@ if (!file_exists(__DIR__ . '/../../vendor/autoload.php')) {
15 die('Twig does not seem installed. Have a look at <a href="http://inthepoche.com/?pages/Documentation">the documentation.</a>'); 15 die('Twig does not seem installed. Have a look at <a href="http://inthepoche.com/?pages/Documentation">the documentation.</a>');
16} 16}
17 17
18if (file_exists(__DIR__ . '/../../inc/poche/myconfig.inc.php')) { 18// if (file_exists(__DIR__ . '/../../inc/poche/myconfig.inc.php')) {
19 require_once __DIR__ . '/../../inc/poche/myconfig.inc.php'; 19 // require_once __DIR__ . '/../../inc/poche/myconfig.inc.php';
20} 20// }
21require_once __DIR__ . '/../../inc/poche/User.class.php'; 21require_once __DIR__ . '/../../inc/poche/User.class.php';
22require_once __DIR__ . '/../../inc/poche/Url.class.php'; 22require_once __DIR__ . '/../../inc/poche/Url.class.php';
23require_once __DIR__ . '/../../inc/3rdparty/class.messages.php'; 23require_once __DIR__ . '/../../inc/3rdparty/class.messages.php';
@@ -30,10 +30,25 @@ require_once __DIR__ . '/../../inc/3rdparty/simple_html_dom.php';
30require_once __DIR__ . '/../../inc/3rdparty/paginator.php'; 30require_once __DIR__ . '/../../inc/3rdparty/paginator.php';
31require_once __DIR__ . '/../../inc/3rdparty/Session.class.php'; 31require_once __DIR__ . '/../../inc/3rdparty/Session.class.php';
32 32
33require_once __DIR__ . '/../../inc/3rdparty/simplepie/SimplePieAutoloader.php';
34require_once __DIR__ . '/../../inc/3rdparty/simplepie/SimplePie/Core.php';
35require_once __DIR__ . '/../../inc/3rdparty/content-extractor/ContentExtractor.php';
36require_once __DIR__ . '/../../inc/3rdparty/content-extractor/SiteConfig.php';
37require_once __DIR__ . '/../../inc/3rdparty/humble-http-agent/HumbleHttpAgent.php';
38require_once __DIR__ . '/../../inc/3rdparty/humble-http-agent/SimplePie_HumbleHttpAgent.php';
39require_once __DIR__ . '/../../inc/3rdparty/humble-http-agent/CookieJar.php';
40require_once __DIR__ . '/../../inc/3rdparty/feedwriter/FeedItem.php';
41require_once __DIR__ . '/../../inc/3rdparty/feedwriter/FeedWriter.php';
42require_once __DIR__ . '/../../inc/3rdparty/feedwriter/DummySingleItemFeed.php';
43
33if (DOWNLOAD_PICTURES) { 44if (DOWNLOAD_PICTURES) {
34 require_once __DIR__ . '/../../inc/poche/pochePictures.php'; 45 require_once __DIR__ . '/../../inc/poche/pochePictures.php';
35} 46}
36 47
48if (!ini_get('date.timezone') || !@date_default_timezone_set(ini_get('date.timezone'))) {
49 date_default_timezone_set('UTC');
50}
51
37$poche = new Poche(); 52$poche = new Poche();
38#XSRF protection with token 53#XSRF protection with token
39// if (!empty($_POST)) { 54// if (!empty($_POST)) {
diff --git a/inc/poche/define.inc.php b/inc/poche/define.inc.php
index c32ca098..3f667430 100644
--- a/inc/poche/define.inc.php
+++ b/inc/poche/define.inc.php
@@ -17,14 +17,18 @@ define ('STORAGE_PASSWORD', 'postgres'); # leave blank for sqlite
17 17
18define ('MODE_DEMO', FALSE); 18define ('MODE_DEMO', FALSE);
19define ('DEBUG_POCHE', FALSE); 19define ('DEBUG_POCHE', FALSE);
20define ('CONVERT_LINKS_FOOTNOTES', FALSE);
21define ('REVERT_FORCED_PARAGRAPH_ELEMENTS', FALSE);
22define ('DOWNLOAD_PICTURES', FALSE); 20define ('DOWNLOAD_PICTURES', FALSE);
23define ('SHARE_TWITTER', TRUE); 21define ('SHARE_TWITTER', TRUE);
24define ('SHARE_MAIL', TRUE); 22define ('SHARE_MAIL', TRUE);
23define ('SHARE_SHAARLI', FALSE);
24define ('SHAARLI_URL', 'http://myshaarliurl.com');
25define ('ABS_PATH', 'assets/'); 25define ('ABS_PATH', 'assets/');
26define ('TPL', __DIR__ . '/../../tpl'); 26define ('TPL', __DIR__ . '/../../tpl');
27define ('LOCALE', __DIR__ . '/../../locale'); 27define ('LOCALE', __DIR__ . '/../../locale');
28define ('CACHE', __DIR__ . '/../../cache'); 28define ('CACHE', __DIR__ . '/../../cache');
29define ('PAGINATION', '10'); 29define ('PAGINATION', '10');
30define ('THEME', 'light'); \ No newline at end of file 30define ('THEME', 'light');
31
32define ('IMPORT_POCKET_FILE', './ril_export.html');
33define ('IMPORT_READABILITY_FILE', './readability');
34define ('IMPORT_INSTAPAPER_FILE', './instapaper-export.html'); \ No newline at end of file
diff --git a/index.php b/index.php
index 51a33d71..5f43b740 100644
--- a/index.php
+++ b/index.php
@@ -8,6 +8,9 @@
8 * @license http://www.wtfpl.net/ see COPYING file 8 * @license http://www.wtfpl.net/ see COPYING file
9 */ 9 */
10 10
11if (file_exists(__DIR__ . '/inc/poche/myconfig.inc.php')) {
12 require_once __DIR__ . '/inc/poche/myconfig.inc.php';
13}
11require_once './inc/poche/Tools.class.php'; 14require_once './inc/poche/Tools.class.php';
12Tools::createMyConfig(); 15Tools::createMyConfig();
13 16
diff --git a/install/poche.sqlite b/install/poche.sqlite
index c268223d..7abf1f62 100755
--- a/install/poche.sqlite
+++ b/install/poche.sqlite
Binary files differ
diff --git a/install/update.php b/install/update.php
index 8c93af6d..1deaf7f3 100644
--- a/install/update.php
+++ b/install/update.php
@@ -1,9 +1,9 @@
1<?php 1<?php
2require_once dirname(__FILE__).'/../inc/poche/Tools.class.php'; 2require_once dirname(__FILE__).'/../inc/poche/Tools.class.php';
3include dirname(__FILE__).'/../inc/poche/define.inc.php'; 3include dirname(__FILE__).'/../inc/poche/define.inc.php';
4include dirname(__FILE__).'/../inc/poche/myconfig.inc.php';
4require_once __DIR__ . '/../inc/poche/Database.class.php'; 5require_once __DIR__ . '/../inc/poche/Database.class.php';
5$store = new Database(); 6$store = new Database();
6$old_salt = '464v54gLLw928uz4zUBqkRJeiPY68zCX';
7?> 7?>
8<!DOCTYPE html> 8<!DOCTYPE html>
9<!--[if lte IE 6]> <html class="no-js ie6 ie67 ie678" lang="en"> <![endif]--> 9<!--[if lte IE 6]> <html class="no-js ie6 ie67 ie678" lang="en"> <![endif]-->
@@ -16,25 +16,29 @@ $old_salt = '464v54gLLw928uz4zUBqkRJeiPY68zCX';
16 <title>updating poche</title> 16 <title>updating poche</title>
17 </head> 17 </head>
18 <body> 18 <body>
19 <h1>update poche to 1.0-beta3</h1> 19 <h1>update poche to 1.0-beta4</h1>
20 20
21 <h2>Changelog</h2> 21 <h2>Changelog</h2>
22 <p> 22 <ul>
23 <ul> 23 <li>enhancement: updating and installing poche are more easier</li>
24 <li>this awesome updating step</li> 24 <li>enhancement: poche now uses Full Text RSS to fetch content</li>
25 <li>error message when install folder exists</li> 25 <li>enhancement: css and twig files are more clean, thanks to NumEricR and nicofrand</li>
26 <li>more tests before installation (write access, etc.)</li> 26 <li>enhancement: updated german translation, thanks to HLFH</li>
27 <li>updated README to make installation easier</li> 27 <li>enhancement: add db/, cache/ and assets/ directories in versioning</li>
28 <li>german language thanks to HLFH</li> 28 <li>enhancement: display messages when error with import, thanks to EliasZ</li>
29 <li>spanish language thanks to Nitche</li> 29 <li>enhancement: poche compatibility test file</li>
30 <li>new file ./inc/poche/myconfig.inc.php created to store language and salt</li> 30 <li>enhancement: <a href="https://github.com/inthepoche/poche/issues/112">#112</a>: link with shaarli</li>
31 <li><a href="https://github.com/inthepoche/poche/issues/119">#119</a>: salt is now created when installing poche</li> 31 <li>enhancement: <a href="https://github.com/inthepoche/poche/issues/162">#162</a>: links to firefox / chrome / android apps in config screen</li>
32 <li><a href="https://github.com/inthepoche/poche/issues/130">#130</a>: robots.txt added</li> 32 <li>bug: encode url to share with twitter / email / shaarli</li>
33 <li><a href="https://github.com/inthepoche/poche/issues/136">#136</a>: error during readability import</li> 33 <li>bug: Add IPv4 url support (and others beginning by a digit)</li>
34 <li><a href="https://github.com/inthepoche/poche/issues/137">#137</a>: mixed content alert in https</li> 34 <li>bug: title page in article view was wrong</li>
35 <li><a href="https://github.com/inthepoche/poche/issues/138">#138</a>: change pattern to parse url with #</li> 35 <li>bug: <a href="https://github.com/inthepoche/poche/issues/148">#148</a>: use of undefined constant POCHE_VERSION</li>
36 </ul> 36 <li>bug: <a href="https://github.com/inthepoche/poche/issues/148">#149</a>: can't poche theguardian.com</li>
37 </p> 37 <li>bug: <a href="https://github.com/inthepoche/poche/issues/150">#150</a>: default title for untitled articles</li>
38 <li>bug: <a href="https://github.com/inthepoche/poche/issues/150">#151</a>: HTML entities in titles are encoded twice</li>
39 <li>bug: <a href="https://github.com/inthepoche/poche/issues/169">#169</a>: entries height with short description</li>
40 <li>bug: <a href="https://github.com/inthepoche/poche/issues/175">#175</a>: IP addresses do not appear in "view original"</li>
41 </ul>
38 <p>To update your poche, please fill the following fields.</p> 42 <p>To update your poche, please fill the following fields.</p>
39 <p> 43 <p>
40 <form name="update" method="post"> 44 <form name="update" method="post">
@@ -46,26 +50,20 @@ $old_salt = '464v54gLLw928uz4zUBqkRJeiPY68zCX';
46<?php 50<?php
47if (isset($_POST['go'])) { 51if (isset($_POST['go'])) {
48 if (!empty($_POST['login']) && !empty($_POST['password'])) { 52 if (!empty($_POST['login']) && !empty($_POST['password'])) {
49 $user = $store->login($_POST['login'], sha1($_POST['password'] . $_POST['login'] . $old_salt)); 53 $user = $store->login($_POST['login'], sha1($_POST['password'] . $_POST['login'] . SALT));
50 if ($user != array()) { 54 if ($user != array()) {
51 $new_salt = md5(time() . $_SERVER['SCRIPT_FILENAME'] . rand());
52 $myconfig_file = '../inc/poche/myconfig.inc.php'; 55 $myconfig_file = '../inc/poche/myconfig.inc.php';
53 if (!is_writable('../inc/poche/')) { 56 # just change version number in config file
54 die('You don\'t have write access to create ./inc/poche/myconfig.inc.php.'); 57
58 if (!is_writable('../inc/poche/myconfig.inc.php')) {
59 die('You don\'t have write access to open ./inc/poche/myconfig.inc.php.');
55 } 60 }
56 61
57 if (!file_exists($myconfig_file)) 62 if (file_exists($myconfig_file))
58 { 63 {
59 $fp = fopen($myconfig_file, 'w'); 64 $content = str_replace('1.0-beta3', '1.0-beta4', file_get_contents($myconfig_file));
60 65 file_put_contents($myconfig_file, $content);
61 fwrite($fp, '<?php'."\r\n");
62 fwrite($fp, "define ('POCHE_VERSION', '1.0-beta3');" . "\r\n");
63 fwrite($fp, "define ('SALT', '" . $new_salt . "');" . "\r\n");
64 fwrite($fp, "define ('LANG', 'en_EN.utf8');" . "\r\n");
65 fclose($fp);
66 } 66 }
67 # faire une mise à jour de la table users en prenant en compte le nouveau SALT généré
68 $store->updatePassword($user['id'], sha1($_POST['password'] . $_POST['login'] . $new_salt));
69?> 67?>
70 <p><span style="color: green;">your poche is up to date!</span></p> 68 <p><span style="color: green;">your poche is up to date!</span></p>
71 <p><span style="color: red;">don't forget to delete ./install/ folder after the update.</span></p> 69 <p><span style="color: red;">don't forget to delete ./install/ folder after the update.</span></p>
diff --git a/install/update_to_1beta3.php b/install/update_to_1beta3.php
new file mode 100644
index 00000000..8c93af6d
--- /dev/null
+++ b/install/update_to_1beta3.php
@@ -0,0 +1,79 @@
1<?php
2require_once dirname(__FILE__).'/../inc/poche/Tools.class.php';
3include dirname(__FILE__).'/../inc/poche/define.inc.php';
4require_once __DIR__ . '/../inc/poche/Database.class.php';
5$store = new Database();
6$old_salt = '464v54gLLw928uz4zUBqkRJeiPY68zCX';
7?>
8<!DOCTYPE html>
9<!--[if lte IE 6]> <html class="no-js ie6 ie67 ie678" lang="en"> <![endif]-->
10<!--[if lte IE 7]> <html class="no-js ie7 ie67 ie678" lang="en"> <![endif]-->
11<!--[if IE 8]> <html class="no-js ie8 ie678" lang="en"> <![endif]-->
12<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
13<html>
14 <head>
15 <meta charset="utf-8">
16 <title>updating poche</title>
17 </head>
18 <body>
19 <h1>update poche to 1.0-beta3</h1>
20
21 <h2>Changelog</h2>
22 <p>
23 <ul>
24 <li>this awesome updating step</li>
25 <li>error message when install folder exists</li>
26 <li>more tests before installation (write access, etc.)</li>
27 <li>updated README to make installation easier</li>
28 <li>german language thanks to HLFH</li>
29 <li>spanish language thanks to Nitche</li>
30 <li>new file ./inc/poche/myconfig.inc.php created to store language and salt</li>
31 <li><a href="https://github.com/inthepoche/poche/issues/119">#119</a>: salt is now created when installing poche</li>
32 <li><a href="https://github.com/inthepoche/poche/issues/130">#130</a>: robots.txt added</li>
33 <li><a href="https://github.com/inthepoche/poche/issues/136">#136</a>: error during readability import</li>
34 <li><a href="https://github.com/inthepoche/poche/issues/137">#137</a>: mixed content alert in https</li>
35 <li><a href="https://github.com/inthepoche/poche/issues/138">#138</a>: change pattern to parse url with #</li>
36 </ul>
37 </p>
38 <p>To update your poche, please fill the following fields.</p>
39 <p>
40 <form name="update" method="post">
41 <div><label for="login">login:</label> <input type="text" name="login" id="login" /></div>
42 <div><label for="password">password:</label> <input type="password" name="password" id="password" /></div>
43 <div><input type="hidden" name="go" value="ok" /><input type="submit" value="update" /></div>
44 </form>
45 </p>
46<?php
47if (isset($_POST['go'])) {
48 if (!empty($_POST['login']) && !empty($_POST['password'])) {
49 $user = $store->login($_POST['login'], sha1($_POST['password'] . $_POST['login'] . $old_salt));
50 if ($user != array()) {
51 $new_salt = md5(time() . $_SERVER['SCRIPT_FILENAME'] . rand());
52 $myconfig_file = '../inc/poche/myconfig.inc.php';
53 if (!is_writable('../inc/poche/')) {
54 die('You don\'t have write access to create ./inc/poche/myconfig.inc.php.');
55 }
56
57 if (!file_exists($myconfig_file))
58 {
59 $fp = fopen($myconfig_file, 'w');
60
61 fwrite($fp, '<?php'."\r\n");
62 fwrite($fp, "define ('POCHE_VERSION', '1.0-beta3');" . "\r\n");
63 fwrite($fp, "define ('SALT', '" . $new_salt . "');" . "\r\n");
64 fwrite($fp, "define ('LANG', 'en_EN.utf8');" . "\r\n");
65 fclose($fp);
66 }
67 # faire une mise à jour de la table users en prenant en compte le nouveau SALT généré
68 $store->updatePassword($user['id'], sha1($_POST['password'] . $_POST['login'] . $new_salt));
69?>
70 <p><span style="color: green;">your poche is up to date!</span></p>
71 <p><span style="color: red;">don't forget to delete ./install/ folder after the update.</span></p>
72 <p><a href="../">go back to your poche</a></p>
73<?php
74 }
75 }
76}
77?>
78 </body>
79</html> \ No newline at end of file
diff --git a/locale/de_DE.utf8/LC_MESSAGES/de_DE.utf8.mo b/locale/de_DE.utf8/LC_MESSAGES/de_DE.utf8.mo
index b900f146..3afc2b32 100644
--- a/locale/de_DE.utf8/LC_MESSAGES/de_DE.utf8.mo
+++ b/locale/de_DE.utf8/LC_MESSAGES/de_DE.utf8.mo
Binary files differ
diff --git a/locale/de_DE.utf8/LC_MESSAGES/de_DE.utf8.po b/locale/de_DE.utf8/LC_MESSAGES/de_DE.utf8.po
index 4448bad1..fafe516c 100644
--- a/locale/de_DE.utf8/LC_MESSAGES/de_DE.utf8.po
+++ b/locale/de_DE.utf8/LC_MESSAGES/de_DE.utf8.po
@@ -4,8 +4,8 @@
4msgid "" 4msgid ""
5msgstr "" 5msgstr ""
6"Project-Id-Version: poche\n" 6"Project-Id-Version: poche\n"
7"POT-Creation-Date: 2013-08-02 10:26+0100\n" 7"POT-Creation-Date: 2013-08-06 08:35+0100\n"
8"PO-Revision-Date: 2013-08-06 11:48+0100\n" 8"PO-Revision-Date: 2013-08-23 17:42+0100\n"
9"Last-Translator: Nicolas LÅ“uillet <nicolas.loeuillet@gmail.com>\n" 9"Last-Translator: Nicolas LÅ“uillet <nicolas.loeuillet@gmail.com>\n"
10"Language-Team: German (http://www.transifex.com/projects/p/poche/language/" 10"Language-Team: German (http://www.transifex.com/projects/p/poche/language/"
11"de/)\n" 11"de/)\n"
@@ -20,95 +20,361 @@ msgstr ""
20"X-Poedit-SourceCharset: UTF-8\n" 20"X-Poedit-SourceCharset: UTF-8\n"
21"X-Poedit-SearchPath-0: /var/www/poche-i18n\n" 21"X-Poedit-SearchPath-0: /var/www/poche-i18n\n"
22 22
23#: /var/www/poche-i18n/import.php:17 23#: /var/www/poche-i18n/index.php:43
24msgid "poche, a read it later open source system"
25msgstr "Poche, eine Opensourcelösung, um später zu lesen"
26
27#: /var/www/poche-i18n/inc/poche/Poche.class.php:101
28msgid "the link has been added successfully"
29msgstr "der Link wurde erfolgreich hinzugefügt"
30
31#: /var/www/poche-i18n/inc/poche/Poche.class.php:104
32msgid "error during insertion : the link wasn't added"
33msgstr "Fehler beim Einfügen: der Link wurde nicht hinzugefügt"
34
35#: /var/www/poche-i18n/inc/poche/Poche.class.php:109
36msgid "error during fetching content : the link wasn't added"
37msgstr "Fehler beim Abrufen der Inhalte: der Link wurde nicht hinzugefügt"
38
39#: /var/www/poche-i18n/inc/poche/Poche.class.php:119
40msgid "the link has been deleted successfully"
41msgstr "der Link wurde erfolgreich gelöscht"
42
43#: /var/www/poche-i18n/inc/poche/Poche.class.php:123
44msgid "the link wasn't deleted"
45msgstr "der Link wurde nicht gelöscht"
46
47#: /var/www/poche-i18n/inc/poche/Tools.class.php:18
48msgid "Oops, it seems you don't have PHP 5."
49msgstr "Hoppla, scheint es, dass PHP 5 nicht installiert ist."
50
51#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:32
52#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:70
53#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:50
54msgid "config"
55msgstr "Konfig"
56
57#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:46
58#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:31
59#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:26
60#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:34
61msgid "home"
62msgstr "Hause"
63
64#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:54
65#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:34
66msgid "favorites"
67msgstr "Favoriten"
68
69#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:62
70#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:42
71msgid "archive"
72msgstr "Archive"
73
74#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:74
75#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:76
76#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:54
77#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:56
78msgid "logout"
79msgstr "Trennung"
80
81#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:87
82msgid "Bookmarklet"
83msgstr "Bookmarklet"
84
85#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:91
86msgid ""
87"Thanks to the bookmarklet, you will be able to easily add a link to your "
88"poche."
89msgstr ""
90"Mit dem Bookmarklet, können Sie ganz einfach einen Link in Poche hinzufügen."
91
92#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:93
93msgid "Have a look to this documentation:"
94msgstr "Werfen Sie einen Blick in die Dokumentation:"
95
96#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:97
97msgid "Drag & drop this link to your bookmarks bar and have fun with poche."
98msgstr ""
99"Ziehen / Ablegen Sie diesen Link in die Lesezeichenleiste Ihres Browsers und "
100"genießen Sie mit Poche."
101
102#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:103
103msgid "poche it!"
104msgstr "Pochert es!"
105
106#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:108
107msgid "Updating poche"
108msgstr "Poche aktualisieren "
109
110#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:113
111msgid "your version"
112msgstr "Ihre Version"
113
114#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:119
115msgid "latest stable version"
116msgstr "letzte stabile Version"
117
118#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:125
119msgid "a more recent stable version is available."
120msgstr "eine neuere stabile Version ist verfügbar."
121
122#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:128
123#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:142
124msgid "you are up to date."
125msgstr "Sie sind auf den neuesten Stand."
126
127#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:133
128msgid "latest dev version"
129msgstr "letzte Entwicklungsversion"
130
131#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:139
132msgid "a more recent development version is available."
133msgstr "eine neuere Entwicklungsversion ist verfügbar."
134
135#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:150
136msgid "Change your password"
137msgstr "Ihr Passwort ändern"
138
139#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:157
140msgid "New password:"
141msgstr "Neues Passwort:"
142
143#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:161
144#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:171
145#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:60
146#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:68
147msgid "Password"
148msgstr "Passwort"
149
150#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:167
151msgid "Repeat your new password:"
152msgstr "neues Passwort wiederholen:"
153
154#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:177
155msgid "Update"
156msgstr "Aktualisieren"
157
158#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:193
159msgid "Import"
160msgstr "Import"
161
162#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:197
24msgid "Please execute the import script locally, it can take a very long time." 163msgid "Please execute the import script locally, it can take a very long time."
25msgstr "" 164msgstr ""
26"Wir danken Ihnen, den Import in lokal zu ausführen, kann es einige Zeit " 165"Wir danken Ihnen, den Import in lokal zu ausführen, kann es einige Zeit "
27"dauern." 166"dauern."
28 167
29#: /var/www/poche-i18n/import.php:17 168#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:201
30msgid "Please choose between Pocket & Readabilty :" 169msgid "More infos in the official doc:"
31msgstr "Wir danken Ihnen, zwischen Pocket und Readability zu wählen:" 170msgstr "Mehr Informationen auf der offiziellen Dokumentation:"
32 171
33#: /var/www/poche-i18n/import.php:17 172#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:206
34msgid "Bye bye Pocket, let's go !" 173msgid "import from Pocket"
35msgstr "Auf wiedersehen Pocket, auf geht's!" 174msgstr "Der Import aus Pocket ist abgeschlossen."
36 175
37#: /var/www/poche-i18n/import.php:17 176#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:210
38msgid "Bye bye Readability, let's go !" 177msgid "import from Readability"
39msgstr "Auf wiedersehen Readability, auf geht's!" 178msgstr "Der Import aus Readability ist abgeschlossen."
40 179
41#: /var/www/poche-i18n/import.php:48 180#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:214
42msgid "Import from Pocket completed." 181msgid "import from Instapaper"
43msgstr "Der Import aus Poche ist abgeschlossen." 182msgstr "Import aus Instapaper"
44 183
45#: /var/www/poche-i18n/import.php:48 /var/www/poche-i18n/import.php:66 184#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:220
46msgid "Welcome to poche !" 185msgid "Export your poche datas"
47msgstr "Willkommen in Poche!" 186msgstr "Exportieren Sie Ihre Daten aus Poche."
48 187
49#: /var/www/poche-i18n/import.php:66 188#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:224
50msgid "Import from Readability completed." 189msgid "Click here"
51msgstr "Der Import aus Readability ist abgeschlossen." 190msgstr "klicken Sie hier"
52 191
53#: /var/www/poche-i18n/import.php:70 192#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:226
54msgid "Error with the import." 193msgid "to export your poche datas."
55msgstr "Fehler beim Import." 194msgstr "um Ihre Daten aus Poche zu exportieren."
56 195
57#: /var/www/poche-i18n/import.php:70 196#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:46
58msgid "Back to poche" 197#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:139
59msgstr "Rückkehr zu Poche" 198#: /var/www/poche-i18n/cache/30/97/b548692380c89d047a16cec7af79.php:22
199msgid "back to home"
200msgstr "züruck zur Hauptseite"
60 201
61#: /var/www/poche-i18n/index.php:18 202#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:50
62msgid "Wrong token." 203#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:147
63msgstr "Ungültiges Token." 204#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:119
205msgid "toggle mark as read"
206msgstr "als gelesen markieren"
64 207
65#: /var/www/poche-i18n/index.php:43 208#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:60
66msgid "Login failed !" 209#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:157
67msgstr "Fehler bei der Anmeldung." 210#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:129
211msgid "toggle favorite"
212msgstr "Favorit"
213
214#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:70
215#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:167
216#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:139
217msgid "delete"
218msgstr "löschen"
219
220#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:82
221#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:179
222msgid "tweet"
223msgstr "twittern"
224
225#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:93
226#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:190
227msgid "email"
228msgstr "senden per E-Mail"
229
230#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:109
231#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:125
232#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:153
233msgid "original"
234msgstr "Original"
235
236#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:143
237msgid "back to top"
238msgstr "zurück nach oben"
239
240#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:198
241msgid "this article appears wrong?"
242msgstr "dieser Artikel erscheint falsch?"
243
244#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:200
245msgid "create an issue"
246msgstr "ein Ticket erstellen"
247
248#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:202
249msgid "or"
250msgstr "oder"
68 251
69#: /var/www/poche-i18n/index.php:59 252#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:206
70msgid "your password has been updated" 253msgid "contact us by mail"
71msgstr "Ihr Passwort wurde aktualisiert." 254msgstr "kontaktieren Sie uns per E-Mail"
72 255
73#: /var/www/poche-i18n/index.php:62 256#: /var/www/poche-i18n/cache/88/8a/ee3b7080c13204391c14947a0c2c.php:22
74msgid "in demo mode, you can't update password" 257msgid "powered by"
75msgstr "Im Demo-Modus kann das Passwort nicht geändert werden." 258msgstr "bereitgestellt von"
76 259
77#: /var/www/poche-i18n/index.php:66 260#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:31
261msgid "installation"
262msgstr "Installierung"
263
264#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:42
265msgid "install your poche"
266msgstr "installieren Sie Poche"
267
268#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:47
78msgid "" 269msgid ""
79"your password can't be empty and you have to repeat it in the second field" 270"poche is still not installed. Please fill the below form to install it. "
271"Don't hesitate to <a href='http://inthepoche.com/?pages/Documentation'>read "
272"the documentation on poche website</a>."
80msgstr "" 273msgstr ""
81"Ihr Passwort darf nicht leer sein, und Sie müssen es in das zweite Feld zu " 274"Poche ist noch nicht installiert. Wir danken Ihnen, die Felder unten zu "
82"wiederholen." 275"befüllen, um es zu machen. Zögern sie nicht, <a href='http://inthepoche.com/?"
276"pages/Documentation'> die Dokumentation auf der Website von Poche zu lesen."
83 277
84#: /var/www/poche-i18n/index.php:83 278#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:53
85msgid "poche, a read it later open source system" 279#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:55
86msgstr "Poche, eine Opensourceanwendung, um später zu lesen" 280msgid "Login"
281msgstr "Benutzername"
87 282
88#: /var/www/poche-i18n/inc/MyTool.class.php:18 283#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:67
89msgid "Oops, it seems you don't have PHP 5." 284msgid "Repeat your password"
90msgstr "Hoppla, scheint es, dass PHP 5 nicht installiert ist." 285msgstr "Wiederholen Sie Ihr Passwort"
91 286
92#: /var/www/poche-i18n/inc/functions.php:352 287#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:74
93msgid "the link has been added successfully" 288msgid "Install"
94msgstr "der Link wurde erfolgreich hinzugefügt" 289msgstr "Installieren"
95 290
96#: /var/www/poche-i18n/inc/functions.php:355 291#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:31
97msgid "error during insertion : the link wasn't added" 292#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:42
98msgstr "Fehler beim Einfügen: der Link wurde nicht hinzugefügt" 293msgid "login to your poche"
294msgstr "Verbinden zu Poche"
99 295
100#: /var/www/poche-i18n/inc/functions.php:359 296#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:48
101msgid "error during url preparation : the link wasn't added" 297msgid "you are in demo mode, some features may be disabled."
102msgstr "Fehler beim Einfügen: der Link wurde nicht hinzugefügt" 298msgstr "Sie sind im Demomodus, können einige Funktionen deaktiviert werden."
103 299
104#: /var/www/poche-i18n/inc/functions.php:364 300#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:80
105msgid "error during url preparation : the link is not valid" 301msgid "Stay signed in"
106msgstr "Fehler bei der Herstellung der URL: der Link ist nicht gültig" 302msgstr "bleiben Sie verbunden"
107 303
108#: /var/www/poche-i18n/inc/functions.php:373 304#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:86
109msgid "the link has been deleted successfully" 305msgid "(Do not check on public computers)"
110msgstr "der Link wurde erfolgreich hinzugefügt" 306msgstr "(nicht auf einem öffentlichen Computer überprüfen)"
111 307
112#: /var/www/poche-i18n/inc/functions.php:377 308#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:93
113msgid "the link wasn't deleted" 309msgid "Sign in"
114msgstr "der Link wurde nicht entfernt." 310msgstr "Einloggen"
311
312#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:55
313#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:57
314msgid "by date asc"
315msgstr "nach Datum asc"
316
317#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:59
318msgid "by date"
319msgstr "nach Datum"
320
321#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:65
322#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:67
323msgid "by date desc"
324msgstr "nach Datum desc"
325
326#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:75
327#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:77
328msgid "by title asc"
329msgstr "nach Titel asc"
330
331#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:79
332msgid "by title"
333msgstr "nach Titel"
334
335#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:85
336#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:87
337msgid "by title desc"
338msgstr "nach Titel desc"
339
340#~ msgid "Please choose between Pocket & Readabilty :"
341#~ msgstr "Merci de choisir entre Pocket & Readability :"
342
343#~ msgid "Bye bye Pocket, let's go !"
344#~ msgstr "Bye bye Pocket, en route !"
345
346#~ msgid "Bye bye Readability, let's go !"
347#~ msgstr "Bye bye Readability, en route !"
348
349#~ msgid "Welcome to poche !"
350#~ msgstr "Bienvenue dans poche !"
351
352#~ msgid "Error with the import."
353#~ msgstr "Erreur durant l'import."
354
355#~ msgid "Wrong token."
356#~ msgstr "Mauvais jeton."
357
358#~ msgid "Login failed !"
359#~ msgstr "Connexion échouée."
360
361#~ msgid "your password has been updated"
362#~ msgstr "Votre mot de passe a été mis à jour. "
363
364#~ msgid "in demo mode, you can't update password"
365#~ msgstr "En mode démo, le mot de passe ne peut être modifié."
366
367#~ msgid ""
368#~ "your password can't be empty and you have to repeat it in the second field"
369#~ msgstr ""
370#~ "Votre mot de passe ne peut être vide et vous devez le répéter dans le "
371#~ "second champ."
372
373#~ msgid "error during url preparation : the link wasn't added"
374#~ msgstr "erreur durant l'insertion : le lien n'a pas été ajouté"
375
376#~ msgid "error during url preparation : the link is not valid"
377#~ msgstr "erreur durant la préparation de l'URL : le lien n'est pas valide"
378
379#~ msgid "TEST"
380#~ msgstr "NICOLAS"
diff --git a/locale/fr_FR.utf8/LC_MESSAGES/fr_FR.utf8.mo b/locale/fr_FR.utf8/LC_MESSAGES/fr_FR.utf8.mo
index c0d4a9d6..ec72b830 100644
--- a/locale/fr_FR.utf8/LC_MESSAGES/fr_FR.utf8.mo
+++ b/locale/fr_FR.utf8/LC_MESSAGES/fr_FR.utf8.mo
Binary files differ
diff --git a/locale/fr_FR.utf8/LC_MESSAGES/fr_FR.utf8.po b/locale/fr_FR.utf8/LC_MESSAGES/fr_FR.utf8.po
index 7f8cf784..a643f6e3 100644
--- a/locale/fr_FR.utf8/LC_MESSAGES/fr_FR.utf8.po
+++ b/locale/fr_FR.utf8/LC_MESSAGES/fr_FR.utf8.po
@@ -2,14 +2,14 @@ msgid ""
2msgstr "" 2msgstr ""
3"Project-Id-Version: poche\n" 3"Project-Id-Version: poche\n"
4"POT-Creation-Date: 2013-08-06 08:35+0100\n" 4"POT-Creation-Date: 2013-08-06 08:35+0100\n"
5"PO-Revision-Date: 2013-08-06 08:35+0100\n" 5"PO-Revision-Date: 2013-08-24 10:25+0100\n"
6"Last-Translator: Nicolas LÅ“uillet <nicolas.loeuillet@gmail.com>\n" 6"Last-Translator: Eric R (NumEricR)\n"
7"Language-Team: poche <support@inthepoche.com>\n" 7"Language-Team: poche <support@inthepoche.com>\n"
8"Language: Français\n" 8"Language: Français\n"
9"MIME-Version: 1.0\n" 9"MIME-Version: 1.0\n"
10"Content-Type: text/plain; charset=UTF-8\n" 10"Content-Type: text/plain; charset=UTF-8\n"
11"Content-Transfer-Encoding: 8bit\n" 11"Content-Transfer-Encoding: 8bit\n"
12"X-Generator: Poedit 1.5.4\n" 12"X-Generator: Poedit 1.5.7\n"
13"X-Poedit-KeywordsList: _;gettext;gettext_noop\n" 13"X-Poedit-KeywordsList: _;gettext;gettext_noop\n"
14"X-Poedit-Basepath: /\n" 14"X-Poedit-Basepath: /\n"
15"Plural-Forms: nplurals=2; plural=(n > 1);\n" 15"Plural-Forms: nplurals=2; plural=(n > 1);\n"
@@ -303,7 +303,7 @@ msgstr "(à ne pas cocher sur un ordinateur public)"
303 303
304#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:93 304#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:93
305msgid "Sign in" 305msgid "Sign in"
306msgstr "" 306msgstr "Se connecter"
307 307
308#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:55 308#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:55
309#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:57 309#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:57
diff --git a/poche_compatibility_test.php b/poche_compatibility_test.php
new file mode 100644
index 00000000..7c85a583
--- /dev/null
+++ b/poche_compatibility_test.php
@@ -0,0 +1,353 @@
1<?php
2/*
3FULL-TEXT-RSS V2 COMPATIBILITY TEST
4
51) Upload ftr_compatibility_test.php to the web-accessible root of your website.
6For example, if your website is www.example.com, upload it so that you can get
7to it at www.example.com/ftr_compatibility_test.php
8
92) Open your web browser and go to the page you just uploaded.
10
11Note: This compatibility test has been borrowed (and slightly adapted) from the one supplied by
12SimplePie.org. We have kept most of their checks intact as we use SimplePie in our application.
13http://github.com/simplepie/simplepie/tree/master/compatibility_test/
14*/
15
16$app_name = 'poche 1.0';
17
18$php_ok = (function_exists('version_compare') && version_compare(phpversion(), '5.2.0', '>='));
19$pcre_ok = extension_loaded('pcre');
20$zlib_ok = extension_loaded('zlib');
21$mbstring_ok = extension_loaded('mbstring');
22$iconv_ok = extension_loaded('iconv');
23$tidy_ok = function_exists('tidy_parse_string');
24$curl_ok = function_exists('curl_exec');
25$parallel_ok = ((extension_loaded('http') && class_exists('HttpRequestPool')) || ($curl_ok && function_exists('curl_multi_init')));
26$allow_url_fopen_ok = (bool)ini_get('allow_url_fopen');
27$filter_ok = extension_loaded('filter');
28
29if (extension_loaded('xmlreader')) {
30 $xml_ok = true;
31} elseif (extension_loaded('xml')) {
32 $parser_check = xml_parser_create();
33 xml_parse_into_struct($parser_check, '<foo>&amp;</foo>', $values);
34 xml_parser_free($parser_check);
35 $xml_ok = isset($values[0]['value']);
36} else {
37 $xml_ok = false;
38}
39
40header('Content-type: text/html; charset=UTF-8');
41
42?><!DOCTYPE html>
43
44<html lang="en">
45<head>
46<title><?php echo $app_name; ?>: Server Compatibility Test</title>
47
48<style type="text/css">
49body {
50 font:14px/1.4em "Lucida Grande", Verdana, Arial, Helvetica, Clean, Sans, sans-serif;
51 letter-spacing:0px;
52 color:#333;
53 margin:0;
54 padding:0;
55 background:#fff;
56}
57
58div#site {
59 width:550px;
60 margin:20px auto 0 auto;
61}
62
63a {
64 color:#000;
65 text-decoration:underline;
66 padding:0 1px;
67}
68
69a:hover {
70 color:#fff;
71 background-color:#333;
72 text-decoration:none;
73 padding:0 1px;
74}
75
76p {
77 margin:0;
78 padding:5px 0;
79}
80
81em {
82 font-style:normal;
83 background-color:#ffc;
84 padding: 0.1em 0;
85}
86
87ul, ol {
88 margin:10px 0 10px 20px;
89 padding:0 0 0 15px;
90}
91
92ul li, ol li {
93 margin:0 0 7px 0;
94 padding:0 0 0 3px;
95}
96
97h2 {
98 font-size:18px;
99 padding:0;
100 margin:30px 0 20px 0;
101}
102
103h3 {
104 font-size:16px;
105 padding:0;
106 margin:20px 0 5px 0;
107}
108
109h4 {
110 font-size:14px;
111 padding:0;
112 margin:15px 0 5px 0;
113}
114
115code {
116 font-size:1.1em;
117 background-color:#f3f3ff;
118 color:#000;
119}
120
121em strong {
122 text-transform: uppercase;
123}
124
125table#chart {
126 border-collapse:collapse;
127}
128
129table#chart th {
130 background-color:#eee;
131 padding:2px 3px;
132 border:1px solid #fff;
133}
134
135table#chart td {
136 text-align:center;
137 padding:2px 3px;
138 border:1px solid #eee;
139}
140
141table#chart tr.enabled td {
142 /* Leave this alone */
143}
144
145table#chart tr.disabled td,
146table#chart tr.disabled td a {
147 color:#999;
148 font-style:italic;
149}
150
151table#chart tr.disabled td a {
152 text-decoration:underline;
153}
154
155div.chunk {
156 margin:20px 0 0 0;
157 padding:0 0 10px 0;
158 border-bottom:1px solid #ccc;
159}
160
161.footnote,
162.footnote a {
163 font:10px/12px verdana, sans-serif;
164 color:#aaa;
165}
166
167.footnote em {
168 background-color:transparent;
169 font-style:italic;
170}
171</style>
172
173</head>
174
175<body>
176
177<div id="site">
178 <div id="content">
179
180 <div class="chunk">
181 <h2 style="text-align:center;"><?php echo $app_name; ?>: Compatibility Test</h2>
182 <table cellpadding="0" cellspacing="0" border="0" width="100%" id="chart">
183 <thead>
184 <tr>
185 <th>Test</th>
186 <th>Should Be</th>
187 <th>What You Have</th>
188 </tr>
189 </thead>
190 <tbody>
191 <tr class="<?php echo ($php_ok) ? 'enabled' : 'disabled'; ?>">
192 <td>PHP</td>
193 <td>5.2.0 or higher</td>
194 <td><?php echo phpversion(); ?></td>
195 </tr>
196 <tr class="<?php echo ($xml_ok) ? 'enabled, and sane' : 'disabled, or broken'; ?>">
197 <td><a href="http://php.net/xml">XML</a></td>
198 <td>Enabled</td>
199 <td><?php echo ($xml_ok) ? 'Enabled, and sane' : 'Disabled, or broken'; ?></td>
200 </tr>
201 <tr class="<?php echo ($pcre_ok) ? 'enabled' : 'disabled'; ?>">
202 <td><a href="http://php.net/pcre">PCRE</a></td>
203 <td>Enabled</td>
204 <td><?php echo ($pcre_ok) ? 'Enabled' : 'Disabled'; ?></td>
205 </tr>
206<!-- <tr class="<?php echo ($zlib_ok) ? 'enabled' : 'disabled'; ?>">
207 <td><a href="http://php.net/zlib">Zlib</a></td>
208 <td>Enabled</td>
209 <td><?php echo ($zlib_ok) ? 'Enabled' : 'Disabled'; ?></td>
210 </tr> -->
211<!-- <tr class="<?php echo ($mbstring_ok) ? 'enabled' : 'disabled'; ?>">
212 <td><a href="http://php.net/mbstring">mbstring</a></td>
213 <td>Enabled</td>
214 <td><?php echo ($mbstring_ok) ? 'Enabled' : 'Disabled'; ?></td>
215 </tr> -->
216<!-- <tr class="<?php echo ($iconv_ok) ? 'enabled' : 'disabled'; ?>">
217 <td><a href="http://php.net/iconv">iconv</a></td>
218 <td>Enabled</td>
219 <td><?php echo ($iconv_ok) ? 'Enabled' : 'Disabled'; ?></td>
220 </tr> -->
221 <tr class="<?php echo ($filter_ok) ? 'enabled' : 'disabled'; ?>">
222 <td><a href="http://uk.php.net/manual/en/book.filter.php">Data filtering</a></td>
223 <td>Enabled</td>
224 <td><?php echo ($filter_ok) ? 'Enabled' : 'Disabled'; ?></td>
225 </tr>
226 <tr class="<?php echo ($tidy_ok) ? 'enabled' : 'disabled'; ?>">
227 <td><a href="http://php.net/tidy">Tidy</a></td>
228 <td>Enabled</td>
229 <td><?php echo ($tidy_ok) ? 'Enabled' : 'Disabled'; ?></td>
230 </tr>
231 <tr class="<?php echo ($curl_ok) ? 'enabled' : 'disabled'; ?>">
232 <td><a href="http://php.net/curl">cURL</a></td>
233 <td>Enabled</td>
234 <td><?php echo (extension_loaded('curl')) ? 'Enabled' : 'Disabled'; ?></td>
235 </tr>
236 <tr class="<?php echo ($parallel_ok) ? 'enabled' : 'disabled'; ?>">
237 <td>Parallel URL fetching</td>
238 <td>Enabled</td>
239 <td><?php echo ($parallel_ok) ? 'Enabled' : 'Disabled'; ?></td>
240 </tr>
241 <tr class="<?php echo ($allow_url_fopen_ok) ? 'enabled' : 'disabled'; ?>">
242 <td><a href="http://www.php.net/manual/en/filesystem.configuration.php#ini.allow-url-fopen">allow_url_fopen</a></td>
243 <td>Enabled</td>
244 <td><?php echo ($allow_url_fopen_ok) ? 'Enabled' : 'Disabled'; ?></td>
245 </tr>
246 </tbody>
247 </table>
248 </div>
249
250 <div class="chunk">
251 <h3>What does this mean?</h3>
252 <ol>
253 <?php //if ($php_ok && $xml_ok && $pcre_ok && $mbstring_ok && $iconv_ok && $filter_ok && $zlib_ok && $tidy_ok && $curl_ok && $parallel_ok && $allow_url_fopen_ok): ?>
254 <?php if ($php_ok && $xml_ok && $pcre_ok && $filter_ok && $tidy_ok && $curl_ok && $parallel_ok && $allow_url_fopen_ok): ?>
255 <li><em>You have everything you need to run <?php echo $app_name; ?> properly! Congratulations!</em></li>
256 <?php else: ?>
257 <?php if ($php_ok): ?>
258 <li><strong>PHP:</strong> You are running a supported version of PHP. <em>No problems here.</em></li>
259 <?php if ($xml_ok): ?>
260 <li><strong>XML:</strong> You have XMLReader support or a version of XML support that isn't broken installed. <em>No problems here.</em></li>
261 <?php if ($pcre_ok): ?>
262 <li><strong>PCRE:</strong> You have PCRE support installed. <em>No problems here.</em></li>
263
264 <?php if ($allow_url_fopen_ok): ?>
265 <li><strong>allow_url_fopen:</strong> You have allow_url_fopen enabled. <em>No problems here.</em></li>
266
267 <?php if ($filter_ok): ?>
268 <li><strong>Data filtering:</strong> You have the PHP filter extension enabled. <em>No problems here.</em></li>
269
270 <?php if ($zlib_ok): ?>
271 <li><strong>Zlib:</strong> You have <code>Zlib</code> enabled. This allows SimplePie to support GZIP-encoded feeds. <em>No problems here.</em></li>
272 <?php else: ?>
273 <li><strong>Zlib:</strong> The <code>Zlib</code> extension is not available. SimplePie will ignore any GZIP-encoding, and instead handle feeds as uncompressed text.</li>
274 <?php endif; ?>
275
276 <?php if ($mbstring_ok && $iconv_ok): ?>
277 <li><strong>mbstring and iconv:</strong> You have both <code>mbstring</code> and <code>iconv</code> installed! This will allow <?php echo $app_name; ?> to handle the greatest number of languages. <em>No problems here.</em></li>
278 <?php elseif ($mbstring_ok): ?>
279 <li><strong>mbstring:</strong> <code>mbstring</code> is installed, but <code>iconv</code> is not.</li>
280 <?php elseif ($iconv_ok): ?>
281 <li><strong>iconv:</strong> <code>iconv</code> is installed, but <code>mbstring</code> is not.</li>
282 <?php else: ?>
283 <li><strong>mbstring and iconv:</strong> <em>You do not have either of the extensions installed.</em> This will significantly impair your ability to read non-English feeds, as well as even some English ones.</li>
284 <?php endif; ?>
285
286 <?php if ($tidy_ok): ?>
287 <li><strong>Tidy:</strong> You have <code>Tidy</code> support installed. <em>No problems here.</em></li>
288 <?php else: ?>
289 <li><strong>Tidy:</strong> The <code>Tidy</code> extension is not available. <?php echo $app_name; ?> should still work with most feeds, but you may experience problems with some.</li>
290 <?php endif; ?>
291
292 <?php if ($curl_ok): ?>
293 <li><strong>cURL:</strong> You have <code>cURL</code> support installed. <em>No problems here.</em></li>
294 <?php else: ?>
295 <li><strong>cURL:</strong> The <code>cURL</code> extension is not available. SimplePie will use <code>fsockopen()</code> instead.</li>
296 <?php endif; ?>
297
298 <?php if ($parallel_ok): ?>
299 <li><strong>Parallel URL fetching:</strong> You have <code>HttpRequestPool</code> or <code>curl_multi</code> support installed. <em>No problems here.</em></li>
300 <?php else: ?>
301 <li><strong>Parallel URL fetching:</strong> <code>HttpRequestPool</code> or <code>curl_multi</code> support is not available. <?php echo $app_name; ?> will use <code>file_get_contents()</code> instead to fetch URLs sequentially rather than in parallel.</li>
302 <?php endif; ?>
303
304 <?php else: ?>
305 <li><strong>Data filtering:</strong> Your PHP configuration has the filter extension disabled. <em><?php echo $app_name; ?> will not work here.</em></li>
306 <?php endif; ?>
307
308 <?php else: ?>
309 <li><strong>allow_url_fopen:</strong> Your PHP configuration has allow_url_fopen disabled. <em><?php echo $app_name; ?> will not work here.</em></li>
310 <?php endif; ?>
311
312 <?php else: ?>
313 <li><strong>PCRE:</strong> Your PHP installation doesn't support Perl-Compatible Regular Expressions. <em><?php echo $app_name; ?> will not work here.</em></li>
314 <?php endif; ?>
315 <?php else: ?>
316 <li><strong>XML:</strong> Your PHP installation doesn't support XML parsing. <em><?php echo $app_name; ?> will not work here.</em></li>
317 <?php endif; ?>
318 <?php else: ?>
319 <li><strong>PHP:</strong> You are running an unsupported version of PHP. <em><?php echo $app_name; ?> will not work here.</em></li>
320 <?php endif; ?>
321 <?php endif; ?>
322 </ol>
323 </div>
324
325 <div class="chunk">
326 <?php //if ($php_ok && $xml_ok && $pcre_ok && $mbstring_ok && $iconv_ok && $filter_ok && $allow_url_fopen_ok) { ?>
327 <?php if ($php_ok && $xml_ok && $pcre_ok && $filter_ok && $allow_url_fopen_ok) { ?>
328 <h3>Bottom Line: Yes, you can!</h3>
329 <p><em>Your webhost has its act together!</em></p>
330 <p>You can download the latest version of <?php echo $app_name; ?> from <a href="http://inthepoche.com/download">inthepoche.com</a>.</p>
331 <p><strong>Note</strong>: Passing this test does not guarantee that <?php echo $app_name; ?> will run on your webhost &mdash; it only ensures that the basic requirements have been addressed. If you experience any problems, please let us know.</p>
332 <?php //} else if ($php_ok && $xml_ok && $pcre_ok && $mbstring_ok && $allow_url_fopen_ok && $filter_ok) { ?>
333 <?php } else if ($php_ok && $xml_ok && $pcre_ok && $allow_url_fopen_ok && $filter_ok) { ?>
334 <h3>Bottom Line: Yes, you can!</h3>
335 <p><em>For most feeds, it'll run with no problems.</em> There are certain languages that you might have a hard time with though.</p>
336 <p>You can download the latest version of <?php echo $app_name; ?> from <a href="http://inthepoche.com/download">inthepoche.com</a>.</p>
337 <p><strong>Note</strong>: Passing this test does not guarantee that <?php echo $app_name; ?> will run on your webhost &mdash; it only ensures that the basic requirements have been addressed. If you experience any problems, please let us know.</p>
338 <?php } else { ?>
339 <h3>Bottom Line: We're sorry…</h3>
340 <p><em>Your webhost does not support the minimum requirements for <?php echo $app_name; ?>.</em> It may be a good idea to contact your webhost and point them to the results of this test. They may be able to enable/install the required components.</p>
341 <?php } ?>
342 </div>
343
344 <div class="chunk">
345 <p class="footnote">This compatibility test has been borrowed (and slightly adapted by <a href="http://fivefilters.org/content-only/">fivefilters.org</a>) from the one supplied by <a href="http://simplepie.org/">SimplePie.org</a>.</a></p>
346 </div>
347
348 </div>
349
350</div>
351
352</body>
353</html> \ No newline at end of file
diff --git a/tpl/_head.twig b/tpl/_head.twig
index 60ef8883..cab317a8 100644
--- a/tpl/_head.twig
+++ b/tpl/_head.twig
@@ -6,6 +6,7 @@
6 <link rel="stylesheet" href="./tpl/css/style.css" media="all"> 6 <link rel="stylesheet" href="./tpl/css/style.css" media="all">
7 <link rel="stylesheet" href="./tpl/css/style-{{ constant('THEME') }}.css" media="all" title="{{ constant('THEME') }} theme"> 7 <link rel="stylesheet" href="./tpl/css/style-{{ constant('THEME') }}.css" media="all" title="{{ constant('THEME') }} theme">
8 <link rel="stylesheet" href="./tpl/css/messages.css" media="all"> 8 <link rel="stylesheet" href="./tpl/css/messages.css" media="all">
9 <link rel="stylesheet" href="./tpl/css/print.css" media="print">
9 <link href='//fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'> 10 <link href='//fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'>
10 <script src="./tpl/js/jquery-2.0.3.min.js"></script> 11 <script src="./tpl/js/jquery-2.0.3.min.js"></script>
11 <script type="text/javascript">$(document).ready(function(){$("body").prepend('<a href="#top" class="top_link" title="{% trans "back to top" %}"><img src="./tpl/img/{{ constant("THEME") }}/backtotop.png" alt={% trans "back to top" %}"/></a>');$(".top_link").css({position:"fixed",right:"15px",bottom:"15px",display:"none",padding:"20px",background:"#ccc","-moz-border-radius":"40px","-webkit-border-radius":"40px","border-radius":"40px",opacity:"0.9","z-index":"2000"});$(window).scroll(function(){posScroll=$(document).scrollTop();if(posScroll>=400)$(".top_link").fadeIn(600);else $(".top_link").fadeOut(600)})})</script> \ No newline at end of file 12 <script type="text/javascript">$(document).ready(function(){$("body").prepend('<a href="#top" class="top_link" title="{% trans "back to top" %}"><img src="./tpl/img/{{ constant("THEME") }}/backtotop.png" alt={% trans "back to top" %}"/></a>');$(window).scroll(function(){posScroll=$(document).scrollTop();if(posScroll>=400)$(".top_link").fadeIn(600);else $(".top_link").fadeOut(600)})})</script> \ No newline at end of file
diff --git a/tpl/config.twig b/tpl/config.twig
index 5324cda7..c838c319 100644
--- a/tpl/config.twig
+++ b/tpl/config.twig
@@ -11,18 +11,21 @@
11 </ul> 11 </ul>
12{% endblock %} 12{% endblock %}
13{% block content %} 13{% block content %}
14 <h2>{% trans "Bookmarklet" %}</h2> 14 <h2>{% trans "Poching a link" %}</h2>
15 <p>{% trans "Thanks to the bookmarklet, you will be able to easily add a link to your poche." %} {% trans "Have a look to this documentation:" %} <a href="http://inthepoche.com/?pages/Documentation">inthepoche.com</a>.</p> 15 <p>You can poche a link by several methods: (<a href="http://www.inthepoche.com/?pages/Documentation" title="{% trans "read the documentation" %}">?</a>)</p>
16 <p>{% trans "Drag & drop this link to your bookmarks bar and have fun with poche." %}</p> 16 <ul>
17 <p class="txtcenter"><a ondragend="this.click();" style="cursor: move; border: 1px dashed grey; background: white; padding: 5px;" title="i am a bookmarklet, use me !" href="javascript:if(top['bookmarklet-url@inthepoche.com']){top['bookmarklet-url@inthepoche.com'];}else{(function(){var%20url%20=%20location.href%20||%20url;window.open('{{ poche_url }}?action=add&url='%20+%20btoa(url),'_self');})();void(0);}">{% trans "poche it!" %}</a></p> 17 <li>firefox: <a href="https://bitbucket.org/jogaulupeau/poche/downloads/poche.xpi" title="download the firefox extension">download the extension</a></li>
18 <li>chrome: <a href="https://bitbucket.org/jogaulupeau/poche/downloads/poche.crx" title="download the chrome extension">download the extension</a></li>
19 <li>android: <a href="https://bitbucket.org/jogaulupeau/poche/downloads/Poche.apk" title="download the application">download the application</a></li>
20 <li>bookmarklet: drag & drop this link to your bookmarks bar <a id="bookmarklet" ondragend="this.click();" title="i am a bookmarklet, use me !" href="javascript:if(top['bookmarklet-url@inthepoche.com']){top['bookmarklet-url@inthepoche.com'];}else{(function(){var%20url%20=%20location.href%20||%20url;window.open('{{ poche_url }}?action=add&url='%20+%20btoa(url),'_self');})();void(0);}">{% trans "poche it!" %}</a></li>
21 </ul>
18 22
19 <h2>{% trans "Updating poche" %}</h2> 23 <h2>{% trans "Updating poche" %}</h2>
20 <p><ul> 24 <ul>
21 <li>{% trans "your version" %} : <strong>{{ constant('POCHE_VERSION') }}</strong></li> 25 <li>{% trans "your version" %} : <strong>{{ constant('POCHE_VERSION') }}</strong></li>
22 <li>{% trans "latest stable version" %} : {{ prod }}. {% if compare_prod == -1 %}<strong><a href="http://inthepoche.com/?pages/T%C3%A9l%C3%A9charger-poche">{% trans "a more recent stable version is available." %}</a></strong>{% else %}{% trans "you are up to date." %}{% endif %}</li> 26 <li>{% trans "latest stable version" %} : {{ prod }}. {% if compare_prod == -1 %}<strong><a href="http://inthepoche.com/?pages/T%C3%A9l%C3%A9charger-poche">{% trans "a more recent stable version is available." %}</a></strong>{% else %}{% trans "you are up to date." %}{% endif %}</li>
23 {% if constant('DEBUG_POCHE') == 1 %}<li>{% trans "latest dev version" %} : {{ dev }}. {% if compare_dev == -1 %}<strong><a href="http://inthepoche.com/?pages/T%C3%A9l%C3%A9charger-poche">{% trans "a more recent development version is available." %}</a></strong>{% else %}{% trans "you are up to date." %}{% endif %}</li>{% endif %} 27 {% if constant('DEBUG_POCHE') == 1 %}<li>{% trans "latest dev version" %} : {{ dev }}. {% if compare_dev == -1 %}<strong><a href="http://inthepoche.com/?pages/T%C3%A9l%C3%A9charger-poche">{% trans "a more recent development version is available." %}</a></strong>{% else %}{% trans "you are up to date." %}{% endif %}</li>{% endif %}
24 </ul> 28 </ul>
25 </p>
26 29
27 <h2>{% trans "Change your password" %}</h2> 30 <h2>{% trans "Change your password" %}</h2>
28 <form method="post" action="?config" name="loginform"> 31 <form method="post" action="?config" name="loginform">
@@ -46,11 +49,11 @@
46 <h2>{% trans "Import" %}</h2> 49 <h2>{% trans "Import" %}</h2>
47 <p>{% trans "Please execute the import script locally, it can take a very long time." %}</p> 50 <p>{% trans "Please execute the import script locally, it can take a very long time." %}</p>
48 <p>{% trans "More infos in the official doc:" %} <a href="http://inthepoche.com/?pages/Documentation">inthepoche.com</a></p> 51 <p>{% trans "More infos in the official doc:" %} <a href="http://inthepoche.com/?pages/Documentation">inthepoche.com</a></p>
49 <p><ul> 52 <ul>
50 <li><a href="./?import&from=pocket">{% trans "import from Pocket" %}</a> (you must have a "ril_export.html" file on your server)</li> 53 <li><a href="./?import&from=pocket">{% trans "import from Pocket" %}</a> (you must have a "{{ constant('IMPORT_POCKET_FILE')}}" file on your server)</li>
51 <li><a href="./?import&from=readability">{% trans "import from Readability" %}</a> (you must have a "readability" file on your server)</li> 54 <li><a href="./?import&from=readability">{% trans "import from Readability" %}</a> (you must have a "{{constant('IMPORT_READABILITY_FILE')}}" file on your server)</li>
52 <li><a href="./?import&from=instapaper">{% trans "import from Instapaper" %}</a> (you must have a "instapaper-export.html" file on your server)</li> 55 <li><a href="./?import&from=instapaper">{% trans "import from Instapaper" %}</a> (you must have a "{{constant('IMPORT_INSTAPAPER_FILE')}}" file on your server)</li>
53 </ul></p> 56 </ul>
54 57
55 <h2>{% trans "Export your poche datas" %}</h2> 58 <h2>{% trans "Export your poche datas" %}</h2>
56 <p><a href="./?export" target="_blank">{% trans "Click here" %}</a> {% trans "to export your poche datas." %}</p> 59 <p><a href="./?export" target="_blank">{% trans "Click here" %}</a> {% trans "to export your poche datas." %}</p>
diff --git a/tpl/css/print.css b/tpl/css/print.css
new file mode 100644
index 00000000..a3b0a599
--- /dev/null
+++ b/tpl/css/print.css
@@ -0,0 +1,19 @@
1body > header,
2body > footer,
3a.top_link,
4div.tools,
5header div
6{
7 display: none !important;
8}
9
10article
11{
12 border: none !important;
13}
14
15div.vieworiginal a::after
16{
17 margin-left: 5px;
18 content: "("attr(href)")";
19}
diff --git a/tpl/css/style-light.css b/tpl/css/style-light.css
index 9ea7955a..e2788a1c 100644
--- a/tpl/css/style-light.css
+++ b/tpl/css/style-light.css
@@ -44,6 +44,10 @@ a.twitter span {
44 background: url('../img/light/twitter.png') no-repeat; 44 background: url('../img/light/twitter.png') no-repeat;
45} 45}
46 46
47a.shaarli span {
48 background: url('../img/light/shaarli.png') no-repeat;
49}
50
47a.email span { 51a.email span {
48 background: url('../img/light/envelop.png') no-repeat; 52 background: url('../img/light/envelop.png') no-repeat;
49} 53}
diff --git a/tpl/css/style.css b/tpl/css/style.css
index e2a512f7..7633534c 100644
--- a/tpl/css/style.css
+++ b/tpl/css/style.css
@@ -1,7 +1,8 @@
1body { 1body {
2 font-size: 16px; 2 font-size: 16px;
3 font-family: 'Roboto', sans-serif; 3 font-family: 'Roboto', Verdana, Geneva, sans-serif;
4 margin: 10px; 4 margin: 10px;
5 color: #000;
5} 6}
6 7
7header { 8header {
@@ -12,52 +13,67 @@ header h1 {
12 font-size: 1.3em; 13 font-size: 1.3em;
13} 14}
14 15
16a, a:hover, a:visited {
17 color: #000;
18}
19
15.bouton { 20.bouton {
16 border-radius: 2px; 21 background-color: #000;
22 color: #fff;
23 border: none;
24 border-radius: 2px;
25}
26.bouton:hover {
27 background-color: #222;
28 color: #f1f1f1;
29 cursor: pointer;
17} 30}
18 31
19#main { 32#main {
20 margin: 0 auto; 33 margin: 0 auto;
21} 34}
22 35
23#main ul#links { 36#main #links {
24 padding: 0; 37 padding: 0;
25 list-style-type: none; 38 list-style-type: none;
26 text-align: center; 39 text-align: center;
27 font-size: 0.9em; 40 font-size: 0.9em;
28} 41}
29 42
30#main ul#links li { 43#main #links li {
31 display: inline; 44 display: inline;
32} 45}
33 46
34#main ul#links li a.current { 47#main #links li .current {
48 background-color: #000;
49 color: #fff;
35 -webkit-border-radius: 2px; 50 -webkit-border-radius: 2px;
36 border-radius: 2px; 51 border-radius: 2px;
37} 52}
38 53
39#main ul#sort { 54#main #sort {
40 padding: 0; 55 padding: 0;
41 list-style-type: none; 56 list-style-type: none;
42 text-align: center; 57 text-align: center;
43 opacity: 0.5; 58 opacity: 0.5;
44} 59}
45 60
46#main ul#sort li { 61#main #sort li {
47 display: inline; 62 display: inline;
48 font-size: 0.9em; 63 font-size: 0.9em;
49} 64}
50 65
51#main ul#sort img:hover { 66#main #sort img:hover {
52 cursor: pointer; 67 cursor: pointer;
53} 68}
54 69
55 70#links a {
56#links a{
57 text-decoration: none; 71 text-decoration: none;
58 padding: 5px 10px; 72 padding: 5px 10px;
59} 73}
60#links a:hover{ 74#links a:hover {
75 background-color: #040707;
76 color: #F1F1F1;
61 -webkit-border-radius: 2px; 77 -webkit-border-radius: 2px;
62 border-radius: 2px; 78 border-radius: 2px;
63} 79}
@@ -65,7 +81,7 @@ header h1 {
65/*** ***/ 81/*** ***/
66/*** LINKS DISPLAY ***/ 82/*** LINKS DISPLAY ***/
67 83
68#main a.tool { 84#main .tool {
69 text-decoration: none; 85 text-decoration: none;
70 cursor: pointer; 86 cursor: pointer;
71} 87}
@@ -80,17 +96,23 @@ header h1 {
80} 96}
81 97
82#main #content .entrie { 98#main #content .entrie {
83 border-bottom: 1px dashed #222222; 99 border-bottom: 1px dashed #222;
84} 100}
85 101
86#main .entrie ul.tools { 102#main .entrie .tools {
87 list-style-type: none; 103 list-style-type: none;
88} 104}
89 105
90#main .entrie ul.tools li { 106#main .entrie .tools + p {
91 /*display: inline;*/ 107 min-height: 5.5em;
92} 108}
93 109
110/*
111#main .entrie .tools li {
112 display: inline;
113}
114*/
115
94.tools { 116.tools {
95 float: right; 117 float: right;
96 text-align: right; 118 text-align: right;
@@ -98,7 +120,8 @@ header h1 {
98} 120}
99 121
100.tools p { 122.tools p {
101 font-size: 0.8em;} 123 font-size: 0.8em;
124}
102 125
103/* 126/*
104.tools ul { 127.tools ul {
@@ -110,22 +133,32 @@ header h1 {
110 line-height: 20px; 133 line-height: 20px;
111} 134}
112 135
113.tools a.tool { 136.tools .tool {
114 cursor: pointer; 137 cursor: pointer;
115}*/ 138}*/
116 139
117#main .entrie .tools a.tool span, #article .tools a.tool span { 140#main .entrie .tools .tool span, #article .tools .tool span {
118 display: inline-block; 141 display: inline-block;
119 width: 16px; 142 width: 16px;
120 height: 16px; 143 height: 16px;
144 /* Hide textual content */
145 text-indent: -9999px;
146 text-align: left;
147 overflow: hidden;
121} 148}
122 149
150
123/*** ***/ 151/*** ***/
124/*** ARTICLE PAGE ***/ 152/*** ARTICLE PAGE ***/
125 153
126#article { 154#article {
127 margin: 0 auto; 155 margin: 0 auto;
128} 156}
157
158#article header, #article article {
159 border-bottom: 1px solid #222;
160}
161
129#article header { 162#article header {
130 text-align: left; 163 text-align: left;
131} 164}
@@ -136,7 +169,7 @@ header h1 {
136 169
137.vieworiginal a, .vieworiginal a:hover, .vieworiginal a:visited { 170.vieworiginal a, .vieworiginal a:hover, .vieworiginal a:visited {
138 text-decoration: none; 171 text-decoration: none;
139 color: #888888; 172 color: #888;
140} 173}
141 174
142.backhome { 175.backhome {
@@ -146,54 +179,16 @@ header h1 {
146#article .tools { 179#article .tools {
147 position: relative; 180 position: relative;
148 display: inline; 181 display: inline;
149 top: 0px; 182 top: 0;
150 right: 0px; 183 right: 0;
151 width: 100%; 184 width: 100%;
152} 185}
153 186
154#article .tools ul li{ 187#article .tools ul li {
155 display: inline; 188 display: inline;
156} 189}
157 190
158 191
159/*** GENERAL ***/
160body {
161 color: #000;
162}
163
164a, a:hover, a:visited {
165 color: #000;
166}
167
168.bouton {
169 background-color: #000;
170 color: #fff;
171 border: none;
172}
173.bouton:hover {
174 background-color: #222222;
175 color: #F1F1F1;
176}
177
178#main ul#links li a.current {
179 background-color: #000;
180 color: #fff;
181}
182
183#links a:hover{
184 background-color: #040707;
185 color: #F1F1F1;
186}
187
188
189/*** ***/
190/*** ARTICLE PAGE ***/
191
192#article header, #article article {
193 border-bottom: 1px solid #222222;
194}
195
196
197/* Pagination */ 192/* Pagination */
198.pagination { 193.pagination {
199 clear: both; 194 clear: both;
@@ -202,42 +197,63 @@ a, a:hover, a:visited {
202 text-align: right; 197 text-align: right;
203} 198}
204.pagination a { 199.pagination a {
205 border: 1px solid #D5D5D5; 200 border: 1px solid #d5d5d5;
206 color: #333; 201 color: #333;
207 font-size: 11px; 202 font-size: 11px;
208 font-weight: bold; 203 font-weight: bold;
209 height: 25px; 204 height: 25px;
210 padding: 4px 8px; 205 padding: 4px 8px;
211 text-decoration: none; 206 text-decoration: none;
212 margin:2px; 207 margin: 2px;
213} 208}
214.pagination a:hover, .pagination a:active { 209.pagination a:hover, .pagination a:active {
215 background:#efefef; 210 background-color: #efefef;
216} 211}
217.pagination span.current { 212.pagination .current {
218 background-color: #ccc; 213 background-color: #ccc;
219 border: 1px solid #D5D5D5; 214 border: 1px solid #d5d5d5;
220 color: #000; 215 color: #000;
221 font-size: 11px; 216 font-size: 11px;
222 font-weight: bold; 217 font-weight: bold;
223 height: 25px; 218 height: 25px;
224 padding: 4px 8px; 219 padding: 4px 8px;
225 text-decoration: none; 220 text-decoration: none;
226 margin:2px; 221 margin: 2px;
227} 222}
228.pagination span.disabled { 223.pagination .disabled {
229 border: 1px solid #EEEEEE; 224 border: 1px solid #eee;
230 color: #DDDDDD; 225 color: #ddd;
231 margin:2px; 226 margin: 2px;
232 padding: 4px 8px; 227 padding: 4px 8px;
233 font-size: 11px; 228 font-size: 11px;
234 font-weight: bold; 229 font-weight: bold;
235} 230}
236 231
232#bookmarklet {
233 padding: 5px;
234 border: 1px dashed #808080;
235 background: #fff;
236 cursor: move;
237}
238
239.top_link {
240 position: fixed;
241 right: 15px;
242 bottom: 15px;
243 display: none;
244 padding: 20px;
245 background: #ccc;
246 -moz-border-radius: 40px;
247 -webkit-border-radius: 40px;
248 border-radius: 40px;
249 opacity: 0.9;
250 z-index: 2000;
251}
252
237footer { 253footer {
238 clear: both; 254 clear: both;
239} 255}
240 256
241.reading-time { 257.reading-time {
242 font-size: 0.8em; 258 font-size: 0.8em;
243} \ No newline at end of file 259}
diff --git a/tpl/error.twig b/tpl/error.twig
index 84c3bc1c..14c49925 100644
--- a/tpl/error.twig
+++ b/tpl/error.twig
@@ -1,7 +1,6 @@
1{% extends "layout.twig" %} 1{% extends "layout.twig" %}
2{% block title %}{% trans "error" %}{% endblock %} 2{% block title %}{% trans "plop" %}{% endblock %}
3{% block content %} 3{% block content %}
4 <h1>error</h1> 4 {{ msg|raw }}
5 <p>{{ msg|raw }}</p>
6 <p>Don't forget <a href="http://inthepoche.com/?pages/Documentation">the documentation</a>.</p> 5 <p>Don't forget <a href="http://inthepoche.com/?pages/Documentation">the documentation</a>.</p>
7{% endblock %} \ No newline at end of file 6{% endblock %} \ No newline at end of file
diff --git a/tpl/home.twig b/tpl/home.twig
index 03f9f70d..a2fa9a93 100644
--- a/tpl/home.twig
+++ b/tpl/home.twig
@@ -5,22 +5,20 @@
5{% endblock %} 5{% endblock %}
6{% block precontent %} 6{% block precontent %}
7 <ul id="sort"> 7 <ul id="sort">
8 <li><a href="./?sort=ia&view={{ view }}"><img src="./tpl/img/{{ constant('THEME') }}/top.png" alt="{% trans "by date asc" %}" title="{% trans "by date asc" %}" /></a> {% trans "by date" %} <a href="./?sort=id&view={{ view }}"><img src="./tpl/img/{{ constant('THEME') }}/down.png" alt="{% trans "by date desc" %}" title="{% trans "by date desc" %}" /></a></li> 8 <li><a href="./?sort=ia&amp;view={{ view }}"><img src="./tpl/img/{{ constant('THEME') }}/top.png" alt="{% trans "by date asc" %}" title="{% trans "by date asc" %}" /></a> {% trans "by date" %} <a href="./?sort=id&amp;view={{ view }}"><img src="./tpl/img/{{ constant('THEME') }}/down.png" alt="{% trans "by date desc" %}" title="{% trans "by date desc" %}" /></a></li>
9 <li><a href="./?sort=ta&view={{ view }}"><img src="./tpl/img/{{ constant('THEME') }}/top.png" alt="{% trans "by title asc" %}" title="{% trans "by title asc" %}" /></a> {% trans "by title" %} <a href="./?sort=td&view={{ view }}"><img src="./tpl/img/{{ constant('THEME') }}/down.png" alt="{% trans "by title desc" %}" title="{% trans "by title desc" %}" /></a></li> 9 <li><a href="./?sort=ta&amp;view={{ view }}"><img src="./tpl/img/{{ constant('THEME') }}/top.png" alt="{% trans "by title asc" %}" title="{% trans "by title asc" %}" /></a> {% trans "by title" %} <a href="./?sort=td&amp;view={{ view }}"><img src="./tpl/img/{{ constant('THEME') }}/down.png" alt="{% trans "by title desc" %}" title="{% trans "by title desc" %}" /></a></li>
10 </ul> 10 </ul>
11{% endblock %} 11{% endblock %}
12{% block content %} 12{% block content %}
13 {{ page_links | raw }} 13 {{ page_links | raw }}
14 {% for entry in entries %} 14 {% for entry in entries %}
15 <div id="entry-{{ entry.id|e }}" class="entrie"> 15 <div id="entry-{{ entry.id|e }}" class="entrie">
16 <h2><a href="index.php?view=view&id={{ entry.id|e }}">{{ entry.title|e }}</a></h2> 16 <h2><a href="index.php?view=view&amp;id={{ entry.id|e }}">{{ entry.title|raw }}</a></h2>
17 <ul class="tools"> 17 <ul class="tools">
18 <li> 18 <li><a title="{% trans "toggle mark as read" %}" class="tool archive {% if entry.is_read == 0 %}archive-off{% endif %}" href="./?action=toggle_archive&amp;id={{ entry.id|e }}"><span>{% trans "toggle mark as read" %}</span></a></li>
19 <a title="{% trans "toggle mark as read" %}" class="tool archive {% if entry.is_read == 0 %}archive-off{% endif %}" href="./?action=toggle_archive&id={{ entry.id|e }}"><span></span></a></li> 19 <li><a title="{% trans "toggle favorite" %}" class="tool fav {% if entry.is_fav == 0 %}fav-off{% endif %}" href="./?action=toggle_fav&amp;id={{ entry.id|e }}"><span>{% trans "toggle favorite" %}</span></a></li>
20 <li><a title="{% trans "toggle favorite" %}" class="tool fav {% if entry.is_fav == 0 %}fav-off{% endif %}" href="./?action=toggle_fav&id={{ entry.id|e }}"><span></span></a></li> 20 <li><a title="{% trans "delete" %}" class="tool delete" href="./?action=delete&amp;id={{ entry.id|e }}"><span>{% trans "delete" %}</span></a></li>
21 <li><a title="{% trans "delete" %}" class="tool delete" href="./?action=delete&id={{ entry.id|e }}"><span></span></a></li> 21 <li class="reading-time">{{ entry.content| getReadingTime }} min</li>
22 <li class="reading-time">{{ entry.content| getReadingTime }} min</li>
23 </li>
24 </ul> 22 </ul>
25 <p>{{ entry.content|striptags|slice(0, 300) }}...</p> 23 <p>{{ entry.content|striptags|slice(0, 300) }}...</p>
26 <p class="vieworiginal txtright small"><a href="{{ entry.url|e }}" target="_blank" title="{% trans "original" %} : {{ entry.title|e }}">{{ entry.url | e | getDomain }}</a></p> 24 <p class="vieworiginal txtright small"><a href="{{ entry.url|e }}" target="_blank" title="{% trans "original" %} : {{ entry.title|e }}">{{ entry.url | e | getDomain }}</a></p>
diff --git a/tpl/img/light/shaarli.png b/tpl/img/light/shaarli.png
new file mode 100644
index 00000000..1eb30f60
--- /dev/null
+++ b/tpl/img/light/shaarli.png
Binary files differ
diff --git a/tpl/layout.twig b/tpl/layout.twig
index b86983da..07ca231c 100644
--- a/tpl/layout.twig
+++ b/tpl/layout.twig
@@ -1,13 +1,15 @@
1<!DOCTYPE html> 1<!DOCTYPE html>
2<!--[if lte IE 6]> <html class="no-js ie6 ie67 ie678" lang="en"> <![endif]--> 2<!--[if lte IE 6]><html class="no-js ie6 ie67 ie678" lang="en"><![endif]-->
3<!--[if lte IE 7]> <html class="no-js ie7 ie67 ie678" lang="en"> <![endif]--> 3<!--[if lte IE 7]><html class="no-js ie7 ie67 ie678" lang="en"><![endif]-->
4<!--[if IE 8]> <html class="no-js ie8 ie678" lang="en"> <![endif]--> 4<!--[if IE 8]><html class="no-js ie8 ie678" lang="en"><![endif]-->
5<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]--> 5<!--[if gt IE 8]><html class="no-js" lang="en"><![endif]-->
6<html> 6<html>
7 <head> 7 <head>
8 <meta name="viewport" content="initial-scale=1.0"> 8 <meta name="viewport" content="initial-scale=1.0">
9 <meta charset="utf-8"> 9 <meta charset="utf-8">
10 <!--[if IE]>
10 <meta http-equiv="X-UA-Compatible" content="IE=10"> 11 <meta http-equiv="X-UA-Compatible" content="IE=10">
12 <![endif]-->
11 <title>{% block title %}{% endblock %} - poche</title> 13 <title>{% block title %}{% endblock %} - poche</title>
12{% include '_head.twig' %} 14{% include '_head.twig' %}
13{% include '_bookmarklet.twig' %} 15{% include '_bookmarklet.twig' %}
diff --git a/tpl/login.twig b/tpl/login.twig
index 0ae130bc..b24674e2 100644
--- a/tpl/login.twig
+++ b/tpl/login.twig
@@ -23,7 +23,7 @@
23 </div> 23 </div>
24 </div> 24 </div>
25 <div class="row mts txtcenter"> 25 <div class="row mts txtcenter">
26 <button class="bouton" type="submit" tabindex="4">{% trans "Login" %}</button> 26 <button class="bouton" type="submit" tabindex="4">{% trans "Sign in" %}</button>
27 </div> 27 </div>
28 </fieldset> 28 </fieldset>
29 <input type="hidden" name="returnurl" value="{{ referer }}"> 29 <input type="hidden" name="returnurl" value="{{ referer }}">
diff --git a/tpl/view.twig b/tpl/view.twig
index 2cbb9ca9..28508772 100644
--- a/tpl/view.twig
+++ b/tpl/view.twig
@@ -1,21 +1,20 @@
1{% extends "layout.twig" %} 1{% extends "layout.twig" %}
2{% block title %}{% trans "home" %}{% endblock %} 2{% block title %}{{ entry.title|raw }} ({{ entry.url | e | getDomain }}){% endblock %}
3{% block content %} 3{% block content %}
4 <div id="article"> 4 <div id="article">
5 <div class="tools"> 5 <div class="tools">
6 <ul class="tools"> 6 <ul class="tools">
7 <li> 7 <li><a href="./" title="{% trans "back to home" %}" class="tool back"><span>{% trans "back to home" %}</span></a></li>
8 <li><a href="./" title="{% trans "back to home" %}" class="tool back"><span></span></a></li> 8 <li><a title="{% trans "toggle mark as read" %}" class="tool archive {% if entry.is_read == 0 %}archive-off{% endif %}" href="./?action=toggle_archive&amp;id={{ entry.id|e }}"><span>{% trans "toggle mark as read" %}</span></a></li>
9 <a title="{% trans "toggle mark as read" %}" class="tool archive {% if entry.is_read == 0 %}archive-off{% endif %}" href="./?action=toggle_archive&id={{ entry.id|e }}"><span></span></a></li> 9 <li><a title="{% trans "toggle favorite" %}" class="tool fav {% if entry.is_fav == 0 %}fav-off{% endif %}" href="./?action=toggle_fav&amp;id={{ entry.id|e }}"><span>{% trans "toggle favorite" %}</span></a></li>
10 <li><a title="{% trans "toggle favorite" %}" class="tool fav {% if entry.is_fav == 0 %}fav-off{% endif %}" href="./?action=toggle_fav&id={{ entry.id|e }}"><span></span></a></li> 10 <li><a title="{% trans "delete" %}" class="tool delete" href="./?action=delete&amp;id={{ entry.id|e }}"><span>{% trans "delete" %}</span></a></li>
11 <li><a title="{% trans "delete" %}" class="tool delete" href="./?action=delete&id={{ entry.id|e }}"><span></span></a></li> 11 {% if constant('SHARE_TWITTER') == 1 %}<li><a href="https://twitter.com/home?status={{entry.title|url_encode}}%20{{ entry.url|url_encode }}%20via%20@getpoche" target="_blank" class="tool twitter" title="{% trans "tweet" %}"><span>{% trans "tweet" %}</span></a></li>{% endif %}
12 {% if constant('SHARE_TWITTER') == 1 %}<li><a href="https://twitter.com/home?status={{entry.title}}%20{{ entry.url|e }}%20via%20@getpoche" target="_blank" class="tool twitter" title="{% trans "tweet" %}"><span></span></a></li>{% endif %} 12 {% if constant('SHARE_MAIL') == 1 %}<li><a href="mailto:?subject={{ entry.title|url_encode }}&amp;body={{ entry.url|url_encode }}%20via%20@getpoche" class="tool email" title="{% trans "email" %}"><span>{% trans "email" %}</span></a></li>{% endif %}
13 {% if constant('SHARE_MAIL') == 1 %}<li><a href="mailto:?subject={{ entry.title|e }}&body={{ entry.url|e }} via @getpoche" class="tool email" title="{% trans "email" %}"><span></span></a></li>{% endif %} 13 {% if constant('SHARE_SHAARLI') == 1 %}<li><a href="{{ constant('SHAARLI_URL') }}/index.php?post={{ entry.url|url_encode }}&amp;title={{ entry.title|url_encode }}" target="_blank" class="tool shaarli" title="{% trans "shaarli" %}"><span>{% trans "shaarli" %}</span></a></li>{% endif %}
14 </li>
15 </ul> 14 </ul>
16 </div> 15 </div>
17 <header class="mbm"> 16 <header class="mbm">
18 <h1>{{ entry.title|e }}</h1> 17 <h1>{{ entry.title|raw }}</h1>
19 <div class="vieworiginal txtright small"><a href="{{ entry.url|e }}" target="_blank" title="{% trans "original" %} : {{ entry.title|e }}">{{ entry.url | e | getDomain }}</a></div> 18 <div class="vieworiginal txtright small"><a href="{{ entry.url|e }}" target="_blank" title="{% trans "original" %} : {{ entry.title|e }}">{{ entry.url | e | getDomain }}</a></div>
20 </header> 19 </header>
21 <article> 20 <article>
@@ -24,17 +23,16 @@
24 </article> 23 </article>
25 <div class="tools"> 24 <div class="tools">
26 <ul class="tools"> 25 <ul class="tools">
27 <li> 26 <li><a href="./?" title="{% trans "back to home" %}" class="tool back"><span>{% trans "back to home" %}</span></a></li>
28 <li><a href="./?" title="{% trans "back to home" %}" class="tool back"><span></span></a></li> 27 <li><a href="#top" title="{% trans "back to top" %}" class="tool top"><span>{% trans "back to top" %}</span></a></li>
29 <li><a href="#top" title="{% trans "back to top" %}" class="tool top"><span></span></a></li> 28 <li><a title="{% trans "toggle mark as read" %}" class="tool archive {% if entry.is_read == 0 %}archive-off{% endif %}" href="./?action=toggle_archive&amp;id={{ entry.id|e }}"><span>{% trans "toggle mark as read" %}</span></a></li>
30 <a title="{% trans "toggle mark as read" %}" class="tool archive {% if entry.is_read == 0 %}archive-off{% endif %}" href="./?action=toggle_archive&id={{ entry.id|e }}"><span></span></a></li> 29 <li><a title="{% trans "toggle favorite" %}" class="tool fav {% if entry.is_fav == 0 %}fav-off{% endif %}" href="./?action=toggle_fav&amp;id={{ entry.id|e }}"><span>{% trans "toggle favorite" %}</span></a></li>
31 <li><a title="{% trans "toggle favorite" %}" class="tool fav {% if entry.is_fav == 0 %}fav-off{% endif %}" href="./?action=toggle_fav&id={{ entry.id|e }}"><span></span></a></li> 30 <li><a title="{% trans "delete" %}" class="tool delete" href="./?action=delete&amp;id={{ entry.id|e }}"><span>{% trans "delete" %}</span></a></li>
32 <li><a title="{% trans "delete" %}" class="tool delete" href="./?action=delete&id={{ entry.id|e }}"><span></span></a></li> 31 {% if constant('SHARE_TWITTER') == 1 %}<li><a href="https://twitter.com/home?status={{entry.title|url_encode}}%20{{ entry.url|url_encode }}%20via%20@getpoche" target="_blank" class="tool twitter" title="{% trans "tweet" %}"><span>{% trans "tweet" %}</span></a></li>{% endif %}
33 {% if constant('SHARE_TWITTER') == 1 %}<li><a href="https://twitter.com/home?status={{entry.title}}%20{{ entry.url|e }}%20via%20@getpoche" target="_blank" class="tool twitter" title="{% trans "tweet" %}"><span></span></a></li>{% endif %} 32 {% if constant('SHARE_MAIL') == 1 %}<li><a href="mailto:?subject={{ entry.title|url_encode }}&amp;body={{ entry.url|url_encode }}%20via%20@getpoche" class="tool email" title="{% trans "email" %}"><span>{% trans "email" %}</span></a></li>{% endif %}
34 {% if constant('SHARE_MAIL') == 1 %}<li><a href="mailto:?subject={{ entry.title|e }}&body={{ entry.url|e }} via @getpoche" class="tool email" title="{% trans "email" %}"><span></span></a></li>{% endif %} 33 {% if constant('SHARE_SHAARLI') == 1 %}<li><a href="{{ constant('SHAARLI_URL') }}/index.php?post={{ entry.url|url_encode }}&amp;title={{ entry.title|url_encode }}" target="_blank" class="tool shaarli" title="{% trans "shaarli" %}"><span>{% trans "shaarli" %}</span></a></li>{% endif %}
35 </li>
36 </ul> 34 </ul>
37 <p>{% trans "this article appears wrong?" %} <a href="https://github.com/inthepoche/poche/issues/new">{% trans "create an issue" %}</a> {% trans "or" %} <a href="mailto:support@inthepoche.com?subject=Wrong display in poche&body={{ entry.url|e }}">{% trans "contact us by mail" %}</a></p> 35 <p>{% trans "this article appears wrong?" %} <a href="https://github.com/inthepoche/poche/issues/new">{% trans "create an issue" %}</a> {% trans "or" %} <a href="mailto:support@inthepoche.com?subject=Wrong%20display%20in%20poche&amp;body={{ entry.url|url_encode }}">{% trans "contact us by mail" %}</a></p>
38 </div> 36 </div>
39 </div> 37 </div>
40{% endblock %} \ No newline at end of file 38{% endblock %} \ No newline at end of file