]> git.wh0rd.org - tt-rss.git/blobdiff - include/functions.php
api: add author to getHeadlines
[tt-rss.git] / include / functions.php
index 1ae5430c1c654c0858bf3d61d7fd5a9eeb039717..307f662009b14dc83c304a86477f4a5e57c96508 100644 (file)
@@ -1,8 +1,12 @@
 <?php
        define('EXPECTED_CONFIG_VERSION', 26);
-       define('SCHEMA_VERSION', 102);
+       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) {
@@ -44,6 +48,7 @@
                $tr = array(
                                        "auto"  => "Detect automatically",
                                        "ca_CA" => "Català",
+                                       "cs_CZ" => "Česky",
                                        "en_US" => "English",
                                        "es_ES" => "Español",
                                        "de_DE" => "Deutsch",
@@ -51,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",
@@ -63,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;
                }
 
-               /* In login action of mobile version */
-               if ($_POST["language"] && defined('MOBILE_VERSION')) {
-                       $lang = $_POST["language"];
-               } else {
+               if ($_SESSION["language"] && $_SESSION["language"] != "auto") {
                        $lang = $_SESSION["language"];
                }
 
                                _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';
+       require_once 'ccache.php';
+       require_once 'labels.php';
 
+       define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . VERSION . ' (http://tt-rss.org/)');
        ini_set('user_agent', SELF_USER_AGENT);
 
        require_once 'lib/pubsubhubbub/publisher.php';
-       require_once 'lib/htmLawed.php';
 
        $tz_offset = -1;
        $utc_tz = new DateTimeZone('UTC');
         * @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 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;
 
                        global $pluginhost;
                        }
 
                        if ($user_id && !$check_only) {
+                               @session_start();
+
                                $_SESSION["uid"] = $user_id;
 
                                $result = db_query($link, "SELECT login,access_level,pwd_hash FROM ttrss_users
                }
        }
 
-       function login_sequence($link, $login_form = 0) {
+       function login_sequence($link) {
+               $_SESSION["prefs_cache"] = false;
+
                if (SINGLE_USER_MODE) {
+                       @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"]);
+                               $_SESSION["last_login_update"] = time();
                        }
 
                        if ($_SESSION["uid"] && $_SESSION["language"] && SESSION_COOKIE_LIFETIME > 0) {
                        }
 
                        if ($_SESSION["uid"]) {
+                               cache_prefs($link);
                                load_user_plugins($link, $_SESSION["uid"]);
-                       }
-               }
-       }
-
-       function truncate_string($str, $max_len, $suffix = '&hellip;') {
-               if (mb_strlen($str, "utf-8") > $max_len - 3) {
-                       return mb_substr($str, 0, $max_len, "utf-8") . $suffix;
-               } else {
-                       return $str;
-               }
-       }
-
-       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 = '';
-               }
+                               /* cleanup ccache */
 
-               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 '';
-       }
+                               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");
 
-       function get_user_theme_options($link) {
-               $t = get_user_theme_path($link);
+                               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");
 
-               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);
-                               }
-                       }
+       function truncate_string($str, $max_len, $suffix = '&hellip;') {
+               if (mb_strlen($str, "utf-8") > $max_len - 3) {
+                       return mb_substr($str, 0, $max_len, "utf-8") . $suffix;
+               } else {
+                       return $str;
                }
-
-               return $rv;
        }
 
        function convert_timestamp($timestamp, $source_tz, $dest_tz) {
        }
 
        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) {
 
                                                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 $ref_check_qpart
+                                                       AND unread = true
                                                        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;
        }
                        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);
 
                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();
                        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" => (int) $count);
         *                 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;
 
        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/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.svg");
-               $params["sign_info"] = theme_image($link, "images/sign_info.svg");
-
                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["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["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;
        }
                                "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"),
                                "article_scroll_up" => __("Scroll up"),
                                "select_article_cursor" => __("Select article under cursor"),
                                "email_article" => __("Email article"),
