OSDN Git Service

BugTrack/2176 showrss plugin: Use default template if error
[pukiwiki/pukiwiki.git] / plugin / showrss.inc.php
index 8040989..a42157f 100644 (file)
 <?php
-/////////////////////////////////////////////////
-// PukiWiki - Yet another WikiWikiWeb clone.
-//
-// $Id: showrss.inc.php,v 1.11 2003/12/03 12:30:02 arino Exp $
-//
-// modified by PANDA <panda@arino.jp>
+// PukiWiki - Yet another WikiWikiWeb clone
+// showrss.inc.php
+// Copyright:
+//     2002-2017 PukiWiki Development Team
+//     2002      PANDA <panda@arino.jp>
+//     (Original)hiro_do3ob@yahoo.co.jp
+// License: GPL, same as PukiWiki
 //
+// Show RSS (of remote site) plugin
+// NOTE:
+//    * This plugin needs 'PHP xml extension'
+//    * Cache data will be stored as CACHE_DIR/*.tmp
 
-/**
- *
- * showrss ¥×¥é¥°¥¤¥ó
- * 
- * ¥é¥¤¥»¥ó¥¹¤Ï PukiWiki ËÜÂΤÈƱ¤¸¤¯ GNU General Public License (GPL) ¤Ç¤¹¡£
- * http://www.gnu.org/licenses/gpl.txt
- *
- * pukiwikiÍѤΥץ饰¥¤¥ó¤Ç¤¹¡£
- * pukiwiki1.3.2°Ê¾å¤ÇÆ°¤¯¤È»×¤¤¤Þ¤¹¡£
- * 
- * º£¤Î¤È¤³¤íÆ°ºî¤µ¤»¤ë¤¿¤á¤Ë¤ÏPHP ¤Î xml extension ¤¬É¬¿Ü¤Ç¤¹¡£PHP¤ËÁȤ߹þ¤Þ¤ì¤Æ¤Ê¤¤¾ì¹ç¤Ï¤½¤Ã¤±¤Ê¤¤¥¨¥é¡¼¤¬½Ð¤ë¤È»×¤¤¤Þ¤¹¡£
- * Àµµ¬É½¸½ or Ê¸»úÎó´Ø¿ô¤Ç¤Ê¤ó¤È¤«¤Ê¤é¤Ê¤¯¤â¤Ê¤µ¤²¤Ê¤ó¤Ç¤¹¤¬¼ûÍפäƤɤ줯¤é¤¤¤¢¤ë¤Î¤«¤ï¤«¤é¤¤¤Î¤ÇÊÝα¤Ç¤¹¡£
- * mbstring ¤â¤¢¤ë¤Û¤¦¤¬¤¤¤¤¤Ç¤¹¡£
- * 
- * ¤Ê¤¤¾ì¹ç¤Ï¡¢ jcode.phps ¤ò¤Á¤ç¤³¤Ã¤È¤¤¤¸¤Ã¤Æ mb_convert_encoding ¤È¤¤¤¦´Ø¿ô¤òÀë¸À¤·¤Æ¤ª¤±¤Ð¤È¤ê¤¢¤¨¤º¤½¤ì¤Ã¤Ý¤¯ÊÑ´¹¤Ç¤­¤ë¤«¤â¤Ç¤¹¡£
- * http://www.spencernetwork.org/
- * 
- * ¤´Ï¢ÍíÀè:
- * do3ob wiki   ->   http://do3ob.com/
- * email        ->   hiro_do3ob@yahoo.co.jp
- * 
- * ÈòÆñ½ê       ->   http://do3ob.s20.xrea.com/
- *
- * version: Id:showrss.inc.php,v 1.40 2003/03/18 11:52:58 hiro Exp
- * 
- */
+define('PLUGIN_SHOWRSS_USAGE', '#showrss(URI-to-RSS[,default|menubar|recent[,Cache-lifetime[,Show-timestamp]]])');
 
