]> git.wh0rd.org - tt-rss.git/blobdiff - include/functions.php
pass logfile to child tasks if locking is possible, lock logfile before writing,...
[tt-rss.git] / include / functions.php
index 2a8c23b5d6aee2a0d42a7d6df9033ae7d07ec6b5..8ede14a0bfa7a188b036d8ac704aff70dd7c22a9 100644 (file)
@@ -1,6 +1,6 @@
 <?php
        define('EXPECTED_CONFIG_VERSION', 26);
-       define('SCHEMA_VERSION', 120);
+       define('SCHEMA_VERSION', 122);
 
        define('LABEL_BASE_INDEX', -1024);
        define('PLUGIN_FEED_BASE_INDEX', -128);
@@ -87,6 +87,7 @@
        require_once "lib/accept-to-gettext.php";
        require_once "lib/gettext/gettext.inc";
 
+       require_once "lib/languagedetect/LanguageDetect.php";
 
        function startup_gettext() {
 
                if ($_SESSION["uid"] && get_schema_version() >= 120) {
                        $pref_lang = get_pref("USER_LANGUAGE", $_SESSION["uid"]);
 
-                       if ($pref_lang) {
+                       if ($pref_lang && $pref_lang != 'auto') {
                                $lang = $pref_lang;
                        }
                }
         * @return void
         */
        function _debug($msg, $show = true) {
+               if (defined('SUPPRESS_DEBUGGING'))
+                       return false;
 
                $ts = strftime("%H:%M:%S", time());
                if (function_exists('posix_getpid')) {
                        $fp = fopen(LOGFILE, 'a+');
 
                        if ($fp) {
+                               $locked = false;
+
+                               if (function_exists("flock")) {
+                                       $tries = 0;
+
+                                       // try to lock logfile for writing
+                                       while ($tries < 5 && !$locked = flock($fp, LOCK_EX | LOCK_NB)) {
+                                               sleep(1);
+                                               ++$tries;
+                                       }
+
+                                       if (!$locked) {
+                                               fclose($fp);
+                                               return;
+                                       }
+                               }
+
                                fputs($fp, "[$ts] $msg\n");
+
+                               if (function_exists("flock")) {
+                                       flock($fp, LOCK_UN);
+                               }
+
                                fclose($fp);
                        }
                }
                        $fetch_curl_used = true;
 
                        if (ini_get("safe_mode") || ini_get("open_basedir")) {
-                               $ch = curl_init(geturl($url));
+                               $new_url = geturl($url);
+                               if (!$new_url) {
+                                   // geturl has already populated $fetch_last_error
+                                   return false;
+                               }
+                               $ch = curl_init($new_url);
                        } else {
                                $ch = curl_init($url);
                        }
                                curl_setopt($ch, CURLOPT_POSTFIELDS, $post_query);
                        }
 
+                       if ((OPENSSL_VERSION_NUMBER >= 0x0090808f) && (OPENSSL_VERSION_NUMBER < 0x10000000)) {
+                               curl_setopt($ch, CURLOPT_SSLVERSION, 3);
+                       }
+
                        if ($login && $pass)
                                curl_setopt($ch, CURLOPT_USERPWD, "$login:$pass");
 
        }
 
        function file_is_locked($filename) {
-               if (function_exists('flock')) {
-                       $fp = @fopen(LOCK_DIRECTORY . "/$filename", "r");
-                       if ($fp) {
-                               if (flock($fp, LOCK_EX | LOCK_NB)) {
-                                       flock($fp, LOCK_UN);
+               if (file_exists(LOCK_DIRECTORY . "/$filename")) {
+                       if (function_exists('flock')) {
+                               $fp = @fopen(LOCK_DIRECTORY . "/$filename", "r");
+                               if ($fp) {
+                                       if (flock($fp, LOCK_EX | LOCK_NB)) {
+                                               flock($fp, LOCK_UN);
+                                               fclose($fp);
+                                               return false;
+                                       }
                                        fclose($fp);
+                                       return true;
+                               } else {
                                        return false;
                                }
-                               fclose($fp);
-                               return true;
-                       } else {
-                               return false;
                        }
+                       return true; // consider the file always locked and skip the test
+               } else {
+                       return false;
                }
-               return true; // consider the file always locked and skip the test
        }
 
+
        function make_lockfile($filename) {
                $fp = fopen(LOCK_DIRECTORY . "/$filename", "w");
 
                if ($fp && flock($fp, LOCK_EX | LOCK_NB)) {
+                       $stat_h = fstat($fp);
+                       $stat_f = stat(LOCK_DIRECTORY . "/$filename");
+
+                       if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
+                               if ($stat_h["ino"] != $stat_f["ino"] ||
+                                               $stat_h["dev"] != $stat_f["dev"]) {
+
+                                       return false;
+                               }
+                       }
+
                        if (function_exists('posix_getpid')) {
                                fwrite($fp, posix_getpid() . "\n");
                        }
                                        $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 WEEK) ";
                                }
                                break;
-                       case "2weeks":
+                       case "2week":
                                if (DB_TYPE == "pgsql") {
                                        $date_qpart = "date_entered < NOW() - INTERVAL '2 week' ";
                                } else {
                        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);
+
+                               if (method_exists($feed['sender'], 'get_total'))
+                                       $cv["auxcounter"] = $feed['sender']->get_total($feed['id']);
+
+                               array_push($ret_arr, $cv);
                        }
                }
 
 
                $owner_uid = $_SESSION["uid"];
 
-               $result = db_query("SELECT id,caption,COUNT(unread) AS unread
+               $result = db_query("SELECT id,caption,COUNT(u1.unread) AS unread,COUNT(u2.unread) AS total
                        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
-                                       AND ttrss_user_entries.owner_uid = $owner_uid)
+                               LEFT JOIN ttrss_user_entries AS u1 ON (u1.ref_id = article_id AND u1.unread = true
+                                       AND u1.owner_uid = $owner_uid)
+                               LEFT JOIN ttrss_user_entries AS u2 ON (u2.ref_id = article_id AND u2.unread = false
+                                       AND u2.owner_uid = $owner_uid)
                                WHERE ttrss_labels2.owner_uid = $owner_uid GROUP BY ttrss_labels2.id,
                                        ttrss_labels2.caption");
 
 
                        $id = label_to_feed_id($line["id"]);
 
-                       $label_name = $line["caption"];
-                       $count = $line["unread"];
-
                        $cv = array("id" => $id,
-                               "counter" => (int) $count);
+                               "counter" => (int) $line["unread"],
+                               "auxcounter" => (int) $line["total"]);
 
                        if ($descriptions)
-                               $cv["description"] = $label_name;
-
-//                     if (get_pref('EXTENDED_FEEDLIST'))
-//                             $cv["xmsg"] = getFeedArticles($id)." ".__("total");
+                               $cv["description"] = $line["caption"];
 
                        array_push($ret_arr, $cv);
                }
                        return "images/archive.png";
                        break;
                case -1:
