]> git.wh0rd.org - tt-rss.git/blobdiff - functions.php
Extended Actions to include Select by tag (add local modifications, fix
[tt-rss.git] / functions.php
index c9a8c86868224032e552cf384e56ff2ad61f0c61..d6f116a8d1edec1d6e0b1c83bd10e324ab50e082 100644 (file)
                return $tr;
        }
 
-       if (ENABLE_TRANSLATIONS == true) { // If translations are enabled.
-               require_once "lib/accept-to-gettext.php";
-               require_once "lib/gettext/gettext.inc";
+       require_once "lib/accept-to-gettext.php";
+       require_once "lib/gettext/gettext.inc";
 
-               function startup_gettext() {
+       function startup_gettext() {
 
-                       # Get locale from Accept-Language header
-                       $lang = al2gt(array_keys(get_translations()), "text/html");
+               # Get locale from Accept-Language header
+               $lang = al2gt(array_keys(get_translations()), "text/html");
 
-                       if (defined('_TRANSLATION_OVERRIDE_DEFAULT')) {
-                               $lang = _TRANSLATION_OVERRIDE_DEFAULT;
-                       }
-
-                       if ($_COOKIE["ttrss_lang"] && $_COOKIE["ttrss_lang"] != "auto") {
-                               $lang = $_COOKIE["ttrss_lang"];
-                       }
-
-                       /* In login action of mobile version */
-                       if ($_POST["language"] && defined('MOBILE_VERSION')) {
-                               $lang = $_POST["language"];
-                               $_COOKIE["ttrss_lang"] = $lang;
-                       }
+               if (defined('_TRANSLATION_OVERRIDE_DEFAULT')) {
+                       $lang = _TRANSLATION_OVERRIDE_DEFAULT;
+               }
 
-                       if ($lang) {
-                               if (defined('LC_MESSAGES')) {
-                                       _setlocale(LC_MESSAGES, $lang);
-                               } else if (defined('LC_ALL')) {
-                                       _setlocale(LC_ALL, $lang);
-                               } else {
-                                       die("can't setlocale(): please set ENABLE_TRANSLATIONS to false in config.php");
-                               }
+               if ($_COOKIE["ttrss_lang"] && $_COOKIE["ttrss_lang"] != "auto") {
+                       $lang = $_COOKIE["ttrss_lang"];
+               }
 
-                               if (defined('MOBILE_VERSION')) {
-                                       _bindtextdomain("messages", "../locale");
-                               } else {
-                                       _bindtextdomain("messages", "locale");
-                               }
+               /* In login action of mobile version */
+               if ($_POST["language"] && defined('MOBILE_VERSION')) {
+                       $lang = $_POST["language"];
+                       $_COOKIE["ttrss_lang"] = $lang;
+               }
 
-                               _textdomain("messages");
-                               _bind_textdomain_codeset("messages", "UTF-8");
+               if ($lang) {
+                       if (defined('LC_MESSAGES')) {
+                               _setlocale(LC_MESSAGES, $lang);
+                       } else if (defined('LC_ALL')) {
+                               _setlocale(LC_ALL, $lang);
                        }
-               }
 
-               startup_gettext();
+                       if (defined('MOBILE_VERSION')) {
+                               _bindtextdomain("messages", "../locale");
+                       } else {
+                               _bindtextdomain("messages", "locale");
+                       }
 
-       } else { // If translations are enabled.
-               function __($msg) {
-                       return $msg;
+                       _textdomain("messages");
+                       _bind_textdomain_codeset("messages", "UTF-8");
                }
-               function startup_gettext() {
-                       // no-op
-                       return true;
-               }
-       } // If translations are enabled.
+       }
+
+       startup_gettext();
 
        if (defined('MEMCACHE_SERVER')) {
                $memcache = new Memcache;
 
        //define('MAGPIE_USER_AGENT_EXT', ' (Tiny Tiny RSS/' . VERSION . ')');
        define('MAGPIE_OUTPUT_ENCODING', 'UTF-8');
-       define('MAGPIE_CACHE_AGE', 60*15); // 15 minutes
 
        define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . VERSION . ' (http://tt-rss.org/)');
        define('MAGPIE_USER_AGENT', SELF_USER_AGENT);
        require_once "lib/magpierss/rss_fetch.inc";
        require_once 'lib/magpierss/rss_utils.inc';
        require_once 'lib/htmlpurifier/library/HTMLPurifier.auto.php';
+       require_once 'lib/pubsubhubbub/publisher.php';
+       require_once 'lib/pubsubhubbub/subscriber.php';
 
        $config = HTMLPurifier_Config::createDefault();
 
-       $allowed = "p,a[href],i,em,b,strong,code,pre,blockquote,br,img[src|alt|title],ul,ol,li,h1,h2,h3,h4,s";
+       $allowed = "p,a[href],i,em,b,strong,code,pre,blockquote,br,img[src|alt|title],ul,ol,li,h1,h2,h3,h4,s,object[classid|type|id|name|width|height|codebase],param[name|value],table,tr,td";
+
+       $config->set('HTML.SafeObject', true);
+       @$config->set('HTML', 'Allowed', $allowed);
+       $config->set('Output.FlashCompat', true);
+       $config->set('Attr.EnableID', true);
+       @$config->set('Cache', 'SerializerPath', CACHE_DIR . "/htmlpurifier");
 
-       $config->set('HTML', 'Allowed', $allowed);
        $purifier = new HTMLPurifier($config);
 
+       $tz_offset = -1;
+       $utc_tz = new DateTimeZone('UTC');
+       $schema_version = false;
+
        /**
         * Print a timestamped debug message.
         *
                if (!$purge_unread) $query_limit = " unread = false AND ";
 
                if (DB_TYPE == "pgsql") {
-/*                     $result = db_query($link, "DELETE FROM ttrss_user_entries WHERE
-                               marked = false AND feed_id = '$feed_id' AND
-                               (SELECT date_updated FROM ttrss_entries WHERE
-                                       id = ref_id) < NOW() - INTERVAL '$purge_interval days'"); */
-
                        $pg_version = get_pgsql_version($link);
 
                        if (preg_match("/^7\./", $pg_version) || preg_match("/^8\.0/", $pg_version)) {
                }
        }
 
-       function fetch_file_contents($url, $type = false, $login = false, $pass = false) {
+       function fetch_file_contents($url, $type = false, $login = false, $pass = false, $post_query = false) {
                $login = urlencode($login);
                $pass = urlencode($pass);
 
-               if (function_exists('curl_init')) {
+               if (function_exists('curl_init') && !ini_get("open_basedir")) {
                        $ch = curl_init($url);
 
                        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
                        curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
                        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
-                       curl_setopt($fp, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
+                       curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
+                       curl_setopt($ch, CURLOPT_USERAGENT, SELF_USER_AGENT);
+
+                       if ($post_query) {
+                               curl_setopt($ch, CURLOPT_POST, true);
+                               curl_setopt($ch, CURLOPT_POSTFIELDS, $post_query);
+                       }
 
                        if ($login && $pass)
                                curl_setopt($ch, CURLOPT_USERPWD, "$login:$pass");
 
                        return $contents;
                } else {
-                       if ($login && $pass && $updated != 3) {
+                       if ($login && $pass ){
                                $url_parts = array();
 
                                preg_match("/(^[^:]*):\/\/(.*)/", $url, $url_parts);
                        $doc = new DOMDocument();
                        $doc->loadHTML($html);
                        $xpath = new DOMXPath($doc);
-                       $entries = $xpath->query('/html/head/link[@rel="shortcut icon"]');
 
+                       $base = $xpath->query('/html/head/base');
+                       foreach ($base as $b) {
+                               $url = $b->getAttribute("href");
+                               break;
+                       }
+
+                       $entries = $xpath->query('/html/head/link[@rel="shortcut icon" or @rel="icon"]');
                        if (count($entries) > 0) {
                                foreach ($entries as $entry) {
                                        $favicon_url = rewrite_relative_url($url, $entry->getAttribute("href"));
 
                if ($favicon_url && !file_exists($icon_file)) {
                        $contents = fetch_file_contents($favicon_url, "image");
-
                        if ($contents) {
                                $fp = fopen($icon_file, "w");
 
                }
        }
 
-       function update_rss_feed($link, $feed, $ignore_daemon = false) {
+       function update_rss_feed($link, $feed, $ignore_daemon = false, $no_cache = false) {
 
                global $memcache;
 
                                WHERE   f2.feed_url = f1.feed_url AND f2.id = '$feed'");
 
                        while ($line = db_fetch_assoc($result)) {
-                               update_rss_feed_real($link, $line["id"], $ignore_daemon);
+                               update_rss_feed_real($link, $line["id"], $ignore_daemon, $no_cache);
                        }
                } else {
-                       update_rss_feed_real($link, $feed, $ignore_daemon);
+                       update_rss_feed_real($link, $feed, $ignore_daemon, $no_cache);
                }
        }
 
-       function update_rss_feed_real($link, $feed, $ignore_daemon = false) {
+       function update_rss_feed_real($link, $feed, $ignore_daemon = false, $no_cache = false) {
 
                global $memcache;
 
+               $debug_enabled = defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug'];
+
                if (!$_REQUEST["daemon"] && !$ignore_daemon) {
                        return false;
                }
 
-               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+               if ($debug_enabled) {
                        _debug("update_rss_feed: start");
                }
 
 
                        $result = db_query($link, "SELECT id,update_interval,auth_login,
                                feed_url,auth_pass,cache_images,update_method,last_updated,
-                               owner_uid
+                               mark_unread_on_update, owner_uid, update_on_checksum_change,
+                               pubsub_state
                                FROM ttrss_feeds WHERE id = '$feed'");
 
                }
 
                if (db_num_rows($result) == 0) {
-                       if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                       if ($debug_enabled) {
                                _debug("update_rss_feed: feed $feed NOT FOUND/SKIPPED");
                        }
                        return false;
                $update_method = db_fetch_result($result, 0, "update_method");
                $last_updated = db_fetch_result($result, 0, "last_updated");
                $owner_uid = db_fetch_result($result, 0, "owner_uid");
+               $mark_unread_on_update = sql_bool_to_bool(db_fetch_result($result,
+                       0, "mark_unread_on_update"));
+               $update_on_checksum_change = sql_bool_to_bool(db_fetch_result($result,
+                       0, "update_on_checksum_change"));
+               $pubsub_state = db_fetch_result($result, 0, "pubsub_state");
 
                db_query($link, "UPDATE ttrss_feeds SET last_update_started = NOW()
                        WHERE id = '$feed'");
                else
                        $use_simplepie = false;
 
-               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+               if ($debug_enabled) {
                        _debug("update method: $update_method (feed setting: $update_method) (use simplepie: $use_simplepie)\n");
                }
 
                $cache_images = sql_bool_to_bool(db_fetch_result($result, 0, "cache_images"));
                $fetch_url = db_fetch_result($result, 0, "feed_url");
 
-               if ($update_interval < 0) { return; }
+               if ($update_interval < 0) { return false; }
 
                $feed = db_escape_string($feed);
 
-               if ($auth_login && $auth_pass && $updated != 3) {
+               if ($auth_login && $auth_pass ){
                        $url_parts = array();
                        preg_match("/(^[^:]*):\/\/(.*)/", $fetch_url, $url_parts);
 
 
                }
 
-               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+               if ($debug_enabled) {
                        _debug("update_rss_feed: fetching [$fetch_url]...");
                }
 
 
                if ($memcache && $obj = $memcache->get($obj_id)) {
 
-                       if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                       if ($debug_enabled) {
                                _debug("update_rss_feed: data found in memcache.");
                        }
 
 
                        if ($update_method == 3) {
                                $rss = fetch_twitter_rss($link, $fetch_url, $owner_uid);
-                                       } else if ($update_method == 1) {
+                       } else if ($update_method == 1) {
+
+                               define('MAGPIE_CACHE_AGE', get_feed_update_interval($link, $feed) * 60);
+                               define('MAGPIE_CACHE_ON', !$no_cache);
+                               define('MAGPIE_FETCH_TIME_OUT', 60);
+                               define('MAGPIE_CACHE_DIR', CACHE_DIR . "/magpie");
+
                                $rss = @fetch_rss($fetch_url);
                        } else {
-                               if (!is_dir(SIMPLEPIE_CACHE_DIR)) {
-                                       mkdir(SIMPLEPIE_CACHE_DIR);
+                               $simplepie_cache_dir = CACHE_DIR . "/simplepie";
+
+                               if (!is_dir($simplepie_cache_dir)) {
+                                       mkdir($simplepie_cache_dir);
                                }
 
                                $rss = new SimplePie();
 
                                if (SIMPLEPIE_CACHE_IMAGES && $cache_images) {
 
-                                       if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                                       if ($debug_enabled) {
                                                _debug("enabling image cache");
                                        }
 
                                        $rss->set_image_handler("image.php", 'i');
                                }
 
-                               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                               if ($debug_enabled) {
                                        _debug("feed update interval (sec): " .
                                                get_feed_update_interval($link, $feed)*60);
                                }
 
-                               if (is_dir(SIMPLEPIE_CACHE_DIR)) {
-                                       $rss->set_cache_location(SIMPLEPIE_CACHE_DIR);
+                               $rss->enable_cache(!$no_cache);
+
+                               if (!$no_cache) {
+                                       $rss->set_cache_location($simplepie_cache_dir);
                                        $rss->set_cache_duration(get_feed_update_interval($link, $feed) * 60);
                                }
 
 
 //             print_r($rss);
 
-               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+               if ($debug_enabled) {
                        _debug("update_rss_feed: fetch done, parsing...");
                }
 
 
                if ($fetch_ok) {
 
-                       if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                       if ($debug_enabled) {
                                _debug("update_rss_feed: processing feed data...");
                        }
 
                                $site_url = $rss->channel["link"];
                        }
 
-                       if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                       $site_url = rewrite_relative_url($fetch_url, $site_url);
+
+                       if ($debug_enabled) {
                                _debug("update_rss_feed: checking favicon...");
                        }
 
                                        $feed_title = db_escape_string($rss->channel["title"]);
                                }
 
-                               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                               if ($debug_enabled) {
                                        _debug("update_rss_feed: registering title: $feed_title");
                                }
 
                                db_query($link, "UPDATE ttrss_feeds SET icon_url = '$icon_url' WHERE id = '$feed'");
                        }
 
-                       if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                       if ($debug_enabled) {
                                _debug("update_rss_feed: loading filters...");
                        }
 
                        $filters = load_filters($link, $feed, $owner_uid);
 
-                       if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
-                               print_r($filters);
-                       }
+//                     if ($debug_enabled) {
+//                             print_r($filters);
+//                     }
 
                        if ($use_simplepie) {
                                $iterator = $rss->get_items();
                                // clear any errors and mark feed as updated if fetched okay
                                // even if it's blank
 
-                               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                               if ($debug_enabled) {
                                        _debug("update_rss_feed: entry iterator is not an array, no articles?");
                                }
 
                                return; // no articles
                        }
 
-                       if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                       if ($pubsub_state != 2 && PUBSUBHUBBUB_ENABLED) {
+
+                               if ($debug_enabled) _debug("update_rss_feed: checking for PUSH hub...");
+
+                               $feed_hub_url = false;
+                               if ($use_simplepie) {
+                                       $links = $rss->get_links('hub');
+
+                                       if ($links && is_array($links)) {
+                                               foreach ($links as $l) {
+                                                       $feed_hub_url = $l;
+                                                       break;
+                                               }
+                                       }
+
+                               } else {
+                                       $atom = $rss->channel['atom'];
+
+                                       if ($atom) {
+                                               if ($atom['link@rel'] == 'hub') {
+                                                       $feed_hub_url = $atom['link@href'];
+                                               }
+
+                                               if (!$feed_hub_url && $atom['link#'] > 1) {
+                                                       for ($i = 2; $i <= $atom['link#']; $i++) {
+                                                               if ($atom["link#$i@rel"] == 'hub') {
+                                                                       $feed_hub_url = $atom["link#$i@href"];
+                                                                       break;
+                                                               }
+                                                       }
+                                               }
+                                       } else {
+                                               $feed_hub_url = $rss->channel['link_hub'];
+                                       }
+                               }
+
+                               if ($debug_enabled) _debug("update_rss_feed: feed hub url: $feed_hub_url");
+
+                               if ($feed_hub_url && function_exists('curl_init') &&
+                                       !ini_get("open_basedir")) {
+
+                                       $callback_url = get_self_url_prefix() .
+                                               "/backend.php?op=pubsub&id=$feed";
+
+                                       $s = new Subscriber($feed_hub_url, $callback_url);
+
+                                       $rc = $s->subscribe($fetch_url);
+
+                                       if ($debug_enabled)
+                                               _debug("update_rss_feed: feed hub url found, subscribe request sent.");
+
+                                       db_query($link, "UPDATE ttrss_feeds SET pubsub_state = 1
+                                               WHERE id = '$feed'");
+                               }
+                       }
+
+                       if ($debug_enabled) {
                                _debug("update_rss_feed: processing articles...");
                        }
 
                                        if (!$entry_guid) $entry_guid = make_guid_from_title($item["title"]);
                                }
 
-                               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                               if ($debug_enabled) {
                                        _debug("update_rss_feed: guid $entry_guid");
                                }
 
 
                                $entry_timestamp_fmt = strftime("%Y/%m/%d %H:%M:%S", $entry_timestamp);
 
-                               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                               if ($debug_enabled) {
                                        _debug("update_rss_feed: date $entry_timestamp [$entry_timestamp_fmt]");
                                }
 
                                        if (!$entry_link) $entry_link = $item["link"];
                                }
 
-                               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                               $entry_link = rewrite_relative_url($site_url, $entry_link);
+
+                               if ($debug_enabled) {
                                        _debug("update_rss_feed: title $entry_title");
+                                       _debug("update_rss_feed: link $entry_link");
                                }
 
                                if (!$entry_title) $entry_title = date("Y-m-d H:i:s", $entry_timestamp);;
 
                                if (!$num_comments) $num_comments = 0;
 
-                               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                               if ($debug_enabled) {
                                        _debug("update_rss_feed: looking for tags [1]...");
                                }
 
                                                }
                                        }
 
-                                       if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                                       if ($debug_enabled) {
                                                _debug("update_rss_feed: category tags:");
                                                print_r($additional_tags);
                                        }
                                        }
                                }
 
-                               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                               if ($debug_enabled) {
                                        _debug("update_rss_feed: looking for tags [2]...");
                                }
 
                                for ($i = 0; $i < count($entry_tags); $i++)
                                        $entry_tags[$i] = mb_strtolower($entry_tags[$i], 'utf-8');
 
-                               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                               if ($debug_enabled) {
                                        _debug("update_rss_feed: unfiltered tags found:");
                                        print_r($entry_tags);
                                }
                                $entry_content = sanitize_article_content($entry_content);
                                $entry_title = sanitize_article_content($entry_title);
 
-                               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                               if ($debug_enabled) {
                                        _debug("update_rss_feed: done collecting data [TITLE:$entry_title]");
                                }
 
 
                                if (db_num_rows($result) == 0) {
 
-                                       if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                                       if ($debug_enabled) {
                                                _debug("update_rss_feed: base guid not found");
                                        }
 
 
                                if (db_num_rows($result) == 1) {
 
-                                       if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                                       if ($debug_enabled) {
                                                _debug("update_rss_feed: base guid found, checking for user record");
                                        }
 
                                                $entry_content, $entry_link, $entry_timestamp, $entry_author,
                                                $entry_tags);
 
-                                       if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                                       if ($debug_enabled) {
                                                _debug("update_rss_feed: article filters: ");
                                                if (count($article_filters) != 0) {
                                                        print_r($article_filters);
 
                                        $score = calculate_article_score($article_filters);
 
-                                       if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                                       if ($debug_enabled) {
                                                _debug("update_rss_feed: initial score: $score");
                                        }
 
                                        // okay it doesn't exist - create user entry
                                        if (db_num_rows($result) == 0) {
 
-                                               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                                               if ($debug_enabled) {
                                                        _debug("update_rss_feed: user record not found, creating...");
                                                }
 
                                                        VALUES ('$ref_id', '$owner_uid', '$feed', $unread,
                                                                $last_read_qpart, $marked, $published, '$score', '', '')");
 
+                                               if (PUBSUBHUBBUB_HUB && $published == 'true') {
+                                                       $rss_link = get_self_url_prefix() .
+                                                               "/backend.php?op=rss&id=-2&key=" .
+                                                               get_feed_access_key($link, -2, false, $owner_uid);
+
+                                                       $p = new Publisher(PUBSUBHUBBUB_HUB);
+
+                                                       $pubsub_result = $p->publish_update($rss_link);
+                                               }
+
                                                $result = db_query($link,
                                                        "SELECT int_id FROM ttrss_user_entries WHERE
                                                                ref_id = '$ref_id' AND owner_uid = '$owner_uid' AND
                                                        $entry_int_id = db_fetch_result($result, 0, "int_id");
                                                }
                                        } else {
-                                               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                                               if ($debug_enabled) {
                                                        _debug("update_rss_feed: user record FOUND");
                                                }
 
                                                $entry_int_id = db_fetch_result($result, 0, "int_id");
                                        }
 
-                                       if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                                       if ($debug_enabled) {
                                                _debug("update_rss_feed: RID: $entry_ref_id, IID: $entry_int_id");
                                        }
 
                                        $post_needs_update = false;
+                                       $update_insignificant = false;
 
-                                       if (get_pref($link, "UPDATE_POST_ON_CHECKSUM_CHANGE", $owner_uid, false) &&
-                                               ($content_hash != $orig_content_hash)) {
-//                                             print "<!-- [$entry_title] $content_hash vs $orig_content_hash -->";
+                                       if ($orig_num_comments != $num_comments) {
                                                $post_needs_update = true;
+                                               $update_insignificant = true;
                                        }
 
-                                       if (db_escape_string($orig_title) != $entry_title) {
+                                       if ($content_hash != $orig_content_hash) {
                                                $post_needs_update = true;
+                                               $update_insignificant = false;
                                        }
 
-                                       if ($orig_num_comments != $num_comments) {
+                                       if (db_escape_string($orig_title) != $entry_title) {
                                                $post_needs_update = true;
+                                               $update_insignificant = false;
                                        }
 
-//                                     this doesn't seem to be very reliable
-//
-//                                     if ($orig_timestamp != $entry_timestamp && !$orig_no_orig_date) {
-//                                             $post_needs_update = true;
-//                                     }
-
                                        // if post needs update, update it and mark all user entries
                                        // linking to this post as updated
                                        if ($post_needs_update) {
                                                db_query($link, "UPDATE ttrss_entries
                                                        SET title = '$entry_title', content = '$entry_content',
                                                                content_hash = '$content_hash',
+                                                               updated = '$entry_timestamp_fmt',
                                                                num_comments = '$num_comments'
                                                        WHERE id = '$ref_id'");
 
-                                               if (get_pref($link, "MARK_UNREAD_ON_UPDATE", $owner_uid, false)) {
-                                                       db_query($link, "UPDATE ttrss_user_entries
-                                                               SET last_read = null, unread = true WHERE ref_id = '$ref_id'");
-                                               } else {
-                                                       db_query($link, "UPDATE ttrss_user_entries
-                                                               SET last_read = null WHERE ref_id = '$ref_id' AND unread = false");
+                                               if (!$update_insignificant) {
+                                                       if ($mark_unread_on_update) {
+                                                               db_query($link, "UPDATE ttrss_user_entries
+                                                                       SET last_read = null, unread = true WHERE ref_id = '$ref_id'");
+                                                       } else if ($update_on_checksum_change) {
+                                                               db_query($link, "UPDATE ttrss_user_entries
+                                                                       SET last_read = null WHERE ref_id = '$ref_id'
+                                                                               AND unread = false");
+                                                       }
                                                }
-
                                        }
                                }
 
                                db_query($link, "COMMIT");
 
-                               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                               if ($debug_enabled) {
                                        _debug("update_rss_feed: assigning labels...");
                                }
 
                                assign_article_to_labels($link, $entry_ref_id, $article_filters,
                                        $owner_uid);
 
-                               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                               if ($debug_enabled) {
                                        _debug("update_rss_feed: looking for enclosures...");
                                }
 
                                }
 
 
-                               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                               if ($debug_enabled) {
                                        _debug("update_rss_feed: article enclosures:");
                                        print_r($enclosures);
                                }
                                foreach ($article_filters as $f) {
                                        if ($f[0] == "tag") {
 
-                                               $manual_tags = trim_array(split(",", $f[1]));
+                                               $manual_tags = trim_array(explode(",", $f[1]));
 
                                                foreach ($manual_tags as $tag) {
                                                        if (tag_is_valid($tag)) {
 
                                // Skip boring tags
 
-                               $boring_tags = trim_array(split(",", mb_strtolower(get_pref($link,
+                               $boring_tags = trim_array(explode(",", mb_strtolower(get_pref($link,
                                        'BLACKLISTED_TAGS', $owner_uid, ''), 'utf-8')));
 
                                $filtered_tags = array();
 
                                $filtered_tags = array_unique($filtered_tags);
 
-                               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                               if ($debug_enabled) {
                                        _debug("update_rss_feed: filtered article tags:");
                                        print_r($filtered_tags);
                                }
                                        db_query($link, "COMMIT");
                                }
 
-                               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                               if ($debug_enabled) {
                                        _debug("update_rss_feed: article processed");
                                }
                        }
 
                        if (!$last_updated) {
-                               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                               if ($debug_enabled) {
                                        _debug("update_rss_feed: new feed, catching it up...");
                                }
                                catchup_feed($link, $feed, false, $owner_uid);
                        }
 
-                       purge_feed($link, $feed, 0);
+                       if ($debug_enabled) {
+                               _debug("purging feed...");
+                       }
+
+                       purge_feed($link, $feed, 0, $debug_enabled);
 
                        db_query($link, "UPDATE ttrss_feeds
                                SET last_updated = NOW(), last_error = '' WHERE id = '$feed'");
                                $error_msg = mb_substr(magpie_error(), 0, 250);
                        }
 
-                       if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+                       if ($debug_enabled) {
                                _debug("update_rss_feed: error fetching feed: $error_msg");
                        }
 
                        unset($rss);
                }
 
-               if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
+               if ($debug_enabled) {
                        _debug("update_rss_feed: done");
                }
 
 
        function lookup_user_id($link, $user) {
 
-               $result = db_query($link, "SELECT id FROM ttrss_users WHERE
-                       login = '$login'");
+               $result = db_query($link, "SELECT id FROM ttrss_users WHERE login = '$user'");
 
                if (db_num_rows($result) == 1) {
                        return db_fetch_result($result, 0, "id");
                }
        }
 
-       function http_authenticate_user($link) {
-
-//             error_log("http_authenticate_user: ".$_SERVER["PHP_AUTH_USER"]."\n", 3, '/tmp/tt-rss.log');
-
+/*     function http_authenticate_user($link) {
                if (!$_SERVER["PHP_AUTH_USER"]) {
 
                        header('WWW-Authenticate: Basic realm="Tiny Tiny RSS RSSGen"');
                }
 
                return true;
+       } */
+
+       function get_ssl_certificate_id() {
+               if ($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"]) {
+                       return sha1($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"] .
+                               $_SERVER["REDIRECT_SSL_CLIENT_V_START"] .
+                               $_SERVER["REDIRECT_SSL_CLIENT_V_END"] .
+                               $_SERVER["REDIRECT_SSL_CLIENT_S_DN"]);
+               }
+               return "";
+       }
+
+       function get_login_by_ssl_certificate($link) {
+
+               $cert_serial = db_escape_string(get_ssl_certificate_id());
+
+               if ($cert_serial) {
+                       $result = db_query($link, "SELECT login FROM ttrss_user_prefs, ttrss_users
+                               WHERE pref_name = 'SSL_CERT_SERIAL' AND value = '$cert_serial' AND
+                               owner_uid = ttrss_users.id");
+
+                       if (db_num_rows($result) != 0) {
+                               return db_escape_string(db_fetch_result($result, 0, "login"));
+                       }
+               }
+
+               return "";
+       }
+
+       function get_remote_user($link) {
+
+               if (defined('ALLOW_REMOTE_USER_AUTH') && ALLOW_REMOTE_USER_AUTH) {
+                       return db_escape_string($_SERVER["REMOTE_USER"]);
+               }
+
+               return db_escape_string(get_login_by_ssl_certificate($link));
+       }
+
+       function get_remote_fakepass($link) {
+               if (get_remote_user($link))
+                       return "******";
+               else
+                       return "";
        }
 
        function authenticate_user($link, $login, $password, $force_auth = false) {
                        $pwd_hash2 = encrypt_password($password, $login);
                        $login = db_escape_string($login);
 
-                       if (defined('ALLOW_REMOTE_USER_AUTH') && ALLOW_REMOTE_USER_AUTH
-                                       && $_SERVER["REMOTE_USER"] && $login != "admin") {
+                       $remote_user = get_remote_user($link);
+
+                       if ($remote_user && $remote_user == $login && $login != "admin") {
 
-                               $login = db_escape_string($_SERVER["REMOTE_USER"]);
+                               $login = $remote_user;
 
                                $query = "SELECT id,login,access_level,pwd_hash
                    FROM ttrss_users WHERE
                                        login = '$login'";
 
+                               if (defined('AUTO_CREATE_USER') && AUTO_CREATE_USER
+                                               && $_SERVER["REMOTE_USER"]) {
+                                       $result = db_query($link, $query);
+
+                                       // First login ?
+                                       if (db_num_rows($result) == 0) {
+                                               $query2 = "INSERT INTO ttrss_users
+                                                               (login,access_level,last_login,created)
+                                                               VALUES ('$login', 0, null, NOW())";
+                                               db_query($link, $query2);
+                                       }
+                               }
+
                        } else {
                                $query = "SELECT id,login,access_level,pwd_hash
                    FROM ttrss_users WHERE
                                db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
                                        $_SESSION["uid"]);
 
+
+                               // LemonLDAP can send user informations via HTTP HEADER
+                               if (defined('AUTO_CREATE_USER') && AUTO_CREATE_USER){
+                                       // update user name
+                                       $fullname = $_SERVER['HTTP_USER_NAME'] ? $_SERVER['HTTP_USER_NAME'] : $_SERVER['AUTHENTICATE_CN'];
+                                       if ($fullname){
+                                               $fullname = db_escape_string($fullname);
+                                               db_query($link, "UPDATE ttrss_users SET full_name = '$fullname' WHERE id = " .
+                                                       $_SESSION["uid"]);
+                                       }
+                                       // update user mail
+                                       $email = $_SERVER['HTTP_USER_MAIL'] ? $_SERVER['HTTP_USER_MAIL'] : $_SERVER['AUTHENTICATE_MAIL'];
+                                       if ($email){
+                                               $email = db_escape_string($email);
+                                               db_query($link, "UPDATE ttrss_users SET email = '$email' WHERE id = " .
+                                                       $_SESSION["uid"]);
+                                       }
+                               }
+
                                $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
                                $_SESSION["pwd_hash"] = db_fetch_result($result, 0, "pwd_hash");
 
                        }
 
                        if (!$_SESSION["uid"] || !validate_session($link)) {
-                               if (defined('ALLOW_REMOTE_USER_AUTH') && ALLOW_REMOTE_USER_AUTH
-                                       && $_SERVER["REMOTE_USER"] && defined('AUTO_LOGIN') && AUTO_LOGIN) {
-                                   authenticate_user($link,$_SERVER['REMOTE_USER'],null);
+
+                               if (get_remote_user($link) && AUTO_LOGIN) {
+                                   authenticate_user($link, get_remote_user($link), null);
                                    $_SESSION["ref_schema_version"] = get_schema_version($link, true);
                                } else {
                                    render_login_form($link, $mobile);
                                        setcookie("ttrss_lang", $_SESSION["language"],
                                                time() + SESSION_COOKIE_LIFETIME);
                                }
+
+                               // try to remove possible duplicates from feed counter cache
+//                             ccache_cleanup($link, $_SESSION["uid"]);
                        }
 
                } else {
                if (!$owner_uid) $owner_uid = $_SESSION['uid'];
                if (!$timestamp) $timestamp = '1970-01-01 0:00';
 
-               $user_tz_string = get_pref($link, 'USER_TIMEZONE', $owner_uid);
+               global $utc_tz;
+               global $tz_offset;
 
-               try {
-                       $user_tz = new DateTimeZone($user_tz_string);
-               } catch (Exception $e) {
-                       $user_tz = new DateTimeZone('UTC');
+               # We store date in UTC internally
+               $dt = new DateTime($timestamp, $utc_tz);
+
+               if ($tz_offset == -1) {
+
+                       $user_tz_string = get_pref($link, 'USER_TIMEZONE', $owner_uid);
+
+                       try {
+                               $user_tz = new DateTimeZone($user_tz_string);
+                       } catch (Exception $e) {
+                               $user_tz = $utc_tz;
+                       }
+
+                       $tz_offset = $user_tz->getOffset($dt);
                }
 
-               # We store date in UTC internally
-               $dt = new DateTime($timestamp, new DateTimeZone('UTC'));
-               $user_timestamp = $dt->format('U') + $user_tz->getOffset($dt);
+               $user_timestamp = $dt->format('U') + $tz_offset;
 
-               if (!$no_smart_dt && get_pref($link, 'HEADLINES_SMART_DATE', $owner_uid)) {
+               if (!$no_smart_dt) {
                        return smart_date_time($link, $user_timestamp,
-                               $user_tz->getOffset($dt), $owner_uid);
+                               $tz_offset, $owner_uid);
                } else {
                        if ($long)
                                $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid);
        }
 
        function sql_bool_to_bool($s) {
-               if ($s == "t" || $s == "1") {
+               if ($s == "t" || $s == "1" || $s == "true") {
                        return true;
                } else {
                        return false;
                        return "even";
        }
 
+       // Session caching removed due to causing wrong redirects to upgrade
+       // script when get_schema_version() is called on an obsolete session
+       // created on a previous schema version.
        function get_schema_version($link, $nocache = false) {
-               if (!$_SESSION["schema_version"] || $nocache) {
+               global $schema_version;
+
+               if (!$schema_version) {
                        $result = db_query($link, "SELECT schema_version FROM ttrss_version");
                        $version = db_fetch_result($result, 0, "schema_version");
-                       $_SESSION["schema_version"] = $version;
+                       $schema_version = $version;
                        return $version;
                } else {
-                       return $_SESSION["schema_version"];
+                       return $schema_version;
                }
        }
 
        function sanity_check($link) {
 
+               global $ERRORS;
+
                $error_code = 0;
-               $schema_version = get_schema_version($link);
+               $schema_version = get_schema_version($link, true);
 
                if ($schema_version != SCHEMA_VERSION) {
                        $error_code = 5;
                        $error_code = 12;
                }
 
-               if ($error_code != 0) {
-                       print_error_xml($error_code);
-                       return false;
-               } else {
-                       return true;
-               }
+               return array("code" => $error_code, "message" => $ERRORS[$error_code]);
        }
 
        function file_is_locked($filename) {
                if (strchr($omode, "l")) $data = array_merge($data, getLabelCounters($link));
                if (strchr($omode, "f")) $data = array_merge($data, getFeedCounters($link, $active_feed));
                if (strchr($omode, "t")) $data = array_merge($data, getTagCounters($link));
-               if (strchr($omode, "c")) {
-                       if (get_pref($link, 'ENABLE_FEED_CATS')) {
-                               $data = array_merge($data, getCategoryCounters($link));
-                       }
-               }
+               if (strchr($omode, "c")) $data = array_merge($data, getCategoryCounters($link));
 
                return $data;
        }
 
        function get_pgsql_version($link) {
                $result = db_query($link, "SELECT version() AS version");
-               $version = split(" ", db_fetch_result($result, 0, "version"));
+               $version = explode(" ", db_fetch_result($result, 0, "version"));
                return $version[1];
        }
 
-       function print_error_xml($code, $add_msg = "") {
-               global $ERRORS;
-
-               $error_msg = $ERRORS[$code];
-
-               if ($add_msg) {
-                       $error_msg = "$error_msg; $add_msg";
-               }
-
-               print "<rpc-reply>";
-               print "<error error-code=\"$code\" error-msg=\"$error_msg\"/>";
-               print "</rpc-reply>";
-       }
-
        /**
         * Subscribes the user to the given feed
         *
                                return "Unknown label ($label_id)";
                        }
 
-               } else if ($id > 0) {
+               } else if (is_numeric($id) && $id > 0) {
                        $result = db_query($link, "SELECT title FROM ttrss_feeds WHERE id = '$id'");
                        if (db_num_rows($result) == 1) {
                                return db_fetch_result($result, 0, "title");
 
                $params["theme"] = get_user_theme($link);
                $params["theme_options"] = get_user_theme_options($link);
-               $params["daemon_enabled"] = ENABLE_UPDATE_DAEMON;
 
                $params["sign_progress"] = theme_image($link, "images/indicator_white.gif");
                $params["sign_progress_tiny"] = theme_image($link, "images/indicator_tiny.gif");
                $data['last_article_id'] = getLastArticleId($link);
                $data['cdm_expanded'] = get_pref($link, 'CDM_EXPANDED');
 
-               if (ENABLE_UPDATE_DAEMON) {
+               if (file_exists(LOCK_DIRECTORY . "/update_daemon.lock")) {
 
                        $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
 
                return $data;
        }
 
-       function getSearchSql($link, $search, $match_on) {
+       function search_to_sql($link, $search, $match_on) {
 
                $search_query_part = "";
 
-               $keywords = split(" ", $search);
+               $keywords = explode(" ", $search);
                $query_keywords = array();
 
                foreach ($keywords as $k) {
                                $not = "";
                        }
 
-                       if (strpos($k, "@") === 0) {
+                       $commandpair = explode(":", mb_strtolower($k), 2);
+
+                       if ($commandpair[0] == "note" && $commandpair[1]) {
+
+                               if ($commandpair[1] == "true")
+                                       array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
+                               else
+                                       array_push($query_keywords, "($not (note IS NULL OR note = ''))");
+
+                       } else if ($commandpair[0] == "star" && $commandpair[1]) {
+
+                               if ($commandpair[1] == "true")
+                                       array_push($query_keywords, "($not (marked = true))");
+                               else
+                                       array_push($query_keywords, "($not (marked = false))");
+
+                       } else if ($commandpair[0] == "pub" && $commandpair[1]) {
+
+                               if ($commandpair[1] == "true")
+                                       array_push($query_keywords, "($not (published = true))");
+                               else
+                                       array_push($query_keywords, "($not (published = false))");
+
+                       } else if (strpos($k, "@") === 0) {
 
                                $user_tz_string = get_pref($link, 'USER_TIMEZONE', $_SESSION['uid']);
                                $orig_ts = strtotime(substr($k, 1));
-
                                $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
 
+                               //$k = date("Y-m-d", strtotime(substr($k, 1)));
+
                                array_push($query_keywords, "(".SUBSTRING_FOR_DATE."(updated,1,LENGTH('$k')) $not = '$k')");
                        } else if ($match_on == "both") {
                                array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
                return $search_query_part;
        }
 
-       function queryFeedHeadlines($link, $feed, $limit, $view_mode, $cat_view, $search, $search_mode, $match_on, $override_order = false, $offset = 0, $owner_uid = 0) {
+
+       function queryFeedHeadlines($link, $feed, $limit, $view_mode, $cat_view, $search, $search_mode, $match_on, $override_order = false, $offset = 0, $owner_uid = 0, $filter = false) {
 
                if (!$owner_uid) $owner_uid = $_SESSION["uid"];
 
                                                $search_query_part = "ref_id = -1 AND ";
 
                                } else {
-                                       $search_query_part = getSearchSql($link, $search, $match_on);
+                                       $search_query_part = search_to_sql($link, $search, $match_on);
                                        $search_query_part .= " AND ";
                                }
 
                                $search_query_part = "";
                        }
 
+                       if ($filter) {
+                               $filter_query_part = filter_to_sql($filter);
+                       } else {
+                               $filter_query_part = "";
+                       }
+
                        $view_query_part = "";
 
                        if ($view_mode == "adaptive" || $view_query_part == "noscores") {
                                if ($cat_view) {
                                        $feed_title = getCategoryTitle($link, $feed);
                                } else {
-                                       if ((int)$feed == $feed && $feed > 0) {
+                                       if (is_numeric($feed) && $feed > 0) {
                                                $result = db_query($link, "SELECT title,site_url,last_error
                                                        FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid");
 
                                        ttrss_user_entries.ref_id = ttrss_entries.id AND
                                        ttrss_user_entries.owner_uid = '$owner_uid' AND
                                        $search_query_part
+                                       $filter_query_part
                                        $view_query_part
                                        $query_strategy_part ORDER BY $order_by
                                        $limit_query_part $offset_query_part";
                        } else {
                                // browsing by tag
 
+                               $select_qpart = "SELECT DISTINCT " .
+                                                               "date_entered," .
+                                                               "guid," .
+                                                               "note," .
+                                                               "ttrss_entries.id as id," .
+                                                               "title," .
+                                                               "updated," .
+                                                               "unread," .
+                                                               "feed_id," .
+                                                               "orig_feed_id," .
+                                                               "marked," .
+                                                               "link," .
+                                                               "last_read," .
+                                                               SUBSTRING_FOR_DATE . "(last_read,1,19) as last_read_noms," .
+                                                               $vfeed_query_part .
+                                                               $content_query_part .
+                                                               SUBSTRING_FOR_DATE . "(updated,1,19) as updated_noms," .
+                                                               "score ";
+
                                $feed_kind = "Tags";
+                               $all_tags = explode(",", $feed);
+                               if ($search_mode == 'any') {
+                                       $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")";
+                                       $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags ";
+                                       $where_qpart = " WHERE " .
+                                                                  "ref_id = ttrss_entries.id AND " .
+                                                                  "ttrss_user_entries.owner_uid = $owner_uid AND " .
+                                                                  "post_int_id = int_id AND $tag_sql AND " .
+                                                                  $view_query_part .
+                                                                  $search_query_part .
+                                                                  $query_strategy_part . " ORDER BY $order_by " .
+                                                                  $limit_query_part;
 
-                               $result = db_query($link, "SELECT DISTINCT
-                                       date_entered,
-                                       guid,
-                                       note,
-                                       ttrss_entries.id as id,title,
-                                       updated,
-                                       unread,feed_id,orig_feed_id,
-                                       marked,link,last_read,
-                                       ".SUBSTRING_FOR_DATE."(last_read,1,19) as last_read_noms,
-                                       $vfeed_query_part
-                                       $content_query_part
-                                       ".SUBSTRING_FOR_DATE."(updated,1,19) as updated_noms,
-                                       score
-                                       FROM
-                                               ttrss_entries,ttrss_user_entries,ttrss_tags
-                                       WHERE
-                                               ref_id = ttrss_entries.id AND
-                                               ttrss_user_entries.owner_uid = '$owner_uid' AND
-                                               post_int_id = int_id AND tag_name = '$feed' AND
-                                               $view_query_part
-                                               $search_query_part
-                                               $query_strategy_part ORDER BY $order_by
-                                       $limit_query_part");
+                               } else {
+                                       $i = 1;
+                                       $sub_selects = array();
+                                       $sub_ands = array();
+                                       foreach ($all_tags as $term) {
+                                               array_push($sub_selects, "(SELECT post_int_id from ttrss_tags WHERE tag_name = " . db_quote($term) . " AND owner_uid = $owner_uid) as A$i");
+                                               $i++;
+                                       }
+                                       if ($i > 2) {
+                                               $x = 1;
+                                               $y = 2;
+                                               do {
+                                                       array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id");
+                                                       $x++;
+                                                       $y++;
+                                               } while ($y < $i);
+                                       }
+                                       array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid");
+                                       array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id");
+                                       $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries";
+                                       $where_qpart = " WHERE " . implode(" AND ", $sub_ands);
+                               }
+                               //                              error_log("TAG SQL: " . $tag_sql);
+                               // $tag_sql = "tag_name = '$feed'";   DEFAULT way
+
+                               //                              error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]");
+                               $result = db_query($link, $select_qpart . $from_qpart . $where_qpart);
                        }
 
                        return array($result, $feed_title, $feed_site_url, $last_error);
        function generate_syndicated_feed($link, $owner_uid, $feed, $is_cat,
                $limit, $search, $search_mode, $match_on, $view_mode = false) {
 
-               $note_style =   "float : right; background-color : #fff7d5; border-width : 1px; ".
+               require_once "lib/MiniTemplator.class.php";
+
+               $note_style =   "background-color : #fff7d5;
+                       border-width : 1px; ".
                        "padding : 5px; border-style : dashed; border-color : #e7d796;".
                        "margin-bottom : 1em; color : #9a8c59;";
 
                $feed_site_url = $qfh_ret[2];
                $last_error = $qfh_ret[3];
 
-//             if (!$feed_site_url) $feed_site_url = "http://localhost/";
+               $feed_self_url = get_self_url_prefix() .
+                       "/backend.php?op=rss&id=-2&key=" .
+                       get_feed_access_key($link, -2, false);
 
-               print "<?xml version=\"1.0\" encoding=\"utf-8\"?>
-                       <?xml-stylesheet type=\"text/xsl\" href=\"rss.xsl\"?>
-                       <rss version=\"2.0\">
-                       <channel>
-                       <title>$feed_title</title>
-                       <link>$feed_site_url</link>
-                       <description>Feed generated by Tiny Tiny RSS</description>";
+               if (!$feed_site_url) $feed_site_url = get_self_url_prefix();
 
-               while ($line = db_fetch_assoc($result)) {
-                       print "<item>";
-                       print "<guid>" . htmlspecialchars($line["guid"]) . "</guid>";
-                       print "<link>" . htmlspecialchars($line["link"]) . "</link>";
+               $tpl = new MiniTemplator;
 
-                       $tags = get_article_tags($link, $line["id"], $owner_uid);
+               $tpl->readTemplateFromFile("templates/generated_feed.txt");
 
-                       foreach ($tags as $tag) {
-                               print "<category>" . htmlspecialchars($tag) . "</category>";
-                       }
+               $tpl->setVariable('FEED_TITLE', $feed_title);
+               $tpl->setVariable('VERSION', VERSION);
+               $tpl->setVariable('FEED_URL', htmlspecialchars($feed_self_url));
 
-                       $rfc822_date = date('r', strtotime($line["updated"]));
+               if (PUBSUBHUBBUB_HUB && $feed == -2) {
+                       $tpl->setVariable('HUB_URL', htmlspecialchars(PUBSUBHUBBUB_HUB));
+                       $tpl->addBlock('feed_hub');
+               }
 
-                       print "<pubDate>$rfc822_date</pubDate>";
+               $tpl->setVariable('SELF_URL', htmlspecialchars(get_self_url_prefix()));
 
-                       if ($line["author"]) {
-                               print "<author>" . htmlspecialchars($line["author"]) . "</author>";
+               while ($line = db_fetch_assoc($result)) {
+                       $tpl->setVariable('ARTICLE_ID', htmlspecialchars($line['link']));
+                       $tpl->setVariable('ARTICLE_LINK', htmlspecialchars($line['link']));
+                       $tpl->setVariable('ARTICLE_TITLE', htmlspecialchars($line['title']));
+                       $tpl->setVariable('ARTICLE_EXCERPT',
+                               truncate_string(strip_tags($line["content_preview"]), 100, '...'));
+
+                       $content = sanitize_rss($link, $line["content_preview"], false, $owner_uid);
+
+                       if ($line['note']) {
+                               $content = "<div style=\"$note_style\">Article note: " . $line['note'] . "</div>" .
+                                       $content;
                        }
 
-                       print "<title><![CDATA[" .
-                               htmlspecialchars($line["title"]) . "]]></title>";
+                       $tpl->setVariable('ARTICLE_CONTENT', $content);
 
-                       print "<description><![CDATA[";
+                       $tpl->setVariable('ARTICLE_UPDATED', date('c', strtotime($line["updated"])));
+                       $tpl->setVariable('ARTICLE_AUTHOR', htmlspecialchars($line['author']));
 
-                       if ($line["note"]) {
-                               print "<div style='$note_style'>";
-                               print $line["note"];
-                               print "</div>";
-                       }
+                       $tags = get_article_tags($link, $line["id"], $owner_uid);
 
-                       print sanitize_rss($link, $line["content_preview"], false, $owner_uid);
-                       print "]]></description>";
+                       foreach ($tags as $tag) {
+                               $tpl->setVariable('ARTICLE_CATEGORY', htmlspecialchars($tag));
+                               $tpl->addBlock('category');
+                       }
 
                        $enclosures = get_article_enclosures($link, $line["id"]);
 
                                $type = htmlspecialchars($e['content_type']);
                                $url = htmlspecialchars($e['content_url']);
                                $length = $e['duration'];
-                               print "<enclosure url=\"$url\" type=\"$type\" length=\"$length\"/>";
+
+                               $tpl->setVariable('ARTICLE_ENCLOSURE_URL', $url);
+                               $tpl->setVariable('ARTICLE_ENCLOSURE_TYPE', $type);
+                               $tpl->setVariable('ARTICLE_ENCLOSURE_LENGTH', $length);
+
+                               $tpl->addBlock('enclosure');
                        }
 
-                       print "</item>";
-               }
+                       $tpl->addBlock('entry');
+               }
+
+               $tmp = "";
 
-               print "</channel></rss>";
+               $tpl->addBlock('feed');
+               $tpl->generateOutputToString($tmp);
 
+               print $tmp;
        }
 
        function getCategoryTitle($link, $cat_id) {
 
                $res = trim($str); if (!$res) return '';
 
-               if (get_pref($link, "STRIP_UNSAFE_TAGS", $owner) || $force_strip_tags) {
-                       $res = $purifier->purify($res);
-               }
+//             if (get_pref($link, "STRIP_UNSAFE_TAGS", $owner) || $force_strip_tags) {
+               $res = $purifier->purify($res);
+//             }
 
                if (get_pref($link, "STRIP_IMAGES", $owner)) {
                        $res = preg_replace('/<img[^>]+>/is', '', $res);
                        published = NOT published,last_read = NOW()
                        WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]);
                }
+
+               if (PUBSUBHUBBUB_HUB) {
+                       $rss_link = get_self_url_prefix() .
+                               "/backend.php?op=rss&id=-2&key=" .
+                               get_feed_access_key($link, -2, false);
+
+                       $p = new Publisher(PUBSUBHUBBUB_HUB);
+
+                       $pubsub_result = $p->publish_update($rss_link);
+               }
        }
 
        function catchupArticlesById($link, $ids, $cmode, $owner_uid = false) {
                        mb_strtolower(strip_tags($title), 'utf-8'));
        }
 
-       function print_headline_subtoolbar($link, $feed_site_url, $feed_title,
+       function format_headline_subtoolbar($link, $feed_site_url, $feed_title,
                        $feed_id, $is_cat, $search, $match_on,
                        $search_mode, $view_mode, $error) {
 
-                       $page_prev_link = "viewFeedGoPage(-1)";
-                       $page_next_link = "viewFeedGoPage(1)";
-                       $page_first_link = "viewFeedGoPage(0)";
+               $page_prev_link = "viewFeedGoPage(-1)";
+               $page_next_link = "viewFeedGoPage(1)";
+               $page_first_link = "viewFeedGoPage(0)";
 
-                       $catchup_page_link = "catchupPage()";
-                       $catchup_feed_link = "catchupCurrentFeed()";
-                       $catchup_sel_link = "catchupSelection()";
+               $catchup_page_link = "catchupPage()";
+               $catchup_feed_link = "catchupCurrentFeed()";
+               $catchup_sel_link = "catchupSelection()";
 
-                       $archive_sel_link = "archiveSelection()";
-                       $delete_sel_link = "deleteSelection()";
+               $archive_sel_link = "archiveSelection()";
+               $delete_sel_link = "deleteSelection()";
 
-                       $sel_all_link = "selectArticles('all')";
-                       $sel_unread_link = "selectArticles('unread')";
-                       $sel_none_link = "selectArticles('none')";
-                       $sel_inv_link = "selectArticles('invert')";
+               $sel_all_link = "selectArticles('all')";
+               $sel_unread_link = "selectArticles('unread')";
+               $sel_none_link = "selectArticles('none')";
+               $sel_inv_link = "selectArticles('invert')";
 
-                       $tog_unread_link = "selectionToggleUnread()";
-                       $tog_marked_link = "selectionToggleMarked()";
-                       $tog_published_link = "selectionTogglePublished()";
+               $tog_unread_link = "selectionToggleUnread()";
+               $tog_marked_link = "selectionToggleMarked()";
+               $tog_published_link = "selectionTogglePublished()";
 
-                       print "<div id=\"subtoolbar_main\">";
+               $reply = "<div id=\"subtoolbar_main\">";
 
-                       print __('Select:')."
-                               <a href=\"#\" onclick=\"$sel_all_link\">".__('All')."</a>,
-                               <a href=\"#\" onclick=\"$sel_unread_link\">".__('Unread')."</a>,
-                               <a href=\"#\" onclick=\"$sel_inv_link\">".__('Invert')."</a>,
-                               <a href=\"#\" onclick=\"$sel_none_link\">".__('None')."</a></li>";
+               $reply .= __('Select:')."
+                       <a href=\"#\" onclick=\"$sel_all_link\">".__('All')."</a>,
+                       <a href=\"#\" onclick=\"$sel_unread_link\">".__('Unread')."</a>,
+                       <a href=\"#\" onclick=\"$sel_inv_link\">".__('Invert')."</a>,
+                       <a href=\"#\" onclick=\"$sel_none_link\">".__('None')."</a></li>";
 
-                       print " ";
+               $reply .= " ";
 
-                       print "<select dojoType=\"dijit.form.Select\"
-                               onchange=\"headlineActionsChange(this)\">";
-                       print "<option value=\"false\">".__('Actions...')."</option>";
+               $reply .= "<select dojoType=\"dijit.form.Select\"
+                       onchange=\"headlineActionsChange(this)\">";
+               $reply .= "<option value=\"false\">".__('Actions...')."</option>";
 
-                       print "<option value=\"0\" disabled=\"1\">".__('Selection toggle:')."</option>";
+               $reply .= "<option value=\"0\" disabled=\"1\">".__('Selection toggle:')."</option>";
 
-                       print "<option value=\"$tog_unread_link\">".__('Unread')."</option>
-                               <option value=\"$tog_marked_link\">".__('Starred')."</option>
-                               <option value=\"$tog_published_link\">".__('Published')."</option>";
+               $reply .= "<option value=\"$tog_unread_link\">".__('Unread')."</option>
+                       <option value=\"$tog_marked_link\">".__('Starred')."</option>
+                       <option value=\"$tog_published_link\">".__('Published')."</option>";
 
-                       print "<option value=\"0\" disabled=\"1\">".__('Selection:')."</option>";
+               $reply .= "<option value=\"0\" disabled=\"1\">".__('Selection:')."</option>";
 
-                       print "<option value=\"$catchup_sel_link\">".__('Mark as read')."</option>";
+               $reply .= "<option value=\"$catchup_sel_link\">".__('Mark as read')."</option>";
 
-                       if ($feed_id != "0") {
-                               print "<option value=\"$archive_sel_link\">".__('Archive')."</option>";
-                       } else {
-                               print "<option value=\"$archive_sel_link\">".__('Move back')."</option>";
-                               print "<option value=\"$delete_sel_link\">".__('Delete')."</option>";
+               if ($feed_id != "0") {
+                       $reply .= "<option value=\"$archive_sel_link\">".__('Archive')."</option>";
+               } else {
+                       $reply .= "<option value=\"$archive_sel_link\">".__('Move back')."</option>";
+                       $reply .= "<option value=\"$delete_sel_link\">".__('Delete')."</option>";
 
-                       }
+               }
 
-                       print "<option value=\"emailArticle(false)\">".__('Forward by email').
-                               "</option>";
+               $reply .= "<option value=\"emailArticle(false)\">".__('Forward by email').
+                       "</option>";
 
-                       $rss_link = htmlspecialchars(get_self_url_prefix() .
-                               "/backend.php?op=rss&id=$feed_id&is_cat=$is_cat&view_mode=$view_mode$search_q");
+               if ($is_cat) $cat_q = "&is_cat=$is_cat";
 
-                       print "<option value=\"0\" disabled=\"1\">".__('Feed:')."</option>";
+               if ($search) {
+                       $search_q = "&q=$search&m=$match_on&smode=$search_mode";
+               } else {
+                       $search_q = "";
+               }
 
-                       print "<option value=\"catchupPage()\">".__('Mark as read')."</option>";
+               $rss_link = htmlspecialchars(get_self_url_prefix() .
+                       "/backend.php?op=rss&id=$feed_id$cat_q$search_q");
 
-                       print "<option value=\"displayDlg('generatedFeed', '$feed_id:$is_cat:$rss_link')\">".__('View as RSS')."</option>";
+               $reply .= "<option value=\"0\" disabled=\"1\">".__('Feed:')."</option>";
 
-                       print "</select>";
+               $reply .= "<option value=\"catchupPage()\">".__('Mark as read')."</option>";
 
-                       print "</div>";
+               $reply .= "<option value=\"displayDlg('generatedFeed', '$feed_id:$is_cat:$rss_link')\">".__('View as RSS')."</option>";
 
-                       print "<div id=\"subtoolbar_ftitle\">";
+               $reply .= "</select>";
 
-                       if ($feed_site_url) {
-                               $target = "target=\"_blank\"";
-                               print "<a title=\"".__("Visit the website")."\" $target href=\"$feed_site_url\">".
-                                       truncate_string($feed_title,30)."</a>";
+               $reply .= "</div>";
 
-                               if ($error) {
-                                       print " (<span class=\"error\" title=\"$error\">Error</span>)";
-                               }
+               $reply .= "<div id=\"subtoolbar_ftitle\">";
 
-                       } else {
-                               if ($feed_id < -10) {
-                                       $label_id = -11-$feed_id;
+               if ($feed_site_url) {
+                       $target = "target=\"_blank\"";
+                       $reply .= "<a title=\"".__("Visit the website")."\" $target href=\"$feed_site_url\">".
+                               truncate_string($feed_title,30)."</a>";
 
-                                       $result = db_query($link, "SELECT fg_color, bg_color
-                                               FROM ttrss_labels2 WHERE id = '$label_id' AND owner_uid = " .
-                                               $_SESSION["uid"]);
+                       if ($error) {
+                               $reply .= " (<span class=\"error\" title=\"$error\">Error</span>)";
+                       }
 
-                                       if (db_num_rows($result) != 0) {
-                                               $fg_color = db_fetch_result($result, 0, "fg_color");
-                                               $bg_color = db_fetch_result($result, 0, "bg_color");
+               } else {
+                       if ($feed_id < -10) {
+                               $label_id = -11-$feed_id;
 
-                                               print "<span style='background : $bg_color; color : $fg_color'>";
-                                               print $feed_title;
-                                               print "</span>";
-                                       } else {
-                                               print $feed_title;
-                                       }
+                               $result = db_query($link, "SELECT fg_color, bg_color
+                                       FROM ttrss_labels2 WHERE id = '$label_id' AND owner_uid = " .
+                                       $_SESSION["uid"]);
+
+                               if (db_num_rows($result) != 0) {
+                                       $fg_color = db_fetch_result($result, 0, "fg_color");
+                                       $bg_color = db_fetch_result($result, 0, "bg_color");
 
+                                       $reply .= "<span style=\"background : $bg_color; color : $fg_color\" >";
+                                       $reply .= $feed_title;
+                                       $reply .= "</span>";
                                } else {
-                                       print $feed_title;
+                                       $reply .= $feed_title;
                                }
-                       }
 
-                       if ($search) {
-                               $search_q = "&q=$search&m=$match_on&smode=$search_mode";
                        } else {
-                               $search_q = "";
+                               $reply .= $feed_title;
                        }
+               }
 
-                       // Adaptive doesn't really make any sense for generated feeds
-                       // All Articles is the default, so no need to insert it either
-                       if ($view_mode == "adaptive" || $view_mode == "all_articles")
-                               $view_mode = "";
-                       else
-                               $view_mode = "&view-mode=$view_mode";
-
-                       print "
-                               <a href=\"#\"
-                                       title=\"".__("View as RSS feed")."\"
-                                       onclick=\"displayDlg('generatedFeed', '$feed_id:$is_cat:$rss_link')\">
-                                       <img class=\"noborder\" style=\"vertical-align : middle\" src=\"images/feed-icon-12x12.png\"></a>";
+               $reply .= "
+                       <a href=\"#\"
+                               title=\"".__("View as RSS feed")."\"
+                               onclick=\"displayDlg('generatedFeed', '$feed_id:$is_cat:$rss_link')\">
+                               <img class=\"noborder\" style=\"vertical-align : middle\" src=\"images/feed-icon-12x12.png\"></a>";
 
-                       print "</div>";
+               $reply .= "</div>";
 
-               }
+               return $reply;
+       }
 
        function outputFeedList($link, $special = true) {
 
                        } else {
 
                                $entry .= "<object type=\"application/x-shockwave-flash\"
-                                       data=\"extras/button/musicplayer.swf?song_url=$url\"
+                                       data=\"lib/button/musicplayer.swf?song_url=$url\"
                                        width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
                                        <param name=\"movie\"
-                                               value=\"extras/button/musicplayer.swf?song_url=$url\" />
+                                               value=\"lib/button/musicplayer.swf?song_url=$url\" />
                                        </object>";
                        }
                }
                return $entry;
        }
 
-       function outputArticleXML($link, $id, $feed_id, $mark_as_read = true,
-               $zoom_mode = false) {
+       function format_article($link, $id, $mark_as_read = true, $zoom_mode = false) {
+
+               $rv = array();
+
+               $rv['id'] = $id;
 
                /* we can figure out feed_id from article id anyway, why do we
                 * pass feed_id here? let's ignore the argument :( */
 
                $feed_id = (int) db_fetch_result($result, 0, "feed_id");
 
-               if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
+               $rv['feed_id'] = $feed_id;
+
+               //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
 
                $result = db_query($link, "SELECT rtl_content, always_display_enclosures FROM ttrss_feeds
                        WHERE id = '$feed_id' AND owner_uid = " . $_SESSION["uid"]);
 
                        if ($zoom_mode) {
                                header("Content-Type: text/html");
-                               print "<html><head>
+                               $rv['content'] .= "<html><head>
                                                <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
                                                <title>Tiny Tiny RSS - ".$line["title"]."</title>
                                                <link rel=\"stylesheet\" type=\"text/css\" href=\"tt-rss.css\">
                                        </head><body>";
                        }
 
-                       print "<div id=\"PTITLE-$id\" style=\"display : none\">" .
+                       $rv['content'] .= "<div id=\"PTITLE-$id\" style=\"display : none\">" .
                                truncate_string(strip_tags($line['title']), 15) . "</div>";
 
-                       print "<div class=\"postReply\" id=\"POST-$id\">";
-
-                       /* print "<div dojoType=\"dijit.Menu\" style=\"display: none;\"
-                               targetNodeIds=\"POSTHDR-$id\">";
-                       print "<div onclick=\"postOpenInNewTab(event, $id)\"
-                               dojoType=\"dijit.MenuItem\">".__('View in a new tab')."</div>";
-                       print "<div dojoType=\"dijit.MenuSeparator\"></div>";
-                       print "<div onclick=\"openArticleInNewWindow($id)\"
-                               dojoType=\"dijit.MenuItem\">".__('Open original article')."</div>";
-                       print "</div>"; */
+                       $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
 
-                       print "<div onclick=\"return postClicked(event, $id)\"
+                       $rv['content'] .= "<div onclick=\"return postClicked(event, $id)\"
                                class=\"postHeader\" id=\"POSTHDR-$id\">";
 
                        $entry_author = $line["author"];
                        $parsed_updated = make_local_datetime($link, $line["updated"], true,
                                false, true);
 
-                       print "<div class=\"postDate$rtl_class\">$parsed_updated</div>";
+                       $rv['content'] .= "<div class=\"postDate$rtl_class\">$parsed_updated</div>";
 
                        if ($line["link"]) {
-                               print "<div clear='both'><a target='_blank'
+                               $rv['content'] .= "<div clear='both'><a target='_blank'
                                        title=\"".htmlspecialchars($line['title'])."\"
                                        href=\"" .
                                        $line["link"] . "\">" .
                                        truncate_string($line["title"], 100) .
                                        "<span class='author'>$entry_author</span></a></div>";
                        } else {
-                               print "<div clear='both'>" . $line["title"] . "$entry_author</div>";
+                               $rv['content'] .= "<div clear='both'>" . $line["title"] . "$entry_author</div>";
                        }
 
-                       $tags_str = format_tags_string(get_article_tags($link, $id), $id);
+                       $tags = get_article_tags($link, $id);
+                       $tags_str = format_tags_string($tags, $id);
+                       $tags_str_full = join(", ", $tags);
+
+                       if (!$tags_str_full) $tags_str_full = __("no tags");
 
                        if (!$entry_comments) $entry_comments = "&nbsp;"; # placeholder
 
-                       print "<div style='float : right'>
+                       $rv['content'] .= "<div style='float : right'>
                                <img src='".theme_image($link, 'images/tag.png')."'
                                class='tagsPic' alt='Tags' title='Tags'>&nbsp;";
 
                        if (!$zoom_mode) {
-                               print "<span id=\"ATSTR-$id\">$tags_str</span>
+                               $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
                                        <a title=\"".__('Edit tags for this article')."\"
                                        href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
 
-                               print "<img src=\"".theme_image($link, 'images/art-zoom.png')."\"
+                               $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
+                                       id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
+                                       position=\"below\">$tags_str_full</div>";
+
+                               $rv['content'] .= "<img src=\"".theme_image($link, 'images/art-zoom.png')."\"
                                                class='tagsPic' style=\"cursor : pointer\"
                                                onclick=\"postOpenInNewTab(event, $id)\"
                                                alt='Zoom' title='".__('Open article in new tab')."'>";
 
                                //$note_escaped = htmlspecialchars($line['note'], ENT_QUOTES);
 
-                               print "<img src=\"".theme_image($link, 'images/art-pub-note.png')."\"
+                               $rv['content'] .= "<img src=\"".theme_image($link, 'images/art-pub-note.png')."\"
                                                class='tagsPic' style=\"cursor : pointer\"
                                                onclick=\"editArticleNote($id)\"
                                                alt='PubNote' title='".__('Edit article note')."'>";
 
                                if (DIGEST_ENABLE) {
-                                       print "<img src=\"".theme_image($link, 'images/art-email.png')."\"
+                                       $rv['content'] .= "<img src=\"".theme_image($link, 'images/art-email.png')."\"
                                                class='tagsPic' style=\"cursor : pointer\"
                                                onclick=\"emailArticle($id)\"
                                                alt='Zoom' title='".__('Forward by email')."'>";
                                }
 
                                if (ENABLE_TWEET_BUTTON) {
-                                       print "<img src=\"".theme_image($link, 'images/art-tweet.png')."\"
+                                       $rv['content'] .= "<img src=\"".theme_image($link, 'images/art-tweet.png')."\"
                                                        class='tagsPic' style=\"cursor : pointer\"
                                                        onclick=\"tweetArticle($id)\"
                                                        alt='Zoom' title='".__('Share on Twitter')."'>";
                                }
 
-                               print "<img src=\"".theme_image($link, 'images/digest_checkbox.png')."\"
+                               $rv['content'] .= "<img src=\"".theme_image($link, 'images/digest_checkbox.png')."\"
                                                class='tagsPic' style=\"cursor : pointer\"
                                                onclick=\"closeArticlePanel($id)\"
                                                alt='Zoom' title='".__('Close this panel')."'>";
 
                        } else {
                                $tags_str = strip_tags($tags_str);
-                               print "<span id=\"ATSTR-$id\">$tags_str</span>";
+                               $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
                        }
-                       print "</div>";
-                       print "<div clear='both'>$entry_comments</div>";
+                       $rv['content'] .= "</div>";
+                       $rv['content'] .= "<div clear='both'>$entry_comments</div>";
 
                        if ($line["orig_feed_id"]) {
 
 
                                if (db_num_rows($tmp_result) != 0) {
 
-                                       print "<div clear='both'>";
-                                       print __("Originally from:");
+                                       $rv['content'] .= "<div clear='both'>";
+                                       $rv['content'] .= __("Originally from:");
 
-                                       print "&nbsp;";
+                                       $rv['content'] .= "&nbsp;";
 
                                        $tmp_line = db_fetch_assoc($tmp_result);
 
-                                       print "<a target='_blank'
+                                       $rv['content'] .= "<a target='_blank'
                                                href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
                                                $tmp_line['title'] . "</a>";
 
-                                       print "&nbsp;";
+                                       $rv['content'] .= "&nbsp;";
 
-                                       print "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
-                                       print "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.gif'></a>";
+                                       $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
+                                       $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.gif'></a>";
 
-                                       print "</div>";
+                                       $rv['content'] .= "</div>";
                                }
                        }
 
-                       print "</div>";
+                       $rv['content'] .= "</div>";
 
-                       print "<div class=\"postIcon\">" .
+                       $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
+                               if ($line['note']) {
+                                       $rv['content'] .= format_article_note($id, $line['note']);
+                               }
+                       $rv['content'] .= "</div>";
+
+                       $rv['content'] .= "<div class=\"postIcon\">" .
                                "<a target=\"_blank\" title=\"".__("Visit the website")."\"$
                                href=\"".htmlspecialchars($feed_site_url)."\">".
                                $feed_icon . "</a></div>";
 
-                       print "<div id=\"POSTNOTE-$id\">";
-                               if ($line['note']) {
-                                       print format_article_note($id, $line['note']);
-                               }
-                       print "</div>";
-
-                       print "<div class=\"postContent\">";
+                       $rv['content'] .= "<div class=\"postContent\">";
 
                        $article_content = sanitize_rss($link, $line["content"], false, false,
                                $feed_site_url);
 
-                       print $article_content;
+                       $rv['content'] .= $article_content;
 
-                       print_article_enclosures($link, $id, $always_display_enclosures,
-                               $article_content);
+                       $rv['content'] .= format_article_enclosures($link, $id,
+                               $always_display_enclosures, $article_content);
 
-                       print "</div>";
+                       $rv['content'] .= "</div>";
 
-                       print "</div>";
+                       $rv['content'] .= "</div>";
 
                }
 
-               if (!$zoom_mode) {
-                       print "]]></article>";
-               } else {
-                       print "
+               if ($zoom_mode) {
+                       $rv['content'] .= "
                                <div style=\"text-align : center\">
                                <button onclick=\"return window.close()\">".
                                        __("Close this window")."</button></div>";
-                       print "</body></html>";
-
+                       $rv['content'] .= "</body></html>";
                }
 
+               return $rv;
+
        }
 
-       function outputHeadlinesList($link, $feed, $subop, $view_mode, $limit, $cat_view,
+       function format_headlines_list($link, $feed, $subop, $view_mode, $limit, $cat_view,
                                        $next_unread_feed, $offset, $vgr_last_feed = false,
                                        $override_order = false) {
 
                $disable_cache = false;
 
+               $reply = array();
+
                $timing_info = getmicrotime();
 
                $topmost_article_ids = array();
 
-               if (!$offset) {
-                       $offset = 0;
-               }
-
+               if (!$offset) $offset = 0;
                if ($subop == "undefined") $subop = "";
 
-               $subop_split = split(":", $subop);
+               $subop_split = explode(":", $subop);
 
-               if ($subop == "CatchupSelected") {
-                       $ids = split(",", db_escape_string($_REQUEST["ids"]));
+/*             if ($subop == "CatchupSelected") {
+                       $ids = explode(",", db_escape_string($_REQUEST["ids"]));
                        $cmode = sprintf("%d", $_REQUEST["cmode"]);
 
                        catchupArticlesById($link, $ids, $cmode);
-               }
+               } */
 
-               if ($subop == "ForceUpdate" && sprintf("%d", $feed) > 0) {
+               if ($subop == "ForceUpdate" && $feed && is_numeric($feed) > 0) {
                        update_rss_feed($link, $feed, true);
                }
 
 
                // FIXME: might break tag display?
 
-               if ($feed > 0 && !$cat_view) {
+               if (is_numeric($feed) && $feed > 0 && !$cat_view) {
                        $result = db_query($link,
                                "SELECT id FROM ttrss_feeds WHERE id = '$feed' LIMIT 1");
 
                        if (db_num_rows($result) == 0) {
-                               print "<div align='center'>".__('Feed not found.')."</div>";
-                               return;
+                               $reply['content'] = "<div align='center'>".__('Feed not found.')."</div>";
                        }
                }
 
                        $rtl_content = false;
                }
 
-               /// START /////////////////////////////////////////////////////////////////////////////////
-
                @$search = db_escape_string($_REQUEST["query"]);
 
                if ($search) {
                        $match_on = "both";
                }
 
-               $real_offset = $offset * $limit;
-
                if ($_REQUEST["debug"]) $timing_info = print_checkpoint("H0", $timing_info);
 
+//             error_log("format_headlines_list: [" . $feed . "] subop [" . $subop . "]");
+               if( $search_mode == '' && $subop != '' ){
+                   $search_mode = $subop;
+               }
+//             error_log("search_mode: " . $search_mode);
                $qfh_ret = queryFeedHeadlines($link, $feed, $limit, $view_mode, $cat_view,
-                       $search, $search_mode, $match_on, $override_order, $real_offset);
+                       $search, $search_mode, $match_on, $override_order, $offset);
 
                if ($_REQUEST["debug"]) $timing_info = print_checkpoint("H1", $timing_info);
 
 
                $vgroup_last_feed = $vgr_last_feed;
 
-/*             if ($feed == -2) {
-                       $feed_site_url = article_publish_url($link);
-               } */
-
-               /// STOP //////////////////////////////////////////////////////////////////////////////////
-
-               print "<toolbar><![CDATA[";
-
                if (!$offset) {
-//                     print "<div id=\"headlinesContainer\" $rtl_tag>";
-
-                       if (!$result) {
-                               print "<div align='center'>".__("Could not display feed (query failed). Please check label match syntax or local configuration.")."</div>";
-                               return;
-                       }
 
                        if (db_num_rows($result) > 0) {
-                               print_headline_subtoolbar($link, $feed_site_url, $feed_title,
+                               $reply['toolbar'] = format_headline_subtoolbar($link, $feed_site_url,
+                                       $feed_title,
                                        $feed, $cat_view, $search, $match_on, $search_mode, $view_mode,
                                        $last_error);
-
-//                             print "<div id=\"headlinesInnerContainer\" onscroll=\"headlines_scroll_handler()\">";
-
                        }
                }
 
-               print "]]></toolbar><content><![CDATA[";
-
                $headlines_count = db_num_rows($result);
 
                if (db_num_rows($result) > 0) {
 
-                       $lnum = $limit*$offset;
+                       $lnum = $offset;
 
                        $num_unread = 0;
                        $cur_feed_title = '';
 
                        $fresh_intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE") * 60 * 60;
 
+                       if ($_REQUEST["debug"]) $timing_info = print_checkpoint("PS", $timing_info);
+
                        while ($line = db_fetch_assoc($result)) {
 
                                $class = ($lnum % 2) ? "even" : "odd";
 
                                                        $vf_catchup_link = "(<a onclick='javascript:catchupFeedInGroup($feed_id);' href='#'>".__('mark as read')."</a>)";
 
-                                                       print "<div class='cdmFeedTitle'>".
+                                                       $reply['content'] .= "<div class='cdmFeedTitle'>".
                                                                "<div style=\"float : right\">$feed_icon_img</div>".
                                                                "<a href=\"#\" onclick=\"viewfeed($feed_id)\">".
                                                                $line["feed_title"]."</a> $vf_catchup_link</div>";
                                        $mouseover_attrs = "onmouseover='postMouseIn($id)'
                                                onmouseout='postMouseOut($id)'";
 
-                                       print "<div class='$class' id='RROW-$id' $mouseover_attrs>";
+                                       $reply['content'] .= "<div class='$class' id='RROW-$id' $mouseover_attrs>";
 
-                                       print "<div class='hlUpdPic'>$update_pic</div>";
+                                       $reply['content'] .= "<div class='hlUpdPic'>$update_pic</div>";
 
-                                       print "<div class='hlLeft'>";
+                                       $reply['content'] .= "<div class='hlLeft'>";
 
-                                       print "<input type=\"checkbox\" onclick=\"tSR(this)\"
+                                       $reply['content'] .= "<input type=\"checkbox\" onclick=\"tSR(this)\"
                                                        id=\"RCHK-$id\">";
 
-                                       print "$marked_pic";
-                                       print "$published_pic";
+                                       $reply['content'] .= "$marked_pic";
+                                       $reply['content'] .= "$published_pic";
 
-                                       print "</div>";
+                                       $reply['content'] .= "</div>";
 
-                                       print "<div onclick='return hlClicked(event, $id)'
+                                       $reply['content'] .= "<div onclick='return hlClicked(event, $id)'
                                                class=\"hlTitle\"><span class='hlContent$hlc_suffix'>";
-                                       print "<a id=\"RTITLE-$id\"
+                                       $reply['content'] .= "<a id=\"RTITLE-$id\"
                                                href=\"" . htmlspecialchars($line["link"]) . "\"
-                                               onclick=\"return false;\">" .
+                                               onclick=\"\">" .
                                                $line["title"];
 
                                        if (get_pref($link, 'SHOW_CONTENT_PREVIEW')) {
                                                if ($content_preview) {
-                                                       print "<span class=\"contentPreview\"> - $content_preview</span>";
+                                                       $reply['content'] .= "<span class=\"contentPreview\"> - $content_preview</span>";
                                                }
                                        }
 
-                                       print "</a></span>";
+                                       $reply['content'] .= "</a></span>";
 
-                                       print $labels_str;
+                                       $reply['content'] .= $labels_str;
 
-                                       /* if (!get_pref($link, 'VFEED_GROUP_BY_FEED')) {
+                                       if (!get_pref($link, 'VFEED_GROUP_BY_FEED') &&
+                                               defined('_SHOW_FEED_TITLE_IN_VFEEDS')) {
                                                if (@$line["feed_title"]) {
-                                                       print "<span class=\"hlFeed\">
+                                                       $reply['content'] .= "<span class=\"hlFeed\">
                                                                (<a href=\"#\" onclick=\"viewfeed($feed_id)\">".
                                                                $line["feed_title"]."</a>)
                                                        </span>";
                                                }
-                                       } */
-
-                                       print "</div>";
+                                       }
 
+                                       $reply['content'] .= "</div>";
 
+                                       $reply['content'] .= "<span class=\"hlUpdated\">$updated_fmt</span>";
+                                       $reply['content'] .= "<div class=\"hlRight\">";
 
-                                       print "<div class=\"hlRight\">";
-                                       print "<span class=\"hlUpdated\">$updated_fmt</span>";
-                                       print $score_pic;
+                                       $reply['content'] .= $score_pic;
 
                                        if ($line["feed_title"] && !get_pref($link, 'VFEED_GROUP_BY_FEED')) {
 
-                                               print "<span onclick=\"viewfeed($feed_id)\"
+                                               $reply['content'] .= "<span onclick=\"viewfeed($feed_id)\"
+                                                       style=\"cursor : pointer\"
                                                        title=\"".htmlspecialchars($line['feed_title'])."\">
                                                        $feed_icon_img<span>";
                                        }
 
-                                       print "</div>";
-                                       print "</div>";
+                                       $reply['content'] .= "</div>";
+                                       $reply['content'] .= "</div>";
 
                                } else {
 
                                                                //$feed_icon_img = "<img class=\"tinyFeedIcon\" src=\"images/blank_icon.gif\" alt=\"\">";
                                                        }
 
-                                                       print "<div class='cdmFeedTitle'>".
+                                                       $reply['content'] .= "<div class='cdmFeedTitle'>".
                                                                "<div style=\"float : right\">$feed_icon_img</div>".
                                                                "<a href=\"#\" onclick=\"viewfeed($feed_id)\">".
                                                                $line["feed_title"]."</a> $vf_catchup_link</div>";
                                        $mouseover_attrs = "onmouseover='postMouseIn($id)'
                                                onmouseout='postMouseOut($id)'";
 
-                                       print "<div class=\"$class\"
+                                       $reply['content'] .= "<div class=\"$class\"
                                                id=\"RROW-$id\" $mouseover_attrs'>";
 
-                                       print "<div class=\"cdmHeader\">";
+                                       $reply['content'] .= "<div class=\"cdmHeader\">";
 
-                                       print "<div style='float : right'>";
-                                       print "<span class='updated'>$updated_fmt</span>";
-                                       print "$score_pic";
+                                       $reply['content'] .= "<div style='float : right'>";
+                                       $reply['content'] .= "<span class='updated'>$updated_fmt</span>";
+                                       $reply['content'] .= "$score_pic";
 
                                        if (!get_pref($link, "VFEED_GROUP_BY_FEED") && $line["feed_title"]) {
-                                               print "<span style=\"cursor : pointer\"
+                                               $reply['content'] .= "<span style=\"cursor : pointer\"
                                                        title=\"".htmlspecialchars($line["feed_title"])."\"
                                                        onclick=\"viewfeed($feed_id)\">$feed_icon_img</span>";
                                        }
-                                       print "<div class=\"updPic\">$update_pic</div>";
+                                       $reply['content'] .= "<div class=\"updPic\">$update_pic</div>";
 
-                                       print "</div>";
+                                       $reply['content'] .= "</div>";
 
-                                       print "<input type=\"checkbox\" onclick=\"toggleSelectRowById(this,
+                                       $reply['content'] .= "<input type=\"checkbox\" onclick=\"toggleSelectRowById(this,
                                                        'RROW-$id')\" id=\"RCHK-$id\"/>";
 
-                                       print "$marked_pic";
-                                       print "$published_pic";
+                                       $reply['content'] .= "$marked_pic";
+                                       $reply['content'] .= "$published_pic";
 
-                                       print "<span id=\"RTITLE-$id\"
+                                       $reply['content'] .= "<span id=\"RTITLE-$id\"
                                                onclick=\"return cdmClicked(event, $id);\"
                                                class=\"titleWrap$hlc_suffix\">
                                                <a class=\"title\"
                                                truncate_string($line["title"], 100) .
                                                " $entry_author</a>";
 
-                                       print $labels_str;
+                                       $reply['content'] .= $labels_str;
+
+                                       if (!get_pref($link, 'VFEED_GROUP_BY_FEED') &&
+                                               defined('_SHOW_FEED_TITLE_IN_VFEEDS')) {
+                                               if (@$line["feed_title"]) {
+                                                       $reply['content'] .= "<span class=\"hlFeed\">
+                                                               (<a href=\"#\" onclick=\"viewfeed($feed_id)\">".
+                                                               $line["feed_title"]."</a>)
+                                                       </span>";
+                                               }
+                                       }
 
                                        if (!$expand_cdm)
                                                $content_hidden = "style=\"display : none\"";
                                        else
                                                $excerpt_hidden = "style=\"display : none\"";
 
-                                       print "<span $excerpt_hidden
+                                       $reply['content'] .= "<span $excerpt_hidden
                                                id=\"CEXC-$id\" class=\"cdmExcerpt\"> - $content_preview</span>";
 
-                                       print "</span>";
+                                       $reply['content'] .= "</span>";
 
-                                       print "</div>";
+                                       $reply['content'] .= "</div>";
 
-                                       print "<div class=\"cdmContent\" $content_hidden
+                                       $reply['content'] .= "<div class=\"cdmContent\" $content_hidden
                                                onclick=\"return cdmClicked(event, $id);\"
                                                id=\"CICD-$id\">";
 
-                                       print "<div class=\"cdmContentInner\">";
+                                       $reply['content'] .= "<div class=\"cdmContentInner\">";
 
                        if ($line["orig_feed_id"]) {
 
 
                                                if (db_num_rows($tmp_result) != 0) {
 
-                                                       print "<div clear='both'>";
-                                                       print __("Originally from:");
+                                                       $reply['content'] .= "<div clear='both'>";
+                                                       $reply['content'] .= __("Originally from:");
 
-                                                       print "&nbsp;";
+                                                       $reply['content'] .= "&nbsp;";
 
                                                        $tmp_line = db_fetch_assoc($tmp_result);
 
-                                                       print "<a target='_blank'
+                                                       $reply['content'] .= "<a target='_blank'
                                                                href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
                                                                $tmp_line['title'] . "</a>";
 
-                                                       print "&nbsp;";
+                                                       $reply['content'] .= "&nbsp;";
 
-                                                       print "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
-                                                       print "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.gif'></a>";
+                                                       $reply['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
+                                                       $reply['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.gif'></a>";
 
-                                                       print "</div>";
+                                                       $reply['content'] .= "</div>";
                                                }
                                        }
 
                                                $article_content = '';
                                        }
 
-                                       print "<div id=\"POSTNOTE-$id\">";
+                                       $reply['content'] .= "<div id=\"POSTNOTE-$id\">";
                                        if ($line['note']) {
-                                               print format_article_note($id, $line['note']);
+                                               $reply['content'] .= format_article_note($id, $line['note']);
                                        }
-                                       print "</div>";
+                                       $reply['content'] .= "</div>";
 
-                                       print "<span id=\"CWRAP-$id\">$article_content</span>";
+                                       $reply['content'] .= "<span id=\"CWRAP-$id\">$article_content</span>";
 
                                        $tmp_result = db_query($link, "SELECT always_display_enclosures FROM
                                                ttrss_feeds WHERE id = ".
                                        $always_display_enclosures = sql_bool_to_bool(db_fetch_result($tmp_result,
                                                0, "always_display_enclosures"));
 
-                                       print_article_enclosures($link, $id, $always_display_enclosures,
+                                       $reply['content'] .= format_article_enclosures($link, $id, $always_display_enclosures,
                                                $article_content);
 
-                                       print "</div>";
+                                       $reply['content'] .= "</div>";
 
-                                       print "<div class=\"cdmFooter\">";
+                                       $reply['content'] .= "<div class=\"cdmFooter\">";
 
                                        $tags_str = format_tags_string(get_article_tags($link, $id), $id);
 
-                                       print "<img src='".theme_image($link,
+                                       $reply['content'] .= "<img src='".theme_image($link,
                                                        'images/tag.png')."' alt='Tags' title='Tags'>
                                                <span id=\"ATSTR-$id\">$tags_str</span>
                                                <a title=\"".__('Edit tags for this article')."\"
                                                href=\"#\" onclick=\"editArticleTags($id, $feed_id, true)\">(+)</a>";
 
-                                       print "<div style=\"float : right\">";
+                                       $reply['content'] .= "<div style=\"float : right\">";
 
-                                       print "<img src=\"images/art-zoom.png\"
+                                       $reply['content'] .= "<img src=\"images/art-zoom.png\"
                                                onclick=\"zoomToArticle(event, $id)\"
                                                style=\"cursor : pointer\"
                                                alt='Zoom'
 
                                        //$note_escaped = htmlspecialchars($line['note'], ENT_QUOTES);
 
-                                       print "<img src=\"images/art-pub-note.png\"
+                                       $reply['content'] .= "<img src=\"images/art-pub-note.png\"
                                                style=\"cursor : pointer\" style=\"cursor : pointer\"
                                                onclick=\"editArticleNote($id)\"
                                                alt='PubNote' title='".__('Edit article note')."'>";
 
                                        if (DIGEST_ENABLE) {
-                                               print "<img src=\"".theme_image($link, 'images/art-email.png')."\"
+                                               $reply['content'] .= "<img src=\"".theme_image($link, 'images/art-email.png')."\"
                                                        style=\"cursor : pointer\"
                                                        onclick=\"emailArticle($id)\"
                                                        alt='Zoom' title='".__('Forward by email')."'>";
                                        }
 
                                        if (ENABLE_TWEET_BUTTON) {
-                                               print "<img src=\"".theme_image($link, 'images/art-tweet.png')."\"
+                                               $reply['content'] .= "<img src=\"".theme_image($link, 'images/art-tweet.png')."\"
                                                        class='tagsPic' style=\"cursor : pointer\"
                                                        onclick=\"tweetArticle($id)\"
                                                        alt='Zoom' title='".__('Share on Twitter')."'>";
                                        }
 
-                                       print "<img src=\"images/digest_checkbox.png\"
+                                       $reply['content'] .= "<img src=\"images/digest_checkbox.png\"
                                                style=\"cursor : pointer\" style=\"cursor : pointer\"
                                                onclick=\"dismissArticle($id)\"
                                                alt='Dismiss' title='".__('Dismiss article')."'>";
 
-                                       print "</div>";
-                                       print "</div>";
+                                       $reply['content'] .= "</div>";
+                                       $reply['content'] .= "</div>";
 
-                                       print "</div>";
+                                       $reply['content'] .= "</div>";
 
-                                       print "</div>";
+                                       $reply['content'] .= "</div>";
 
                                }
 
                                ++$lnum;
                        }
 
+                       if ($_REQUEST["debug"]) $timing_info = print_checkpoint("PE", $timing_info);
+
                } else {
                        $message = "";
 
                        }
 
                        if (!$offset && $message) {
-                               print "<div class='whiteBox'>$message";
+                               $reply['content'] .= "<div class='whiteBox'>$message";
 
-                               print "<p class=\"small\"><span class=\"insensitive\">";
+                               $reply['content'] .= "<p class=\"small\"><span class=\"insensitive\">";
 
                                $result = db_query($link, "SELECT ".SUBSTRING_FOR_DATE."(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds
                                        WHERE owner_uid = " . $_SESSION['uid']);
                                $last_updated = db_fetch_result($result, 0, "last_updated");
                                $last_updated = make_local_datetime($link, $last_updated, false);
 
-                               printf(__("Feeds last updated at %s"), $last_updated);
+                               $reply['content'] .= sprintf(__("Feeds last updated at %s"), $last_updated);
 
                                $result = db_query($link, "SELECT COUNT(id) AS num_errors
                                        FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ".$_SESSION["uid"]);
                                $num_errors = db_fetch_result($result, 0, "num_errors");
 
                                if ($num_errors > 0) {
-                                       print "<br/>";
-                                       print "<a class=\"insensitive\" href=\"#\" onclick=\"showFeedsWithErrors()\">".
+                                       $reply['content'] .= "<br/>";
+                                       $reply['content'] .= "<a class=\"insensitive\" href=\"#\" onclick=\"showFeedsWithErrors()\">".
                                                __('Some feeds have update errors (click for details)')."</a>";
                                }
-                               print "</span></p></div>";
+                               $reply['content'] .= "</span></p></div>";
                        }
                }
 
-#              if (!$offset) {
-#                      if ($headlines_count > 0) print "</div>";
-#                      print "</div>";
-#              }
+               if ($_REQUEST["debug"]) $timing_info = print_checkpoint("H2", $timing_info);
 
-               print "]]></content>";
-
-               return array($topmost_article_ids, $headlines_count, $feed, $disable_cache, $vgroup_last_feed);
+               return array($topmost_article_ids, $headlines_count, $feed, $disable_cache,
+                       $vgroup_last_feed, $reply);
        }
 
 // from here: http://www.roscripts.com/Create_tag_cloud-71.html
                        $tags[$line["tag_name"]] = $line["count"];
                }
 
+        if( count($tags) == 0 ){ return; }
+
                ksort($tags);
 
                $max_size = 32; // max font size in pixels
 
                $tag = mb_strtolower($tag, 'utf-8');
 
-               $tag = preg_replace('/[\"\+\>\<]/', "", $tag);
+               $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag);
 
 //             $tag = str_replace('"', "", $tag);
 //             $tag = str_replace("+", " ", $tag);
        }
 
        function get_self_url_prefix() {
-
-               $url_path = "";
-
-               if ($_SERVER['HTTPS'] != "on") {
-                       $url_path = "http://";
-               } else {
-                       $url_path = "https://";
-               }
-
-               $url_path .= $_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF']);
-
-               return $url_path;
-
+               return SELF_URL_PATH;
        }
+
        function opml_publish_url($link){
 
                $url_path = get_self_url_prefix();
         * @return string The Mozilla Firefox feed adding URL.
         */
        function add_feed_url() {
-               $url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' :  'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
-               $url_path .= "?op=pref-feeds&quiet=1&subop=add&feed_url=%s";
+               //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' :  'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
+
+               $url_path = get_self_url_prefix() .
+                       "/backend.php?op=pref-feeds&quiet=1&subop=add&feed_url=%s";
                return $url_path;
        } // function add_feed_url
 
 
        function update_feedbrowser_cache($link) {
 
-               $result = db_query($link, "SELECT feed_url,title, COUNT(id) AS subscribers
+               $result = db_query($link, "SELECT feed_url, site_url, title, COUNT(id) AS subscribers
                        FROM ttrss_feeds WHERE (SELECT COUNT(id) = 0 FROM ttrss_feeds AS tf
                                WHERE tf.feed_url = ttrss_feeds.feed_url
-                               AND (private IS true OR feed_url LIKE '%:%@%/%'))
-                               GROUP BY feed_url, title ORDER BY subscribers DESC LIMIT 1000");
+                               AND (private IS true OR auth_login != '' OR auth_pass != '' OR feed_url LIKE '%:%@%/%'))
+                               GROUP BY feed_url, site_url, title ORDER BY subscribers DESC LIMIT 1000");
 
                db_query($link, "BEGIN");
 
                        $subscribers = db_escape_string($line["subscribers"]);
                        $feed_url = db_escape_string($line["feed_url"]);
                        $title = db_escape_string($line["title"]);
+                       $site_url = db_escape_string($line["site_url"]);
 
                        $tmp_result = db_query($link, "SELECT subscribers FROM
                                ttrss_feedbrowser_cache WHERE feed_url = '$feed_url'");
                        if (db_num_rows($tmp_result) == 0) {
 
                                db_query($link, "INSERT INTO ttrss_feedbrowser_cache
-                                       (feed_url, title, subscribers) VALUES ('$feed_url',
-                                               '$title', '$subscribers')");
+                                       (feed_url, site_url, title, subscribers) VALUES ('$feed_url',
+                                               '$site_url', '$title', '$subscribers')");
 
                                ++$count;
 
                return $unread;
        }
 
+       function ccache_cleanup($link, $owner_uid) {
+
+               if (DB_TYPE == "pgsql") {
+                       db_query($link, "DELETE FROM ttrss_counters_cache AS c1 WHERE
+                               (SELECT count(*) FROM ttrss_counters_cache AS c2
+                                       WHERE c1.feed_id = c2.feed_id AND c2.owner_uid = c1.owner_uid) > 1
+                                       AND owner_uid = '$owner_uid'");
+
+                       db_query($link, "DELETE FROM ttrss_cat_counters_cache AS c1 WHERE
+                               (SELECT count(*) FROM ttrss_cat_counters_cache AS c2
+                                       WHERE c1.feed_id = c2.feed_id AND c2.owner_uid = c1.owner_uid) > 1
+                                       AND owner_uid = '$owner_uid'");
+               } else {
+                       db_query($link, "DELETE c1 FROM
+                                       ttrss_counters_cache AS c1,
+                                       ttrss_counters_cache AS c2
+                               WHERE
+                                       c1.owner_uid = '$owner_uid' AND
+                                       c1.owner_uid = c2.owner_uid AND
+                                       c1.feed_id = c2.feed_id");
+
+                       db_query($link, "DELETE c1 FROM
+                                       ttrss_cat_counters_cache AS c1,
+                                       ttrss_cat_counters_cache AS c2
+                               WHERE
+                                       c1.owner_uid = '$owner_uid' AND
+                                       c1.owner_uid = c2.owner_uid AND
+                                       c1.feed_id = c2.feed_id");
+
+               }
+       }
+
        function label_find_id($link, $label, $owner_uid) {
                $result = db_query($link,
                        "SELECT id FROM ttrss_labels2 WHERE caption = '$label'
 
                $num_tags = 0;
 
-/*             if (get_user_theme($link) == "3pane") {
-                       $tag_limit = 3;
-               } else {
-                       $tag_limit = 6;
-               } */
-
                $tag_limit = 6;
 
                $formatted_tags = array();
 
        function format_article_note($id, $note) {
 
-               $str = "<div class='articleNote' title=\"".__('edit note')."\"
-                       onclick=\"editArticleNote($id)\">$note</div>";
+               $str = "<div class='articleNote'        onclick=\"editArticleNote($id)\">
+                       <div class='noteEdit' onclick=\"editArticleNote($id)\">".
+                       __('(edit note)')."</div>$note</div>";
 
                return $str;
        }
        }
 
        function api_get_headlines($link, $feed_id, $limit, $offset,
-                                       $filter, $is_cat, $show_excerpt, $show_content, $view_mode, $order) {
+                               $filter, $is_cat, $show_excerpt, $show_content, $view_mode, $order,
+                               $include_attachments) {
 
                        /* do not rely on params below */
 
                                                "tags" => get_article_tags($link, $line["id"]),
                                        );
 
+                                       if ($include_attachments)
+                                               $headline_row['attachments'] = get_article_enclosures($link,
+                                                       $line['id']);
+
                                if ($show_excerpt) {
                                        $excerpt = truncate_string(strip_tags($line["content_preview"]), 100);
                                        $headline_row["excerpt"] = $excerpt;
                        return $headlines;
        }
 
-       function generate_dashboard_feed($link) {
-               print "<headlines id=\"-5\" is_cat=\"\">";
+       function generate_error_feed($link, $error) {
+               $reply = array();
+
+               $reply['headlines']['id'] = -6;
+               $reply['headlines']['is_cat'] = false;
+
+               $reply['headlines']['toolbar'] = '';
+               $reply['headlines']['content'] = "<div class='whiteBox'>". $error . "</div>";
+
+               $reply['headlines-info'] = array("count" => 0,
+                       "vgroup_last_feed" => '',
+                       "unread" => 0,
+                       "disable_cache" => true);
+
+               return $reply;
+       }
+
 
-               print "<toolbar><![CDATA[]]></toolbar>";
+       function generate_dashboard_feed($link) {
+               $reply = array();
 
-               print '<content><![CDATA[';
+               $reply['headlines']['id'] = -5;
+               $reply['headlines']['is_cat'] = false;
 
-               print "<div class='whiteBox'>".__('No feed selected.');
+               $reply['headlines']['toolbar'] = '';
+               $reply['headlines']['content'] = "<div class='whiteBox'>".__('No feed selected.');
 
-               print "<p class=\"small\"><span class=\"insensitive\">";
+               $reply['headlines']['content'] .= "<p class=\"small\"><span class=\"insensitive\">";
 
                $result = db_query($link, "SELECT ".SUBSTRING_FOR_DATE."(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds
                        WHERE owner_uid = " . $_SESSION['uid']);
                $last_updated = db_fetch_result($result, 0, "last_updated");
                $last_updated = make_local_datetime($link, $last_updated, false);
 
-               printf(__("Feeds last updated at %s"), $last_updated);
+               $reply['headlines']['content'] .= sprintf(__("Feeds last updated at %s"), $last_updated);
 
                $result = db_query($link, "SELECT COUNT(id) AS num_errors
                        FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ".$_SESSION["uid"]);
                $num_errors = db_fetch_result($result, 0, "num_errors");
 
                if ($num_errors > 0) {
-                       print "<br/>";
-                       print "<a class=\"insensitive\" href=\"#\" onclick=\"showFeedsWithErrors()\">".
+                       $reply['headlines']['content'] .= "<br/>";
+                       $reply['headlines']['content'] .= "<a class=\"insensitive\" href=\"#\" onclick=\"showFeedsWithErrors()\">".
                                __('Some feeds have update errors (click for details)')."</a>";
                }
-               print "</span></p>";
-
-               print "]]></content>";
-               print "</headlines>";
-
-               print "<headlines-info><![CDATA[";
+               $reply['headlines']['content'] .= "</span></p>";
 
-               $info = array("count" => 0,
+               $reply['headlines-info'] = array("count" => 0,
                        "vgroup_last_feed" => '',
                        "unread" => 0,
                        "disable_cache" => true);
 
-               print json_encode($info);
-
-               print "]]></headlines-info>";
-
+               return $reply;
        }
 
        function save_email_address($link, $email) {
 
        }
 
-       function print_article_enclosures($link, $id, $always_display_enclosures,
+       function format_article_enclosures($link, $id, $always_display_enclosures,
                                        $article_content) {
 
                $result = get_article_enclosures($link, $id);
+               $rv = '';
 
                if (count($result) > 0) {
 
                                array_push($entries, $entry);
                        }
 
-                       print "<div class=\"postEnclosures\">";
+                       $rv .= "<div class=\"postEnclosures\">";
 
                        if (!get_pref($link, "STRIP_IMAGES")) {
                                if ($always_display_enclosures ||
                                                if (preg_match("/image/", $entry["type"]) ||
                                                                preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
 
-                                                               print "<p><img
+                                                               $rv .= "<p><img
                                                                alt=\"".htmlspecialchars($entry["filename"])."\"
                                                                src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
                                                }
                        }
 
                        if (count($entries) == 1) {
-                               print __("Attachment:") . " ";
+                               $rv .= __("Attachment:") . " ";
                        } else {
-                               print __("Attachments:") . " ";
+                               $rv .= __("Attachments:") . " ";
                        }
 
-                       print join(", ", $entries_html);
+                       $rv .= join(", ", $entries_html);
 
-                       print "</div>";
+                       $rv .= "</div>";
                }
+
+               return $rv;
        }
 
        function getLastArticleId($link) {
 
                                $content = $tmhOAuth->response['response'];
 
+                               define('MAGPIE_CACHE_ON', false);
+
                                $rss = new MagpieRSS($content, MAGPIE_OUTPUT_ENCODING,
                                        MAGPIE_INPUT_ENCODING, MAGPIE_DETECT_ENCODING );
 
                return $result;
        }
 
+       function filter_to_sql($filter) {
+               $query = "";
+
+               if (DB_TYPE == "pgsql")
+                       $reg_qpart = "~";
+               else
+                       $reg_qpart = "REGEXP";
+
+               switch ($filter["type"]) {
+                       case "title":
+                               $query = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
+                                       $filter['reg_exp'] . "')";
+                               break;
+                       case "content":
+                               $query = "LOWER(ttrss_entries.content) $reg_qpart LOWER('".
+                                       $filter['reg_exp'] . "')";
+                               break;
+                       case "both":
+                               $query = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
+                                       $filter['reg_exp'] . "') OR LOWER(" .
+                                       "ttrss_entries.content) $reg_qpart LOWER('" . $filter['reg_exp'] . "')";
+                               break;
+                       case "tag":
+                               $query = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('".
+                                       $filter['reg_exp'] . "')";
+                               break;
+                       case "link":
+                               $query = "LOWER(ttrss_entries.link) $reg_qpart LOWER('".
+                                       $filter['reg_exp'] . "')";
+                               break;
+                       case "date":
+
+                               if ($filter["filter_param"] == "before")
+                                       $cmp_qpart = "<";
+                               else
+                                       $cmp_qpart = ">=";
+
+                               $timestamp = date("Y-m-d H:N:s", strtotime($filter["reg_exp"]));
+                               $query = "ttrss_entries.date_entered $cmp_qpart '$timestamp'";
+                               break;
+                       case "author":
+                               $query = "LOWER(ttrss_entries.author) $reg_qpart LOWER('".
+                                       $filter['reg_exp'] . "')";
+                               break;
+               }
+
+               if ($filter["inverse"])
+                       $query = "NOT ($query)";
+
+               if ($query) {
+                       if (DB_TYPE == "pgsql") {
+                               $query = " ($query) AND ttrss_entries.date_entered > NOW() - INTERVAL '14 days'";
+                       } else {
+                               $query = " ($query) AND ttrss_entries.date_entered > DATE_SUB(NOW(), INTERVAL 14 DAY)";
+                       }
+                       $query .= " AND ";
+               }
+
+
+               return $query;
+       }
+
+       // Status codes:
+       // -1  - never connected
+       // 0   - no data received
+       // 1   - data received successfully
+       // 2   - did not receive valid data
+       // >10 - server error, code + 10 (e.g. 16 means server error 6)
+
+       function get_linked_feeds($link, $instance_id = false) {
+               if ($instance_id)
+                       $instance_qpart = "id = '$instance_id' AND ";
+               else
+                       $instance_qpart = "";
+
+               if (DB_TYPE == "pgsql") {
+                       $date_qpart = "last_connected < NOW() - INTERVAL '6 hours'";
+               } else {
+                       $date_qpart = "last_connected < DATE_SUB(NOW(), INTERVAL 6 HOUR)";
+               }
+
+               $result = db_query($link, "SELECT id, access_key, access_url FROM ttrss_linked_instances
+                       WHERE $instance_qpart $date_qpart ORDER BY last_connected");
+
+               while ($line = db_fetch_assoc($result)) {
+                       $id = $line['id'];
+
+                       _debug("Updating: " . $line['access_url'] . " ($id)");
+
+                       $fetch_url = $line['access_url'] . '/backend.php?op=fbexport';
+                       $post_query = 'key=' . $line['access_key'];
+
+                       $feeds = fetch_file_contents($fetch_url, false, false, false, $post_query);
+
+                       if ($feeds) {
+                               $feeds = json_decode($feeds, true);
+
+                               if ($feeds) {
+                                       if ($feeds['error']) {
+                                               $status = $feeds['error']['code'] + 10;
+                                       } else {
+                                               $status = 1;
+
+                                               if (count($feeds['feeds']) > 0) {
+
+                                                       db_query($link, "DELETE FROM ttrss_linked_feeds
+                                                               WHERE instance_id = '$id'");
+
+                                                       foreach ($feeds['feeds'] as $feed) {
+                                                               $feed_url = db_escape_string($feed['feed_url']);
+                                                               $title = db_escape_string($feed['title']);
+                                                               $subscribers = db_escape_string($feed['subscribers']);
+                                                               $site_url = db_escape_string($feed['site_url']);
+
+                                                               db_query($link, "INSERT INTO ttrss_linked_feeds
+                                                                       (feed_url, site_url, title, subscribers, instance_id, created, updated)
+                                                               VALUES
+                                                                       ('$feed_url', '$site_url', '$title', '$subscribers', '$id', NOW(), NOW())");
+                                                       }
+                                               } else {
+                                                       // received 0 feeds, this might indicate that
+                                                       // the instance on the other hand is rebuilding feedbrowser cache
+                                                       // we will try again later
+
+                                                       // TODO: maybe perform expiration based on updated here?
+                                               }
+
+                                               _debug("Processed " . count($feeds['feeds']) . " feeds.");
+                                       }
+                               } else {
+                                       $status = 2;
+                               }
+
+                       } else {
+                               $status = 0;
+                       }
+
+                       _debug("Status: $status");
+
+                       db_query($link, "UPDATE ttrss_linked_instances SET
+                               last_status_out = '$status', last_connected = NOW() WHERE id = '$id'");
+
+               }
+
+       }
 ?>