-                               "toggle_widescreen" => __("Toggle widescreen mode")),
+                               "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"),
                                "feed_reverse" => __("Reverse headlines"),
                                "feed_debug_update" => __("Debug feed update"),
                                "catchup_all" => __("Mark all feeds as read"),
-                               "cat_toggle_collapse" => __("Un/collapse current category")),
+                               "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"),
                                "j" => "prev_feed",
                                "n" => "next_article",
                                "p" => "prev_article",
-                               "(38)" => "prev_article",
-                               "(40)" => "next_article",
-                               "(191)" => "search_dialog",
+                               "(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",
+                               "*s" => "toggle_publ",
                                "u" => "toggle_unread",
-                               "T" => "edit_tags",
-                               "D" => "dismiss_selected",
-                               "X" => "dismiss_read",
+                               "*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",
-                               "a W" => "toggle_widescreen",
+                               "*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 *u" => "select_marked",
                                "a p" => "select_published",
                                "a i" => "select_invert",
                                "a n" => "select_none",
                                "f e" => "feed_edit",
                                "f q" => "feed_catchup",
                                "f x" => "feed_reverse",
-                               "f D" => "feed_debug_update",
-                               "Q" => "catchup_all",
+                               "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 s" => "goto_marked",
                                "g p" => "goto_published",
                                "g t" => "goto_tagcloud",
-                               "g P" => "goto_prefs",
+                               "g *p" => "goto_prefs",
 //                     "other" => array(
-                               "(9)" => "select_article_cursor", // tab
+                               "(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);
                $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 ";
                                }
 
                                        ttrss_user_entries, ttrss_feeds, ttrss_feed_categories
                                        WHERE $filter_query_part LIMIT 1", false);
 
-                               $test = db_fetch_result($result, 0, "true_val");
+                               if ($result) {
+                                       $test = db_fetch_result($result, 0, "true_val");
 
-                               if (!$test) {
-                                       $filter_query_part = "false AND";
+                                       if (!$test) {
+                                               $filter_query_part = "false AND";
+                                       } else {
+                                               $filter_query_part .= " AND";
+                                       }
                                } else {
-                                       $filter_query_part .= " AND";
+                                       $filter_query_part = "false AND";
                                }
 
                        } else {
 
                        $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;
                        }
                                $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,";
 
                                $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
                                $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) {
                                }
 
                                // proper override_order applied above
-                               if ($vfeed_query_part && get_pref($link, 'VFEED_GROUP_BY_FEED', $owner_uid)) {
+                               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 {
                                                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) {
+       function sanitize($link, $str, $force_remove_images = false, $owner = false, $site_url = false) {
                if (!$owner) $owner = $_SESSION["uid"];
 
                $res = trim($str); if (!$res) return '';
 
-               $config = array('safe' => 1, 'deny_attribute' => 'style, width, height, class, id', 'comment' => 1, 'cdata' => 1, 'balance' => 0);
-               $res = htmLawed($res, $config);
-
-               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"]) {
 
-               // http://tt-rss.org/redmine/issues/357
-               return $doc->saveXML($node, LIBXML_NOEMPTYTAG);
-       }
+                                               $p = $doc->createElement('p');
 
-       function check_for_update($link) {
-               if (CHECK_FOR_NEW_VERSION && $_SESSION['access_level'] >= 10) {
-                       $version_url = "http://tt-rss.org/version.php?ver=" . VERSION;
+                                               $a = $doc->createElement('a');
+                                               $a->setAttribute('href', $entry->getAttribute('src'));
 
-                       $version_data = @fetch_file_contents($version_url);
+                                               $a->appendChild(new DOMText($entry->getAttribute('src')));
+                                               $a->setAttribute('target', '_blank');
 
-                       if ($version_data) {
-                               $version_data = json_decode($version_data, true);
-                               if ($version_data && $version_data['version']) {
+                                               $p->appendChild($a);
 
-                                       if (version_compare(VERSION, $version_data['version']) == -1) {
-                                               return $version_data;
+                                               $entry->parentNode->replaceChild($p, $entry);
                                        }
                                }
                        }
-               }
-               return false;
-       }
 
-       function markArticlesById($link, $ids, $cmode) {
+                       if (strtolower($entry->nodeName) == "a") {
+                               $entry->setAttribute("target", "_blank");
+                       }
+               }
 
-               $tmp_ids = array();
+               $entries = $xpath->query('//iframe');
+               foreach ($entries as $entry) {
+                       $entry->setAttribute('sandbox', 'allow-scripts');
 
-               foreach ($ids as $id) {
-                       array_push($tmp_ids, "ref_id = '$id'");
                }
 
-               $ids_qpart = join(" OR ", $tmp_ids);
+               $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 ($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"]);
+               if ($_SESSION['hasSandbox']) $allowed_elements[] = 'iframe';
+
+               $disallowed_attributes = array('id', 'style', 'class');
+
+               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;
+                               }
+                       }
                }
+
+               $doc->removeChild($doc->firstChild); //remove doctype
+               $doc = strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes);
+               $res = $doc->saveHTML();
+               return $res;
        }
 
