]> git.wh0rd.org - tt-rss.git/blobdiff - include/functions.php
api: add author to getHeadlines
[tt-rss.git] / include / functions.php
index 0231c52de87920b7c99113e5ff96266a3a38df2b..307f662009b14dc83c304a86477f4a5e57c96508 100644 (file)
@@ -1,8 +1,13 @@
 <?php
        define('EXPECTED_CONFIG_VERSION', 26);
-       define('SCHEMA_VERSION', 97);
+       define('SCHEMA_VERSION', 111);
+
+       define('LABEL_BASE_INDEX', -1024);
+       define('PLUGIN_FEED_BASE_INDEX', -128);
 
        $fetch_last_error = false;
+       $fetch_last_error_code = false;
+       $pluginhost = false;
 
        function __autoload($class) {
                $class_file = str_replace("_", "/", strtolower(basename($class)));
@@ -12,6 +17,7 @@
                if (file_exists($file)) {
                        require $file;
                }
+
        }
 
        mb_internal_encoding("UTF-8");
@@ -42,6 +48,7 @@
                $tr = array(
                                        "auto"  => "Detect automatically",
                                        "ca_CA" => "Català",
+                                       "cs_CZ" => "Česky",
                                        "en_US" => "English",
                                        "es_ES" => "Español",
                                        "de_DE" => "Deutsch",
@@ -49,7 +56,9 @@
                                        "hu_HU" => "Magyar (Hungarian)",
                                        "it_IT" => "Italiano",
                                        "ja_JP" => "日本語 (Japanese)",
+                                       "lv_LV" => "Latviešu",
                                        "nb_NO" => "Norwegian bokmål",
+                                       "nl_NL" => "Dutch",
                                        "pl_PL" => "Polski",
                                        "ru_RU" => "Русский",
                                        "pt_BR" => "Portuguese/Brazil",
@@ -61,6 +70,7 @@
        require_once "lib/accept-to-gettext.php";
        require_once "lib/gettext/gettext.inc";
 
+
        function startup_gettext() {
 
                # Get locale from Accept-Language header
                        $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 ($_SESSION["language"] && $_SESSION["language"] != "auto") {
+                       $lang = $_SESSION["language"];
                }
 
                if ($lang) {
                                _setlocale(LC_ALL, $lang);
                        }
 
-                       if (defined('MOBILE_VERSION')) {
-                               _bindtextdomain("messages", "../locale");
-                       } else {
-                               _bindtextdomain("messages", "locale");
-                       }
+                       _bindtextdomain("messages", "locale");
 
                        _textdomain("messages");
                        _bind_textdomain_codeset("messages", "UTF-8");
 
        require_once 'db-prefs.php';
        require_once 'version.php';
-
-       define('MAGPIE_OUTPUT_ENCODING', 'UTF-8');
+       require_once 'ccache.php';
+       require_once 'labels.php';
 
        define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . VERSION . ' (http://tt-rss.org/)');
-       define('MAGPIE_USER_AGENT', SELF_USER_AGENT);
-
        ini_set('user_agent', SELF_USER_AGENT);
 
        require_once 'lib/pubsubhubbub/publisher.php';
 
-       $purifier = false;
-
        $tz_offset = -1;
        $utc_tz = new DateTimeZone('UTC');
        $schema_version = false;
         * @return void
         */
        function _debug($msg) {
-               if (defined('QUIET') && QUIET) {
-                       return;
-               }
                $ts = strftime("%H:%M:%S", time());
                if (function_exists('posix_getpid')) {
                        $ts = "$ts/" . posix_getpid();
                }
-               print "[$ts] $msg\n";
+
+               if (!(defined('QUIET') && QUIET)) {
+                       print "[$ts] $msg\n";
+               }
+
+               if (defined('LOGFILE'))  {
+                       $fp = fopen(LOGFILE, 'a+');
+
+                       if ($fp) {
+                               fputs($fp, "[$ts] $msg\n");
+                               fclose($fp);
+                       }
+               }
+
        } // function _debug
 
        /**
                if ($debug) {
                        _debug("Purged feed $feed_id ($purge_interval): deleted $rows articles");
                }
+
+               return $rows;
        } // function purge_feed
 
        function feed_purge_interval($link, $feed_id) {
                }
        }
 
-       function fetch_file_contents($url, $type = false, $login = false, $pass = false, $post_query = false) {
-               $login = urlencode($login);
-               $pass = urlencode($pass);
+       function fetch_file_contents($url, $type = false, $login = false, $pass = false, $post_query = false, $timeout = false, $timestamp = 0) {
 
                global $fetch_last_error;
+               global $fetch_last_error_code;
 
                if (function_exists('curl_init') && !ini_get("open_basedir")) {
-                       $ch = curl_init($url);
 
-                       curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
-                       curl_setopt($ch, CURLOPT_TIMEOUT, 45);
-                       curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
+                       if (ini_get("safe_mode")) {
+                               $ch = curl_init(geturl($url));
+                       } else {
+                               $ch = curl_init($url);
+                       }
+
+                       if ($timestamp) {
+                               curl_setopt($ch, CURLOPT_HTTPHEADER,
+                                       array("If-Modified-Since: ".gmdate('D, d M Y H:i:s \G\M\T', $timestamp)));
+                       }
+
+                       curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout ? $timeout : 15);
+                       curl_setopt($ch, CURLOPT_TIMEOUT, $timeout ? $timeout : 45);
+                       curl_setopt($ch, CURLOPT_FOLLOWLOCATION, !ini_get("safe_mode"));
                        curl_setopt($ch, CURLOPT_MAXREDIRS, 20);
                        curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
                        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                        curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
                        curl_setopt($ch, CURLOPT_USERAGENT, SELF_USER_AGENT);
                        curl_setopt($ch, CURLOPT_ENCODING , "gzip");
+                       curl_setopt($ch, CURLOPT_REFERER, $url);
 
                        if ($post_query) {
                                curl_setopt($ch, CURLOPT_POST, true);
 
                        $contents = @curl_exec($ch);
 
+                       if (curl_errno($ch) === 23 || curl_errno($ch) === 61) {
+                               curl_setopt($ch, CURLOPT_ENCODING, 'none');
+                               $contents = @curl_exec($ch);
+                       }
+
                        if ($contents === false) {
-                               $fetch_last_error = curl_error($ch);
+                               $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
                                curl_close($ch);
                                return false;
                        }
 
                        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
                        $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
-                       curl_close($ch);
+
+                       $fetch_last_error_code = $http_code;
 
                        if ($http_code != 200 || $type && strpos($content_type, "$type") === false) {
+                               if (curl_errno($ch) != 0) {
+                                       $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
+                               } else {
+                                       $fetch_last_error = "HTTP Code: $http_code";
+                               }
+                               curl_close($ch);
                                return false;
                        }
 
+                       curl_close($ch);
+
                        return $contents;
                } else {
-                       if ($login && $pass ){
+                       if ($login && $pass){
                                $url_parts = array();
 
                                preg_match("/(^[^:]*):\/\/(.*)/", $url, $url_parts);
 
+                               $pass = urlencode($pass);
+
                                if ($url_parts[1] && $url_parts[2]) {
                                        $url = $url_parts[1] . "://$login:$pass@" . $url_parts[2];
                                }
 
                        $data = @file_get_contents($url);
 
+                       @$gzdecoded = gzdecode($data);
+                       if ($gzdecoded) $data = $gzdecoded;
+
                        if (!$data && function_exists('error_get_last')) {
                                $error = error_get_last();
                                $fetch_last_error = $error["message"];
                         else
                                $sel = "";
 
+                       $v = trim($v);
+
                        print "<option value=\"$v\" $sel>$v</option>";
                }
                print "</select>";
                         else
                                $sel = "";
 
+                       $v = trim($v);
+
                        print "<option $sel value=\"$v\">".$values[$v]."</option>";
                }
 
                print "</select>";
        }
 
-       function get_article_filters($filters, $title, $content, $link, $timestamp, $author, $tags) {
-               $matches = array();
-
-               foreach ($filters as $filter) {
-                       $match_any_rule = $filter["match_any_rule"];
-                       $filter_match = false;
-
-                       foreach ($filter["rules"] as $rule) {
-                               $match = false;
-                               $reg_exp = $rule["reg_exp"];
-
-                               if (!$reg_exp)
-                                       continue;
-
-                               switch ($rule["type"]) {
-                               case "title":
-                                       $match = @preg_match("/$reg_exp/i", $title);
-                                       break;
-                               case "content":
-                                       $match = @preg_match("/$reg_exp/i", $content);
-                                       break;
-                               case "both":
-                                       $match = (@preg_match("/$reg_exp/i", $title) || @preg_match("/$reg_exp/i", $title));
-                                       break;
-                               case "link":
-                                       $match = @preg_match("/$reg_exp/i", $link);
-                                       break;
-                               case "author":
-                                       $match = @preg_match("/$reg_exp/i", $author);
-                                       break;
-                               case "tag":
-                                       $tag_string = join(",", $tags);
-                                       $match = @preg_match("/$reg_exp/i", $tag_string);
-                                       break;
-                               }
-
-                               if ($match_any_rule) {
-                                       if ($match) {
-                                               $filter_match = true;
-                                               break;
-                                       }
-                               } else {
-                                       $filter_match = $match;
-                                       if (!$match) {
-                                               break;
-                                       }
-                               }
-                       }
-
-                       if ($filter_match) {
-                               foreach ($filter["actions"] AS $action) {
-                                       array_push($matches, $action);
-                               }
-                       }
-               }
-
-               return $matches;
-       }
-
-       function find_article_filter($filters, $filter_name) {
-               foreach ($filters as $f) {
-                       if ($f["type"] == $filter_name) {
-                               return $f;
-                       };
-               }
-               return false;
-       }
-
-       function find_article_filters($filters, $filter_name) {
-               $results = array();
-
-               foreach ($filters as $f) {
-                       if ($f["type"] == $filter_name) {
-                               array_push($results, $f);
-                       };
-               }
-               return $results;
-       }
-
-       function calculate_article_score($filters) {
-               $score = 0;
-
-               foreach ($filters as $f) {
-                       if ($f["type"] == "score") {
-                               $score += $f["param"];
-                       };
-               }
-               return $score;
-       }
-
-       function assign_article_to_labels($link, $id, $filters, $owner_uid) {
-               foreach ($filters as $f) {
-                       if ($f["type"] == "label") {
-                               label_add_article($link, $id, $f["param"], $owner_uid);
-                       };
-               }
-       }
-
-       function getmicrotime() {
-               list($usec, $sec) = explode(" ",microtime());
-               return ((float)$usec + (float)$sec);
-       }
-
        function print_radio($id, $default, $true_is, $values, $attributes = "") {
                foreach ($values as $v) {
 
 
        function initialize_user_prefs($link, $uid, $profile = false) {
 
-               $uid = db_escape_string($uid);
+               $uid = db_escape_string($link, $uid);
 
                if (!$profile) {
                        $profile = "NULL";
                        if (array_search($line["pref_name"], $active_prefs) === FALSE) {
 //                             print "adding " . $line["pref_name"] . "<br>";
 
+                               $line["def_value"] = db_escape_string($link, $line["def_value"]);
+                               $line["pref_name"] = db_escape_string($link, $line["pref_name"]);
+
                                if (get_schema_version($link) < 63) {
                                        db_query($link, "INSERT INTO ttrss_user_prefs
                                                (owner_uid,pref_name,value) VALUES
        function authenticate_user($link, $login, $password, $check_only = false) {
 
                if (!SINGLE_USER_MODE) {
-
                        $user_id = false;
-                       $modules = explode(",", AUTH_MODULES);
-
-                       foreach ($modules as $module) {
-                               $module_class = "auth_$module";
-                               if (class_exists($module_class)) {
-                                       $authenticator = new $module_class($link);
 
-                                       $user_id = (int) $authenticator->authenticate($login, $password);
+                       global $pluginhost;
+                       foreach ($pluginhost->get_hooks($pluginhost::HOOK_AUTH_USER) as $plugin) {
 
-                                       if ($user_id) {
-                                               $_SESSION["auth_module"] = $module;
-                                               break;
-                                       }
+                               $user_id = (int) $plugin->authenticate($login, $password);
 
-                               } else {
-                                       print T_sprintf("Fatal: authentication module %s not found.", $module);
-                                       die;
+                               if ($user_id) {
+                                       $_SESSION["auth_module"] = strtolower(get_class($plugin));
+                                       break;
                                }
                        }
 
                        if ($user_id && !$check_only) {
+                               @session_start();
+
                                $_SESSION["uid"] = $user_id;
 
                                $result = db_query($link, "SELECT login,access_level,pwd_hash FROM ttrss_users
                return true;
        }
 
-       function login_sequence($link, $login_form = 0) {
+       function load_user_plugins($link, $owner_uid) {
+               if ($owner_uid) {
+                       $plugins = get_pref($link, "_ENABLED_PLUGINS", $owner_uid);
+
+                       global $pluginhost;
+                       $pluginhost->load($plugins, $pluginhost::KIND_USER, $owner_uid);
+
+                       if (get_schema_version($link) > 100) {
+                               $pluginhost->load_data();
+                       }
+               }
+       }
+
+       function login_sequence($link) {
+               $_SESSION["prefs_cache"] = false;
+
                if (SINGLE_USER_MODE) {
-                       return authenticate_user($link, "admin", null);
+                       @session_start();
+                       authenticate_user($link, "admin", null);
+                       cache_prefs($link);
+                       load_user_plugins($link, $_SESSION["uid"]);
                } else {
                        if (!$_SESSION["uid"] || !validate_session($link)) {
 
                                         authenticate_user($link, null, null, true);
                                }
 
-                               if (!$_SESSION["uid"]) render_login_form($link, $login_form);
+                               if (!$_SESSION["uid"]) render_login_form($link);
 
                        } else {
                                /* bump login timestamp */
                                db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
                                        $_SESSION["uid"]);
-
-                               if ($_SESSION["language"] && SESSION_COOKIE_LIFETIME > 0) {
-                                       setcookie("ttrss_lang", $_SESSION["language"],
-                                               time() + SESSION_COOKIE_LIFETIME);
-                               }
+                               $_SESSION["last_login_update"] = time();
                        }
-               }
-       }
-
-
-       /* function login_sequence($link, $mobile = false) {
-               $_SESSION["prefs_cache"] = array();
-
-               if (!SINGLE_USER_MODE) {
-
-                       $login_action = $_POST["login_action"];
-
-                       # try to authenticate user if called from login form
-                       if ($login_action == "do_login") {
-                               $login = db_escape_string($_POST["login"]);
-                               $password = $_POST["password"];
-                               $remember_me = $_POST["remember_me"];
 
-                               if (authenticate_user($link, $login, $password)) {
-                                       $_POST["password"] = "";
-
-                                       $_SESSION["language"] = $_POST["language"];
-                                       $_SESSION["ref_schema_version"] = get_schema_version($link, true);
-                                       $_SESSION["bw_limit"] = !!$_POST["bw_limit"];
-
-                                       if ($_POST["profile"]) {
-
-                                               $profile = db_escape_string($_POST["profile"]);
-
-                                               $result = db_query($link, "SELECT id FROM ttrss_settings_profiles
-                                                       WHERE id = '$profile' AND owner_uid = " . $_SESSION["uid"]);
-
-                                               if (db_num_rows($result) != 0) {
-                                                       $_SESSION["profile"] = $profile;
-                                                       $_SESSION["prefs_cache"] = array();
-                                               }
-                                       }
-
-                                       if ($_REQUEST['return']) {
-                                               header("Location: " . $_REQUEST['return']);
-                                       } else {
-                                               header("Location: " . $_SERVER["REQUEST_URI"]);
-                                       }
-
-                                       exit;
-
-                                       return;
-                               } else {
-                                       $_SESSION["login_error_msg"] = __("Incorrect username or password");
-                               }
+                       if ($_SESSION["uid"] && $_SESSION["language"] && SESSION_COOKIE_LIFETIME > 0) {
+                               setcookie("ttrss_lang", $_SESSION["language"],
+                                       time() + SESSION_COOKIE_LIFETIME);
                        }
 
-                       if (!$_SESSION["uid"] || !validate_session($link)) {
+                       if ($_SESSION["uid"]) {
+                               cache_prefs($link);
+                               load_user_plugins($link, $_SESSION["uid"]);
 
-                               if (AUTH_AUTO_LOGIN && authenticate_user($link, null, null)) {
-                                   $_SESSION["ref_schema_version"] = get_schema_version($link, true);
-                               } else {
-                                        authenticate_user($link, null, null, true);
-                                   render_login_form($link, $mobile);
-                                   exit;
-                               }
-                       } else {
-                               // bump login timestamp
-                               db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
-                                       $_SESSION["uid"]);
+                               /* cleanup ccache */
 
-                               if ($_SESSION["language"] && SESSION_COOKIE_LIFETIME > 0) {
-                                       setcookie("ttrss_lang", $_SESSION["language"],
-                                               time() + SESSION_COOKIE_LIFETIME);
-                               }
+                               db_query($link, "DELETE FROM ttrss_counters_cache WHERE owner_uid = ".
+                                       $_SESSION["uid"] . " AND
+                                               (SELECT COUNT(id) FROM ttrss_feeds WHERE
+                                                       ttrss_feeds.id = feed_id) = 0");
+
+                               db_query($link, "DELETE FROM ttrss_cat_counters_cache WHERE owner_uid = ".
+                                       $_SESSION["uid"] . " AND
+                                               (SELECT COUNT(id) FROM ttrss_feed_categories WHERE
+                                                       ttrss_feed_categories.id = feed_id) = 0");
 
-                               // try to remove possible duplicates from feed counter cache
-//                             ccache_cleanup($link, $_SESSION["uid"]);
                        }
 
-               } else {
-                       return authenticate_user($link, "admin", null);
                }