-                       return "images/mark_set.svg";
+                       return "images/star.png";
                        break;
                case -2:
-                       return "images/pub_set.svg";
+                       return "images/feed.png";
                        break;
                case -3:
                        return "images/fresh.png";
                        break;
                case -4:
-                       return "images/tag.png";
+                       return "images/folder.png";
                        break;
                case -6:
-                       return "images/recently_read.png";
+                       return "images/time.png";
                        break;
                default:
                        if ($id < LABEL_BASE_INDEX) {
                $params["max_feed_id"] = (int) $max_feed_id;
                $params["num_feeds"] = (int) $num_feeds;
 
-               $params["collapsed_feedlist"] = (int) get_pref("_COLLAPSED_FEEDLIST");
                $params["hotkeys"] = get_hotkeys_map();
 
                $params["csrf_token"] = $_SESSION["csrf_token"];
 
                $keywords = explode(" ", $search);
                $query_keywords = array();
+               $search_words = array();
 
                foreach ($keywords as $k) {
                        if (strpos($k, "-") === 0) {
                                } else {
                                        array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
                                                        OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
+                                       array_push($search_words, $k);
                                }
                                break;
                        case "author":
                                } else {
                                        array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
                                                        OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
+                                       array_push($search_words, $k);
                                }
                                break;
                        case "note":
                                } else {
                                        array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
                                                        OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
+                                       if (!$not) array_push($search_words, $k);
                                }
                                break;
                        case "star":
                                } else {
                                        array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
                                                        OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
+                                       if (!$not) array_push($search_words, $k);
                                }
                                break;
                        case "pub":
                                } else {
                                        array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
                                                        OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
+                                       if (!$not) array_push($search_words, $k);
                                }
                                break;
                        default:
                                } else {
                                        array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
                                                        OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
+
+                                       if (!$not) array_push($search_words, $k);
                                }
                        }
                }
 
                $search_query_part = implode("AND", $query_keywords);
 