-       function publishArticlesById($link, $ids, $cmode) {
+       function strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes) {
+               $entries = $doc->getElementsByTagName("*");
 
-               $tmp_ids = array();
+               foreach ($entries as $entry) {
+                       if (!in_array($entry->nodeName, $allowed_elements)) {
+                               $entry->parentNode->removeChild($entry);
+                       }
 
-               foreach ($ids as $id) {
-                       array_push($tmp_ids, "ref_id = '$id'");
-               }
+                       if ($entry->hasAttributes()) {
+                               $attrs_to_remove = array();
 
-               $ids_qpart = join(" OR ", $tmp_ids);
+                               foreach ($entry->attributes as $attr) {
 
-               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 (strpos($attr->nodeName, 'on') === 0) {
+                                               array_push($attrs_to_remove, $attr);
+                                       }
+
+                                       if (in_array($attr->nodeName, $disallowed_attributes)) {
+                                               array_push($attrs_to_remove, $attr);
+                                       }
+                               }
+
+                               foreach ($attrs_to_remove as $attr) {
+                                       $entry->removeAttributeNode($attr);
+                               }
+                       }
                }
 
-               if (PUBSUBHUBBUB_HUB) {
-                       $rss_link = get_self_url_prefix() .
-                               "/public.php?op=rss&id=-2&key=" .
-                               get_feed_access_key($link, -2, false);
+               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 .
+                               "&iid=" . sha1(SELF_URL_PATH);
+
+                       $version_data = @fetch_file_contents($version_url);
 
-                       $p = new Publisher(PUBSUBHUBBUB_HUB);
+                       if ($version_data) {
+                               $version_data = json_decode($version_data, true);
+                               if ($version_data && $version_data['version']) {
 
-                       $pubsub_result = $p->publish_update($rss_link);
+                                       if (version_compare(VERSION, $version_data['version']) == -1) {
+                                               return $version_data;
+                                       }
+                               }
+                       }
                }
+               return false;
        }
 
        function catchupArticlesById($link, $ids, $cmode, $owner_uid = false) {
                }
        }
 
-       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.svg")."\">$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.svg")."\">$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.svg")."\">$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, cache_content 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"));
-                       $cache_content = sql_bool_to_bool(db_fetch_result($result, 0, "cache_content"));
-               } else {
-                       $rtl_content = false;
-                       $always_display_enclosures = false;
-                       $cache_content = 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()
 
                $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,
 
                        $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 = "";
                                                <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 = htmlspecialchars($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=\"" .
                                        htmlspecialchars($line["link"]) . "\">" .
-                                       $line["title"] .
-                                       "<span class='author'>$entry_author</span></a></div>";
+                                       $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;
 
                                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'] .= "<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/";
-                               }
-                       }
-
-                       if ($cache_content && $line["cached_content"] != "") {
-                               $line["content"] =& $line["cached_content"];
-                       }
-
-                       $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 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"]);
-               } else {
-                       $result = db_query($link, "DELETE FROM ttrss_user_entries
-                       WHERE feed_id IS NULL AND marked = false AND owner_uid = " . $_SESSION["uid"]);
-               }
-
-               $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.
         *
                        $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;
 
                }
        }
 
