root/branches/multiuser/util.php

Revision 1606, 38.3 kB (checked in by mdodoo, 2 years ago)

This was really annoying, and took more time than the previous commit. For some reason, "svn diff" does not work correctly on my machine.

  • 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
31function getLastModif() {
32        return getProperty('__meta__','meta.lastupdate');
33}
34
35function getETag() {
36    return md5(getLastModif().$_SERVER['PHP_SELF']);
37}
38
39
40function 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 */
54function getHttpResponseCode($forUri) {
55    return getUrl($forUri, 255);
56}
57
58function 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/
95function 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 */
110function 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) **/
130function 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            } else {
319                $enclosure = "";
320            }
321
322            // drop items with an url exceeding our column length: we couldn't provide a
323            // valid link back anyway.
324            if (strlen($url) >= 255) {
325                continue;
326            }
327
328            $dbtitle = rss_real_escape_string($title);
329            if (strlen($dbtitle) >= 255) {
330                $dbtitle=substr($dbtitle,0,254);
331            }
332
333            if ($cDate > 0) {
334                $sec = "FROM_UNIXTIME($cDate)";
335            } else {
336                $sec = "null";
337            }
338
339            // check whether we already have this item
340            if ($guid) {
341                $sql = "select id, i2u.flgunread,i2u.flgdeleted, md5sum, guid, pubdate "
342                ."from "
343                .getTable('item2user') ." i2u "
344                ."left join "
345                .getTable('item') ." i "
346                ." on (i2u.fkiid=i.id) "
347                ." where cid=$cid and guid='$guid'";
348            } else {
349                                $sql = "select id, i2u.flgunread,i2u.flgdeleted, md5sum, guid, pubdate "
350                ."from "
351                .getTable('item2user') ." i2u "
352                ."left join "
353                .getTable('item') ." i "
354                ." on (i2u.fkiid=i.id) "
355                ." where cid=$cid and url='$url' and title='$dbtitle'"
356                ." and (pubdate is NULL OR pubdate=$sec)";
357            }
358
359            $subres = rss_query($sql);
360            list ($indb, $unread,$deleted, $dbmd5sum, $dbGuid, $dbPubDate) = rss_fetch_row($subres);
361
362            if ($indb) {
363                $itemIdsInFeed[] = $indb;
364                if (!$deleted && $md5sum != $dbmd5sum) {
365                    // the md5sums do not match.
366                    if($allowUpdates) { // Are we allowed update items in the db?
367                        list ($cid, $indb, $description) =
368                            rss_plugin_hook('rss.plugins.items.updated', array ($cid, $indb, $description));
369
370                        $sql = "update ".getTable("item")
371                               ." set "." description='".rss_real_escape_string($description)."', "
372                               //." unread = unread | ".RSS_MODE_UNREAD_STATE
373                               ." md5sum='$md5sum'" . " where cid=$cid and id=$indb";
374                                               
375                        rss_query($sql);
376                       
377                        rss_query("update " .getTable('item2user') . " i2u set i2u.flgunread=1 where i2u.fkiid=$indb");
378                        $updatedIds[] = $indb;
379                        continue;
380                    }
381                }
382            } else { // $indb = "" . This must be new item then. In you go.
383
384                list ($cid, $dbtitle, $url, $description) =
385                    rss_plugin_hook('rss.plugins.items.new', array ($cid, $dbtitle, $url, $description));
386
387                $sql = "insert into ".getTable("item")
388                       ." (cid, added, title, url, enclosure,"
389                       ." description, author, pubdate, md5sum, guid) "
390                       ." values ("."$cid, now(), '$dbtitle', "
391                       ." '$url', '".rss_real_escape_string($enclosure)."', '"
392                       .rss_real_escape_string($description)."', '"
393                       .rss_real_escape_string($author)."', "
394                       ." $sec, '$md5sum', '$guid')";
395
396                rss_query($sql);
397
398                $newIid = rss_insert_id();
399                $uid=$GLOBALS['rssuser']->getUserId();
400                $prv=($mode & RSS_MODE_PRIVATE_STATE) ?"1":"0";
401                rss_query(
402                        "insert into " .getTable('item2user')
403                        ."  (fkiid,fkuid,fkcid,flgunread,flgprivate) "
404                        ." values ($newIid, $uid, $cid, 1, $prv) "
405                );
406               
407                $itemIdsInFeed[] = $newIid;
408                $updatedIds[] = $newIid;
409                rss_plugin_hook('rss.plugins.items.newiid',array($newIid,$item,$cid));
410            } // end handling of this item
411
412        } // end handling of all the items in this feed
413        $sql = "update " .getTable("channels") . " set "." itemsincache = '"
414               . serialize($itemIdsInFeed) . "' where id=$cid";
415        rss_query($sql);
416
417
418    } // end handling all the feeds we were asked to handle
419
420    if ($id != "" && is_numeric($id)) {
421        if ($rss) {
422            // when everything went well, return the error code
423            // and numer of new items
424            return array ($rss->rss_origin, $updatedIds);
425        } else {
426            return array (-1, array ());
427        }
428    } else {
429        return array (-1, $updatedIds);
430    }
431}
432
433function getRootFolder() {
434    $sql = "select id from ".getTable("folders")."where name = '' order by position asc limit 1";
435    list($root) = rss_fetch_row(rss_query($sql));
436
437    if (!$root) {
438        $root = 0;
439    }
440
441    return $root;
442}
443
444function add_channel($url, $folderid = 0, $title_=null,$descr_=null,$tags = null) {
445    if (!$url || strlen($url) <= 7) {
446        return array (-2, "Invalid URL $url");
447    }
448    if (!is_numeric($folderid)) {
449        return array (-2, "Invalid folderid $folderid");
450    }
451
452    $url = sanitize(str_replace('&amp;','&',$url), RSS_SANITIZER_URL);
453
454    $urlDB = rss_real_escape_string($url); //htmlentities($url);
455
456    $res = rss_query("select count(*) as channel_exists from ".getTable("channels")." where url='$urlDB'");
457    list ($channel_exists) = rss_fetch_row($res);
458    if ($channel_exists > 0) {
459        // fatal
460        return array (-2, "Looks like you are already subscribed to this channel");
461    }
462
463    $res = rss_query("select 1+max(position) as np from ".getTable("channels"));
464    list ($np) = rss_fetch_row($res);
465
466    if (!$np) {
467        $np = "0";
468    }
469
470    // Here we go!
471    //error_reporting(E_ALL);
472    $old_level = error_reporting(E_ERROR);
473    $rss = fetch_rss($url);
474    error_reporting($old_level);
475
476
477    if ($rss) {
478        if ($title_) {
479            $title = rss_real_escape_string($title_);
480        }
481        elseif (is_object($rss) && array_key_exists('title#', $rss->channel)) {
482                if (array_key_exists('title', $rss->channel)) {
483                $title = rss_real_escape_string($rss->channel['title']);
484            } else {
485                $title = " ";
486            }
487        }
488        else {
489            $title = "";
490        }
491
492        if (is_object($rss) && array_key_exists('link', $rss->channel)) {
493            $siteurl = rss_real_escape_string(htmlentities($rss->channel['link']));
494        } else {
495            $siteurl = "";
496        }
497
498        if ($descr_) {
499            $descr = rss_real_escape_string($descr_);
500        }
501        elseif  (is_object($rss) && array_key_exists('description', $rss->channel)) {
502            $descr = rss_real_escape_string($rss->channel['description']);
503        }
504        else {
505            $descr = "";
506        }
507
508        //lets see if this server has a favicon
509        $icon = "";
510        if (getConfig('rss.output.showfavicons')) {
511            // if we got nothing so far, lets try to fall back to
512            // favicons
513            if ($icon == "" && $siteurl != "") {
514                $match = get_host($siteurl, $host);
515                $uri = "http://".$host."favicon.ico";
516                if ($match && getContentType($uri, $contentType)) {
517                    if (preg_match("/image\/x-icon/", $contentType)) {
518                        $icon = $uri;
519                    }
520                }
521            }
522        }
523
524        $private = preg_match('|(https?://)([^:]+:[^@]+@)(.+)$|',$url);
525
526        if ($title != "") {
527            $title = strip_tags($title);
528            $descr = strip_tags($descr);
529
530            // add channel to root folder by default
531            if(!$folderid) {
532                $folderid = getRootFolder();
533            }
534
535            list($title,$urlDB,$siteurl,$folderid,$descr,$icon) =
536                rss_plugin_hook('rss.plugins.feed.new',
537                                array ($title,$urlDB,$siteurl,$folderid,$descr,$icon));
538
539            $mode = RSS_MODE_UNREAD_STATE;
540            if ($private) {
541                $mode |= RSS_MODE_PRIVATE_STATE;
542            }
543           
544            $sql = "insert into ".getTable("channels")
545                   ." (title, url, siteurl, parent, descr, dateadded, icon, position, mode, daterefreshed)"
546                   ." values ('$title', '$urlDB', '$siteurl', $folderid, '$descr', now(), '$icon', $np, $mode, '0000-00-00 00:00:00')";
547
548            rss_query($sql);
549            $newid = rss_insert_id();
550
551            if ($icon && cacheFavicon($icon)) {
552                rss_query("update " . getTable("channels") . " set icon='blob:".$icon."'"
553                          ." where id=$newid");
554            }
555
556            if($tags != ""){
557                __exp__submitTag($newid,$tags,"'channel'");
558            }
559
560            return array ($newid, "");
561
562        } else {
563            // non-fatal, will look further
564            return array (-1, "I'm sorry, I couldn't extract a valid RSS feed from <a href=\"$url\">$url</a>.");
565        }
566    } else {
567        global $MAGPIE_ERROR;
568        $retError = "I'm sorry, I couldn't retrieve <a href=\"$url\">$url</a>.";
569        if ($MAGPIE_ERROR) {
570            $retError .= "\n<br />$MAGPIE_ERROR\n";
571        }
572        // non-fatal, will look further
573        return array (-1, $retError);
574    }
575}
576
577/**
578 * Replaces relative urls with absolute ones for anchors and images
579 * Credits: Julien Mudry
580 */
581function relative_to_absolute($content, $feed_url) {
582    preg_match('/(http|https|ftp):\/\//', $feed_url, $protocol);
583    $server_url = preg_replace("/(http|https|ftp|news):\/\//", "", $feed_url);
584    $server_url = preg_replace("/\/.*/", "", $server_url);
585
586    if ($server_url == '') {
587        return $content;
588    }
589
590    if (isset($protocol[0])) {
591        $new_content = preg_replace('/href="\//', 'href="'.$protocol[0].$server_url.'/', $content);
592        $new_content = preg_replace('/src="\//', 'src="'.$protocol[0].$server_url.'/', $new_content);
593    } else {
594        $new_content = $content;
595    }
596    return $new_content;
597}
598
599/**
600 * parse an ISO 8601 date, losely based on parse_w3cdtf from MagpieRSS
601 */
602function parse_iso8601($date_str) {
603# regex to match wc3dtf
604    $pat = "/(\d{4})-?(\d{2})-?(\d{2})T?(\d{2}):?(\d{2})(:?(\d{2}))?(?:([-+])(\d{2}):?(\d{2})|(Z))?/";
605
606    if (preg_match($pat, $date_str, $match)) {
607        list