-               return $search_query_part;
+               return array($search_query_part, $search_words);
        }
 
        function getParentCategories($cat, $owner_uid) {
                return $rv;
        }
 
-       function queryFeedHeadlines($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) {
+       function queryFeedHeadlines($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, $override_strategy = false, $override_vfeed = false) {
 
                if (!$owner_uid) $owner_uid = $_SESSION["uid"];
 
                $ext_tables_part = "";
+               $search_words = array();
 
                        if ($search) {
 
                                                $search_query_part = "ref_id = -1 AND ";
 
                                } else {
-                                       $search_query_part = search_to_sql($search);
+                                       list($search_query_part, $search_words) = search_to_sql($search);
                                        $search_query_part .= " AND ";
                                }
 
                                $allow_archived = true;
 
                                if (!$override_order) $override_order = "last_read DESC";
+
+/*                     } else if ($feed == -7) { // shared
+                               $query_strategy_part = "uuid != ''";
+                               $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
+                               $allow_archived = true; */
                        } else if ($feed == -3) { // fresh virtual feed
                                $query_strategy_part = "unread = true AND score >= 0";
 
                                $order_by = $override_order;
                        }
 
+                       if ($override_strategy) {
+                               $query_strategy_part = $override_strategy;
+                       }
+
+                       if ($override_vfeed) {
+                               $vfeed_query_part = $override_vfeed;
+                       }
+
                        $feed_title = "";
 
                        if ($search) {
                                }
                        }
 
-                       $content_query_part = "content as content_preview, cached_content, ";
+
+                       $content_query_part = "content, content AS content_preview, ";
+
 
                        if (is_numeric($feed)) {
 
                                                num_comments,
                                                comments,
                                                int_id,
+                                               uuid,
+                                               lang,
                                                hide_images,
                                                unread,feed_id,marked,published,link,last_read,orig_feed_id,
                                                last_marked, last_published,
                                                                "tag_cache," .
                                                                "label_cache," .
                                                                "link," .
+                                                               "lang," .
+                                                               "uuid," .
                                                                "last_read," .
                                                                "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
                                                                "last_marked, last_published, " .
                                $result = db_query($select_qpart . $from_qpart . $where_qpart);
                        }
 
-                       return array($result, $feed_title, $feed_site_url, $last_error, $last_updated);
+                       return array($result, $feed_title, $feed_site_url, $last_error, $last_updated, $search_words);
 
        }
 
-       function sanitize($str, $force_remove_images = false, $owner = false, $site_url = false) {
+       function sanitize($str, $force_remove_images = false, $owner = false, $site_url = false, $highlight_words = false, $article_id = false) {
                if (!$owner) $owner = $_SESSION["uid"];
 
                $res = trim($str); if (!$res) return '';
 
-               if (strpos($res, "href=") === false)
-                       $res = rewrite_urls($res);
-
                $charset_hack = '<head>
                        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
                </head>';
                $disallowed_attributes = array('id', 'style', 'class');
 
                foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SANITIZE) as $plugin) {
-                       $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes);
+                       $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes, $article_id);
                        if (is_array($retval)) {
                                $doc = $retval[0];
                                $allowed_elements = $retval[1];
 
                $doc->removeChild($doc->firstChild); //remove doctype
                $doc = strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes);