-       /* 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";
-               }
-
-               db_query($link, "DELETE FROM $table WHERE
-                       feed_id = '$feed_id' AND owner_uid = '$owner_uid'");
-
-       }
-
-       function ccache_update_all($link, $owner_uid) {
-
-               if (get_pref($link, 'ENABLE_FEED_CATS', $owner_uid)) {
-
-                       $result = db_query($link, "SELECT feed_id FROM ttrss_cat_counters_cache
-                               WHERE feed_id > 0 AND owner_uid = '$owner_uid'");
-
-                       while ($line = db_fetch_assoc($result)) {
-                               ccache_update($link, $line["feed_id"], $owner_uid, true);
-                       }
-
-                       /* We have to manually include category 0 */
-
-                       ccache_update($link, 0, $owner_uid, true);
-
-               } else {
-                       $result = db_query($link, "SELECT feed_id FROM ttrss_counters_cache
-                               WHERE feed_id > 0 AND owner_uid = '$owner_uid'");
-
-                       while ($line = db_fetch_assoc($result)) {
-                               print ccache_update($link, $line["feed_id"], $owner_uid);
-
-                       }
-
-               }
-       }
-
-       function ccache_find($link, $feed_id, $owner_uid, $is_cat = false,
-               $no_update = false) {
-
-               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, $owner_uid = false) {
-               $rv = array();
-
-               if (!$owner_uid) $owner_uid = $_SESSION["uid"];
-
-               $result = db_query($link, "SELECT label_cache FROM
-                       ttrss_user_entries WHERE ref_id = '$id' AND owner_uid = " .
-                       $owner_uid);
-
-               if (db_num_rows($result) > 0) {
-                       $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 = ". $owner_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, $owner_uid, $id, $rv);
-               else
-                       label_update_cache($link, $owner_uid, $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 get_all_labels($link, $owner_uid) {
-               $rv = array();
-
-               $result = db_query($link, "SELECT fg_color, bg_color, caption FROM ttrss_labels2 WHERE owner_uid = " . $owner_uid);
-
-               while ($line = db_fetch_assoc($result)) {
-                       array_push($rv, $line);
-               }
-
-               return $rv;
-       }
-
-       function label_update_cache($link, $owner_uid, $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' AND  owner_uid = '$owner_uid'");
-
-       }
-
-       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 = "";
 
        }
 
-       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"]);
                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, $sanitize_content = true) {
-
-                       $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) {
-
-                                       if ($line["cached_content"] != "") {
-                                               $line["content_preview"] =& $line["cached_content"];
-                                       }
-
-                                       if ($sanitize_content) {
-                                               $headline_row["content"] = sanitize($link,
-                                                       $line["content_preview"], false, false, $line["site_url"]);
-                                       } else {
-                                               $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"];
-
-                               $headline_row["comments_count"] = (int)$line["num_comments"];
-                               $headline_row["comments_link"] = $line["comments"];
-
-                               $headline_row["always_display_attachments"] = sql_bool_to_bool($line["always_display_enclosures"]);
-
-                               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;
-       }
-
-
-       function generate_dashboard_feed($link) {
-               $reply = array();
-
-               $reply['headlines']['id'] = -5;
-               $reply['headlines']['is_cat'] = false;
-
-               $reply['headlines']['toolbar'] = '';
-               $reply['headlines']['content'] = "<div class='whiteBox'>".__('No feed selected.');
-
-               $reply['headlines']['content'] .= "<p class=\"small\"><span class=\"insensitive\">";
-
-               $result = db_query($link, "SELECT ".SUBSTRING_FOR_DATE."(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds
-                       WHERE owner_uid = " . $_SESSION['uid']);
-
-               $last_updated = db_fetch_result($result, 0, "last_updated");
-               $last_updated = make_local_datetime($link, $last_updated, false);
-
-               $reply['headlines']['content'] .= sprintf(__("Feeds last updated at %s"), $last_updated);
-
-               $result = db_query($link, "SELECT COUNT(id) AS num_errors
-                       FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ".$_SESSION["uid"]);
-
-               $num_errors = db_fetch_result($result, 0, "num_errors");
-
-               if ($num_errors > 0) {
-                       $reply['headlines']['content'] .= "<br/>";
-                       $reply['headlines']['content'] .= "<a class=\"insensitive\" href=\"#\" onclick=\"showFeedsWithErrors()\">".
-                               __('Some feeds have update errors (click for details)')."</a>";
-               }
-               $reply['headlines']['content'] .= "</span></p>";
-
-               $reply['headlines-info'] = array("count" => 0,
-                       "vgroup_last_feed" => '',
-                       "unread" => 0,
-                       "disable_cache" => true);
-
-               return $reply;
-       }
-
        function save_email_address($link, $email) {
                // FIXME: implement persistent storage of emails
 
                        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 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;
        }
 
 
                // 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;
        }
 
                        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"])) {
                }
 
                if (count($query) > 0) {
-                       return "(" . join($filter["match_any_rule"] ? "OR" : "AND", $query) . ")";
+                       $fullquery = "(" . join($filter["match_any_rule"] ? "OR" : "AND", $query) . ")";
                } else {
-                       return "(false)";
+                       $fullquery = "(false)";
                }
+
+               if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
+
+               return $fullquery;
        }
 
        if (!function_exists('gzdecode')) {
 
        }
 
-       function create_published_article($link, $title, $url, $content, $labels_str,
-                       $owner_uid) {
-
-               $guid = sha1($url . $owner_uid); // include owner_uid to prevent global GUID clash
-               $content_hash = sha1($content);
+       function implements_interface($class, $interface) {
+               return in_array($interface, class_implements($class));
+       }
 
-               if ($labels_str != "") {
-                       $labels = explode(",", $labels_str);
-               } else {
-                       $labels = array();
-               }
+       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);
 
-                       if (count($labels) != 0) {
-                               foreach ($labels as $label) {
-                                       label_add_article($link, $ref_id, trim($label), $owner_uid);
+                               } 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";
+       }
 
-                       $result = db_query($link, "SELECT id FROM ttrss_entries WHERE guid = '$guid'");
+       function javascript_tag($filename) {
+               $query = "";
 
-                       if (db_num_rows($result) != 0) {
-                               $ref_id = db_fetch_result($result, 0, "id");
+               if (!(strpos($filename, "?") === FALSE)) {
+                       $query = substr($filename, strpos($filename, "?")+1);
+                       $filename = substr($filename, 0, strpos($filename, "?"));
+               }
 
-                               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)");
+               $timestamp = filemtime($filename);
 
-                               if (count($labels) != 0) {
-                                       foreach ($labels as $label) {
-                                               label_add_article($link, $ref_id, trim($label), $owner_uid);
-                                       }
-                               }
+               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);
 
-                               $rc = true;
+                       $s1 = preg_replace("/\"/", "\\\"", $s1);
+                       $s2 = preg_replace("/\"/", "\\\"", $s2);
+
+                       return "T_messages[\"$s1\"] = \"$s2\";\n";
+               }
+       }
+
+       function init_js_translations() {
+
+       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;
+               }';
 
-               return $rc;
+               $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 implements_interface($class, $interface) {
-               return in_array($interface, class_implements($class));
+       function label_to_feed_id($label) {
+               return LABEL_BASE_INDEX - 1 - abs($label);
+       }
+
+       function feed_to_label_id($feed) {
+               return LABEL_BASE_INDEX - 1 + abs($feed);
        }
 
 ?>