-// showrss¥×¥é¥°¥¤¥ó¤¬»ÈÍѲÄǽ¤«¤É¤¦¤«¤òɽ¼¨
+// Show related extensions are found or not
 function plugin_showrss_action()
 {
-       $xml_extension = extension_loaded('xml');
-       $mbstring_extension = extension_loaded('mbstring');
-
-       $xml_msg      = $xml_extension      ? 'xml extension is loaded' : 'COLOR(RED){xml extension is not loaded}';
-       $mbstring_msg = $mbstring_extension ? 'mbstring extension is loaded' : 'COLOR(RED){mbstring extension is not loaded}';
-
-       $showrss_info = '';
-       $showrss_info .= "| xml parser | $xml_msg |\n";
-       $showrss_info .= "| multibyte | $mbstring_msg |\n";
+       if (PKWK_SAFE_MODE) die_message('PKWK_SAFE_MODE prohibit this');
 
-       return array('msg' => 'showrss_info', 'body' => convert_html($showrss_info));
+       $body = '';
+       foreach(array('xml', 'mbstring') as $extension){
+               $$extension = extension_loaded($extension) ?
+                       '&color(green){Found};' :
+                       '&color(red){Not found};';
+               $body .= '| ' . $extension . ' extension | ' . $$extension . ' |' . "\n";
+       }
+       return array('msg' => 'showrss_info', 'body' => convert_html($body));
 }
 
 function plugin_showrss_convert()
 {
-       if (func_num_args() == 0)
-       {
-               // °ú¿ô¤¬¤Ê¤¤¾ì¹ç¤Ï¥¨¥é¡¼
-               return "<p>showrss: no parameter(s).</p>\n";
-       }
-       if (!extension_loaded('xml'))
-       {
-               // xml ³ÈÄ¥µ¡Ç½¤¬Í­¸ú¤Ç¤Ê¤¤¾ì¹ç¡£
-               return "<p>showrss: xml extension is not loaded</p>\n";
-       }
+       static $_xml;
 
-       $array = func_get_args();
-       $rssurl = $tmplname = $usecache = $usetimestamp = '';
+       if (! isset($_xml)) $_xml = extension_loaded('xml');
+       if (! $_xml) return '#showrss: xml extension is not found<br />' . "\n";
 
-       switch (func_num_args())
-       {
-               case 4:
-                       $usetimestamp = trim($array[3]);
-               case 3:
-                       $usecache = $array[2];
-               case 2:
-                       $tmplname = strtolower(trim($array[1]));
-               case 1:
-                       $rssurl = trim($array[0]);
-       }
+       $num = func_num_args();
+       if ($num == 0) return PLUGIN_SHOWRSS_USAGE . '<br />' . "\n";
 
-       // RSS ¥Ñ¥¹¤ÎÃÍ¥Á¥§¥Ã¥¯
-       if (!is_url($rssurl))
-       {
-               return '<p>showrss: syntax error. '.htmlspecialchars($rssurl)."</p>\n";
+       $argv = func_get_args();
+       $timestamp = FALSE;
+       $cachehour = 0;
+       $template = $uri = '';
+       switch ($num) {
+       case 4: $timestamp = (trim($argv[3]) == '1');   /*FALLTHROUGH*/
+       case 3: $cachehour = trim($argv[2]);            /*FALLTHROUGH*/
+       case 2: $template  = strtolower(trim($argv[1]));/*FALLTHROUGH*/
+       case 1: $uri       = trim($argv[0]);
        }
 
-       $class = "ShowRSS_html_$tmplname";
-       if (!class_exists($class))
-       {
-               $class = 'ShowRSS_html';
-       }
+       $class = ($template == '' || $template == 'default') ? 'ShowRSS_html' : 'ShowRSS_html_' . $template;
+       if (! class_exists($class)) $class = 'ShowRSS_html';
 
-       list($rss,$time) = plugin_showrss_get_rss($rssurl,$usecache);
-       if ($rss === FALSE)
-       {
-               return "<p>showrss: cannot get rss from server.</p>\n";
+       if (! is_numeric($cachehour))
+               return '#showrss: Cache-lifetime seems not numeric: ' . htmlsc($cachehour) . '<br />' . "\n";
+       if (! is_url($uri))
+               return '#showrss: Seems not URI: ' . htmlsc($uri) . '<br />' . "\n";
+
+       // Remove old caches in 5% rate
+       if (mt_rand(1, 20) === 1) {
+               plugin_showrss_cache_expire(24);
+       }
+       list($rss, $time) = plugin_showrss_get_rss($uri, $cachehour);
+       if ($rss === FALSE) return '#showrss: Failed fetching RSS from the server<br />' . "\n";
+       if (! is_array($rss)) {
+               // Show XML error message
+               return '#showrss: Error - ' . htmlsc($rss) . '<br />' . "\n";
+       }
+       $time_display = '';
+       if ($timestamp > 0) {
+               $time_display = '<p style="font-size:10px; font-weight:bold">Last-Modified:' .
+                       get_date('Y/m/d H:i:s', $time) .  '</p>';
        }
 
        $obj = new $class($rss);
-
-       $timestamp = '';
-       if ($usetimestamp > 0)
-       {
-               $time = get_date('Y/m/d H:i:s',$time);
-               $timestamp = "<p style=\"font-size:10px; font-weight:bold\">Last-Modified:$time</p>";
-       }
-       return $obj->toString($timestamp);
+       return $obj->toString($time_display);
 }
-// rssÇÛÎ󤫤éhtml¤òºî¤ë
+
+// Create HTML from RSS array()
 class ShowRSS_html
 {
        var $items = array();
@@ -114,37 +86,42 @@ class ShowRSS_html
 
        function ShowRSS_html($rss)
        {
-               foreach ($rss as $date=>$items)
-               {
-                       foreach ($items as $item)
-                       {
-                               $link = $item['LINK'];
+               $this->__construct($rss);
+       }
+       function __construct($rss)
+       {
+               foreach ($rss as $date=>$items) {
+                       foreach ($items as $item) {
+                               $link  = $item['LINK'];
                                $title = $item['TITLE'];
                                $passage = get_passage($item['_TIMESTAMP']);
-                               $link = "<a href=\"$link\" title=\"$title $passage\">$title</a>";
+                               $link = '<a href="' . $link . '" title="' .  $title . ' ' .
+                                       $passage . '" rel="nofollow">' . $title . '</a>';
                                $this->items[$date][] = $this->format_link($link);
                        }
                }
        }
+
        function format_link($link)
        {
-               return "$link<br />\n";
+               return $link . '<br />' . "\n";
        }
-       function format_list($date,$str)
+
+       function format_list($date, $str)
        {
                return $str;
        }
+
        function format_body($str)
        {
                return $str;
        }
+
        function toString($timestamp)
        {
                $retval = '';
                foreach ($this->items as $date=>$items)
-               {
-                       $retval .= $this->format_list($date,join('',$items));
-               }
+                       $retval .= $this->format_list($date, join('', $items));
                $retval = $this->format_body($retval);
                return <<<EOD
 <div{$this->class}>
@@ -153,217 +130,221 @@ $retval$timestamp
 EOD;
        }
 }
+
 class ShowRSS_html_menubar extends ShowRSS_html
 {
        var $class = ' class="small"';
 
-       function format_link($link)
-       {
-               return "<li>$link</li>\n";
+       function format_link($link) {
+               return '<li>' . $link . '</li>' . "\n";
        }
-       function format_body($str)
-       {
-               return "<ul class=\"recent_list\">\n$str</ul>\n";
+
+       function format_body($str) {
+               return '<ul class="recent_list">' . "\n" . $str . '</ul>' . "\n";
        }
 }
+
 class ShowRSS_html_recent extends ShowRSS_html
 {
        var $class = ' class="small"';
 
-       function format_link($link)
-       {
-               return "<li>$link</li>\n";
+       function format_link($link) {
+               return '<li>' . $link . '</li>' . "\n";
        }
-       function format_list($date,$str)
-       {
-               return "<strong>$date</strong>\n<ul class=\"recent_list\">\n$str</ul>\n";
+
+       function format_list($date, $str) {
+               return '<strong>' . $date . '</strong>' . "\n" .
+                       '<ul class="recent_list">' . "\n" . $str . '</ul>' . "\n";
        }
 }
-// rss¤ò¼èÆÀ¤¹¤ë
-function plugin_showrss_get_rss($target,$usecache)
+
+// Get and save RSS
+function plugin_showrss_get_rss($target, $cachehour)
 {
-       $buf = '';
+       $buf  = '';
        $time = NULL;
-       if ($usecache)
-       {
-               // ´ü¸ÂÀÚ¤ì¤Î¥­¥ã¥Ã¥·¥å¤ò¥¯¥ê¥¢
-               plugin_showrss_cache_expire($usecache);
-
-               // ¥­¥ã¥Ã¥·¥å¤¬¤¢¤ì¤Ð¼èÆÀ¤¹¤ë
+       if ($cachehour) {
                $filename = CACHE_DIR . encode($target) . '.tmp';
-               if (is_readable($filename))
-               {
-                       $buf = join('',file($filename));
+               // Remove expired cache
+               plugin_showrss_cache_expire_file($filename, $cachehour);
+               // Get the cache not expired
+               if (is_readable($filename)) {
+                       $buf  = join('', file($filename));
                        $time = filemtime($filename) - LOCALZONE;
                }
        }
-       if ($time === NULL)
-       {
-               // rssËÜÂΤò¼èÆÀ
-               $data = http_request($target);
-               if ($data['rc'] !== 200)
-               {
-                       return array(FALSE,0);
+
+       if (is_null($time)) {
+               // Newly get RSS
+               $data = pkwk_http_request($target);
+               if ($data['rc'] !== 200) {
+                       return array(FALSE, 0);
                }
                $buf = $data['data'];
                $time = UTIME;
-               // ¥­¥ã¥Ã¥·¥å¤òÊݸ
-               if ($usecache)
-               {
+
+               // Save RSS into cache
+               if ($cachehour) {
                        $fp = fopen($filename, 'w');
-                       fwrite($fp,$buf);
+                       fwrite($fp, $buf);
                        fclose($fp);
                }
        }
-
-       // parse
+       // Parse
        $obj = new ShowRSS_XML();
+       $obj->modified_date = (is_null($time) ? UTIME : $time);
        return array($obj->parse($buf),$time);
 }
-// ´ü¸ÂÀÚ¤ì¤Î¥­¥ã¥Ã¥·¥å¤ò¥¯¥ê¥¢
-function plugin_showrss_cache_expire($usecache)
-{
-       $expire = $usecache * 60 * 60; // Hour
 
+// Remove cache if expired limit exeed
+function plugin_showrss_cache_expire($cachehour)
+{
+       $expire = $cachehour * 60 * 60; // Hour
        $dh = dir(CACHE_DIR);
-       while (($file = $dh->read()) !== FALSE)
-       {
-               if (substr($file,-4) != '.tmp')
-               {
-                       continue;
-               }
-               $file = CACHE_DIR.$file;
+       while (($file = $dh->read()) !== FALSE) {
+               if (substr($file, -4) != '.tmp') continue;
+               $file = CACHE_DIR . $file;
                $last = time() - filemtime($file);
-
-               if ($last > $expire)
-               {
-                       unlink($file);
-               }
+               if ($last > $expire) unlink($file);
        }
        $dh->close();
 }
-// rss¤ò¼èÆÀ¡¦ÇÛÎó²½
+
+/**
+ * Remove single file cache if expired limit exeed
+ * @param $filename
+ * @param $cachehour
+ */
+function plugin_showrss_cache_expire_file($filename, $cachehour)
+{
+       $expire = $cachehour * 60 * 60; // Hour
+       $last = time() - filemtime($filename);
+       if ($last > $expire) {
+               unlink($filename);
+       }
+}
+
+// Get RSS and array() them
 class ShowRSS_XML
 {
        var $items;
        var $item;
        var $is_item;
        var $tag;
+       var $encoding;
+       var $modified_date;
 
        function parse($buf)
        {
-               // ½é´ü²½
-               $this->items = array();
-               $this->item = array();
+               $this->items   = array();
+               $this->item    = array();
                $this->is_item = FALSE;
-               $this->tag = '';
-
-               $xml_parser = xml_parser_create();
-               xml_set_element_handler($xml_parser,array(&$this,'start_element'),array(&$this,'end_element'));
-               xml_set_character_data_handler($xml_parser,array(&$this,'character_data'));
-
-               if (!xml_parse($xml_parser,$buf,1))
-               {
-                       return(sprintf('XML error: %s at line %d in %s',
+               $this->tag     = '';
+               $utf8 = 'UTF-8';
+               $this->encoding = $utf8;
+               // Detect encoding
+               $matches = array();
+               if(preg_match('/<\?xml [^>]*\bencoding="([a-z0-9-_]+)"/i', $buf, $matches)) {
+                       $encoding = $matches[1];
+                       if (strtoupper($encoding) !== $utf8) {
+                               // xml_parse() fails on non UTF-8 encoding attr in XML decLaration
+                               $buf = preg_replace('/<\?xml ([^>]*)\bencoding="[a-z0-9-_]+"/i', '<?xml $1', $buf);
+                               // xml_parse() requires UTF-8 compatible encoding
+                               $buf = mb_convert_encoding($buf, $utf8, $encoding);
+                       }
+               }
+               // Parsing
+               $xml_parser = xml_parser_create($utf8);
+               xml_set_element_handler($xml_parser, array(& $this, 'start_element'), array(& $this, 'end_element'));
+               xml_set_character_data_handler($xml_parser, array(& $this, 'character_data'));
+               if (! xml_parse($xml_parser, $buf, 1)) {
+                       return sprintf('XML error: %s at line %d in %s',
                                xml_error_string(xml_get_error_code($xml_parser)),
-                               xml_get_current_line_number($xml_parser),$buf));
+                               xml_get_current_line_number($xml_parser),
+                               (strlen($buf) < 500 ? $buf : substr($buf, 0, 500) . '...'));
                }
                xml_parser_free($xml_parser);
-
                return $this->items;
        }
+
        function escape($str)
        {
-               // RSSÃæ¤Î "&lt; &gt; &amp;" ¤Ê¤É¤ò °ìö "< > &" ¤ËÌᤷ¡¢ ¡ã "&amp;" ¤¬ "&amp;amp;" ¤Ë¤Ê¤Ã¤Á¤ã¤¦¤ÎÂкö
-               // ¤½¤Î¸å¤â¤Ã¤«¤¤"< > &"¤Ê¤É¤ò"&lt; &gt; &amp;"¤Ë¤¹¤ë  ¡ã XSSÂкö¡©
+               // Unescape already-escaped chars (&lt;, &gt;, &amp;, ...) in RSS body before htmlsc()
                $str = strtr($str, array_flip(get_html_translation_table(ENT_COMPAT)));
-               $str = htmlspecialchars($str);
-
-               // Ê¸»ú¥³¡¼¥ÉÊÑ´¹
-               $str = mb_convert_encoding($str, SOURCE_ENCODING, 'auto');
-
+               // Escape
+               $str = htmlsc($str, ENT_COMPAT, $this->encoding);
+               // Encoding conversion
+               $str = mb_convert_encoding($str, SOURCE_ENCODING, $this->encoding);
                return trim($str);
        }
 
-       // ¥¿¥°³«»Ï
-       function start_element($parser,$name,$attrs)
+       // Tag start
+       function start_element($parser, $name, $attrs)
        {
-               if ($this->is_item)
-               {
-                       $this->tag = $name;
-               }
-               else if ($name == 'ITEM')
-               {
+               if ($this->is_item) {
+                       $this->tag     = $name;
+               } else if ($name == 'ITEM') {
                        $this->is_item = TRUE;
                }
        }
-       // ¥¿¥°½ªÎ»
-       function end_element($parser,$name)
+
+       // Tag end
+       function end_element($parser, $name)
        {
-               if (!$this->is_item or $name != 'ITEM')
-               {
-                       return;
-               }
-               $item = array_map(array(&$this,'escape'),$this->item);
+               if (! $this->is_item || $name != 'ITEM') return;
 
+               $item = array_map(array(& $this, 'escape'), $this->item);
                $this->item = array();
 
-               if (array_key_exists('DC:DATE',$item))
-               {
-                       $time = plugin_showrss_get_timestamp($item['DC:DATE']);
-               }
-               else if (array_key_exists('PUBDATE',$item))
-               {
-                       $time = plugin_showrss_get_timestamp($item['PUBDATE']);
-               }
-               else if (array_key_exists('DESCRIPTION',$item)
-                       and ($description = trim($item['DESCRIPTION'])) != ''
-                       and ($time = strtotime($description)) != -1)
-               {
-                       $time -= LOCALZONE;
-               }
-               else
-               {
-                       $time = time() - LOCALZONE;
+               if (isset($item['DC:DATE'])) {
+                       $time = plugin_showrss_get_timestamp($item['DC:DATE'], $this->modified_date);
+                       
+               } else if (isset($item['PUBDATE'])) {
+                       $time = plugin_showrss_get_timestamp($item['PUBDATE'], $this->modified_date);
+               } else {
+                       $time_from_desc = FALSE;
+                       if (isset($item['DESCRIPTION']) &&
+                               (($description = trim($item['DESCRIPTION'])) != '')) {
+                               $time_from_desc = strtotime($description);
+                       }
+                       if ($time_from_desc !== FALSE && $time_from_desc !== -1) {
+                               $time = $time_from_desc - LOCALZONE;
+                       } else {
+                               $time = time() - LOCALZONE;
+                       }
                }
                $item['_TIMESTAMP'] = $time;
-               $date = get_date('Y-m-d',$item['_TIMESTAMP']);
+               $date = get_date('Y-m-d', $item['_TIMESTAMP']);
 
                $this->items[$date][] = $item;
-               $this->is_item = FALSE;
+               $this->is_item        = FALSE;
        }
-       // ¥­¥ã¥é¥¯¥¿
-       function character_data($parser,$data)
+
+       function character_data($parser, $data)
        {
-               if (!$this->is_item)
-               {
-                       return;
-               }
-               if (!array_key_exists($this->tag,$this->item))
-               {
-                       $this->item[$this->tag] = '';
-               }
+               if (! $this->is_item) return;
+               if (! isset($this->item[$this->tag])) $this->item[$this->tag] = '';
                $this->item[$this->tag] .= $data;
        }
 }
-function plugin_showrss_get_timestamp($str)
+
+function plugin_showrss_get_timestamp($str, $default_date)
 {
-       if (($str = trim($str)) == '')
-       {
-               return UTIME;
-       }
-       if (!preg_match('/(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2})(([+-])(\d{2}):(\d{2}))?/',$str,$matches))
-       {
+       $str = trim($str);
+       if ($str == '') return UTIME;
+
+       $matches = array();
+       if (preg_match('/(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2})(([+-])(\d{2}):(\d{2}))?/', $str, $matches)) {
+               $time = strtotime($matches[1] . ' ' . $matches[2]);
+               if ($time === FALSE || $time === -1) {
+                       $time = $default_date;
+               } else if (isset($matches[3])) {
+                       $diff = ($matches[5] * 60 + $matches[6]) * 60;
+                       $time += ($matches[4] == '-' ? $diff : -$diff);
+               }
+               return $time;
+       } else {
                $time = strtotime($str);
-               return ($time == -1) ? UTIME : $time - LOCALZONE;
-       }
-       $str = $matches[1];
-       $time = strtotime($matches[1].' '.$matches[2]);
-       if (!empty($matches[3]))
-       {
-               $diff = ($matches[5]*60+$matches[6])*60;
-               $time += ($matches[4] == '-' ? $diff : -$diff);
+               return ($time === FALSE || $time === -1) ? $default_date : $time - LOCALZONE;
        }
-       return $time;
 }
-?>