+
+               if ($highlight_words) {
+                       foreach ($highlight_words as $word) {
+
+                               // http://stackoverflow.com/questions/4081372/highlight-keywords-in-a-paragraph
+
+                               $elements = $xpath->query("//*/text()");
+
+                               foreach ($elements as $child) {
+
+                                       $fragment = $doc->createDocumentFragment();
+                                       $text = $child->textContent;
+                                       $stubs = array();
+
+                                       while (($pos = mb_stripos($text, $word)) !== false) {
+                                               $fragment->appendChild(new DomText(mb_substr($text, 0, $pos)));
+                                               $word = mb_substr($text, $pos, mb_strlen($word));
+                                               $highlight = $doc->createElement('span');
+                                               $highlight->appendChild(new DomText($word));
+                                               $highlight->setAttribute('class', 'highlight');
+                                               $fragment->appendChild($highlight);
+                                               $text = mb_substr($text, $pos + mb_strlen($word));
+                                       }
+
+                                       if (!empty($text)) $fragment->appendChild(new DomText($text));
+
+                                       $child->parentNode->replaceChild($fragment, $child);
+                               }
+                       }
+               }
+
                $res = $doc->saveHTML();
+
                return $res;
        }
 
                if (preg_match("/^[0-9]*$/", $tag)) return false;
                if (mb_strlen($tag) > 250) return false;
 
-               if (function_exists('iconv')) {
-                       $tag = iconv("utf-8", "utf-8", $tag);
-               }
-
                if (!$tag) return false;
 
                return true;
        function format_warning($msg, $id = "") {
                global $link;
                return "<div class=\"warning\" id=\"$id\">
-                       <span><img src=\"images/sign_excl.svg\"></span><span>$msg</span></div>";
+                       <span><img src=\"images/alert.png\"></span><span>$msg</span></div>";
        }
 
        function format_notice($msg, $id = "") {
                global $link;
                return "<div class=\"notice\" id=\"$id\">
-                       <span><img src=\"images/sign_info.svg\"></span><span>$msg</span></div>";
+                       <span><img src=\"images/information.png\"></span><span>$msg</span></div>";
        }
 
        function format_error($msg, $id = "") {
                global $link;
                return "<div class=\"error\" id=\"$id\">
-                       <span><img src=\"images/sign_excl.svg\"></span><span>$msg</span></div>";
+                       <span><img src=\"images/alert.png\"></span><span>$msg</span></div>";
        }
 
        function print_notice($msg) {
                        if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
                                $_SESSION["hasMp3"])) {
 
-                               $entry .= "<audio controls>
+                               $entry .= "<audio preload=\"none\" controls>
                                        <source type=\"$ctype\" src=\"$url\"></source>
                                        </audio>";
 
                        ccache_update($feed_id, $owner_uid);
                }
 
