root/trunk/gregarius/util.php

Revision 1762, 38.8 kB (checked in by cfriesen, 9 months ago)

Refresh interval in the feed properties.

  • Property svn:eol-style set to native
  • Property svn:eolstyle set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 <?php
2
3 ###############################################################################
4 # Gregarius - A PHP based RSS aggregator.
5 # Copyright (C) 2003 - 2006 Marco Bonetti
6 #
7 ###############################################################################
8 # This program is free software and open source software; you can redistribute
9 # it and/or modify it under the terms of the GNU General Public License as
10 # published by the Free Software Foundation; either version 2 of the License,
11 # or (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful, but WITHOUT
14 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
16 # more details.
17 #
18 # You should have received a copy of the GNU General Public License along
19 # with this program; if not, write to the Free Software Foundation, Inc.,
20 # 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  or visit
21 # http://www.gnu.org/licenses/gpl.html
22 #
23 ###############################################################################
24 # E-mail:      mbonetti at gmail dot com
25 # Web page:    http://gregarius.net/
26 #
27 ###############################################################################
28
29
30
31 function getLastModif() {
32     return getProperty('__meta__','meta.lastupdate');
33 }
34
35 function getETag() {
36     return md5(getLastModif().$_SERVER['PHP_SELF']);
37 }
38
39
40 function rss_error($message, $severity = RSS_ERROR_ERROR, $render = false) {
41     if ($render) {
42         echo "<p class=\"error\">$message</p>\n";
43         return;
44     }
45
46     if (!isset($GLOBALS['rss'])) {
47         rss_require('cls/rss.php');
48     }
49
50     $GLOBALS['rss'] -> error($message, $severity);
51 }
52
53 /** this functions checks whether a URI exists */
54 function getHttpResponseCode($forUri) {
55     return getUrl($forUri, 255);
56 }
57
58 function getContentType($link, & $contentType) {
59     $url_parts = @ parse_url($link);
60     if (empty ($url_parts["host"])) {
61         return (false);
62     }
63     if (!empty ($url_parts["path"])) {
64         $documentpath = $url_parts["path"];
65     } else {
66         $documentpath = "/";
67     }
68     if (!empty ($url_parts["query"])) {
69         $documentpath .= "?".$url_parts["query"];
70     }
71     $host = $url_parts["host"];
72     $port = (array_key_exists('port', $url_parts) ? $url_parts["port"] : "80");
73
74     $socket = @ fsockopen($host, $port, $errno, $errstr, 30);
75     if (!$socket) {
76         return (false);
77     }
78
79     $ret = false;
80     fwrite($socket, "GET ".$documentpath." HTTP/1.0\r\nHost: $host\r\n\r\n");
81     while (!feof($socket)) {
82         $line = fgets($socket, 100);
83         if (preg_match("/Content-Type: (.*)/i", $line, $matches)) {
84             $contentType = $matches[1];
85             $ret = true;
86             break;
87         }
88     }
89
90     return $ret;
91 }
92
93 // basically strips folder resources from URIs.
94 // http://pear.php.net/package/HTTP_Client/ --> http://pear.php.net/
95 function get_host($url, & $host) {
96     $ret = preg_match("/^(http:\/\/)?([^\/]+)/i", $url, $matches);
97     $host = $matches[2];
98
99     //ensure we have a slash
100     if (substr($host, -1) != "/") {
101         $host .= "/";
102     }
103
104     return $ret;
105 }
106
107 /**
108  * Builds a title out of an already encoded string.
109  */
110 function makeTitle($title) {
111     // Let us find out if the user has set a title.
112     $userTitle = _TITLE_;
113     if (getConfig('rss.output.title')) {
114         $userTitle = getConfig('rss.output.title');
115     }
116     $ret = "". $userTitle ."";
117     if ($title) {
118         if (is_array($title)) {
119             foreach($title as $token) {
120                 $ret .= " ".TITLE_SEP." ".$token;
121             }
122         } else {
123             $ret .= " ".TITLE_SEP." ".$title;
124         }
125     }
126     return $ret;
127 }
128
129 /*** update the given feed(s) **/
130 function update($id) {
131     $kses_allowed = getConfig('rss.input.allowed'); //getAllowedTags();
132     $updatedIds = array ();
133
134
135     $sql = "select id, url, title, mode from ".getTable("channels");
136     if ($id != "" && is_numeric($id)) {
137         $sql .= " where id=$id";
138         $sql .= " and not(mode & ".RSS_MODE_DELETED_STATE.") ";
139     } else {
140         $sql .= " where not(mode & ".RSS_MODE_DELETED_STATE.") ";
141     }
142
143     if (getConfig('rss.config.absoluteordering')) {
144         $sql .= " order by parent, position";
145     } else {
146         $sql .= " order by parent, title";
147     }
148
149     $res = rss_query($sql);
150     while (list ($cid, $url, $title, $mode) = rss_fetch_row($res)) {
151
152         // suppress warnings because Magpie is rather noisy
153         $old_level = error_reporting(E_ERROR);
154         $rss = fetch_rss($url);
155
156         //reset
157         error_reporting($old_level);
158
159         if (!$rss && $id != "" && is_numeric($id)) {
160             return array (magpie_error(), array ());
161         }
162         elseif (!$rss || !($rss->rss_origin & MAGPIE_FEED_ORIGIN_HTTP_200) ) {
163             continue; // no need to do anything if we do not get a 200 OK from the feed
164         }
165
166         // base URL for items in this feed.
167         if (array_key_exists('link', $rss->channel)) {
168             $baseUrl = $rss->channel['link'];
169         } else {
170             $baseUrl = $url; // The feed is invalid
171         }
172
173         // Keep track of guids we've handled, because some feeds (hello,
174         // Technorati!) have this insane habit of serving the same item
175         // twice in the same feed.
176         $guids = array();
177
178         // Allow updates in this feed?
179         $allowUpdates = getProperty($cid,'rss.input.allowupdates');
180         if ($allowUpdates === null) {
181             $allowUpdates = getConfig('rss.input.allowupdates');
182         }
183
184         $itemIdsInFeed = array(); // This variable will store the item id's of the elements in the feed
185         foreach ($rss->items as $item) {
186
187             $item = rss_plugin_hook('rss.plugins.rssitem', $item);
188             // a plugin might delete this item
189             if(!isset($item))
190                 continue;
191
192             // item title: strip out html tags
193             $title = array_key_exists('title', $item) ? strip_tags($item['title']) : "";
194             //$title = str_replace('& ', '&amp; ', $title);
195
196
197             $description = "";
198             // item content, if any
199             if (array_key_exists('content', $item) && is_array($item['content']) && array_key_exists('encoded', $item['content'])) {
200                 $description = $item['content']['encoded'];
201             }
202             elseif (array_key_exists('description', $item)) {
203                 $description = $item['description'];
204             }
205             elseif (array_key_exists('atom_content', $item)) {
206                 $description = $item['atom_content'];
207             }
208             elseif (array_key_exists('summary', $item)) {
209                 $description = $item['summary'];
210             }
211             else {
212                 $description = "";
213             }
214
215             $md5sum = "";
216             $guid = "";
217
218             if(array_key_exists('guid', $item) && $item['guid'] != "") {
219                 $guid = $item['guid'];
220             }
221             elseif(array_key_exists('id', $item) && $item['id'] != "") {
222                 $guid = $item['id'];
223             }
224             $guid = trim($guid);
225             $guid = rss_real_escape_string($guid);
226
227             // skip this one if it's an  in-feed-dupe
228             if ($guid && isset($guids[$guid])) {
229                 continue;
230             }
231             elseif($guid) {
232                 $guids[$guid] = true;
233             }
234
235             if ($description != "") {
236                 $md5sum = md5($description);
237                 $description = kses($description, $kses_allowed); // strip out tags
238
239                 if ($baseUrl != "") {
240                     $description = relative_to_absolute($description, $baseUrl);
241                 }
242             }
243
244             // Now let plugins modify the description
245             $description = rss_plugin_hook('rss.plugins.import.description', $description);
246
247
248             // link
249             if (array_key_exists('link', $item) && $item['link'] != "") {
250                 $url = $item['link'];
251             }
252             elseif (array_key_exists('guid', $item) && $item['guid'] != "") {
253                 $url = $item['guid'];
254             }
255             elseif (array_key_exists('link_', $item) && $item['link_'] != "") {
256                 $url = $item['link_'];
257             }
258             else {
259                 // fall back to something basic
260                 $url = md5($title);
261             }
262
263             // make sure the url is properly escaped
264             $url = htmlentities($url, ENT_QUOTES );
265
266             $url = rss_real_escape_string($url);
267
268             // author
269             if (array_key_exists('dc', $item) && array_key_exists('creator', $item['dc'])) {
270                 // RSS 1.0
271                 $author = $item['dc']['creator'];
272             } else if (array_key_exists('author_name', $item)) {
273                 // Atom 0.3
274                 $author = $item['author_name'];
275             } else {
276                 $author = "";
277             }
278
279             $author = trim(strip_tags($author));
280
281             // pubdate
282             $cDate = -1;
283             if (array_key_exists('dc', $item) && array_key_exists('date', $item['dc'])) {
284                 // RSS 1.0
285                 $cDate = parse_w3cdtf($item['dc']['date']);
286             }
287             elseif (array_key_exists('pubdate', $item)) {
288                 // RSS 2.0 (?)
289                 // We use the second param of strtotime here as a workaround
290                 // of a PHP bug with strtotime. If the pubdate field doesn't
291                 // contain seconds, the strtotime function will use the current
292                 // time to fill in seconds in PHP4. This interferes with the
293                 // update mechanism of gregarius. See ticket #328 for the full
294                 // gory details. Giving a known date as a second param to
295                 // strtotime fixes this problem, hence the 0 here.
296                 $cDate = strtotime($item['pubdate'], 0);
297             }
298             elseif (array_key_exists('published',$item)) {
299                 // atom 1.0
300                 $cDate = parse_iso8601($item['published']);
301             }
302             elseif (array_key_exists('issued', $item)) {
303                 //Atom, alternative
304                 $cDate = parse_iso8601($item['issued']);
305             }
306             elseif (array_key_exists('updated', $item)) {
307                 //Atom, alternative
308                 $cDate = parse_iso8601($item['updated']);
309             }
310             elseif (array_key_exists('created', $item)) {
311                 // atom 0.3
312                 $cDate = parse_iso8601($item['created']);
313             }
314
315             // enclosure
316             if (array_key_exists('enclosure@url', $item) ) {
317                 $enclosure = $item['enclosure@url'];
318                 // If the enclosure is an image, append it to the content
319                 // but only if it isn't there yet
320                 if ($enclosure &&
321                     array_key_exists('enclosure@type', $item) &&
322                     preg_match('#image/(png|gif|jpe?g)#', $item['enclosure@type']) &&
323                     (FALSE == strpos($description,$enclosure))) {
324                         $description = '<img src="'.$enclosure.'" alt="" />' . $description;
325                         $enclosure = '';
326                 }
327             } else {
328                 $enclosure = "";
329             }
330
331             // drop items with an url exceeding our column length: we couldn't provide a
332             // valid link back anyway.
333             if (strlen($url) >= 255) {
334                 continue;
335             }
336
337             $dbtitle = rss_real_escape_string($title);
338             if (strlen($dbtitle) >= 255) {
339                 $dbtitle=substr($dbtitle,0,254);
340             }
341
342             if ($cDate > 0) {
343                 $sec = "FROM_UNIXTIME($cDate)";
344             } else {
345                 $sec = "null";
346             }
347
348             // check whether we already have this item
349             if ($guid) {
350                 $sql = "select id,unread, md5sum, guid, pubdate from ".getTable("item")
351                        ." where cid=$cid and guid='$guid'";
352             } else {
353                 $sql = "select id,unread, md5sum, guid, pubdate from ".getTable("item")
354                        ." where cid=$cid and url='$url' and title='$dbtitle'"
355                        ." and (pubdate is NULL OR pubdate=$sec)";
356             }
357
358             $subres = rss_query($sql);
359             list ($indb, $state, $dbmd5sum, $dbGuid, $dbPubDate) = rss_fetch_row($subres);
360
361             if ($indb) {
362                 $itemIdsInFeed[] = $indb;
363                 if (!($state & RSS_MODE_DELETED_STATE) && $md5sum != $dbmd5sum) {
364                     // the md5sums do not match.
365                     if($allowUpdates) { // Are we allowed update items in the db?
366                         list ($cid, $indb, $description) =
367                             rss_plugin_hook('rss.plugins.items.updated', array ($cid, $indb, $description));
368
369                         $sql = "update ".getTable("item")
370                                ." set "." description='".rss_real_escape_string($description)."', "
371                                ." unread = unread | ".RSS_MODE_UNREAD_STATE
372                                .", md5sum='$md5sum'" . " where cid=$cid and id=$indb";
373
374                         rss_query($sql);
375                         $updatedIds[] = $indb;
376                         continue;
377                     }
378                 }
379             } else { // $indb = "" . This must be new item then. In you go.
380
381                 list ($cid, $dbtitle, $url, $description) =
382                     rss_plugin_hook('rss.plugins.items.new', array ($cid, $dbtitle, $url, $description));
383
384                 $sql = "insert into ".getTable("item")
385                        ." (cid, added, title, url, enclosure,"
386                        ." description, author, unread, pubdate, md5sum, guid) "
387                        ." values ("."$cid, now(), '$dbtitle', "
388                        ." '$url', '".rss_real_escape_string($enclosure)."', '"
389                        .rss_real_escape_string($description)."', '"
390                        .rss_real_escape_string($author)."', "
391                        ."$mode, $sec, '$md5sum', '$guid')";
392
393                 rss_query($sql);
394
395                 $newIid = rss_insert_id();
396                 $itemIdsInFeed[] = $newIid;
397                 $updatedIds[] = $newIid;
398                 rss_plugin_hook('rss.plugins.items.newiid',array($newIid,$item,$cid));
399             } // end handling of this item
400
401         } // end handling of all the items in this feed
402         $sql = "update " .getTable("channels") . " set "." itemsincache = '"
403                . serialize($itemIdsInFeed) . "' where id=$cid";
404         rss_query($sql);
405
406
407     } // end handling all the feeds we were asked to handle
408
409     if ($id != "" && is_numeric($id)) {
410         if ($rss) {
411             // when everything went well, return the error code
412             // and numer of new items
413             return array ($rss->rss_origin, $updatedIds);
414         } else {
415             return array (-1, array ());
416         }
417     } else {
418         return array (-1, $updatedIds);
419     }
420 }
421
422 function getRootFolder() {
423     $sql = "select id from ".getTable("folders")."where name = '' order by position asc limit 1";
424     list($root) = rss_fetch_row(rss_query($sql));
425
426     if (!$root) {
427         $root = 0;
428     }
429
430     return $root;
431 }
432
433 function add_channel($url, $folderid = 0, $title_=null,$descr_=null,$tags = null) {
434     if (!$url || strlen($url) <= 7) {
435         return array (-2, "Invalid URL $url");
436     }
437     if (!is_numeric($folderid)) {
438         return array (-2, "Invalid folderid $folderid");
439     }
440
441     $url = sanitize(str_replace('&amp;','&',$url), RSS_SANITIZER_URL);
442
443     $urlDB = rss_real_escape_string($url); //htmlentities($url);
444
445     $res = rss_query("select count(*) as channel_exists from ".getTable("channels")." where url='$urlDB'");
446     list ($channel_exists) = rss_fetch_row($res);
447     if ($channel_exists > 0) {
448         // fatal
449         return array (-2, "Looks like you are already subscribed to this channel");
450     }
451
452     $res = rss_query("select 1+max(position) as np from ".getTable("channels"));
453     list ($np) = rss_fetch_row($res);
454
455     if (!$np) {
456         $np = "0";
457     }
458             
459     // Here we go!
460     //error_reporting(E_ALL);
461     $old_level = error_reporting(E_ERROR);
462     $rss = fetch_rss($url);
463     error_reporting($old_level);
464
465     if ($rss) {
466         if ($title_) {
467             $title = rss_real_escape_string($title_);
468         }
469         elseif (is_object($rss) && array_key_exists('title#', $rss->channel)) {
470             if (array_key_exists('title', $rss->channel)) {
471                 $title = rss_real_escape_string($rss->channel['title']);
472             } else {
473                 $title = " ";
474             }
475         }
476         else {
477             $title = "";
478         }
479
480         if (is_object($rss) && array_key_exists('link', $rss->channel)) {
481             $siteurl = rss_real_escape_string(htmlentities($rss->channel['link']));
482         } else {
483             $siteurl = "";
484         }
485
486                 $refreshinterval = 0;
487                 if(is_object($rss) && array_key_exists('syn', $rss->channel)) {
488                         $syn = $rss->channel['syn'];
489
490                         if(array_key_exists('updateperiod', $syn)) {
491                      &