-       } */
+       }
 
        function truncate_string($str, $max_len, $suffix = '&hellip;') {
                if (mb_strlen($str, "utf-8") > $max_len - 3) {
                }
        }
 
-       function theme_image($link, $filename) {
-               if ($link) {
-                       $theme_path = get_user_theme_path($link);
-
-                       if ($theme_path && is_file($theme_path.$filename)) {
-                               return $theme_path.$filename;
-                       } else {
-                               return $filename;
-                       }
-               } else {
-                       return $filename;
-               }
-       }
-
-       function get_user_theme($link) {
-
-               if (get_schema_version($link) >= 63 && $_SESSION["uid"]) {
-                       $theme_name = get_pref($link, "_THEME_ID");
-                       if (is_dir("themes/$theme_name")) {
-                               return $theme_name;
-                       } else {
-                               return '';
-                       }
-               } else {
-                       return '';
-               }
-
-       }
-
-       function get_user_theme_path($link) {
-               $theme_path = '';
-
-               if (get_schema_version($link) >= 63 && $_SESSION["uid"]) {
-                       $theme_name = get_pref($link, "_THEME_ID");
-
-                       if ($theme_name && is_dir("themes/$theme_name")) {
-                               $theme_path = "themes/$theme_name/";
-                       } else {
-                               $theme_name = '';
-                       }
-               } else {
-                       $theme_path = '';
-               }
-
-               if ($theme_path) {
-                       if (is_file("$theme_path/theme.ini")) {
-                               $ini = parse_ini_file("$theme_path/theme.ini", true);
-                               if ($ini['theme']['version'] >= THEME_VERSION_REQUIRED) {
-                                       return $theme_path;
-                               }
-                       }
-               }
-               return '';
-       }
-
-       function get_user_theme_options($link) {
-               $t = get_user_theme_path($link);
-
-               if ($t) {
-                       if (is_file("$t/theme.ini")) {
-                               $ini = parse_ini_file("$t/theme.ini", true);
-                               if ($ini['theme']['version']) {
-                                       return $ini['theme']['options'];
-                               }
-                       }
-               }
-               return '';
-       }
-
-       function print_theme_includes($link) {
-
-               $t = get_user_theme_path($link);
-               $time = time();
-
-               if ($t) {
-                       print "<link rel=\"stylesheet\" type=\"text/css\"
-                               href=\"$t/theme.css?$time \">";
-                       if (file_exists("$t/theme.js")) {
-                               print "<script type=\"text/javascript\" src=\"$t/theme.js?$time\">
-                                       </script>";
-                       }
-               }
-       }
-
-       function get_all_themes() {
-               $themes = glob("themes/*");
-
-               asort($themes);
-
-               $rv = array();
-
-               foreach ($themes as $t) {
-                       if (is_file("$t/theme.ini")) {
-                               $ini = parse_ini_file("$t/theme.ini", true);
-                               if ($ini['theme']['version'] >= THEME_VERSION_REQUIRED &&
-                                                       !$ini['theme']['disabled']) {
-                                       $entry = array();
-                                       $entry["path"] = $t;
-                                       $entry["base"] = basename($t);
-                                       $entry["name"] = $ini['theme']['name'];
-                                       $entry["version"] = $ini['theme']['version'];
-                                       $entry["author"] = $ini['theme']['author'];
-                                       $entry["options"] = $ini['theme']['options'];
-                                       array_push($rv, $entry);
-                               }
-                       }
-               }
-
-               return $rv;
-       }
-
        function convert_timestamp($timestamp, $source_tz, $dest_tz) {
 
                try {
        }
 
        function sql_bool_to_bool($s) {
-               if ($s == "t" || $s == "1" || $s == "true") {
+               if ($s == "t" || $s == "1" || strtolower($s) == "true") {
                        return true;
                } else {
                        return false;
                        }
                }
 
-               if (db_escape_string("testTEST") != "testTEST") {
+               if (db_escape_string($link, "testTEST") != "testTEST") {
                        $error_code = 12;
                }
 
        function make_lockfile($filename) {
                $fp = fopen(LOCK_DIRECTORY . "/$filename", "w");
 
-               if (flock($fp, LOCK_EX | LOCK_NB)) {
+               if ($fp && flock($fp, LOCK_EX | LOCK_NB)) {
                        if (function_exists('posix_getpid')) {
                                fwrite($fp, posix_getpid() . "\n");
                        }
 
                        //if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) {
 
-                       $ref_check_qpart = ($max_id &&
-                               !get_pref($link, 'REVERSE_HEADLINES')) ? "ref_id <= '$max_id'" : "true";
-
                        if (is_numeric($feed)) {
                                if ($cat_view) {
 
                                        if ($feed >= 0) {
 
                                                if ($feed > 0) {
-                                                       $cat_qpart = "cat_id = '$feed'";
+                                                       $children = getChildCategories($link, $feed, $owner_uid);
+                                                       array_push($children, $feed);
+
+                                                       $children = join(",", $children);
+
+                                                       $cat_qpart = "cat_id IN ($children)";
                                                } else {
                                                        $cat_qpart = "cat_id IS NULL";
                                                }
 
-                                               $tmp_result = db_query($link, "SELECT id
-                                                       FROM ttrss_feeds WHERE $cat_qpart AND owner_uid = $owner_uid");
-
-                                               while ($tmp_line = db_fetch_assoc($tmp_result)) {
-
-                                                       $tmp_feed = $tmp_line["id"];
+                                               db_query($link, "UPDATE ttrss_user_entries
+                                                       SET unread = false,last_read = NOW()
+                                                       WHERE feed_id IN (SELECT id FROM ttrss_feeds WHERE $cat_qpart)
+                                                       AND unread = true
+                                                       AND owner_uid = $owner_uid");
 
-                                                       db_query($link, "UPDATE ttrss_user_entries
-                                                               SET unread = false,last_read = NOW()
-                                                               WHERE feed_id = '$tmp_feed'
-                                                               AND $ref_check_qpart
-                                                               AND owner_uid = $owner_uid");
-                                               }
                                        } else if ($feed == -2) {
 
                                                db_query($link, "UPDATE ttrss_user_entries
                                                        SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*)
                                                                FROM ttrss_user_labels2 WHERE article_id = ref_id) > 0
-                                                               AND $ref_check_qpart
                                                                AND unread = true AND owner_uid = $owner_uid");
                                        }
 
                                        db_query($link, "UPDATE ttrss_user_entries
                                                        SET unread = false,last_read = NOW()
                                                        WHERE feed_id = '$feed'
-                                                       AND $ref_check_qpart
+                                                       AND unread = true
                                                        AND owner_uid = $owner_uid");
 
-                               } else if ($feed < 0 && $feed > -10) { // special, like starred
+                               } else if ($feed < 0 && $feed > LABEL_BASE_INDEX) { // special, like starred
 
                                        if ($feed == -1) {
                                                db_query($link, "UPDATE ttrss_user_entries
                                                        SET unread = false,last_read = NOW()
                                                        WHERE marked = true
-                                                       AND $ref_check_qpart
+                                                       AND unread = true
                                                        AND owner_uid = $owner_uid");
                                        }
 
                                                db_query($link, "UPDATE ttrss_user_entries
                                                        SET unread = false,last_read = NOW()
                                                        WHERE published = true
-                                                       AND $ref_check_qpart
+                                                       AND unread = true
                                                        AND owner_uid = $owner_uid");
                                        }
 
                                        if ($feed == -4) {
                                                db_query($link, "UPDATE ttrss_user_entries
                                                        SET unread = false,last_read = NOW()
-                                                       WHERE $ref_check_qpart AND owner_uid = $owner_uid");
+                                                       WHERE unread = true AND
+                                                       owner_uid = $owner_uid");
                                        }
 
-                               } else if ($feed < -10) { // label
+                               } else if ($feed < LABEL_BASE_INDEX) { // label
 
-                                       $label_id = -$feed - 11;
+                                       $label_id = feed_to_label_id($feed);
 
                                        db_query($link, "UPDATE ttrss_user_entries, ttrss_user_labels2
                                                SET unread = false, last_read = NOW()
                                                        WHERE label_id = '$label_id' AND unread = true
-                                                       AND $ref_check_qpart
                                                        AND owner_uid = '$owner_uid' AND ref_id = article_id");
 
                                }
                        } else { // tag
                                db_query($link, "BEGIN");
 
-                               $tag_name = db_escape_string($feed);
+                               $tag_name = db_escape_string($link, $feed);
 
                                $result = db_query($link, "SELECT post_int_id FROM ttrss_tags
                                        WHERE tag_name = '$tag_name' AND owner_uid = $owner_uid");
                                while ($line = db_fetch_assoc($result)) {
                                        db_query($link, "UPDATE ttrss_user_entries SET
                                                unread = false, last_read = NOW()
-                                               WHERE $ref_check_qpart AND int_id = " . $line["post_int_id"]);
+                                               WHERE unread = true
+                                               AND int_id = " . $line["post_int_id"]);
                                }
                                db_query($link, "COMMIT");
                        }
        }
 
-       function getAllCounters($link, $omode = "flc", $active_feed = false) {
-
-               if (!$omode) $omode = "flc";
-
+       function getAllCounters($link) {
                $data = getGlobalCounters($link);
 
                $data = array_merge($data, getVirtCounters($link));
-
-               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")) $data = array_merge($data, getCategoryCounters($link));
+               $data = array_merge($data, getLabelCounters($link));
+               $data = array_merge($data, getFeedCounters($link, $active_feed));
+               $data = array_merge($data, getCategoryCounters($link));
 
                return $data;
        }
                        }
 
                        $cv = array("id" => $line["cat_id"], "kind" => "cat",
-                               "child_counter" => $child_counter,
-                               "counter" => $line["unread"]);
+                               "counter" => $line["unread"] + $child_counter);
 
                        array_push($ret_arr, $cv);
                }
                /* Special case: NULL category doesn't actually exist in the DB */
 
                $cv = array("id" => 0, "kind" => "cat",
-                       "counter" => ccache_find($link, 0, $_SESSION["uid"], true));
+                       "counter" => (int) ccache_find($link, 0, $_SESSION["uid"], true));
 
                array_push($ret_arr, $cv);
 
                        return 0;
                } else if ($feed != "0" && $n_feed == 0) {
 
-                       $feed = db_escape_string($feed);
+                       $feed = db_escape_string($link, $feed);
 
                        $result = db_query($link, "SELECT SUM((SELECT COUNT(int_id)
                                FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id
                                $match_part = "feed_id IS NULL";
                        }
 
-               } else if ($feed < -10) {
+               } else if ($feed < LABEL_BASE_INDEX) {
 
-                       $label_id = -$feed - 11;
+                       $label_id = feed_to_label_id($feed);
 
                        return getLabelUnread($link, $label_id, $owner_uid);
 
                }
 
                $cv = array("id" => "global-unread",
-                       "counter" => $global_unread);
+                       "counter" => (int) $global_unread);
 
                array_push($ret_arr, $cv);
 
                $subscribed_feeds = db_fetch_result($result, 0, "fn");
 
                $cv = array("id" => "subscribed-feeds",
-                       "counter" => $subscribed_feeds);
+                       "counter" => (int) $subscribed_feeds);
 
                array_push($ret_arr, $cv);
 
                return $ret_arr;
        }
 
-       function getTagCounters($link) {
-
-               $ret_arr = array();
-
-               $result = db_query($link, "SELECT tag_name,SUM((SELECT COUNT(int_id)
-                       FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id
-                               AND ref_id = id AND unread = true)) AS count FROM ttrss_tags
-                               WHERE owner_uid = ".$_SESSION['uid']." GROUP BY tag_name
-                               ORDER BY count DESC LIMIT 55");
-
-               $tags = array();
-
-               while ($line = db_fetch_assoc($result)) {
-                       $tags[$line["tag_name"]] += $line["count"];
-               }
-
-               foreach (array_keys($tags) as $tag) {
-                       $unread = $tags[$tag];
-                       $tag = htmlspecialchars($tag);
-
-                       $cv = array("id" => $tag,
-                               "kind" => "tag",
-                               "counter" => $unread);
-
-                       array_push($ret_arr, $cv);
-               }
-
-               return $ret_arr;
-       }
-
        function getVirtCounters($link) {
 
                $ret_arr = array();
                        $count = getFeedUnread($link, $i);
 
                        $cv = array("id" => $i,
-                               "counter" => $count);
+                               "counter" => (int) $count);
 
 //                     if (get_pref($link, 'EXTENDED_FEEDLIST'))
 //                             $cv["xmsg"] = getFeedArticles($link, $i)." ".__("total");
                        array_push($ret_arr, $cv);
                }
 
+               global $pluginhost;
+
+               if ($pluginhost) {
+                       $feeds = $pluginhost->get_feeds(-1);
+
+                       if (is_array($feeds)) {
+                               foreach ($feeds as $feed) {
+                                       $cv = array("id" => PluginHost::pfeed_to_feed_id($feed['id']),
+                                               "counter" => $feed['sender']->get_unread($feed['id']));
+
+                                       array_push($ret_arr, $cv);
+                               }
+                       }
+               }
+
                return $ret_arr;
        }
 
 
                $owner_uid = $_SESSION["uid"];
 
-               $result = db_query($link, "SELECT id, caption FROM ttrss_labels2
-                       WHERE owner_uid = '$owner_uid'");
+               $result = db_query($link, "SELECT id,caption,COUNT(unread) AS unread
+                       FROM ttrss_labels2 LEFT JOIN ttrss_user_labels2 ON
+                               (ttrss_labels2.id = label_id)
+                                       LEFT JOIN ttrss_user_entries ON (ref_id = article_id AND unread = true)
+                               WHERE ttrss_labels2.owner_uid = $owner_uid GROUP BY ttrss_labels2.id,
+                                       ttrss_labels2.caption");
 
                while ($line = db_fetch_assoc($result)) {
 
-                       $id = -$line["id"] - 11;
+                       $id = label_to_feed_id($line["id"]);
 
                        $label_name = $line["caption"];
-                       $count = getFeedUnread($link, $id);
+                       $count = $line["unread"];
 
                        $cv = array("id" => $id,
-                               "counter" => $count);
+                               "counter" => (int) $count);
 
                        if ($descriptions)
                                $cv["description"] = $label_name;
 
                        $cv = array("id" => $id,
                                "updated" => $last_updated,
-                               "counter" => $count,
+                               "counter" => (int) $count,
                                "has_img" => (int) $has_img);
 
                        if ($last_error)
         *                 5 - Couldn't download the URL content.
         */
        function subscribe_to_feed($link, $url, $cat_id = 0,
-                       $auth_login = '', $auth_pass = '', $need_auth = false) {
+                       $auth_login = '', $auth_pass = '') {
 
                global $fetch_last_error;
 
 
                if (!$url || !validate_feed_url($url)) return array("code" => 2);
 
-               $update_method = 0;
-
                $contents = @fetch_file_contents($url, false, $auth_login, $auth_pass);
 
                if (!$contents) {
                                "INSERT INTO ttrss_feeds
                                        (owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method)
                                VALUES ('".$_SESSION["uid"]."', '$url',
-                               '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', '$update_method')");
+                               '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', 0)");
 
                        $result = db_query($link,
                                "SELECT id FROM ttrss_feeds WHERE feed_url = '$url'
        function getFeedCatTitle($link, $id) {
                if ($id == -1) {
                        return __("Special");
-               } else if ($id < -10) {
+               } else if ($id < LABEL_BASE_INDEX) {
                        return __("Labels");
                } else if ($id > 0) {
                        $result = db_query($link, "SELECT ttrss_feed_categories.title
                        return "images/archive.png";
                        break;
                case -1:
-                       return "images/mark_set.png";
+                       return "images/mark_set.svg";
                        break;
                case -2:
-                       return "images/pub_set.png";
+                       return "images/pub_set.svg";
                        break;
                case -3:
                        return "images/fresh.png";
                        return "images/recently_read.png";
                        break;
                default:
-                       if ($id < -10) {
+                       if ($id < LABEL_BASE_INDEX) {
                                return "images/label.png";
                        } else {
                                if (file_exists(ICONS_DIR . "/$id.ico"))
                        return __("Archived articles");
                } else if ($id == -6) {
                        return __("Recently read");
-               } else if ($id < -10) {
-                       $label_id = -$id - 11;
+               } else if ($id < LABEL_BASE_INDEX) {
+                       $label_id = feed_to_label_id($id);
                        $result = db_query($link, "SELECT caption FROM ttrss_labels2 WHERE id = '$label_id'");
                        if (db_num_rows($result) == 1) {
                                return db_fetch_result($result, 0, "caption");
        function make_init_params($link) {
                $params = array();
 
-               $params["theme"] = get_user_theme($link);
-               $params["theme_options"] = get_user_theme_options($link);
-
-               $params["sign_progress"] = theme_image($link, "images/indicator_white.gif");
-               $params["sign_progress_tiny"] = theme_image($link, "images/indicator_tiny.gif");
-               $params["sign_excl"] = theme_image($link, "images/sign_excl.png");
-               $params["sign_info"] = theme_image($link, "images/sign_info.png");
-
                foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS",
                        "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP",
                        "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE", "DEFAULT_ARTICLE_LIMIT",
 
                $params["icons_url"] = ICONS_URL;
                $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME;
-               $params["default_include_children"] = get_pref($link, "_DEFAULT_INCLUDE_CHILDREN");
                $params["default_view_mode"] = get_pref($link, "_DEFAULT_VIEW_MODE");
                $params["default_view_limit"] = (int) get_pref($link, "_DEFAULT_VIEW_LIMIT");
                $params["default_view_order_by"] = get_pref($link, "_DEFAULT_VIEW_ORDER_BY");
                $params["bw_limit"] = (int) $_SESSION["bw_limit"];
+               $params["label_base_index"] = (int) LABEL_BASE_INDEX;
 
                $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
                        ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
                $params["num_feeds"] = (int) $num_feeds;
 
                $params["collapsed_feedlist"] = (int) get_pref($link, "_COLLAPSED_FEEDLIST");
+               $params["hotkeys"] = get_hotkeys_map($link);
 
                $params["csrf_token"] = $_SESSION["csrf_token"];
+               $params["widescreen"] = (int) $_COOKIE["ttrss_widescreen"];
+
+               $params['simple_update'] = defined('SIMPLE_UPDATE_MODE') && SIMPLE_UPDATE_MODE;
 
                return $params;
        }
 
+       function get_hotkeys_info($link) {
+               $hotkeys = array(
+                       __("Navigation") => array(
+                               "next_feed" => __("Open next feed"),
+                               "prev_feed" => __("Open previous feed"),
+                               "next_article" => __("Open next article"),
+                               "prev_article" => __("Open previous article"),
+                               "next_article_noscroll" => __("Open next article (don't scroll long articles)"),
+                               "prev_article_noscroll" => __("Open previous article (don't scroll long articles)"),
+                               "search_dialog" => __("Show search dialog")),
+                       __("Article") => array(
+                               "toggle_mark" => __("Toggle starred"),
+                               "toggle_publ" => __("Toggle published"),
+                               "toggle_unread" => __("Toggle unread"),
+                               "edit_tags" => __("Edit tags"),
+                               "dismiss_selected" => __("Dismiss selected"),
+                               "dismiss_read" => __("Dismiss read"),
+                               "open_in_new_window" => __("Open in new window"),
+                               "catchup_below" => __("Mark below as read"),
+                               "catchup_above" => __("Mark above as read"),
+                               "article_scroll_down" => __("Scroll down"),
+                               "article_scroll_up" => __("Scroll up"),
+                               "select_article_cursor" => __("Select article under cursor"),
+                               "email_article" => __("Email article"),
+                               "close_article" => __("Close/collapse article"),
+                               "toggle_widescreen" => __("Toggle widescreen mode"),
+                               "toggle_embed_original" => __("Toggle embed original")),
+                       __("Article selection") => array(
+                               "select_all" => __("Select all articles"),
+                               "select_unread" => __("Select unread"),
+                               "select_marked" => __("Select starred"),
+                               "select_published" => __("Select published"),
+                               "select_invert" => __("Invert selection"),
+                               "select_none" => __("Deselect everything")),
+                       __("Feed") => array(
+                               "feed_refresh" => __("Refresh current feed"),
+                               "feed_unhide_read" => __("Un/hide read feeds"),
+                               "feed_subscribe" => __("Subscribe to feed"),
+                               "feed_edit" => __("Edit feed"),
+                               "feed_catchup" => __("Mark as read"),
+                               "feed_reverse" => __("Reverse headlines"),
+                               "feed_debug_update" => __("Debug feed update"),
+                               "catchup_all" => __("Mark all feeds as read"),
+                               "cat_toggle_collapse" => __("Un/collapse current category"),
+                               "toggle_combined_mode" => __("Toggle combined mode"),
+                               "toggle_cdm_expanded" => __("Toggle auto expand in combined mode")),
+                       __("Go to") => array(
+                               "goto_all" => __("All articles"),
+                               "goto_fresh" => __("Fresh"),
+                               "goto_marked" => __("Starred"),
+                               "goto_published" => __("Published"),
+                               "goto_tagcloud" => __("Tag cloud"),
+                               "goto_prefs" => __("Preferences")),
+                       __("Other") => array(
+                               "create_label" => __("Create label"),
+                               "create_filter" => __("Create filter"),
+                               "collapse_sidebar" => __("Un/collapse sidebar"),
+                               "help_dialog" => __("Show help dialog"))
+                       );
+
+               return $hotkeys;
+       }
+
+       function get_hotkeys_map($link) {
+               $hotkeys = array(
+//                     "navigation" => array(
+                               "k" => "next_feed",
+                               "j" => "prev_feed",
+                               "n" => "next_article",
+                               "p" => "prev_article",
+                               "(38)|up" => "prev_article",
+                               "(40)|down" => "next_article",
+//                             "^(38)|Ctrl-up" => "prev_article_noscroll",
+//                             "^(40)|Ctrl-down" => "next_article_noscroll",
+                               "(191)|/" => "search_dialog",
+//                     "article" => array(
+                               "s" => "toggle_mark",
+                               "*s" => "toggle_publ",
+                               "u" => "toggle_unread",
+                               "*t" => "edit_tags",
+                               "*d" => "dismiss_selected",
+                               "*x" => "dismiss_read",
+                               "o" => "open_in_new_window",
+                               "c p" => "catchup_below",
+                               "c n" => "catchup_above",
+                               "*n" => "article_scroll_down",
+                               "*p" => "article_scroll_up",
+                               "*(38)|Shift+up" => "article_scroll_up",
+                               "*(40)|Shift+down" => "article_scroll_down",
+                               "a *w" => "toggle_widescreen",
+                               "a e" => "toggle_embed_original",
+                               "e" => "email_article",
+                               "a q" => "close_article",
+//                     "article_selection" => array(
+                               "a a" => "select_all",
+                               "a u" => "select_unread",
+                               "a *u" => "select_marked",
+                               "a p" => "select_published",
+                               "a i" => "select_invert",
+                               "a n" => "select_none",
+//                     "feed" => array(
+                               "f r" => "feed_refresh",
+                               "f a" => "feed_unhide_read",
+                               "f s" => "feed_subscribe",
+                               "f e" => "feed_edit",
+                               "f q" => "feed_catchup",
+                               "f x" => "feed_reverse",
+                               "f *d" => "feed_debug_update",
+                               "f *c" => "toggle_combined_mode",
+                               "f c" => "toggle_cdm_expanded",
+                               "*q" => "catchup_all",
+                               "x" => "cat_toggle_collapse",
+//                     "goto" => array(
+                               "g a" => "goto_all",
+                               "g f" => "goto_fresh",
+                               "g s" => "goto_marked",
+                               "g p" => "goto_published",
+                               "g t" => "goto_tagcloud",
+                               "g *p" => "goto_prefs",
+//                     "other" => array(
+                               "(9)|Tab" => "select_article_cursor", // tab
+                               "c l" => "create_label",
+                               "c f" => "create_filter",
+                               "c s" => "collapse_sidebar",
+                               "^(191)|Ctrl+/" => "help_dialog",
+                       );
+
+               if (get_pref($link, 'COMBINED_DISPLAY_MODE')) {
+                       $hotkeys["^(38)|Ctrl-up"] = "prev_article_noscroll";
+                       $hotkeys["^(40)|Ctrl-down"] = "next_article_noscroll";
+               }
+
+               global $pluginhost;
+               foreach ($pluginhost->get_hooks($pluginhost::HOOK_HOTKEY_MAP) as $plugin) {
+                       $hotkeys = $plugin->hook_hotkey_map($hotkeys);
+               }
+
+               $prefixes = array();
+
+               foreach (array_keys($hotkeys) as $hotkey) {
+                       $pair = explode(" ", $hotkey, 2);
+
+                       if (count($pair) > 1 && !in_array($pair[0], $prefixes)) {
+                               array_push($prefixes, $pair[0]);
+                       }
+               }
+
+               return array($prefixes, $hotkeys);
+       }
+
        function make_runtime_info($link) {
                $data = array();
 
                $data['last_article_id'] = getLastArticleId($link);
                $data['cdm_expanded'] = get_pref($link, 'CDM_EXPANDED');
 
+               $data['dep_ts'] = calculate_dep_timestamp();
+               $data['reload_on_ts_change'] = !defined('_NO_RELOAD_ON_TS_CHANGE');
+
                if (file_exists(LOCK_DIRECTORY . "/update_daemon.lock")) {
 
                        $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
                return $data;
        }
 
-       function search_to_sql($link, $search, $match_on) {
+       function search_to_sql($link, $search) {
 
                $search_query_part = "";
 
                                //$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") {
+                       } else {
                                array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
                                                OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
-                       } else if ($match_on == "title") {
-                               array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%'))");
-                       } else if ($match_on == "content") {
-                               array_push($query_keywords, "(UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
                        }
                }
 
                return $rv;
        }
 
-       function queryFeedHeadlines($link, $feed, $limit, $view_mode, $cat_view, $search, $search_mode, $match_on, $override_order = false, $offset = 0, $owner_uid = 0, $filter = false, $since_id = 0, $include_children = false) {
+       function queryFeedHeadlines($link, $feed, $limit, $view_mode, $cat_view, $search, $search_mode, $override_order = false, $offset = 0, $owner_uid = 0, $filter = false, $since_id = 0, $include_children = false, $ignore_vfeed_group = false) {
 
                if (!$owner_uid) $owner_uid = $_SESSION["uid"];
 
                                                $search_query_part = "ref_id = -1 AND ";
 
                                } else {
-                                       $search_query_part = search_to_sql($link, $search, $match_on);
+                                       $search_query_part = search_to_sql($link, $search);
                                        $search_query_part .= " AND ";
                                }
 
 
                                $override_order = "updated DESC";
 
-                               $filter_query_part = filter_to_sql($filter) . " AND";
+                               $filter_query_part = filter_to_sql($link, $filter, $owner_uid);
+
+                               // Try to check if SQL regexp implementation chokes on a valid regexp
+                               $result = db_query($link, "SELECT true AS true_val FROM ttrss_entries,
+                                       ttrss_user_entries, ttrss_feeds, ttrss_feed_categories
+                                       WHERE $filter_query_part LIMIT 1", false);
+
+                               if ($result) {
+                                       $test = db_fetch_result($result, 0, "true_val");
+
+                                       if (!$test) {
+                                               $filter_query_part = "false AND";
+                                       } else {
+                                               $filter_query_part .= " AND";
+                                       }
+                               } else {
+                                       $filter_query_part = "false AND";
+                               }
+
                        } else {
                                $filter_query_part = "";
                        }
 
                        $view_query_part = "";
 
-                       if ($view_mode == "adaptive" || $view_query_part == "noscores") {
+                       if ($view_mode == "adaptive") {
                                if ($search) {
                                        $view_query_part = " ";
                                } else if ($feed != -1) {
+
                                        $unread = getFeedUnread($link, $feed, $cat_view);
 
                                        if ($cat_view && $feed > 0 && $include_children)
                                                $unread += getCategoryChildrenUnread($link, $feed);
 
-                                       if ($unread > 0) {
-                                               $view_query_part = " unread = true AND ";
-                                       }
+                                       if ($unread > 0)
+                               $view_query_part = " unread = true AND ";
+
                                }
                        }
 
                                $view_query_part = " marked = true AND ";
                        }
 
+                       if ($view_mode == "has_note") {
+                               $view_query_part = " (note IS NOT NULL AND note != '') AND ";
+                       }
+
                        if ($view_mode == "published") {
                                $view_query_part = " published = true AND ";
                        }
 
-                       if ($view_mode == "unread") {
+                       if ($view_mode == "unread" && $feed != -6) {
                                $view_query_part = " unread = true AND ";
                        }
 
-                       if ($view_mode == "updated") {
-                               $view_query_part = " (last_read is null and unread = false) AND ";
-                       }
-
                        if ($limit > 0) {
                                $limit_query_part = "LIMIT " . $limit;
                        }
                        } else if ($feed == -1) { // starred virtual feed
                                $query_strategy_part = "marked = true";
                                $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
+                               $allow_archived = true;
+
+                               if (!$override_order) {
+                                       $override_order = "last_marked DESC, date_entered DESC, updated DESC";
+                               }
+
                        } else if ($feed == -2) { // published virtual feed OR labels category
 
                                if (!$cat_view) {
                                        $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
                                        $allow_archived = true;
 
-                                       if (!$override_order) $override_order = "last_read DESC, updated DESC";
+                                       if (!$override_order) {
+                                               $override_order = "last_published DESC, date_entered DESC, updated DESC";
+                                       }
+
                                } else {
                                        $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
 
                        } else if ($feed == -6) { // recently read
                                $query_strategy_part = "unread = false AND last_read IS NOT NULL";
                                $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
+                               $allow_archived = true;
 
                                if (!$override_order) $override_order = "last_read DESC";
                        } else if ($feed == -3) { // fresh virtual feed
                                $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
 
                                if (DB_TYPE == "pgsql") {
-                                       $query_strategy_part .= " AND updated > NOW() - INTERVAL '$intl hour' ";
+                                       $query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
                                } else {
-                                       $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
+                                       $query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
                                }
 
                                $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
                        } else if ($feed == -4) { // all articles virtual feed
                                $query_strategy_part = "true";
                                $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
-                       } else if ($feed <= -10) { // labels
-                               $label_id = -$feed - 11;
+                       } else if ($feed <= LABEL_BASE_INDEX) { // labels
+                               $label_id = feed_to_label_id($feed);
 
                                $query_strategy_part = "label_id = '$label_id' AND
                                        ttrss_labels2.id = ttrss_user_labels2.label_id AND
 
                                $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
                                $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
+                               $allow_archived = true;
 
                        } else {
-                               $query_strategy_part = "id > 0"; // dumb
+                               $query_strategy_part = "true";
                        }
 
                        if (get_pref($link, "SORT_HEADLINES_BY_FEED_DATE", $owner_uid)) {
                                $date_sort_field = "date_entered";
                        }
 
-                       if (get_pref($link, 'REVERSE_HEADLINES', $owner_uid)) {
-                               $order_by = "$date_sort_field";
-                       } else {
-                               $order_by = "$date_sort_field DESC";
-                       }
+                       $order_by = "$date_sort_field DESC, updated DESC";
 
-                       if ($view_mode != "noscores") {
-                               $order_by = "score DESC, $order_by";
+                       if ($view_mode == "unread_first") {
+                               $order_by = "unread DESC, $order_by";
                        }
 
                        if ($override_order) {
                                }
                        }
 
-                       $content_query_part = "content as content_preview,";
+                       $content_query_part = "content as content_preview, cached_content, ";
 
                        if (is_numeric($feed)) {
 
                                        $offset_query_part = "OFFSET $offset";
                                }
 
-                               if ($vfeed_query_part && get_pref($link, 'VFEED_GROUP_BY_FEED', $owner_uid)) {
+                               // proper override_order applied above
+                               if ($vfeed_query_part && !$ignore_vfeed_group && get_pref($link, 'VFEED_GROUP_BY_FEED', $owner_uid)) {
                                        if (!$override_order) {
                                                $order_by = "ttrss_feeds.title, $order_by";
+                                       } else {
+                                               $order_by = "ttrss_feeds.title, $override_order";
                                        }
                                }
 
                                        $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
 
                                } else {
-                                       $from_qpart = "ttrss_entries,ttrss_user_entries$ext_tables_part
+                                       $from_qpart = "ttrss_entries$ext_tables_part,ttrss_user_entries
                                                LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
                                }
 
                                                num_comments,
                                                comments,
                                                int_id,
+                                               hide_images,
                                                unread,feed_id,marked,published,link,last_read,orig_feed_id,
-                                               ".SUBSTRING_FOR_DATE."(last_read,1,19) as last_read_noms,
+                                               last_marked, last_published,
                                                $vfeed_query_part
                                                $content_query_part
-                                               ".SUBSTRING_FOR_DATE."(updated,1,19) as updated_noms,
                                                author,score
                                        FROM
                                                $from_qpart
                                                                "label_cache," .
                                                                "link," .
                                                                "last_read," .
-                                                               SUBSTRING_FOR_DATE . "(last_read,1,19) as last_read_noms," .
+                                                               "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
+                                                               "last_marked, last_published, " .
                                                                $since_id_part .
                                                                $vfeed_query_part .
                                                                $content_query_part .
-                                                               SUBSTRING_FOR_DATE . "(updated,1,19) as updated_noms," .
                                                                "score ";
 
                                $feed_kind = "Tags";
 
        }
 
-       function sanitize($link, $str, $force_strip_tags = false, $owner = false, $site_url = false) {
-               global $purifier;
-
+       function sanitize($link, $str, $force_remove_images = false, $owner = false, $site_url = false) {
                if (!$owner) $owner = $_SESSION["uid"];
 
                $res = trim($str); if (!$res) return '';
 
-               // create global Purifier object if needed
-               if (!$purifier) {
-                       require_once 'lib/htmlpurifier/library/HTMLPurifier.auto.php';
-
-                       $config = HTMLPurifier_Config::createDefault();
-
-                       $allowed = "p,a[href],i,em,b,strong,code,pre,blockquote,br,img[src|alt|title|align|hspace],ul,ol,li,h1,h2,h3,h4,s,object[classid|type|id|name|width|height|codebase],param[name|value],table,tr,td,span[class]";
-
-                       $config->set('HTML.SafeObject', true);
-                       @$config->set('HTML', 'Allowed', $allowed);
-                       $config->set('Output.FlashCompat', true);
-                       $config->set('Attr.EnableID', true);
-                       if (!defined('MOBILE_VERSION')) {
-                               @$config->set('Cache', 'SerializerPath', CACHE_DIR . "/htmlpurifier");
-                       } else {
-                               @$config->set('Cache', 'SerializerPath', "../" . CACHE_DIR . "/htmlpurifier");
-                       }
-
-                       $config->set('Filter.YouTube', true);
-
-                       $purifier = new HTMLPurifier($config);
-               }
-
-               $res = $purifier->purify($res);
-
-               if (get_pref($link, "STRIP_IMAGES", $owner)) {
-                       $res = preg_replace('/<img[^>]+>/is', '', $res);
-               }
-
                if (strpos($res, "href=") === false)
                        $res = rewrite_urls($res);
 
                $xpath = new DOMXPath($doc);
 
                $entries = $xpath->query('(//a[@href]|//img[@src])');
-               $br_inserted = 0;
 
                foreach ($entries as $entry) {
 
                                        $entry->setAttribute('href',
                                                rewrite_relative_url($site_url, $entry->getAttribute('href')));
 
-                               if ($entry->hasAttribute('src'))
-                                       if (preg_match('/^image.php\?i=[a-z0-9]+$/', $entry->getAttribute('src')) == 0)
-                                               $entry->setAttribute('src',
-                                                       rewrite_relative_url($site_url, $entry->getAttribute('src')));
-                       }
+                               if ($entry->hasAttribute('src')) {
+                                       $src = rewrite_relative_url($site_url, $entry->getAttribute('src'));
 
-                       if (strtolower($entry->nodeName) == "a") {
-                               $entry->setAttribute("target", "_blank");
-                       }
+                                       $cached_filename = CACHE_DIR . '/images/' . sha1($src) . '.png';
 
-                       if (strtolower($entry->nodeName) == "img" && !$br_inserted) {
-                               $br = $doc->createElement("br");
+                                       if (file_exists($cached_filename)) {
+                                               $src = SELF_URL_PATH . '/image.php?hash=' . sha1($src);
+                                       }
 
-                               if ($entry->parentNode->nextSibling) {
-                                       $entry->parentNode->insertBefore($br, $entry->nextSibling);
-                                       $br_inserted = 1;
+                                       $entry->setAttribute('src', $src);
                                }
 
-                       }
-               }
-
-               $node = $doc->getElementsByTagName('body')->item(0);
+                               if ($entry->nodeName == 'img') {
+                                       if (($owner && get_pref($link, "STRIP_IMAGES", $owner)) ||
+                                                       $force_remove_images || $_SESSION["bw_limit"]) {
 
-               return $doc->saveXML($node, LIBXML_NOEMPTYTAG);
-       }
+                                               $p = $doc->createElement('p');
 
-       /**
-        * Send by mail a digest of last articles.
-        *
-        * @param mixed $link The database connection.
-        * @param integer $limit The maximum number of articles by digest.
-        * @return boolean Return false if digests are not enabled.
-        */
-       function send_headlines_digests($link, $debug = false) {
+                                               $a = $doc->createElement('a');
+                                               $a->setAttribute('href', $entry->getAttribute('src'));
 
-               require_once 'lib/phpmailer/class.phpmailer.php';
+                                               $a->appendChild(new DOMText($entry->getAttribute('src')));
+                                               $a->setAttribute('target', '_blank');
 
-               $user_limit = 15; // amount of users to process (e.g. emails to send out)
-               $limit = 1000; // maximum amount of headlines to include
+                                               $p->appendChild($a);
 
-               if ($debug) _debug("Sending digests, batch of max $user_limit users, headline limit = $limit");
+                                               $entry->parentNode->replaceChild($p, $entry);
+                                       }
+                               }
+                       }
 
-               if (DB_TYPE == "pgsql") {
-                       $interval_query = "last_digest_sent < NOW() - INTERVAL '1 days'";
-               } else if (DB_TYPE == "mysql") {
-                       $interval_query = "last_digest_sent < DATE_SUB(NOW(), INTERVAL 1 DAY)";
+                       if (strtolower($entry->nodeName) == "a") {
+                               $entry->setAttribute("target", "_blank");
+                       }
                }
 
-               $result = db_query($link, "SELECT id,email FROM ttrss_users
-                               WHERE email != '' AND (last_digest_sent IS NULL OR $interval_query)");
-
-               while ($line = db_fetch_assoc($result)) {
-
-                       if (get_pref($link, 'DIGEST_ENABLE', $line['id'], false)) {
-                               $preferred_ts = strtotime(get_pref($link, 'DIGEST_PREFERRED_TIME', $line['id'], '00:00'));
-
-                               // try to send digests within 2 hours of preferred time
-                               if ($preferred_ts && time() >= $preferred_ts &&
-                                               time() - $preferred_ts <= 7200) {
-
-                                       if ($debug) print "Sending digest for UID:" . $line['id'] . " - " . $line["email"] . " ... ";
-
-                                       $do_catchup = get_pref($link, 'DIGEST_CATCHUP', $line['id'], false);
-
-                                       global $tz_offset;
-
-                                       // reset tz_offset global to prevent tz cache clash between users
-                                       $tz_offset = -1;
-
-                                       $tuple = prepare_headlines_digest($link, $line["id"], 1, $limit);
-                                       $digest = $tuple[0];
-                                       $headlines_count = $tuple[1];
-                                       $affected_ids = $tuple[2];
-                                       $digest_text = $tuple[3];
-
-                                       if ($headlines_count > 0) {
-
-                                               $mail = new PHPMailer();
-
-                                               $mail->PluginDir = "lib/phpmailer/";
-                                               $mail->SetLanguage("en", "lib/phpmailer/language/");
-
-                                               $mail->CharSet = "UTF-8";
-
-                                               $mail->From = SMTP_FROM_ADDRESS;
-                                               $mail->FromName = SMTP_FROM_NAME;
-                                               $mail->AddAddress($line["email"], $line["login"]);
-
-                                               if (SMTP_HOST) {
-                                                       $mail->Host = SMTP_HOST;
-                                                       $mail->Mailer = "smtp";
-                                                       $mail->SMTPAuth = SMTP_LOGIN != '';
-                                                       $mail->Username = SMTP_LOGIN;
-                                                       $mail->Password = SMTP_PASSWORD;
-                                               }
-
-                                               $mail->IsHTML(true);
-                                               $mail->Subject = DIGEST_SUBJECT;
-                                               $mail->Body = $digest;
-                                               $mail->AltBody = $digest_text;
+               $entries = $xpath->query('//iframe');
+               foreach ($entries as $entry) {
+                       $entry->setAttribute('sandbox', 'allow-scripts');
 
-                                               $rc = $mail->Send();
+               }
 
-                                               if (!$rc && $debug) print "ERROR: " . $mail->ErrorInfo;
+               $allowed_elements = array('a', 'address', 'audio', 'article',
+                       'b', 'big', 'blockquote', 'body', 'br', 'cite', 'center',
+                       'code', 'dd', 'del', 'details', 'div', 'dl', 'font',
+                       'dt', 'em', 'footer', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
+                       'header', 'html', 'i', 'img', 'ins', 'kbd',
+                       'li', 'nav', 'noscript', 'ol', 'p', 'pre', 'q', 's','small',
+                       'source', 'span', 'strike', 'strong', 'sub', 'summary',
+                       'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead',
+                       'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
 
-                                               if ($debug) print "RC=$rc\n";
+               if ($_SESSION['hasSandbox']) $allowed_elements[] = 'iframe';
 
-                                               if ($rc && $do_catchup) {
-                                                       if ($debug) print "Marking affected articles as read...\n";
-                                                       catchupArticlesById($link, $affected_ids, 0, $line["id"]);
-                                               }
-                                       } else {
-                                               if ($debug) print "No headlines\n";
-                                       }
+               $disallowed_attributes = array('id', 'style', 'class');
 
-                                       db_query($link, "UPDATE ttrss_users SET last_digest_sent = NOW()
-                                               WHERE id = " . $line["id"]);
+               global $pluginhost;
 
+               if (isset($pluginhost)) {
+                       foreach ($pluginhost->get_hooks($pluginhost::HOOK_SANITIZE) as $plugin) {
+                               $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes);
+                               if (is_array($retval)) {
+                                       $doc = $retval[0];
+                                       $allowed_elements = $retval[1];
+                                       $disallowed_attributes = $retval[2];
+                               } else {
+                                       $doc = $retval;
                                }
                        }
                }
 
-               if ($debug) _debug("All done.");
-
+               $doc->removeChild($doc->firstChild); //remove doctype
+               $doc = strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes);
+               $res = $doc->saveHTML();
+               return $res;
        }
 
-       function prepare_headlines_digest($link, $user_id, $days = 1, $limit = 1000) {
-
-               require_once "lib/MiniTemplator.class.php";
-
-               $tpl = new MiniTemplator;
-               $tpl_t = new MiniTemplator;
-
-               $tpl->readTemplateFromFile("templates/digest_template_html.txt");
-               $tpl_t->readTemplateFromFile("templates/digest_template.txt");
-
-               $user_tz_string = get_pref($link, 'USER_TIMEZONE', $user_id);
-               $local_ts = convert_timestamp(time(), 'UTC', $user_tz_string);
-
-               $tpl->setVariable('CUR_DATE', date('Y/m/d', $local_ts));
-               $tpl->setVariable('CUR_TIME', date('G:i', $local_ts));
-
-               $tpl_t->setVariable('CUR_DATE', date('Y/m/d', $local_ts));
-               $tpl_t->setVariable('CUR_TIME', date('G:i', $local_ts));
-
-               $affected_ids = array();
-
-               if (DB_TYPE == "pgsql") {
-                       $interval_query = "ttrss_entries.date_updated > NOW() - INTERVAL '$days days'";
-               } else if (DB_TYPE == "mysql") {
-                       $interval_query = "ttrss_entries.date_updated > DATE_SUB(NOW(), INTERVAL $days DAY)";
-               }
-
-               $result = db_query($link, "SELECT ttrss_entries.title,
-                               ttrss_feeds.title AS feed_title,
-                               COALESCE(ttrss_feed_categories.title, '".__('Uncategorized')."') AS cat_title,
-                               date_updated,
-                               ttrss_user_entries.ref_id,
-                               link,
-                               score,
-                               content,
-                               ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated
-                       FROM
-                               ttrss_user_entries,ttrss_entries,ttrss_feeds
-                       LEFT JOIN
-                               ttrss_feed_categories ON (cat_id = ttrss_feed_categories.id)
-                       WHERE
-                               ref_id = ttrss_entries.id AND feed_id = ttrss_feeds.id
-                               AND include_in_digest = true
-                               AND $interval_query
-                               AND ttrss_user_entries.owner_uid = $user_id
-                               AND unread = true
-                               AND score >= 0
-                       ORDER BY ttrss_feed_categories.title, ttrss_feeds.title, score DESC, date_updated DESC
-                       LIMIT $limit");
-
-               $cur_feed_title = "";
-
-               $headlines_count = db_num_rows($result);
-
-               $headlines = array();
-
-               while ($line = db_fetch_assoc($result)) {
-                       array_push($headlines, $line);
-               }
-
-               for ($i = 0; $i < sizeof($headlines); $i++) {
-
-                       $line = $headlines[$i];
+       function strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes) {
+               $entries = $doc->getElementsByTagName("*");
 
-                       array_push($affected_ids, $line["ref_id"]);
+               foreach ($entries as $entry) {
+                       if (!in_array($entry->nodeName, $allowed_elements)) {
+                               $entry->parentNode->removeChild($entry);
+                       }
 
-                       $updated = make_local_datetime($link, $line['last_updated'], false,
-                               $user_id);
+                       if ($entry->hasAttributes()) {
+                               $attrs_to_remove = array();
 
-/*                     if ($line["score"] != 0) {
-                               if ($line["score"] > 0) $line["score"] = '+' . $line["score"];
+                               foreach ($entry->attributes as $attr) {
 
-                               $line["title"] .= " (".$line['score'].")";
-                       } */
+                                       if (strpos($attr->nodeName, 'on') === 0) {
+                                               array_push($attrs_to_remove, $attr);
+                                       }
 
-                       if (get_pref($link, 'ENABLE_FEED_CATS', $user_id)) {
-                               $line['feed_title'] = $line['cat_title'] . " / " . $line['feed_title'];
-                       }
+                                       if (in_array($attr->nodeName, $disallowed_attributes)) {
+                                               array_push($attrs_to_remove, $attr);
+                                       }
+                               }
 
-                       $tpl->setVariable('FEED_TITLE', $line["feed_title"]);
-                       $tpl->setVariable('ARTICLE_TITLE', $line["title"]);
-                       $tpl->setVariable('ARTICLE_LINK', $line["link"]);
-                       $tpl->setVariable('ARTICLE_UPDATED', $updated);
-                       $tpl->setVariable('ARTICLE_EXCERPT',
-                               truncate_string(strip_tags($line["content"]), 300));
-//                     $tpl->setVariable('ARTICLE_CONTENT',
-//                             strip_tags($article_content));
-
-                       $tpl->addBlock('article');
-
-                       $tpl_t->setVariable('FEED_TITLE', $line["feed_title"]);
-                       $tpl_t->setVariable('ARTICLE_TITLE', $line["title"]);
-                       $tpl_t->setVariable('ARTICLE_LINK', $line["link"]);
-                       $tpl_t->setVariable('ARTICLE_UPDATED', $updated);
-//                     $tpl_t->setVariable('ARTICLE_EXCERPT',
-//                             truncate_string(strip_tags($line["excerpt"]), 100));
-
-                       $tpl_t->addBlock('article');
-
-                       if ($headlines[$i]['feed_title'] != $headlines[$i+1]['feed_title']) {
-                               $tpl->addBlock('feed');
-                               $tpl_t->addBlock('feed');
+                               foreach ($attrs_to_remove as $attr) {
+                                       $entry->removeAttributeNode($attr);
+                               }
                        }
-
                }
 
-               $tpl->addBlock('digest');
-               $tpl->generateOutputToString($tmp);
-
-               $tpl_t->addBlock('digest');
-               $tpl_t->generateOutputToString($tmp_t);
-
-               return array($tmp, $headlines_count, $affected_ids, $tmp_t);
+               return $doc;
        }
 
        function check_for_update($link) {
                if (CHECK_FOR_NEW_VERSION && $_SESSION['access_level'] >= 10) {
-                       $version_url = "http://tt-rss.org/version.php?ver=" . VERSION;
+                       $version_url = "http://tt-rss.org/version.php?ver=" . VERSION .
+                               "&iid=" . sha1(SELF_URL_PATH);
 
                        $version_data = @fetch_file_contents($version_url);
 
                return false;
        }
 
-       function markArticlesById($link, $ids, $cmode) {
-
-               $tmp_ids = array();
-
-               foreach ($ids as $id) {
-                       array_push($tmp_ids, "ref_id = '$id'");
-               }
-
-               $ids_qpart = join(" OR ", $tmp_ids);
-
-               if ($cmode == 0) {
-                       db_query($link, "UPDATE ttrss_user_entries SET
-                       marked = false,last_read = NOW()
-                       WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]);
-               } else if ($cmode == 1) {
-                       db_query($link, "UPDATE ttrss_user_entries SET
-                       marked = true
-                       WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]);
-               } else {
-                       db_query($link, "UPDATE ttrss_user_entries SET
-                       marked = NOT marked,last_read = NOW()
-                       WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]);
-               }
-       }
-
-       function publishArticlesById($link, $ids, $cmode) {
-
-               $tmp_ids = array();
-
-               foreach ($ids as $id) {
-                       array_push($tmp_ids, "ref_id = '$id'");
-               }
-
-               $ids_qpart = join(" OR ", $tmp_ids);
-
-               if ($cmode == 0) {
-                       db_query($link, "UPDATE ttrss_user_entries SET
-                       published = false,last_read = NOW()
-                       WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]);
-               } else if ($cmode == 1) {
-                       db_query($link, "UPDATE ttrss_user_entries SET
-                       published = true,last_read = NOW()
-                       WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]);
-               } else {
-                       db_query($link, "UPDATE ttrss_user_entries SET
-                       published = NOT published,last_read = NOW()
-                       WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]);
-               }
-
-               if (PUBSUBHUBBUB_HUB) {
-                       $rss_link = get_self_url_prefix() .
-                               "/public.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) {
 
                if (!$owner_uid) $owner_uid = $_SESSION["uid"];
                }
        }
 
-       function catchupArticleById($link, $id, $cmode) {
-
-               if ($cmode == 0) {
-                       db_query($link, "UPDATE ttrss_user_entries SET
-                       unread = false,last_read = NOW()
-                       WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
-               } else if ($cmode == 1) {
-                       db_query($link, "UPDATE ttrss_user_entries SET
-                       unread = true
-                       WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
-               } else {
-                       db_query($link, "UPDATE ttrss_user_entries SET
-                       unread = NOT unread,last_read = NOW()
-                       WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
-               }
-
-               $feed_id = getArticleFeed($link, $id);
-               ccache_update($link, $feed_id, $_SESSION["uid"]);
-       }
-
-       function make_guid_from_title($title) {
-               return preg_replace("/[ \"\',.:;]/", "-",
-                       mb_strtolower(strip_tags($title), 'utf-8'));
-       }
-
        function get_article_tags($link, $id, $owner_uid = 0, $tag_cache = false) {
 
-               $a_id = db_escape_string($id);
+               $a_id = db_escape_string($link, $id);
 
                if (!$owner_uid) $owner_uid = $_SESSION["uid"];
 
 
                        /* update the cache */
 
-                       $tags_str = db_escape_string(join(",", $tags));
+                       $tags_str = db_escape_string($link, join(",", $tags));
 
                        db_query($link, "UPDATE ttrss_user_entries
                                SET tag_cache = '$tags_str' WHERE ref_id = '$id'
                return true;
        }
 
-       function render_login_form($link, $form_id = 0) {
-               switch ($form_id) {
-               case 0:
-                       require_once "login_form.php";
-                       break;
-               case 1:
-                       require_once "mobile/login_form.php";
-                       break;
-               }
+       function render_login_form($link) {
+               require_once "login_form.php";
                exit;
        }
 
        function format_warning($msg, $id = "") {
                global $link;
                return "<div class=\"warning\" id=\"$id\">
-                       <img src=\"".theme_image($link, "images/sign_excl.png")."\">$msg</div>";
+                       <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
        }
 
        function format_notice($msg, $id = "") {
                global $link;
                return "<div class=\"notice\" id=\"$id\">
-                       <img src=\"".theme_image($link, "images/sign_info.png")."\">$msg</div>";
+                       <img src=\"images/sign_info.svg\"><div class='inner'>$msg</div></div>";
        }
 
        function format_error($msg, $id = "") {
                global $link;
                return "<div class=\"error\" id=\"$id\">
-                       <img src=\"".theme_image($link, "images/sign_excl.png")."\">$msg</div>";
+                       <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
        }
 
        function print_notice($msg) {
 
                $entry = "";
 
+               $url = htmlspecialchars($url);
+
                if (strpos($ctype, "audio/") === 0) {
 
                        if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
 
                                $id = 'AUDIO-' . uniqid();
 
-                               $entry .= "<audio id=\"$id\"\">
-                                       <source src=\"$url\"></source>
+                               $entry .= "<audio id=\"$id\"\" controls style='display : none'>
+                                       <source type=\"$ctype\" src=\"$url\"></source>
                                        </audio>";
 
                                $entry .= "<span onclick=\"player(this)\"
                                                value=\"lib/button/musicplayer.swf?song_url=$url\" />
                                        </object>";
                        }
+
+                       if ($entry) $entry .= "&nbsp; <a target=\"_blank\"
+                               href=\"$url\">" . basename($url) . "</a>";
+
+                       return $entry;
+
                }
 
-               $filename = substr($url, strrpos($url, "/")+1);
+               return "";
+
+/*             $filename = substr($url, strrpos($url, "/")+1);
 
                $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
-                       $filename . " (" . $ctype . ")" . "</a>";
+                       $filename . " (" . $ctype . ")" . "</a>"; */
 
-               return $entry;
        }
 
        function format_article($link, $id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) {
 
                //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 = $owner_uid");
-
-               if (db_num_rows($result) == 1) {
-                       $rtl_content = sql_bool_to_bool(db_fetch_result($result, 0, "rtl_content"));
-                       $always_display_enclosures = sql_bool_to_bool(db_fetch_result($result, 0, "always_display_enclosures"));
-               } else {
-                       $rtl_content = false;
-                       $always_display_enclosures = false;
-               }
-
-               if ($rtl_content) {
-                       $rtl_tag = "dir=\"RTL\"";
-                       $rtl_class = "RTL";
-               } else {
-                       $rtl_tag = "";
-                       $rtl_class = "";
-               }
-
                if ($mark_as_read) {
                        $result = db_query($link, "UPDATE ttrss_user_entries
                                SET unread = false,last_read = NOW()
                        ccache_update($link, $feed_id, $owner_uid);
                }
 
-               $result = db_query($link, "SELECT title,link,content,feed_id,comments,int_id,
+               $result = db_query($link, "SELECT id,title,link,content,feed_id,comments,int_id,
                        ".SUBSTRING_FOR_DATE."(updated,1,16) as updated,
-                       (SELECT icon_url FROM ttrss_feeds WHERE id = feed_id) as icon_url,
                        (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
+                       (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
                        num_comments,
                        tag_cache,
                        author,
                        orig_feed_id,
-                       note
+                       note,
+                       cached_content
                        FROM ttrss_entries,ttrss_user_entries
                        WHERE   id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
 
 
                        $line = db_fetch_assoc($result);
 
-                       if ($line["icon_url"]) {
-                               $feed_icon = "<img src=\"" . $line["icon_url"] . "\">";
-                       } else {
-                               $feed_icon = "&nbsp;";
-                       }
+                       $tag_cache = $line["tag_cache"];
+
+                       $line["tags"] = get_article_tags($link, $id, $owner_uid, $line["tag_cache"]);
+                       unset($line["tag_cache"]);
+
+                       $line["content"] = sanitize($link, $line["content"], false, $owner_uid, $line["site_url"]);
+
+                       global $pluginhost;
 
-                       $feed_site_url = $line['site_url'];
+                       foreach ($pluginhost->get_hooks($pluginhost::HOOK_RENDER_ARTICLE) as $p) {
+                               $line = $p->hook_render_article($line);
+                       }
 
                        $num_comments = $line["num_comments"];
                        $entry_comments = "";
 
                        if ($num_comments > 0) {
                                if ($line["comments"]) {
-                                       $comments_url = $line["comments"];
+                                       $comments_url = htmlspecialchars($line["comments"]);
                                } else {
-                                       $comments_url = $line["link"];
+                                       $comments_url = htmlspecialchars($line["link"]);
                                }
                                $entry_comments = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
                        } else {
                                if ($line["comments"] && $line["link"] != $line["comments"]) {
-                                       $entry_comments = "<a target='_blank' href=\"".$line["comments"]."\">comments</a>";
+                                       $entry_comments = "<a target='_blank' href=\"".htmlspecialchars($line["comments"])."\">comments</a>";
                                }
                        }
 
                                                <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>";
+                                       </head><body id=\"ttrssZoom\">";
                        }
 
-                       $title_escaped = db_escape_string($line['title']);
-
-                       $rv['content'] .= "<div id=\"PTITLE-$id\" style=\"display : none\">" .
-                               truncate_string(strip_tags($line['title']), 15) . "</div>";
-
-                       $rv['content'] .= "<div id=\"PTITLE-FULL-$id\" style=\"display : none\">" .
-                               strip_tags($line['title']) . "</div>";
-
                        $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
 
-                       $rv['content'] .= "<div onclick=\"return postClicked(event, $id)\"
-                               class=\"postHeader\" id=\"POSTHDR-$id\">";
+                       $rv['content'] .= "<div class=\"postHeader\" id=\"POSTHDR-$id\">";
 
                        $entry_author = $line["author"];
 
                        $parsed_updated = make_local_datetime($link, $line["updated"], true,
                                $owner_uid, true);
 
-                       $rv['content'] .= "<div class=\"postDate$rtl_class\">$parsed_updated</div>";
+                       $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
 
                        if ($line["link"]) {
                                $rv['content'] .= "<div class='postTitle'><a target='_blank'
                                        title=\"".htmlspecialchars($line['title'])."\"
                                        href=\"" .
-                                       $line["link"] . "\">" .
-                                       $line["title"] .
-                                       "<span class='author'>$entry_author</span></a></div>";
+                                       htmlspecialchars($line["link"]) . "\">" .
+                                       $line["title"] . "</a>" .
+                                       "<span class='author'>$entry_author</span></div>";
                        } else {
                                $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
                        }
 
-                       $tag_cache = $line["tag_cache"];
-
-                       if (!$tag_cache)
-                               $tags = get_article_tags($link, $id, $owner_uid);
-                       else
-                               $tags = explode(",", $tag_cache);
-
-                       $tags_str = format_tags_string($tags, $id);
-                       $tags_str_full = join(", ", $tags);
+                       $tags_str = format_tags_string($line["tags"], $id);
+                       $tags_str_full = join(", ", $line["tags"]);
 
                        if (!$tags_str_full) $tags_str_full = __("no tags");
 
                        if (!$entry_comments) $entry_comments = "&nbsp;"; # placeholder
 
                        $rv['content'] .= "<div class='postTags' style='float : right'>
-                               <img src='".theme_image($link, 'images/tag.png')."'
+                               <img src='images/tag.png'
                                class='tagsPic' alt='Tags' title='Tags'>&nbsp;";
 
                        if (!$zoom_mode) {
                                        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')."'>";
+                               global $pluginhost;
 
-                               $button_plugins = explode(",", ARTICLE_BUTTON_PLUGINS);
-
-                               foreach ($button_plugins as $p) {
-                                       $pclass = trim("button_${p}");
-
-                                       if (class_exists($pclass)) {
-                                               $plugin = new $pclass($link);
-                                               $rv['content'] .= $plugin->render($id, $line);
-                                       }
+                               foreach ($pluginhost->get_hooks($pluginhost::HOOK_ARTICLE_BUTTON) as $p) {
+                                       $rv['content'] .= $p->hook_article_button($line);
                                }
 
-                               $rv['content'] .= "<img src=\"".theme_image($link, 'images/digest_checkbox.png')."\"
-                                               class='tagsPic' style=\"cursor : pointer\"
-                                               onclick=\"closeArticlePanel($id)\"
-                                               title='".__('Close article')."'>";
 
                        } else {
                                $tags_str = strip_tags($tags_str);
                                        $rv['content'] .= "&nbsp;";
 
                                        $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
-                                       $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.png'></a>";
+                                       $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.svg'></a>";
 
                                        $rv['content'] .= "</div>";
                                }
 
                        $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
                                if ($line['note']) {
-                                       $rv['content'] .= format_article_note($id, $line['note']);
+                                       $rv['content'] .= format_article_note($id, $line['note'], !$zoom_mode);
                                }
                        $rv['content'] .= "</div>";
 
-                       $rv['content'] .= "<div class=\"postIcon\">" .
-                               "<a target=\"_blank\" title=\"".__("Visit the website")."\"$
-                               href=\"".htmlspecialchars($feed_site_url)."\">".
-                               $feed_icon . "</a></div>";
-
                        $rv['content'] .= "<div class=\"postContent\">";
 
-                       // N-grams
-
-                       if (DB_TYPE == "pgsql" and defined('_NGRAM_TITLE_RELATED_THRESHOLD')) {
-
-                               $ngram_result = db_query($link, "SELECT id,title FROM
-                                               ttrss_entries,ttrss_user_entries
-                                       WHERE ref_id = id AND updated >= NOW() - INTERVAL '7 day'
-                                               AND similarity(title, '$title_escaped') >= "._NGRAM_TITLE_RELATED_THRESHOLD."
-                                               AND title != '$title_escaped'
-                                               AND owner_uid = $owner_uid");
-
-                               if (db_num_rows($ngram_result) > 0) {
-                                       $rv['content'] .= "<div dojoType=\"dijit.form.DropDownButton\">".
-                                               "<span>" . __('Related')."</span>";
-                                       $rv['content'] .= "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
-
-                                       while ($nline = db_fetch_assoc($ngram_result)) {
-                                               $rv['content'] .= "<div onclick=\"hlOpenInNewTab(null,".$nline['id'].")\"
-                                                       dojoType=\"dijit.MenuItem\">".$nline['title']."</div>";
-
-                                       }
-                                       $rv['content'] .= "</div></div><br/";
-                               }
-                       }
-
-                       $article_content = sanitize($link, $line["content"], false, $owner_uid,
-                               $feed_site_url);
-
-                       $rv['content'] .= $article_content;
+                       $rv['content'] .= $line["content"];
 
                        $rv['content'] .= format_article_enclosures($link, $id,
-                               $always_display_enclosures, $article_content);
+                               $always_display_enclosures, $line["content"], $line["hide_images"]);
 
                        $rv['content'] .= "</div>";
 
 
                if ($zoom_mode) {
                        $rv['content'] .= "
-                               <div style=\"text-align : center\">
+                               <div class='footer'>
                                <button onclick=\"return window.close()\">".
                                        __("Close this window")."</button></div>";
                        $rv['content'] .= "</body></html>";
        }
 
        function print_checkpoint($n, $s) {
-               $ts = getmicrotime();
+               $ts = microtime(true);
                echo sprintf("<!-- CP[$n] %.4f seconds -->", $ts - $s);
                return $ts;
        }
        }
 
        function get_self_url_prefix() {
-               return SELF_URL_PATH;
-       }
-
-       function opml_publish_url($link){
-
-               $url_path = get_self_url_prefix();
-               $url_path .= "/opml.php?op=publish&key=" .
-                       get_feed_access_key($link, 'OPML:Publish', false, $_SESSION["uid"]);
-
-               return $url_path;
-       }
-
-       /**
-        * Purge a feed contents, marked articles excepted.
-        *
-        * @param mixed $link The database connection.
-        * @param integer $id The id of the feed to purge.
-        * @return void
-        */
-       function clear_feed_articles($link, $id) {
-
-               if ($id != 0) {
-                       $result = db_query($link, "DELETE FROM ttrss_user_entries
-                       WHERE feed_id = '$id' AND marked = false AND owner_uid = " . $_SESSION["uid"]);
+               if (strrpos(SELF_URL_PATH, "/") === strlen(SELF_URL_PATH)-1) {
+                       return substr(SELF_URL_PATH, 0, strlen(SELF_URL_PATH)-1);
                } else {
-                       $result = db_query($link, "DELETE FROM ttrss_user_entries
-                       WHERE feed_id IS NULL AND marked = false AND owner_uid = " . $_SESSION["uid"]);
+                       return SELF_URL_PATH;
                }
-
-               $result = db_query($link, "DELETE FROM ttrss_entries WHERE
-                       (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0");
-
-               ccache_update($link, $id, $_SESSION['uid']);
-       } // function clear_feed_articles
+       }
 
        /**
         * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
                }
        } // function encrypt_password
 
-       function sanitize_article_content($text) {
-               # we don't support CDATA sections in articles, they break our own escaping
-               $text = preg_replace("/\[\[CDATA/", "", $text);
-               $text = preg_replace("/\]\]\>/", "", $text);
-               return $text;
-       }
-
        function load_filters($link, $feed_id, $owner_uid, $action_id = false) {
                $filters = array();
 
                        $filter_id = $line["id"];
 
                        $result2 = db_query($link, "SELECT
-                               r.reg_exp, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
+                               r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
                                FROM ttrss_filters2_rules AS r,
                                ttrss_filter_types AS t
                                WHERE
                                $rule = array();
                                $rule["reg_exp"] = $rule_line["reg_exp"];
                                $rule["type"] = $rule_line["type_name"];
+                               $rule["inverse"] = sql_bool_to_bool($rule_line["inverse"]);
 
                                array_push($rules, $rule);
                        }
 
                        $filter = array();
                        $filter["match_any_rule"] = sql_bool_to_bool($line["match_any_rule"]);
+                       $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
                        $filter["rules"] = $rules;
                        $filter["actions"] = $actions;
 
                                        db_query($link, "SET NAMES " . MYSQL_CHARSET);
                                }
                        }
+
+                       global $pluginhost;
+
+                       $pluginhost = new PluginHost($link);
+                       $pluginhost->load(PLUGINS, $pluginhost::KIND_ALL);
+
                        return true;
                } else {
                        print "Unable to connect to database:" . db_last_error();
                }
        }
 
-       /* function ccache_zero($link, $feed_id, $owner_uid) {
-               db_query($link, "UPDATE ttrss_counters_cache SET
-                       value = 0, updated = NOW() WHERE
-                       feed_id = '$feed_id' AND owner_uid = '$owner_uid'");
-       } */
-
-       function ccache_zero_all($link, $owner_uid) {
-               db_query($link, "UPDATE ttrss_counters_cache SET
-                       value = 0 WHERE owner_uid = '$owner_uid'");
-
-               db_query($link, "UPDATE ttrss_cat_counters_cache SET
-                       value = 0 WHERE owner_uid = '$owner_uid'");
-       }
-
-       function ccache_remove($link, $feed_id, $owner_uid, $is_cat = false) {
-
-               if (!$is_cat) {
-                       $table = "ttrss_counters_cache";
-               } else {
-                       $table = "ttrss_cat_counters_cache";
-               }
+       function format_tags_string($tags, $id) {
 
-               db_query($link, "DELETE FROM $table WHERE
-                       feed_id = '$feed_id' AND owner_uid = '$owner_uid'");
+               $tags_str = "";
+               $tags_nolinks_str = "";
 
-       }
+               $num_tags = 0;
 
-       function ccache_update_all($link, $owner_uid) {
+               $tag_limit = 6;
 
-               if (get_pref($link, 'ENABLE_FEED_CATS', $owner_uid)) {
+               $formatted_tags = array();
 
-                       $result = db_query($link, "SELECT feed_id FROM ttrss_cat_counters_cache
-                               WHERE feed_id > 0 AND owner_uid = '$owner_uid'");
+               foreach ($tags as $tag) {
+                       $num_tags++;
+                       $tag_escaped = str_replace("'", "\\'", $tag);
 
-                       while ($line = db_fetch_assoc($result)) {
-                               ccache_update($link, $line["feed_id"], $owner_uid, true);
+                       if (mb_strlen($tag) > 30) {
+                               $tag = truncate_string($tag, 30);
                        }
 
-                       /* We have to manually include category 0 */
-
-                       ccache_update($link, 0, $owner_uid, true);
+                       $tag_str = "<a href=\"javascript:viewfeed('$tag_escaped')\">$tag</a>";
 
-               } else {
-                       $result = db_query($link, "SELECT feed_id FROM ttrss_counters_cache
-                               WHERE feed_id > 0 AND owner_uid = '$owner_uid'");
+                       array_push($formatted_tags, $tag_str);
 
-                       while ($line = db_fetch_assoc($result)) {
-                               print ccache_update($link, $line["feed_id"], $owner_uid);
+                       $tmp_tags_str = implode(", ", $formatted_tags);
 
+                       if ($num_tags == $tag_limit || mb_strlen($tmp_tags_str) > 150) {
+                               break;
                        }
-
                }
-       }
 
-       function ccache_find($link, $feed_id, $owner_uid, $is_cat = false,
-               $no_update = false) {
+               $tags_str = implode(", ", $formatted_tags);
 
-               if (!is_numeric($feed_id)) return;
-
-               if (!$is_cat) {
-                       $table = "ttrss_counters_cache";
-                       if ($feed_id > 0) {
-                               $tmp_result = db_query($link, "SELECT owner_uid FROM ttrss_feeds
-                                       WHERE id = '$feed_id'");
-                               $owner_uid = db_fetch_result($tmp_result, 0, "owner_uid");
-                       }
-               } else {
-                       $table = "ttrss_cat_counters_cache";
-               }
-
-               if (DB_TYPE == "pgsql") {
-                       $date_qpart = "updated > NOW() - INTERVAL '15 minutes'";
-               } else if (DB_TYPE == "mysql") {
-                       $date_qpart = "updated > DATE_SUB(NOW(), INTERVAL 15 MINUTE)";
-               }
-
-               $result = db_query($link, "SELECT value FROM $table
-                       WHERE owner_uid = '$owner_uid' AND feed_id = '$feed_id'
-                       LIMIT 1");
-
-               if (db_num_rows($result) == 1) {
-                       return db_fetch_result($result, 0, "value");
-               } else {
-                       if ($no_update) {
-                               return -1;
-                       } else {
-                               return ccache_update($link, $feed_id, $owner_uid, $is_cat);
-                       }
-               }
-
-       }
-
-       function ccache_update($link, $feed_id, $owner_uid, $is_cat = false,
-               $update_pcat = true) {
-
-               if (!is_numeric($feed_id)) return;
-
-               if (!$is_cat && $feed_id > 0) {
-                       $tmp_result = db_query($link, "SELECT owner_uid FROM ttrss_feeds
-                               WHERE id = '$feed_id'");
-                       $owner_uid = db_fetch_result($tmp_result, 0, "owner_uid");
-               }
-
-               $prev_unread = ccache_find($link, $feed_id, $owner_uid, $is_cat, true);
-
-               /* When updating a label, all we need to do is recalculate feed counters
-                * because labels are not cached */
-
-               if ($feed_id < 0) {
-                       ccache_update_all($link, $owner_uid);
-                       return;
-               }
-
-               if (!$is_cat) {
-                       $table = "ttrss_counters_cache";
-               } else {
-                       $table = "ttrss_cat_counters_cache";
-               }
-
-               if ($is_cat && $feed_id >= 0) {
-                       if ($feed_id != 0) {
-                               $cat_qpart = "cat_id = '$feed_id'";
-                       } else {
-                               $cat_qpart = "cat_id IS NULL";
-                       }
-
-                       /* Recalculate counters for child feeds */
-
-                       $result = db_query($link, "SELECT id FROM ttrss_feeds
-                                               WHERE owner_uid = '$owner_uid' AND $cat_qpart");
-
-                       while ($line = db_fetch_assoc($result)) {
-                               ccache_update($link, $line["id"], $owner_uid, false, false);
-                       }
-
-                       $result = db_query($link, "SELECT SUM(value) AS sv
-                               FROM ttrss_counters_cache, ttrss_feeds
-                               WHERE id = feed_id AND $cat_qpart AND
-                               ttrss_feeds.owner_uid = '$owner_uid'");
-
-                       $unread = (int) db_fetch_result($result, 0, "sv");
-
-               } else {
-                       $unread = (int) getFeedArticles($link, $feed_id, $is_cat, true, $owner_uid);
-               }
-
-               db_query($link, "BEGIN");
-
-               $result = db_query($link, "SELECT feed_id FROM $table
-                       WHERE owner_uid = '$owner_uid' AND feed_id = '$feed_id' LIMIT 1");
-
-               if (db_num_rows($result) == 1) {
-                       db_query($link, "UPDATE $table SET
-                               value = '$unread', updated = NOW() WHERE
-                               feed_id = '$feed_id' AND owner_uid = '$owner_uid'");
-
-               } else {
-                       db_query($link, "INSERT INTO $table
-                               (feed_id, value, owner_uid, updated)
-                               VALUES
-                               ($feed_id, $unread, $owner_uid, NOW())");
-               }
-
-               db_query($link, "COMMIT");
-
-               if ($feed_id > 0 && $prev_unread != $unread) {
-
-                       if (!$is_cat) {
-
-                               /* Update parent category */
-
-                               if ($update_pcat) {
-
-                                       $result = db_query($link, "SELECT cat_id FROM ttrss_feeds
-                                               WHERE owner_uid = '$owner_uid' AND id = '$feed_id'");
-
-                                       $cat_id = (int) db_fetch_result($result, 0, "cat_id");
-
-                                       ccache_update($link, $cat_id, $owner_uid, true);
-
-                               }
-                       }
-               } else if ($feed_id < 0) {
-                       ccache_update_all($link, $owner_uid);
-               }
-
-               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'
-                               AND owner_uid = '$owner_uid' LIMIT 1");
-
-               if (db_num_rows($result) == 1) {
-                       return db_fetch_result($result, 0, "id");
-               } else {
-                       return 0;
-               }
-       }
-
-       function get_article_labels($link, $id) {
-               $rv = array();
-
-
-               $result = db_query($link, "SELECT label_cache FROM
-                       ttrss_user_entries WHERE ref_id = '$id' AND owner_uid = " .
-                       $_SESSION["uid"]);
-
-               $label_cache = db_fetch_result($result, 0, "label_cache");
-
-               if ($label_cache) {
-
-                       $label_cache = json_decode($label_cache, true);
-
-                       if ($label_cache["no-labels"] == 1)
-                               return $rv;
-                       else
-                               return $label_cache;
-               }
-
-               $result = db_query($link,
-                       "SELECT DISTINCT label_id,caption,fg_color,bg_color
-                               FROM ttrss_labels2, ttrss_user_labels2
-                       WHERE id = label_id
-                               AND article_id = '$id'
-                               AND owner_uid = ".$_SESSION["uid"] . "
-                       ORDER BY caption");
-
-               while ($line = db_fetch_assoc($result)) {
-                       $rk = array($line["label_id"], $line["caption"], $line["fg_color"],
-                               $line["bg_color"]);
-                       array_push($rv, $rk);
-               }
-
-               if (count($rv) > 0)
-                       label_update_cache($link, $id, $rv);
-               else
-                       label_update_cache($link, $id, array("no-labels" => 1));
-
-               return $rv;
-       }
-
-
-       function label_find_caption($link, $label, $owner_uid) {
-               $result = db_query($link,
-                       "SELECT caption FROM ttrss_labels2 WHERE id = '$label'
-                               AND owner_uid = '$owner_uid' LIMIT 1");
-
-               if (db_num_rows($result) == 1) {
-                       return db_fetch_result($result, 0, "caption");
-               } else {
-                       return "";
-               }
-       }
-
-       function label_update_cache($link, $id, $labels = false, $force = false) {
-
-               if ($force)
-                       label_clear_cache($link, $id);
-
-               if (!$labels)
-                       $labels = get_article_labels($link, $id);
-
-               $labels = db_escape_string(json_encode($labels));
-
-               db_query($link, "UPDATE ttrss_user_entries SET
-                       label_cache = '$labels' WHERE ref_id = '$id'");
-
-       }
-
-       function label_clear_cache($link, $id) {
-
-               db_query($link, "UPDATE ttrss_user_entries SET
-                       label_cache = '' WHERE ref_id = '$id'");
-
-       }
-
-       function label_remove_article($link, $id, $label, $owner_uid) {
-
-               $label_id = label_find_id($link, $label, $owner_uid);
-
-               if (!$label_id) return;
-
-               $result = db_query($link,
-                       "DELETE FROM ttrss_user_labels2
-                       WHERE
-                               label_id = '$label_id' AND
-                               article_id = '$id'");
-
-               label_clear_cache($link, $id);
-       }
-
-       function label_add_article($link, $id, $label, $owner_uid) {
-
-               $label_id = label_find_id($link, $label, $owner_uid);
-
-               if (!$label_id) return;
-
-               $result = db_query($link,
-                       "SELECT
-                               article_id FROM ttrss_labels2, ttrss_user_labels2
-                       WHERE
-                               label_id = id AND
-                               label_id = '$label_id' AND
-                               article_id = '$id' AND owner_uid = '$owner_uid'
-                       LIMIT 1");
-
-               if (db_num_rows($result) == 0) {
-                       db_query($link, "INSERT INTO ttrss_user_labels2
-                               (label_id, article_id) VALUES ('$label_id', '$id')");
-               }
-
-               label_clear_cache($link, $id);
-
-       }
-
-       function label_remove($link, $id, $owner_uid) {
-               if (!$owner_uid) $owner_uid = $_SESSION["uid"];
-
-               db_query($link, "BEGIN");
-
-               $result = db_query($link, "SELECT caption FROM ttrss_labels2
-                       WHERE id = '$id'");
-
-               $caption = db_fetch_result($result, 0, "caption");
-
-               $result = db_query($link, "DELETE FROM ttrss_labels2 WHERE id = '$id'
-                       AND owner_uid = " . $owner_uid);
-
-               if (db_affected_rows($link, $result) != 0 && $caption) {
-
-                       /* Remove access key for the label */
-
-                       $ext_id = -11 - $id;
-
-                       db_query($link, "DELETE FROM ttrss_access_keys WHERE
-                               feed_id = '$ext_id' AND owner_uid = $owner_uid");
-
-                       /* Disable filters that reference label being removed */
-
-                       db_query($link, "UPDATE ttrss_filters SET
-                               enabled = false WHERE action_param = '$caption'
-                                       AND action_id = 7
-                                       AND owner_uid = " . $owner_uid);
-
-                       /* Remove cached data */
-
-                       db_query($link, "UPDATE ttrss_user_entries SET label_cache = ''
-                               WHERE label_cache LIKE '%$caption%' AND owner_uid = " . $owner_uid);
-
-               }
-
-               db_query($link, "COMMIT");
-       }
-
-       function label_create($link, $caption, $fg_color = '', $bg_color = '', $owner_uid) {
-
-               if (!$owner_uid) $owner_uid = $_SESSION['uid'];
-
-               db_query($link, "BEGIN");
-
-               $result = false;
-
-               $result = db_query($link, "SELECT id FROM ttrss_labels2
-                       WHERE caption = '$caption' AND owner_uid = $owner_uid");
-
-               if (db_num_rows($result) == 0) {
-                       $result = db_query($link,
-                               "INSERT INTO ttrss_labels2 (caption,owner_uid,fg_color,bg_color)
-                                       VALUES ('$caption', '$owner_uid', '$fg_color', '$bg_color')");
-
-                       $result = db_affected_rows($link, $result) != 0;
-               }
-
-               db_query($link, "COMMIT");
-
-               return $result;
-       }
-
-       function format_tags_string($tags, $id) {
-
-               $tags_str = "";
-               $tags_nolinks_str = "";
-
-               $num_tags = 0;
-
-               $tag_limit = 6;
-
-               $formatted_tags = array();
-
-               foreach ($tags as $tag) {
-                       $num_tags++;
-                       $tag_escaped = str_replace("'", "\\'", $tag);
-
-                       if (mb_strlen($tag) > 30) {
-                               $tag = truncate_string($tag, 30);
-                       }
-
-                       $tag_str = "<a href=\"javascript:viewfeed('$tag_escaped')\">$tag</a>";
-
-                       array_push($formatted_tags, $tag_str);
-
-                       $tmp_tags_str = implode(", ", $formatted_tags);
-
-                       if ($num_tags == $tag_limit || mb_strlen($tmp_tags_str) > 150) {
-                               break;
-                       }
-               }
-
-               $tags_str = implode(", ", $formatted_tags);
-
-               if ($num_tags < count($tags)) {
-                       $tags_str .= ", &hellip;";
-               }
+               if ($num_tags < count($tags)) {
+                       $tags_str .= ", &hellip;";
+               }
 
                if ($num_tags == 0) {
                        $tags_str = __("no tags");
 
        }
 
-       function format_article_note($id, $note) {
+       function format_article_note($id, $note, $allow_edit = true) {
 
                $str = "<div class='articleNote'        onclick=\"editArticleNote($id)\">
                        <div class='noteEdit' onclick=\"editArticleNote($id)\">".
-                       __('(edit note)')."</div>$note</div>";
+                       ($allow_edit ? __('(edit note)') : "")."</div>$note</div>";
 
                return $str;
        }
 
-       function toggle_collapse_cat($link, $cat_id, $mode) {
-               if ($cat_id > 0) {
-                       $mode = bool_to_sql_bool($mode);
-
-                       db_query($link, "UPDATE ttrss_feed_categories SET
-                               collapsed = $mode WHERE id = '$cat_id' AND owner_uid = " .
-                               $_SESSION["uid"]);
-               } else {
-                       $pref_name = '';
-
-                       switch ($cat_id) {
-                       case -1:
-                               $pref_name = '_COLLAPSED_SPECIAL';
-                               break;
-                       case -2:
-                               $pref_name = '_COLLAPSED_LABELS';
-                               break;
-                       case 0:
-                               $pref_name = '_COLLAPSED_UNCAT';
-                               break;
-                       }
-
-                       if ($pref_name) {
-                               if ($mode) {
-                                       set_pref($link, $pref_name, 'true');
-                               } else {
-                                       set_pref($link, $pref_name, 'false');
-                               }
-                       }
-               }
-       }
-
-       function remove_feed($link, $id, $owner_uid) {
-
-               if ($id > 0) {
-
-                       /* save starred articles in Archived feed */
-
-                       db_query($link, "BEGIN");
-
-                       /* prepare feed if necessary */
-
-                       $result = db_query($link, "SELECT id FROM ttrss_archived_feeds
-                               WHERE id = '$id'");
-
-                       if (db_num_rows($result) == 0) {
-                               db_query($link, "INSERT INTO ttrss_archived_feeds
-                                       (id, owner_uid, title, feed_url, site_url)
-                               SELECT id, owner_uid, title, feed_url, site_url from ttrss_feeds
-                               WHERE id = '$id'");
-                       }
-
-                       db_query($link, "UPDATE ttrss_user_entries SET feed_id = NULL,
-                               orig_feed_id = '$id' WHERE feed_id = '$id' AND
-                                       marked = true AND owner_uid = $owner_uid");
-
-                       /* Remove access key for the feed */
-
-                       db_query($link, "DELETE FROM ttrss_access_keys WHERE
-                               feed_id = '$id' AND owner_uid = $owner_uid");
-
-                       /* remove the feed */
-
-                       db_query($link, "DELETE FROM ttrss_feeds
-                                       WHERE id = '$id' AND owner_uid = $owner_uid");
-
-                       db_query($link, "COMMIT");
-
-                       if (file_exists(ICONS_DIR . "/$id.ico")) {
-                               unlink(ICONS_DIR . "/$id.ico");
-                       }
-
-                       ccache_remove($link, $id, $owner_uid);
-
-               } else {
-                       label_remove($link, -11-$id, $owner_uid);
-                       ccache_remove($link, -11-$id, $owner_uid);
-               }
-       }
 
        function get_feed_category($link, $feed_cat, $parent_cat_id = false) {
                if ($parent_cat_id) {
                return false;
        }
 
-       function remove_feed_category($link, $id, $owner_uid) {
-
-               db_query($link, "DELETE FROM ttrss_feed_categories
-                       WHERE id = '$id' AND owner_uid = $owner_uid");
-
-               ccache_remove($link, $id, $owner_uid, true);
-       }
-
-       function archive_article($link, $id, $owner_uid) {
-               db_query($link, "BEGIN");
-
-               $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
-                       WHERE ref_id = '$id' AND owner_uid = $owner_uid");
-
-               if (db_num_rows($result) != 0) {
-
-                       /* prepare the archived table */
-
-                       $feed_id = (int) db_fetch_result($result, 0, "feed_id");
-
-                       if ($feed_id) {
-                               $result = db_query($link, "SELECT id FROM ttrss_archived_feeds
-                                       WHERE id = '$feed_id'");
-
-                               if (db_num_rows($result) == 0) {
-                                       db_query($link, "INSERT INTO ttrss_archived_feeds
-                                               (id, owner_uid, title, feed_url, site_url)
-                                       SELECT id, owner_uid, title, feed_url, site_url from ttrss_feeds
-                                       WHERE id = '$feed_id'");
-                               }
-
-                               db_query($link, "UPDATE ttrss_user_entries
-                                       SET orig_feed_id = feed_id, feed_id = NULL
-                                       WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
-                       }
-               }
-
-               db_query($link, "COMMIT");
-       }
-
        function getArticleFeed($link, $id) {
                $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
                        WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
 
                if (db_num_rows($result) != 0) {
                        return db_fetch_result($result, 0, "feed_id");
-               } else {
-                       return 0;
-               }
-       }
-
-       /**
-        * Fixes incomplete URLs by prepending "http://".
-        * Also replaces feed:// with http://, and
-        * prepends a trailing slash if the url is a domain name only.
-        *
-        * @param string $url Possibly incomplete URL
-        *
-        * @return string Fixed URL.
-        */
-       function fix_url($url) {
-               if (strpos($url, '://') === false) {
-                       $url = 'http://' . $url;
-               } else if (substr($url, 0, 5) == 'feed:') {
-                       $url = 'http:' . substr($url, 5);
-               }
-
-               //prepend slash if the URL has no slash in it
-               // "http://www.example" -> "http://www.example/"
-               if (strpos($url, '/', strpos($url, ':') + 3) === false) {
-                       $url .= '/';
-               }
-
-               if ($url != "http:///")
-                       return $url;
-               else
-                       return '';
-       }
-
-       function validate_feed_url($url) {
-               $parts = parse_url($url);
-
-               return ($parts['scheme'] == 'http' || $parts['scheme'] == 'feed' || $parts['scheme'] == 'https');
-
-       }
-
-       function get_article_enclosures($link, $id) {
-
-               $query = "SELECT * FROM ttrss_enclosures
-                       WHERE post_id = '$id' AND content_url != ''";
-
-               $rv = array();
-
-               $result = db_query($link, $query);
-
-               if (db_num_rows($result) > 0) {
-                       while ($line = db_fetch_assoc($result)) {
-                               array_push($rv, $line);
-                       }
-               }
-
-               return $rv;
-       }
-
-       function api_get_feeds($link, $cat_id, $unread_only, $limit, $offset, $include_nested = false) {
-
-                       $feeds = array();
-
-                       /* Labels */
-
-                       if ($cat_id == -4 || $cat_id == -2) {
-                               $counters = getLabelCounters($link, true);
-
-                               foreach (array_values($counters) as $cv) {
-
-                                       $unread = $cv["counter"];
-
-                                       if ($unread || !$unread_only) {
-
-                                               $row = array(
-                                                               "id" => $cv["id"],
-                                                               "title" => $cv["description"],
-                                                               "unread" => $cv["counter"],
-                                                               "cat_id" => -2,
-                                                       );
-
-                                               array_push($feeds, $row);
-                                       }
-                               }
-                       }
-
-                       /* Virtual feeds */
-
-                       if ($cat_id == -4 || $cat_id == -1) {
-                               foreach (array(-1, -2, -3, -4, -6, 0) as $i) {
-                                       $unread = getFeedUnread($link, $i);
-
-                                       if ($unread || !$unread_only) {
-                                               $title = getFeedTitle($link, $i);
-
-                                               $row = array(
-                                                               "id" => $i,
-                                                               "title" => $title,
-                                                               "unread" => $unread,
-                                                               "cat_id" => -1,
-                                                       );
-                                               array_push($feeds, $row);
-                                       }
-
-                               }
-                       }
-
-                       /* Child cats */
-
-                       if ($include_nested && $cat_id) {
-                               $result = db_query($link, "SELECT
-                                       id, title FROM ttrss_feed_categories
-                                       WHERE parent_cat = '$cat_id' AND owner_uid = " . $_SESSION["uid"] .
-                               " ORDER BY id, title");
-
-                               while ($line = db_fetch_assoc($result)) {
-                                       $unread = getFeedUnread($link, $line["id"], true) +
-                                               getCategoryChildrenUnread($link, $line["id"]);
-
-                                       if ($unread || !$unread_only) {
-                                               $row = array(
-                                                               "id" => $line["id"],
-                                                               "title" => $line["title"],
-                                                               "unread" => $unread,
-                                                               "is_cat" => true,
-                                                       );
-                                               array_push($feeds, $row);
-                                       }
-                               }
-                       }
-
-                       /* Real feeds */
-
-                       if ($limit) {
-                               $limit_qpart = "LIMIT $limit OFFSET $offset";
-                       } else {
-                               $limit_qpart = "";
-                       }
-
-                       if ($cat_id == -4 || $cat_id == -3) {
-                               $result = db_query($link, "SELECT
-                                       id, feed_url, cat_id, title, order_id, ".
-                                               SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated
-                                               FROM ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"] .
-                                               " ORDER BY cat_id, title " . $limit_qpart);
-                       } else {
-
-                               if ($cat_id)
-                                       $cat_qpart = "cat_id = '$cat_id'";
-                               else
-                                       $cat_qpart = "cat_id IS NULL";
-
-                               $result = db_query($link, "SELECT
-                                       id, feed_url, cat_id, title, order_id, ".
-                                               SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated
-                                               FROM ttrss_feeds WHERE
-                                               $cat_qpart AND owner_uid = " . $_SESSION["uid"] .
-                                               " ORDER BY cat_id, title " . $limit_qpart);
-                       }
-
-                       while ($line = db_fetch_assoc($result)) {
-
-                               $unread = getFeedUnread($link, $line["id"]);
-
-                               $has_icon = feed_has_icon($line['id']);
-
-                               if ($unread || !$unread_only) {
-
-                                       $row = array(
-                                                       "feed_url" => $line["feed_url"],
-                                                       "title" => $line["title"],
-                                                       "id" => (int)$line["id"],
-                                                       "unread" => (int)$unread,
-                                                       "has_icon" => $has_icon,
-                                                       "cat_id" => (int)$line["cat_id"],
-                                                       "last_updated" => strtotime($line["last_updated"]),
-                                                       "order_id" => (int) $line["order_id"],
-                                               );
-
-                                       array_push($feeds, $row);
-                               }
-                       }
-
-               return $feeds;
-       }
-
-       function api_get_headlines($link, $feed_id, $limit, $offset,
-                               $filter, $is_cat, $show_excerpt, $show_content, $view_mode, $order,
-                               $include_attachments, $since_id,
-                               $search = "", $search_mode = "", $match_on = "", $include_nested = false) {
-
-                       $qfh_ret = queryFeedHeadlines($link, $feed_id, $limit,
-                               $view_mode, $is_cat, $search, $search_mode, $match_on,
-                               $order, $offset, 0, false, $since_id, $include_nested);
-
-                       $result = $qfh_ret[0];
-                       $feed_title = $qfh_ret[1];
-
-                       $headlines = array();
-
-                       while ($line = db_fetch_assoc($result)) {
-                               $is_updated = ($line["last_read"] == "" &&
-                                       ($line["unread"] != "t" && $line["unread"] != "1"));
-
-                               $tags = explode(",", $line["tag_cache"]);
-                               $labels = json_decode($line["label_cache"], true);
-
-                               //if (!$tags) $tags = get_article_tags($link, $line["id"]);
-                               //if (!$labels) $labels = get_article_labels($link, $line["id"]);
-
-                               $headline_row = array(
-                                               "id" => (int)$line["id"],
-                                               "unread" => sql_bool_to_bool($line["unread"]),
-                                               "marked" => sql_bool_to_bool($line["marked"]),
-                                               "published" => sql_bool_to_bool($line["published"]),
-                                               "updated" => strtotime($line["updated"]),
-                                               "is_updated" => $is_updated,
-                                               "title" => $line["title"],
-                                               "link" => $line["link"],
-                                               "feed_id" => $line["feed_id"],
-                                               "tags" => $tags,
-                                       );
-
-                                       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;
-                               }
-
-                               if ($show_content) {
-                                       $headline_row["content"] = $line["content_preview"];
-                               }
-
-                               // unify label output to ease parsing
-                               if ($labels["no-labels"] == 1) $labels = array();
-
-                               $headline_row["labels"] = $labels;
-
-                               $headline_row["feed_title"] = $line["feed_title"];
-
-                               array_push($headlines, $headline_row);
-                       }
-
-                       return $headlines;
-       }
-
-       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;
+               } else {
+                       return 0;
+               }
        }
 
+       /**
+        * Fixes incomplete URLs by prepending "http://".
+        * Also replaces feed:// with http://, and
+        * prepends a trailing slash if the url is a domain name only.
+        *
+        * @param string $url Possibly incomplete URL
+        *
+        * @return string Fixed URL.
+        */
+       function fix_url($url) {
+               if (strpos($url, '://') === false) {
+                       $url = 'http://' . $url;
+               } else if (substr($url, 0, 5) == 'feed:') {
+                       $url = 'http:' . substr($url, 5);
+               }
 
-       function generate_dashboard_feed($link) {
-               $reply = array();
+               //prepend slash if the URL has no slash in it
+               // "http://www.example" -> "http://www.example/"
+               if (strpos($url, '/', strpos($url, ':') + 3) === false) {
+                       $url .= '/';
+               }
 
-               $reply['headlines']['id'] = -5;
-               $reply['headlines']['is_cat'] = false;
+               if ($url != "http:///")
+                       return $url;
+               else
+                       return '';
+       }
 
-               $reply['headlines']['toolbar'] = '';
-               $reply['headlines']['content'] = "<div class='whiteBox'>".__('No feed selected.');
+       function validate_feed_url($url) {
+               $parts = parse_url($url);
 
-               $reply['headlines']['content'] .= "<p class=\"small\"><span class=\"insensitive\">";
+               return ($parts['scheme'] == 'http' || $parts['scheme'] == 'feed' || $parts['scheme'] == 'https');
 
-               $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);
+       function get_article_enclosures($link, $id) {
 
-               $reply['headlines']['content'] .= sprintf(__("Feeds last updated at %s"), $last_updated);
+               $query = "SELECT * FROM ttrss_enclosures
+                       WHERE post_id = '$id' AND content_url != ''";
 
-               $result = db_query($link, "SELECT COUNT(id) AS num_errors
-                       FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ".$_SESSION["uid"]);
+               $rv = array();
 
-               $num_errors = db_fetch_result($result, 0, "num_errors");
+               $result = db_query($link, $query);
 
-               if ($num_errors > 0) {
-                       $reply['headlines']['content'] .= "<br/>";
-                       $reply['headlines']['content'] .= "<a class=\"insensitive\" href=\"#\" onclick=\"showFeedsWithErrors()\">".
-                               __('Some feeds have update errors (click for details)')."</a>";
+               if (db_num_rows($result) > 0) {
+                       while ($line = db_fetch_assoc($result)) {
+                               array_push($rv, $line);
+                       }
                }
-               $reply['headlines']['content'] .= "</span></p>";
-
-               $reply['headlines-info'] = array("count" => 0,
-                       "vgroup_last_feed" => '',
-                       "unread" => 0,
-                       "disable_cache" => true);
 
-               return $reply;
+               return $rv;
        }
 
        function save_email_address($link, $email) {
                        array_push($_SESSION['stored_emails'], $email);
        }
 
-       function update_feed_access_key($link, $feed_id, $is_cat, $owner_uid = false) {
-               if (!$owner_uid) $owner_uid = $_SESSION["uid"];
-
-               $sql_is_cat = bool_to_sql_bool($is_cat);
-
-               $result = db_query($link, "SELECT access_key FROM ttrss_access_keys
-                       WHERE feed_id = '$feed_id'      AND is_cat = $sql_is_cat
-                       AND owner_uid = " . $owner_uid);
-
-               if (db_num_rows($result) == 1) {
-                       $key = db_escape_string(sha1(uniqid(rand(), true)));
-
-                       db_query($link, "UPDATE ttrss_access_keys SET access_key = '$key'
-                               WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
-                               AND owner_uid = " . $owner_uid);
-
-                       return $key;
-
-               } else {
-                       return get_feed_access_key($link, $feed_id, $is_cat, $owner_uid);
-               }
-       }
 
        function get_feed_access_key($link, $feed_id, $is_cat, $owner_uid = false) {
 
                if (db_num_rows($result) == 1) {
                        return db_fetch_result($result, 0, "access_key");
                } else {
-                       $key = db_escape_string(sha1(uniqid(rand(), true)));
+                       $key = db_escape_string($link, sha1(uniqid(rand(), true)));
 
                        $result = db_query($link, "INSERT INTO ttrss_access_keys
                                (access_key, feed_id, is_cat, owner_uid)
        }
 
        function is_html($content) {
-               return preg_match("/<html|DOCTYPE html/i", $content) !== 0;
+               return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 20)) !== 0;
        }
 
        function url_is_html($url, $login = false, $pass = false) {
        }
 
        function format_article_enclosures($link, $id, $always_display_enclosures,
-                                       $article_content) {
+                                       $article_content, $hide_images = false) {
 
                $result = get_article_enclosures($link, $id);
                $rv = '';
 
                        $entries_html = array();
                        $entries = array();
+                       $entries_inline = array();
 
                        foreach ($result as $line) {
 
 
                                $filename = substr($url, strrpos($url, "/")+1);
 
-#                              $player = format_inline_player($link, $url, $ctype);
+                               $player = format_inline_player($link, $url, $ctype);
+
+                               if ($player) array_push($entries_inline, $player);
 
 #                              $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
 #                                      $filename . " (" . $ctype . ")" . "</a>";
                                array_push($entries, $entry);
                        }
 
-                       if (!get_pref($link, "STRIP_IMAGES")) {
+                       if ($_SESSION['uid'] && !get_pref($link, "STRIP_IMAGES") && !$_SESSION["bw_limit"]) {
                                if ($always_display_enclosures ||
                                                        !preg_match("/<img/i", $article_content)) {
 
                                                if (preg_match("/image/", $entry["type"]) ||
                                                                preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
 
-                                                               $rv .= "<p><img
-                                                               alt=\"".htmlspecialchars($entry["filename"])."\"
-                                                               src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
+                                                               if (!$hide_images) {
+                                                                       $rv .= "<p><img
+                                                                       alt=\"".htmlspecialchars($entry["filename"])."\"
+                                                                       src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
+                                                               } else {
+                                                                       $rv .= "<p><a target=\"_blank\"
+                                                                       href=\"".htmlspecialchars($entry["url"])."\"
+                                                                       >" .htmlspecialchars($entry["url"]) . "</a></p>";
 
+                                                               }
                                                }
                                        }
                                }
                        }
 
-                       $rv .= "<br/><div dojoType=\"dijit.form.DropDownButton\">".
-                               "<span>" . __('Attachments')."</span>";
-                       $rv .= "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
+                       if (count($entries_inline) > 0) {
+                               $rv .= "<hr clear='both'/>";
+                               foreach ($entries_inline as $entry) { $rv .= $entry; };
+                               $rv .= "<hr clear='both'/>";
+                       }
+
+                       $rv .= "<select class=\"attachments\" onchange=\"openSelectedAttachment(this)\">".
+                               "<option value=''>" . __('Attachments')."</option>";
+
+                       foreach ($entries as $entry) {
+                               $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "</option>";
 
-                       foreach ($entries_html as $entry) { $rv .= $entry; };
+                       };
 
-                       $rv .= "</div></div>";
+                       $rv .= "</select>";
                }
 
                return $rv;
 
                        if (count($ids) > 0) {
                                $ids = join(",", $ids);
-                               print ".";
 
                                $tmp_result = db_query($link, "DELETE FROM ttrss_tags WHERE id IN ($ids)");
                                $tags_deleted += db_affected_rows($link, $tmp_result);
                        $limit -= $limit_part;
                }
 
-               print "\n";
-
                return $tags_deleted;
        }
 
 
        }
 
-/*     function rewrite_urls($line) {
-               global $url_regex;
-
-               $urls = null;
-
-               $result = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
-                       "<a target=\"_blank\" href=\"\\1\">\\1</a>", $line);
-
-               return $result;
-       } */
-
        function rewrite_urls($html) {
                libxml_use_internal_errors(true);
 
 
                // http://tt-rss.org/forum/viewtopic.php?f=1&t=970
                if ($node)
-                       return $doc->saveXML($node, LIBXML_NOEMPTYTAG);
+                       return $doc->saveXML($node);
                else
                        return $html;
        }
 
-       function filter_to_sql($filter) {
+       function filter_to_sql($link, $filter, $owner_uid) {
                $query = array();
 
                if (DB_TYPE == "pgsql")
 
                        if ($regexp_valid) {
 
-                               $rule['reg_exp'] = db_escape_string($rule['reg_exp']);
+                               $rule['reg_exp'] = db_escape_string($link, $rule['reg_exp']);
 
-                               switch ($rule["type"]) {
+                                       switch ($rule["type"]) {
                                        case "title":
                                                $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
                                                        $rule['reg_exp'] . "')";
                                                break;
                                }
 
+                               if (isset($rule['inverse'])) $qpart = "NOT ($qpart)";
+
                                if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) {
-                                       $qpart .= " AND feed_id = " . db_escape_string($rule["feed_id"]);
+                                       $qpart .= " AND feed_id = " . db_escape_string($link, $rule["feed_id"]);
                                }
 
                                if (isset($rule["cat_id"])) {
-                                       $qpart .= " AND cat_id " . ($rule["cat_id"] ? '= ' . $rule["cat_id"] : 'IS NULL');
-                               }
-
-                               array_push($query, "($qpart)");
-
-                       }
-               }
-
-               if (count($query) > 0) {
-                       return "(" . join($filter["match_any_rule"] ? "OR" : "AND", $query) . ")";
-               } else {
-                       return "(false)";
-               }
-       }
-
-       // 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'] . '/public.php?op=fbexport';
-                       $post_query = 'key=' . $line['access_key'];
 
-                       $feeds = fetch_file_contents($fetch_url, false, false, false, $post_query);
+                                       if ($rule["cat_id"] > 0) {
+                                               $children = getChildCategories($link, $rule["cat_id"], $owner_uid);
+                                               array_push($children, $rule["cat_id"]);
 
-                       // try doing it the old way
-                       if (!$feeds) {
-                               $fetch_url = $line['access_url'] . '/backend.php?op=fbexport';
-                               $feeds = fetch_file_contents($fetch_url, false, false, false, $post_query);
-                       }
-
-                       if ($feeds) {
-                               $feeds = json_decode($feeds, true);
+                                               $children = join(",", $children);
 
-                               if ($feeds) {
-                                       if ($feeds['error']) {
-                                               $status = $feeds['error']['code'] + 10;
+                                               $cat_qpart = "cat_id IN ($children)";
                                        } 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.");
+                                               $cat_qpart = "cat_id IS NULL";
                                        }
-                               } 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'");
-
-               }
-       }
-
-       function make_feed_browser($link, $search, $limit, $mode = 1) {
-
-               $owner_uid = $_SESSION["uid"];
-               $rv = '';
-
-               if ($search) {
-                       $search_qpart = "AND (UPPER(feed_url) LIKE UPPER('%$search%') OR
-                                               UPPER(title) LIKE UPPER('%$search%'))";
-               } else {
-                       $search_qpart = "";
-               }
-
-               if ($mode == 1) {
-                       /* $result = db_query($link, "SELECT feed_url, subscribers FROM
-                        ttrss_feedbrowser_cache WHERE (SELECT COUNT(id) = 0 FROM ttrss_feeds AS tf
-                       WHERE tf.feed_url = ttrss_feedbrowser_cache.feed_url
-                       AND owner_uid = '$owner_uid') $search_qpart
-                       ORDER BY subscribers DESC LIMIT $limit"); */
-
-                       $result = db_query($link, "SELECT feed_url, site_url, title, SUM(subscribers) AS subscribers FROM
-                                               (SELECT feed_url, site_url, title, subscribers FROM ttrss_feedbrowser_cache UNION ALL
-                                                       SELECT feed_url, site_url, title, subscribers FROM ttrss_linked_feeds) AS qqq
-                                               WHERE
-                                                       (SELECT COUNT(id) = 0 FROM ttrss_feeds AS tf
-                                                               WHERE tf.feed_url = qqq.feed_url
-                                                                       AND owner_uid = '$owner_uid') $search_qpart
-                                               GROUP BY feed_url, site_url, title ORDER BY subscribers DESC LIMIT $limit");
-
-               } else if ($mode == 2) {
-                       $result = db_query($link, "SELECT *,
-                                               (SELECT COUNT(*) FROM ttrss_user_entries WHERE
-                                                       orig_feed_id = ttrss_archived_feeds.id) AS articles_archived
-                                               FROM
-                                                       ttrss_archived_feeds
-                                               WHERE
-                                               (SELECT COUNT(*) FROM ttrss_feeds
-                                                       WHERE ttrss_feeds.feed_url = ttrss_archived_feeds.feed_url AND
-                                                               owner_uid = '$owner_uid') = 0   AND
-                                               owner_uid = '$owner_uid' $search_qpart
-                                               ORDER BY id DESC LIMIT $limit");
-               }
-
-               $feedctr = 0;
-
-               while ($line = db_fetch_assoc($result)) {
-
-                       if ($mode == 1) {
-
-                               $feed_url = htmlspecialchars($line["feed_url"]);
-                               $site_url = htmlspecialchars($line["site_url"]);
-                               $subscribers = $line["subscribers"];
-
-                               $check_box = "<input onclick='toggleSelectListRow2(this)'
-                                                       dojoType=\"dijit.form.CheckBox\"
-                                                       type=\"checkbox\" \">";
-
-                               $class = ($feedctr % 2) ? "even" : "odd";
 
-                               $site_url = "<a target=\"_blank\"
-                                                       href=\"$site_url\">
-                                                       <span class=\"fb_feedTitle\">".
-                               htmlspecialchars($line["title"])."</span></a>";
-
-                               $feed_url = "<a target=\"_blank\" class=\"fb_feedUrl\"
-                                                       href=\"$feed_url\"><img src='images/feed-icon-12x12.png'
-                                                       style='vertical-align : middle'></a>";
-
-                               $rv .= "<li>$check_box $feed_url $site_url".
-                                                       "&nbsp;<span class='subscribers'>($subscribers)</span></li>";
-
-                       } else if ($mode == 2) {
-                               $feed_url = htmlspecialchars($line["feed_url"]);
-                               $site_url = htmlspecialchars($line["site_url"]);
-                               $title = htmlspecialchars($line["title"]);
-
-                               $check_box = "<input onclick='toggleSelectListRow2(this)' dojoType=\"dijit.form.CheckBox\"
-                                                       type=\"checkbox\">";
-
-                               $class = ($feedctr % 2) ? "even" : "odd";
-
-                               if ($line['articles_archived'] > 0) {
-                                       $archived = sprintf(__("%d archived articles"), $line['articles_archived']);
-                                       $archived = "&nbsp;<span class='subscribers'>($archived)</span>";
-                               } else {
-                                       $archived = '';
+                                       $qpart .= " AND $cat_qpart";
                                }
 
-                               $site_url = "<a target=\"_blank\"
-                                                       href=\"$site_url\">
-                                                       <span class=\"fb_feedTitle\">".
-                               htmlspecialchars($line["title"])."</span></a>";
-
-                               $feed_url = "<a target=\"_blank\" class=\"fb_feedUrl\"
-                                                       href=\"$feed_url\"><img src='images/feed-icon-12x12.png'
-                                                       style='vertical-align : middle'></a>";
-
+                               array_push($query, "($qpart)");
 
-                               $rv .= "<li id=\"FBROW-".$line["id"]."\">".
-                                                       "$check_box $feed_url $site_url $archived</li>";
                        }
-
-                       ++$feedctr;
                }
 
-               if ($feedctr == 0) {
-                       $rv .= "<li style=\"text-align : center\"><p>".__('No feeds found.')."</p></li>";
+               if (count($query) > 0) {
+                       $fullquery = "(" . join($filter["match_any_rule"] ? "OR" : "AND", $query) . ")";
+               } else {
+                       $fullquery = "(false)";
                }
 
-               return $rv;
+               if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
+
+               return $fullquery;
        }
 
        if (!function_exists('gzdecode')) {
                }
        }
 
-       function perform_data_import($link, $filename, $owner_uid) {
-
-               $num_imported = 0;
-               $num_processed = 0;
-               $num_feeds_created = 0;
-
-               $doc = @DOMDocument::load($filename);
-
-               if (!$doc) {
-                       $contents = file_get_contents($filename);
-
-                       if ($contents) {
-                               $data = @gzuncompress($contents);
-                       }
-
-                       if (!$data) {
-                               $data = @gzdecode($contents);
-                       }
-
-                       if ($data)
-                               $doc = DOMDocument::loadXML($data);
-               }
-
-               if ($doc) {
-
-                       $xpath = new DOMXpath($doc);
-
-                       $container = $doc->firstChild;
-
-                       if ($container && $container->hasAttribute('schema-version')) {
-                               $schema_version = $container->getAttribute('schema-version');
-
-                               if ($schema_version != SCHEMA_VERSION) {
-                                       print "<p>" .__("Could not import: incorrect schema version.") . "</p>";
-                                       return;
-                               }
-
-                       } else {
-                               print "<p>" . __("Could not import: unrecognized document format.") . "</p>";
-                               return;
-                       }
-
-                       $articles = $xpath->query("//article");
-
-                       foreach ($articles as $article_node) {
-                               if ($article_node->childNodes) {
-
-                                       $ref_id = 0;
-
-                                       $article = array();
-
-                                       foreach ($article_node->childNodes as $child) {
-                                               if ($child->nodeName != 'label_cache')
-                                                       $article[$child->nodeName] = db_escape_string($child->nodeValue);
-                                               else
-                                                       $article[$child->nodeName] = $child->nodeValue;
-                                       }
-
-                                       //print_r($article);
-
-                                       if ($article['guid']) {
-
-                                               ++$num_processed;
-
-                                               //db_query($link, "BEGIN");
-
-                                               //print 'GUID:' . $article['guid'] . "\n";
-
-                                               $result = db_query($link, "SELECT id FROM ttrss_entries
-                                                       WHERE guid = '".$article['guid']."'");
-
-                                               if (db_num_rows($result) == 0) {
-
-                                                       $result = db_query($link,
-                                                               "INSERT INTO ttrss_entries
-                                                                       (title,
-                                                                       guid,
-                                                                       link,
-                                                                       updated,
-                                                                       content,
-                                                                       content_hash,
-                                                                       no_orig_date,
-                                                                       date_updated,
-                                                                       date_entered,
-                                                                       comments,
-                                                                       num_comments,
-                                                                       author)
-                                                               VALUES
-                                                                       ('".$article['title']."',
-                                                                       '".$article['guid']."',
-                                                                       '".$article['link']."',
-                                                                       '".$article['updated']."',
-                                                                       '".$article['content']."',
-                                                                       '".sha1($article['content'])."',
-                                                                       false,
-                                                                       NOW(),
-                                                                       NOW(),
-                                                                       '',
-                                                                       '0',
-                                                                       '')");
-
-                                                       $result = db_query($link, "SELECT id FROM ttrss_entries
-                                                               WHERE guid = '".$article['guid']."'");
-
-                                                       if (db_num_rows($result) != 0) {
-                                                               $ref_id = db_fetch_result($result, 0, "id");
-                                                       }
-
-                                               } else {
-                                                       $ref_id = db_fetch_result($result, 0, "id");
-                                               }
-
-                                               //print "Got ref ID: $ref_id\n";
-
-                                               if ($ref_id) {
-
-                                                       $feed_url = $article['feed_url'];
-                                                       $feed_title = $article['feed_title'];
-
-                                                       $feed = 'NULL';
-
-                                                       if ($feed_url && $feed_title) {
-                                                               $result = db_query($link, "SELECT id FROM ttrss_feeds
-                                                                       WHERE feed_url = '$feed_url' AND owner_uid = '$owner_uid'");
-
-                                                               if (db_num_rows($result) != 0) {
-                                                                       $feed = db_fetch_result($result, 0, "id");
-                                                               } else {
-                                                                       // try autocreating feed in Uncategorized...
-
-                                                                       $result = db_query($link, "INSERT INTO ttrss_feeds (owner_uid,
-                                                                               feed_url, title) VALUES ($owner_uid, '$feed_url', '$feed_title')");
-
-                                                                       $result = db_query($link, "SELECT id FROM ttrss_feeds
-                                                                               WHERE feed_url = '$feed_url' AND owner_uid = '$owner_uid'");
-
-                                                                       if (db_num_rows($result) != 0) {
-                                                                               ++$num_feeds_created;
-
-                                                                               $feed = db_fetch_result($result, 0, "id");
-                                                                       }
-                                                               }
-                                                       }
-
-                                                       if ($feed != 'NULL')
-                                                               $feed_qpart = "feed_id = $feed";
-                                                       else
-                                                               $feed_qpart = "feed_id IS NULL";
-
-                                                       //print "$ref_id / $feed / " . $article['title'] . "\n";
-
-                                                       $result = db_query($link, "SELECT int_id FROM ttrss_user_entries
-                                                               WHERE ref_id = '$ref_id' AND owner_uid = '$owner_uid' AND $feed_qpart");
-
-                                                       if (db_num_rows($result) == 0) {
-
-                                                               $marked = bool_to_sql_bool(sql_bool_to_bool($article['marked']));
-                                                               $published = bool_to_sql_bool(sql_bool_to_bool($article['published']));
-                                                               $score = (int) $article['score'];
-
-                                                               $tag_cache = $article['tag_cache'];
-                                                               $label_cache = db_escape_string($article['label_cache']);
-                                                               $note = $article['note'];
-
-                                                               //print "Importing " . $article['title'] . "<br/>";
-
-                                                               ++$num_imported;
-
-                                                               $result = db_query($link,
-                                                                       "INSERT INTO ttrss_user_entries
-                                                                       (ref_id, owner_uid, feed_id, unread, last_read, marked,
-                                                                               published, score, tag_cache, label_cache, uuid, note)
-                                                                       VALUES ($ref_id, $owner_uid, $feed, false,
-                                                                               NULL, $marked, $published, $score, '$tag_cache',
-                                                                                       '$label_cache', '', '$note')");
-
-                                                               $label_cache = json_decode($label_cache, true);
-
-                                                               if (is_array($label_cache) && $label_cache["no-labels"] != 1) {
-                                                                       foreach ($label_cache as $label) {
-
-                                                                               label_create($link, $label[1],
-                                                                                       $label[2], $label[3], $owner_uid);
-
-                                                                               label_add_article($link, $ref_id, $label[1], $owner_uid);
-
-                                                                       }
-                                                               }
-
-                                                               //db_query($link, "COMMIT");
-                                                       }
-                                               }
-                                       }
-                               }
-                       }
-
-                       print "<p>" .
-                               T_sprintf("Finished: %d articles processed, %d imported, %d feeds created.",
-                                       $num_processed, $num_imported, $num_feeds_created) .
-                                       "</p>";
-
-               } else {
-
-                       print "<p>" . __("Could not load XML document.") . "</p>";
-
-               }
-       }
-
        function get_random_bytes($length) {
                if (function_exists('openssl_random_pseudo_bytes')) {
                        return openssl_random_pseudo_bytes($length);
 
        }
 
-       function create_published_article($link, $title, $url, $content, $owner_uid) {
-               $guid = sha1($url);
-               $content_hash = sha1($content);
+       function implements_interface($class, $interface) {
+               return in_array($interface, class_implements($class));
+       }
+
+       function geturl($url){
 
-               $rc = false;
+               (function_exists('curl_init')) ? '' : die('cURL Must be installed for geturl function to work. Ask your host to enable it or uncomment extension=php_curl.dll in php.ini');
 
-               if (!$title) $title = $url;
-               if (!$title && !$url) return false;
+               $curl = curl_init();
+               $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
+               $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
+               $header[] = "Cache-Control: max-age=0";
+               $header[] = "Connection: keep-alive";
+               $header[] = "Keep-Alive: 300";
+               $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
+               $header[] = "Accept-Language: en-us,en;q=0.5";
+               $header[] = "Pragma: ";
 
-               if (filter_var($url, FILTER_VALIDATE_URL) === FALSE) return false;
+               curl_setopt($curl, CURLOPT_URL, $url);
+               curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0');
+               curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
+               curl_setopt($curl, CURLOPT_HEADER, true);
+               curl_setopt($curl, CURLOPT_REFERER, $url);
+               curl_setopt($curl, CURLOPT_ENCODING, 'gzip,deflate');
+               curl_setopt($curl, CURLOPT_AUTOREFERER, true);
+               curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+               //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
+               curl_setopt($curl, CURLOPT_TIMEOUT, 60);
 
-               db_query($link, "BEGIN");
+               $html = curl_exec($curl);
 
-               // only check for our user data here, others might have shared this with different content etc
-               $result = db_query($link, "SELECT id FROM ttrss_entries, ttrss_user_entries WHERE
-                       link = '$url' AND ref_id = id AND owner_uid = '$owner_uid' LIMIT 1");
+               $status = curl_getinfo($curl);
+               curl_close($curl);
 
-               if (db_num_rows($result) != 0) {
-                       $ref_id = db_fetch_result($result, 0, "id");
+               if($status['http_code']!=200){
+                       if($status['http_code'] == 301 || $status['http_code'] == 302) {
+                               list($header) = explode("\r\n\r\n", $html, 2);
+                               $matches = array();
+                               preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches);
+                               $url = trim(str_replace($matches[1],"",$matches[0]));
+                               $url_parsed = parse_url($url);
+                               return (isset($url_parsed))? geturl($url, $referer):'';
+                       }
+                       $oline='';
+                       foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
+                       $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
+#                      $handle = @fopen('./curl.error.log', 'a');
+#                      fwrite($handle, $line);
+                       return FALSE;
+               }
+               return $url;
+       }
 
-                       $result = db_query($link, "SELECT int_id FROM ttrss_user_entries WHERE
-                               ref_id = '$ref_id' AND owner_uid = '$owner_uid' LIMIT 1");
+       function get_minified_js($files) {
+               require_once 'lib/jshrink/Minifier.php';
 
-                       if (db_num_rows($result) != 0) {
-                               $int_id = db_fetch_result($result, 0, "int_id");
+               $rv = '';
 
-                               db_query($link, "UPDATE ttrss_entries SET
-                                       content = '$content', content_hash = '$content_hash' WHERE id = '$ref_id'");
+               foreach ($files as $js) {
+                       if (!isset($_GET['debug'])) {
+                               $cached_file = CACHE_DIR . "/js/$js.js";
 
-                               db_query($link, "UPDATE ttrss_user_entries SET published = true WHERE
-                                               int_id = '$int_id' AND owner_uid = '$owner_uid'");
-                       } else {
+                               if (file_exists($cached_file) &&
+                                               is_readable($cached_file) &&
+                                               filemtime($cached_file) >= filemtime("js/$js.js")) {
 
-                               db_query($link, "INSERT INTO ttrss_user_entries
-                                       (ref_id, uuid, feed_id, orig_feed_id, owner_uid, published, tag_cache, label_cache, last_read, note, unread)
-                                       VALUES
-                                       ('$ref_id', '', NULL, NULL, $owner_uid, true, '', '', NOW(), '', false)");
+                                       $rv .= file_get_contents($cached_file);
+
+                               } else {
+                                       $minified = JShrink\Minifier::minify(file_get_contents("js/$js.js"));
+                                       file_put_contents($cached_file, $minified);
+                                       $rv .= $minified;
+                               }
+                       } else {
+                               $rv .= file_get_contents("js/$js.js");
                        }
+               }
 
-                       $rc = true;
+               return $rv;
+       }
 
-               } else {
-                       $result = db_query($link, "INSERT INTO ttrss_entries
-                               (title, guid, link, updated, content, content_hash, date_entered, date_updated)
-                               VALUES
-                               ('$title', '$guid', '$url', NOW(), '$content', '$content_hash', NOW(), NOW())");
+       function stylesheet_tag($filename) {
+               $timestamp = filemtime($filename);
+
+               echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
+       }
+
+       function javascript_tag($filename) {
+               $query = "";
+
+               if (!(strpos($filename, "?") === FALSE)) {
+                       $query = substr($filename, strpos($filename, "?")+1);
+                       $filename = substr($filename, 0, strpos($filename, "?"));
+               }
+
+               $timestamp = filemtime($filename);
+
+               if ($query) $timestamp .= "&$query";
+
+               echo "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
+       }
+
+       function calculate_dep_timestamp() {
+               $files = array_merge(glob("js/*.js"), glob("*.css"));
+
+               $max_ts = -1;
+
+               foreach ($files as $file) {
+                       if (filemtime($file) > $max_ts) $max_ts = filemtime($file);
+               }
+
+               return $max_ts;
+       }
+
+       function T_js_decl($s1, $s2) {
+               if ($s1 && $s2) {
+                       $s1 = preg_replace("/\n/", "", $s1);
+                       $s2 = preg_replace("/\n/", "", $s2);
 
-                       $result = db_query($link, "SELECT id FROM ttrss_entries WHERE guid = '$guid'");
+                       $s1 = preg_replace("/\"/", "\\\"", $s1);
+                       $s2 = preg_replace("/\"/", "\\\"", $s2);
 
-                       if (db_num_rows($result) != 0) {
-                               $ref_id = db_fetch_result($result, 0, "id");
+                       return "T_messages[\"$s1\"] = \"$s2\";\n";
+               }
+       }
 
-                               db_query($link, "INSERT INTO ttrss_user_entries
-                                       (ref_id, uuid, feed_id, orig_feed_id, owner_uid, published, tag_cache, label_cache, last_read, note, unread)
-                                       VALUES
-                                       ('$ref_id', '', NULL, NULL, $owner_uid, true, '', '', NOW(), '', false)");
+       function init_js_translations() {
 
-                               $rc = true;
+       print 'var T_messages = new Object();
+
+               function __(msg) {
+                       if (T_messages[msg]) {
+                               return T_messages[msg];
+                       } else {
+                               return msg;
                        }
                }
 
-               db_query($link, "COMMIT");
+               function ngettext(msg1, msg2, n) {
+                       return (parseInt(n) > 1) ? msg2 : msg1;
+               }';
+
+               $l10n = _get_reader();
+
+               for ($i = 0; $i < $l10n->total; $i++) {
+                       $orig = $l10n->get_original_string($i);
+                       $translation = __($orig);
+
+                       print T_js_decl($orig, $translation);
+               }
+       }
+
+       function label_to_feed_id($label) {
+               return LABEL_BASE_INDEX - 1 - abs($label);
+       }
 
-               return $rc;
+       function feed_to_label_id($feed) {
+               return LABEL_BASE_INDEX - 1 + abs($feed);
        }
 
 ?>