-               $result = db_query("SELECT id,title,link,content,feed_id,comments,int_id,
+               $result = db_query("SELECT id,title,link,content,feed_id,comments,int_id,lang,
                        ".SUBSTRING_FOR_DATE."(updated,1,16) as updated,
                        (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,
                        tag_cache,
                        author,
                        orig_feed_id,
-                       note,
-                       cached_content
+                       note
                        FROM ttrss_entries,ttrss_user_entries
                        WHERE   id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
 
                        $line["tags"] = get_article_tags($id, $owner_uid, $line["tag_cache"]);
                        unset($line["tag_cache"]);
 
-                       $line["content"] = sanitize($line["content"], false, $owner_uid,        $line["site_url"]);
+                       $line["content"] = sanitize($line["content"],
+                               sql_bool_to_bool($line['hide_images']),
+                               $owner_uid, $line["site_url"], false, $line["id"]);
 
                        foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_RENDER_ARTICLE) as $p) {
                                $line = $p->hook_render_article($line);
                                $rv['content'] .= "<html><head>
                                                <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
                                                <title>Tiny Tiny RSS - ".$line["title"]."</title>
-                                               <link rel=\"stylesheet\" type=\"text/css\" href=\"tt-rss.css\">
+                                               <link rel=\"stylesheet\" type=\"text/css\" href=\"css/tt-rss.css\">
+                                               <script type=\"text/javascript\">
+                                               function openSelectedAttachment(elem) {
+                                                       try {
+                                                               var url = elem[elem.selectedIndex].value;
+
+                                                               if (url) {
+                                                                       window.open(url);
+                                                                       elem.selectedIndex = 0;
+                                                               }
+
+                                                       } catch (e) {
+                                                               exception_error(\"openSelectedAttachment\", e);
+                                                       }
+                                               }
+                                       </script>
                                        </head><body id=\"ttrssZoom\">";
                        }
 
                        $parsed_updated = make_local_datetime($line["updated"], true,
                                $owner_uid, true);
 
-                       $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
+                       if (!$zoom_mode)
+                               $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
 
                        if ($line["link"]) {
                                $rv['content'] .= "<div class='postTitle'><a target='_blank'
                                $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
                        }
 
+                       if ($zoom_mode)
+                               $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
+
                        $tags_str = format_tags_string($line["tags"], $id);
                        $tags_str_full = join(", ", $line["tags"]);
 
                                }
                        $rv['content'] .= "</div>";
 
-                       $rv['content'] .= "<div class=\"postContent\">";
+                       if (!$line['lang']) $line['lang'] = 'en';
+
+                       $rv['content'] .= "<div class=\"postContent\" lang=\"".$line['lang']."\">";
 
                        $rv['content'] .= $line["content"];
                        $rv['content'] .= format_article_enclosures($id,
 
                $cat_id = (int)getFeedCategory($feed_id);
 
+               if ($cat_id == 0)
+                       $null_cat_qpart = "cat_id IS NULL OR";
+               else
+                       $null_cat_qpart = "";
+
                $result = db_query("SELECT * FROM ttrss_filters2 WHERE
                        owner_uid = $owner_uid AND enabled = true ORDER BY order_id, title");
 
                                FROM ttrss_filters2_rules AS r,
                                ttrss_filter_types AS t
                                WHERE
-                                       (cat_id IS NULL OR cat_id IN ($check_cats)) AND
+                                       ($null_cat_qpart (cat_id IS NULL AND cat_filter = false) OR cat_id IN ($check_cats)) AND
                                        (feed_id IS NULL OR feed_id = '$feed_id') AND
                                        filter_type = t.id AND filter_id = '$filter_id'");
 
                        $maxtags = min(5, count($tags));
 
                        for ($i = 0; $i < $maxtags; $i++) {
-                               $tags_str .= "<a class=\"tag\" href=\"#\" onclick=\"viewfeed('".$tags[$i]."'\")>" . $tags[$i] . "</a>, ";
+                               $tags_str .= "<a class=\"tag\" href=\"#\" onclick=\"viewfeed('".$tags[$i]."')\">" . $tags[$i] . "</a>, ";
                        }
 
                        $tags_str = mb_substr($tags_str, 0, mb_strlen($tags_str)-2);
 
                                $url = $line["content_url"];
                                $ctype = $line["content_type"];
+                               $title = $line["title"];
 
                                if (!$ctype) $ctype = __("unknown type");
 
                                $entry["type"] = $ctype;
                                $entry["filename"] = $filename;
                                $entry["url"] = $url;
+                               $entry["title"] = $title;
 
                                array_push($entries, $entry);
                        }
                                                                        $rv .= "<p><a target=\"_blank\"
                                                                        href=\"".htmlspecialchars($entry["url"])."\"
                                                                        >" .htmlspecialchars($entry["url"]) . "</a></p>";
+                                                               }
 
+                                                               if ($entry['title']) {
+                                                                       $rv.= "<div class=\"enclosure_title\">${entry['title']}</div>";
                                                                }
                                                }
                                        }
                                "<option value=''>" . __('Attachments')."</option>";
 
                        foreach ($entries as $entry) {
-                               $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "</option>";
+                               if ($entry["title"])
+                                       $title = "&mdash; " . truncate_string($entry["title"], 30);
+                               else
+                                       $title = "";
+
+                               $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "$title</option>";
 
                        };
 
         * @return string Absolute URL
         */
        function rewrite_relative_url($url, $rel_url) {
-               if (strpos($rel_url, "magnet:") === 0) {
+               if (strpos($rel_url, ":") !== false) {
                        return $rel_url;
                } else if (strpos($rel_url, "://") !== false) {
                        return $rel_url;
 
                $sphinxpair = explode(":", SPHINX_SERVER, 2);
 
-               $sphinxClient->SetServer($sphinxpair[0], $sphinxpair[1]);
+               $sphinxClient->SetServer($sphinxpair[0], (int)$sphinxpair[1]);
                $sphinxClient->SetConnectTimeout(1);
 
                $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30,
 
        }
 
-       function rewrite_urls($html) {
-               libxml_use_internal_errors(true);
-
-               $charset_hack = '<head>
-                       <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
-               </head>';
-
-               $doc = new DOMDocument();
-               $doc->loadHTML($charset_hack . $html);
-               $xpath = new DOMXPath($doc);
-
-               $entries = $xpath->query('//*/text()');
-
-               foreach ($entries as $entry) {
-                       if (strstr($entry->wholeText, "://") !== false) {
-                               $text = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
-                                       "<a target=\"_blank\" href=\"\\1\">\\1</a>", $entry->wholeText);
-
-                               if ($text != $entry->wholeText) {
-                                       $cdoc = new DOMDocument();
-                                       $cdoc->loadHTML($charset_hack . $text);
-
-
-                                       foreach ($cdoc->childNodes as $cnode) {
-                                               $cnode = $doc->importNode($cnode, true);
-
-                                               if ($cnode) {
-                                                       $entry->parentNode->insertBefore($cnode);
-                                               }
-                                       }
-
-                                       $entry->parentNode->removeChild($entry);
-
-                               }
-                       }
-               }
-
-               $node = $doc->getElementsByTagName('body')->item(0);
-
-               // http://tt-rss.org/forum/viewtopic.php?f=1&t=970
-               if ($node)
-                       return $doc->saveXML($node);
-               else
-                       return $html;
-       }
-
        function filter_to_sql($filter, $owner_uid) {
                $query = array();
 
                        $reg_qpart = "REGEXP";
 
                foreach ($filter["rules"] AS $rule) {
+                       $rule['reg_exp'] = str_replace('/', '\/', $rule["reg_exp"]);
                        $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/',
                                $rule['reg_exp']) !== FALSE;
 
                                        $qpart .= " AND $cat_qpart";
                                }
 
+                               $qpart .= " AND feed_id IS NOT NULL";
+
                                array_push($query, "($qpart)");
 
                        }
                return in_array($interface, class_implements($class));
        }
 
-       function geturl($url){
+       function geturl($url, $depth = 0){
+
+               if ($depth == 20) return $url;
 
                if (!function_exists('curl_init'))
                        return user_error('CURL Must be installed for geturl function to work. Ask your host to enable it or uncomment extension=php_curl.dll in php.ini', E_USER_ERROR);
                curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
                //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
                curl_setopt($curl, CURLOPT_TIMEOUT, 60);
+               curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
+
+               if ((OPENSSL_VERSION_NUMBER >= 0x0090808f) && (OPENSSL_VERSION_NUMBER < 0x10000000)) {
+                       curl_setopt($curl, CURLOPT_SSLVERSION, 3);
+               }
 
                $html = curl_exec($curl);
 
                $status = curl_getinfo($curl);
-               curl_close($curl);
 
                if($status['http_code']!=200){
                        if($status['http_code'] == 301 || $status['http_code'] == 302) {
+                               curl_close($curl);
                                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):'';
+                               return (isset($url_parsed))? geturl($url, $depth + 1):'';
                        }
+
+                       global $fetch_last_error;
+
+                       $fetch_last_error = curl_errno($curl) . " " . curl_error($curl);
+                       curl_close($curl);
+
                        $oline='';
                        foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
                        $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
 #                      fwrite($handle, $line);
                        return FALSE;
                }
+               curl_close($curl);
                return $url;
        }
 
        }
 
        function calculate_dep_timestamp() {
-               $files = array_merge(glob("js/*.js"), glob("*.css"));
+               $files = array_merge(glob("js/*.js"), glob("css/*.css"));
 
                $max_ts = -1;
 
                }
 
                function ngettext(msg1, msg2, n) {
-                       return (parseInt(n) > 1) ? msg2 : msg1;
+                       return __((parseInt(n) > 1) ? msg2 : msg1);
                }';
 
                $l10n = _get_reader();
 
                for ($i = 0; $i < $l10n->total; $i++) {
                        $orig = $l10n->get_original_string($i);
-                       $translation = __($orig);
-
-                       print T_js_decl($orig, $translation);
+                       if(strpos($orig, "\000") !== FALSE) { // Plural forms
+                               $key = explode(chr(0), $orig);
+                               print T_js_decl($key[0], _ngettext($key[0], $key[1], 1)); // Singular
+                               print T_js_decl($key[1], _ngettext($key[0], $key[1], 2)); // Plural
+                       } else {
+                               $translation = __($orig);
+                               print T_js_decl($orig, $translation);
+                       }
                }
        }