diff options
Diffstat (limited to 'classes')
-rwxr-xr-x | classes/api.php | 8 | ||||
-rw-r--r-- | classes/backend.php | 2 | ||||
-rw-r--r-- | classes/digest.php | 2 | ||||
-rwxr-xr-x | classes/feeds.php | 19 | ||||
-rwxr-xr-x | classes/handler/public.php | 20 | ||||
-rwxr-xr-x | classes/logger/sql.php | 11 | ||||
-rw-r--r-- | classes/pluginhandler.php | 5 | ||||
-rwxr-xr-x | classes/pluginhost.php | 65 | ||||
-rwxr-xr-x | classes/pref/feeds.php | 2 | ||||
-rw-r--r-- | classes/pref/prefs.php | 61 | ||||
-rwxr-xr-x | classes/rpc.php | 2 | ||||
-rwxr-xr-x | classes/rssutils.php | 25 |
12 files changed, 182 insertions, 40 deletions
diff --git a/classes/api.php b/classes/api.php index 44c9841ce..01ea1970d 100755 --- a/classes/api.php +++ b/classes/api.php @@ -535,6 +535,7 @@ class API extends Handler { /* Labels */ + /* API only: -4 All feeds, including virtual feeds */ if ($cat_id == -4 || $cat_id == -2) { $counters = Counters::getLabelCounters(true); @@ -582,7 +583,7 @@ class API extends Handler { if ($include_nested && $cat_id) { $sth = $pdo->prepare("SELECT id, title, order_id FROM ttrss_feed_categories - WHERE parent_cat = ? AND owner_uid = ? ORDER BY id, title"); + WHERE parent_cat = ? AND owner_uid = ? ORDER BY order_id, title"); $sth->execute([$cat_id, $_SESSION['uid']]); @@ -611,12 +612,13 @@ class API extends Handler { $limit_qpart = ""; } + /* API only: -3 All feeds, excluding virtual feeds (e.g. Labels and such) */ if ($cat_id == -4 || $cat_id == -3) { $sth = $pdo->prepare("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 = ? - ORDER BY cat_id, title " . $limit_qpart); + ORDER BY order_id, title " . $limit_qpart); $sth->execute([$_SESSION['uid']]); } else { @@ -627,7 +629,7 @@ class API extends Handler { FROM ttrss_feeds WHERE (cat_id = :cat OR (:cat = 0 AND cat_id IS NULL)) AND owner_uid = :uid - ORDER BY cat_id, title " . $limit_qpart); + ORDER BY order_id, title " . $limit_qpart); $sth->execute([":uid" => $_SESSION['uid'], ":cat" => $cat_id]); } diff --git a/classes/backend.php b/classes/backend.php index 5bd724728..122e28c65 100644 --- a/classes/backend.php +++ b/classes/backend.php @@ -88,7 +88,7 @@ class Backend extends Handler { } function help() { - $topic = basename(clean($_REQUEST["topic"])); // only one for now + $topic = clean_filename($_REQUEST["topic"]); // only one for now if ($topic == "main") { $info = get_hotkeys_info(); diff --git a/classes/digest.php b/classes/digest.php index f2533d160..c9e9f24e7 100644 --- a/classes/digest.php +++ b/classes/digest.php @@ -103,9 +103,11 @@ class Digest $tpl->setVariable('CUR_DATE', date('Y/m/d', $local_ts)); $tpl->setVariable('CUR_TIME', date('G:i', $local_ts)); + $tpl->setVariable('TTRSS_HOST', SELF_URL_PATH); $tpl_t->setVariable('CUR_DATE', date('Y/m/d', $local_ts)); $tpl_t->setVariable('CUR_TIME', date('G:i', $local_ts)); + $tpl_t->setVariable('TTRSS_HOST', SELF_URL_PATH); $affected_ids = array(); diff --git a/classes/feeds.php b/classes/feeds.php index b89f4e4ca..bae571a3f 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -2,6 +2,8 @@ require_once "colors.php"; class Feeds extends Handler_Protected { + const NEVER_GROUP_FEEDS = [ -6, 0 ]; + const NEVER_GROUP_BY_DATE = [ -2, -1, -3 ]; private $params; @@ -199,7 +201,8 @@ class Feeds extends Handler_Protected { $qfh_ret = $this->queryFeedHeadlines($params); } - $vfeed_group_enabled = get_pref("VFEED_GROUP_BY_FEED") && $feed != -6; + $vfeed_group_enabled = get_pref("VFEED_GROUP_BY_FEED") && + !(in_array($feed, Feeds::NEVER_GROUP_FEEDS) && !$cat_view); $result = $qfh_ret[0]; // this could be either a PDO query result or a -1 if first id changed $feed_title = $qfh_ret[1]; @@ -1438,7 +1441,7 @@ class Feeds extends Handler_Protected { $start_ts = isset($params["start_ts"]) ? $params["start_ts"] : false; $check_first_id = isset($params["check_first_id"]) ? $params["check_first_id"] : false; $skip_first_id_check = isset($params["skip_first_id_check"]) ? $params["skip_first_id_check"] : false; - $order_by = isset($params["order_by"]) ? $params["order_by"] : false; + //$order_by = isset($params["order_by"]) ? $params["order_by"] : false; $ext_tables_part = ""; $limit_query_part = ""; @@ -1693,12 +1696,18 @@ class Feeds extends Handler_Protected { if (is_numeric($feed)) { // proper override_order applied above if ($vfeed_query_part && !$ignore_vfeed_group && get_pref('VFEED_GROUP_BY_FEED', $owner_uid)) { - $yyiw_desc = $order_by == "date_reverse" ? "" : "desc"; + + if (!(in_array($feed, Feeds::NEVER_GROUP_BY_DATE) && !$cat_view)) { + $yyiw_desc = $order_by == "date_reverse" ? "" : "desc"; + $yyiw_order_qpart = "yyiw $yyiw_desc, "; + } else { + $yyiw_order_qpart = ""; + } if (!$override_order) { - $order_by = "yyiw $yyiw_desc, ttrss_feeds.title, ".$order_by; + $order_by = "$yyiw_order_qpart ttrss_feeds.title, $order_by"; } else { - $order_by = "yyiw $yyiw_desc, ttrss_feeds.title, ".$override_order; + $order_by = "$yyiw_order_qpart ttrss_feeds.title, $override_order"; } } diff --git a/classes/handler/public.php b/classes/handler/public.php index 06c01df57..b81fb03b8 100755 --- a/classes/handler/public.php +++ b/classes/handler/public.php @@ -509,7 +509,7 @@ class Handler_Public extends Handler { <!DOCTYPE html> <html> <head> - <title><?php echo __("Share with Tiny Tiny RSS") ?> ?></title> + <title><?php echo __("Share with Tiny Tiny RSS") ?></title> <?php echo stylesheet_tag("css/default.css"); echo javascript_tag("lib/prototype.js"); @@ -996,6 +996,7 @@ class Handler_Public extends Handler { $tpl->setVariable('LOGIN', $login); $tpl->setVariable('RESETPASS_LINK', $resetpass_link); + $tpl->setVariable('TTRSS_HOST', SELF_URL_PATH); $tpl->addBlock('message'); @@ -1203,27 +1204,30 @@ class Handler_Public extends Handler { public function pluginhandler() { $host = new PluginHost(); - $plugin = basename(clean($_REQUEST["plugin"])); + $plugin_name = clean_filename($_REQUEST["plugin"]); $method = clean($_REQUEST["pmethod"]); - $host->load($plugin, PluginHost::KIND_USER, 0); + $host->load($plugin_name, PluginHost::KIND_USER, 0); $host->load_data(); - $pclass = $host->get_plugin($plugin); + $plugin = $host->get_plugin($plugin_name); - if ($pclass) { - if (method_exists($pclass, $method)) { - if ($pclass->is_public_method($method)) { - $pclass->$method(); + if ($plugin) { + if (method_exists($plugin, $method)) { + if ($plugin->is_public_method($method)) { + $plugin->$method(); } else { + user_error("PluginHandler[PUBLIC]: Requested private method '$method' of plugin '$plugin_name'.", E_USER_WARNING); header("Content-Type: text/json"); print error_json(6); } } else { + user_error("PluginHandler[PUBLIC]: Requested unknown method '$method' of plugin '$plugin_name'.", E_USER_WARNING); header("Content-Type: text/json"); print error_json(13); } } else { + user_error("PluginHandler[PUBLIC]: Requested method '$method' of unknown plugin '$plugin_name'.", E_USER_WARNING); header("Content-Type: text/json"); print error_json(14); } diff --git a/classes/logger/sql.php b/classes/logger/sql.php index 989539e5d..1b44b1e5f 100755 --- a/classes/logger/sql.php +++ b/classes/logger/sql.php @@ -15,6 +15,17 @@ class Logger_SQL { // limit context length, DOMDocument dumps entire XML in here sometimes, which may be huge $context = mb_substr($context, 0, 8192); + $server_params = [ + "IP" => "REMOTE_ADDR", + "Request URI" => "REQUEST_URI", + "User agent" => "HTTP_USER_AGENT", + ]; + + foreach ($server_params as $n => $p) { + if (isset($_SERVER[$p])) + $context .= "\n$n: " . $_SERVER[$p]; + } + // passed error message may contain invalid unicode characters, failing to insert an error here // would break the execution entirely by generating an actual fatal error instead of a E_WARNING etc $errstr = UConverter::transcode($errstr, 'UTF-8', 'UTF-8'); diff --git a/classes/pluginhandler.php b/classes/pluginhandler.php index d10343e09..9682e440f 100644 --- a/classes/pluginhandler.php +++ b/classes/pluginhandler.php @@ -5,15 +5,18 @@ class PluginHandler extends Handler_Protected { } function catchall($method) { - $plugin = PluginHost::getInstance()->get_plugin(clean($_REQUEST["plugin"])); + $plugin_name = clean($_REQUEST["plugin"]); + $plugin = PluginHost::getInstance()->get_plugin($plugin_name); if ($plugin) { if (method_exists($plugin, $method)) { $plugin->$method(); } else { + user_error("PluginHandler: Requested unknown method '$method' of plugin '$plugin_name'.", E_USER_WARNING); print error_json(13); } } else { + user_error("PluginHandler: Requested method '$method' of unknown plugin '$plugin_name'.", E_USER_WARNING); print error_json(14); } } diff --git a/classes/pluginhost.php b/classes/pluginhost.php index d09ecca17..ac782e699 100755 --- a/classes/pluginhost.php +++ b/classes/pluginhost.php @@ -60,6 +60,7 @@ class PluginHost { const HOOK_FILTER_TRIGGERED = 40; const HOOK_GET_FULL_TEXT = 41; const HOOK_ARTICLE_IMAGE = 42; + const HOOK_FEED_TREE = 43; const KIND_ALL = 1; const KIND_SYSTEM = 2; @@ -128,28 +129,44 @@ class PluginHost { } } - function add_hook($type, $sender) { + function add_hook($type, $sender, $priority = 50) { + $priority = (int) $priority; + if (!is_array($this->hooks[$type])) { - $this->hooks[$type] = array(); + $this->hooks[$type] = []; + } + + if (!is_array($this->hooks[$type][$priority])) { + $this->hooks[$type][$priority] = []; } - array_push($this->hooks[$type], $sender); + array_push($this->hooks[$type][$priority], $sender); + ksort($this->hooks[$type]); } function del_hook($type, $sender) { if (is_array($this->hooks[$type])) { - $key = array_Search($sender, $this->hooks[$type]); - if ($key !== FALSE) { - unset($this->hooks[$type][$key]); + foreach (array_keys($this->hooks[$type]) as $prio) { + $key = array_search($sender, $this->hooks[$type][$prio]); + + if ($key !== FALSE) { + unset($this->hooks[$type][$prio][$key]); + } } } } function get_hooks($type) { if (isset($this->hooks[$type])) { - return $this->hooks[$type]; + $tmp = []; + + foreach (array_keys($this->hooks[$type]) as $prio) { + $tmp = array_merge($tmp, $this->hooks[$type][$prio]); + } + + return $tmp; } else { - return array(); + return []; } } function load_all($kind, $owner_uid = false, $skip_init = false) { @@ -170,7 +187,7 @@ class PluginHost { foreach ($plugins as $class) { $class = trim($class); - $class_file = strtolower(basename($class)); + $class_file = strtolower(clean_filename($class)); if (!is_dir(__DIR__."/../plugins/$class_file") && !is_dir(__DIR__."/../plugins.local/$class_file")) continue; @@ -475,4 +492,34 @@ class PluginHost { function get_owner_uid() { return $this->owner_uid; } + + // handled by classes/pluginhandler.php, requires valid session + function get_method_url($sender, $method, $params) { + return get_self_url_prefix() . "/backend.php?" . + http_build_query( + array_merge( + [ + "op" => "pluginhandler", + "plugin" => strtolower(get_class($sender)), + "method" => $method + ], + $params)); + } + + // WARNING: endpoint in public.php, exposed to unauthenticated users + function get_public_method_url($sender, $method, $params) { + if ($sender->is_public_method($method)) { + return get_self_url_prefix() . "/public.php?" . + http_build_query( + array_merge( + [ + "op" => "pluginhandler", + "plugin" => strtolower(get_class($sender)), + "pmethod" => $method + ], + $params)); + } else { + user_error("get_public_method_url: requested method '$method' of '" . get_class($sender) . "' is private."); + } + } } diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php index c55affd77..f672a0375 100755 --- a/classes/pref/feeds.php +++ b/classes/pref/feeds.php @@ -312,7 +312,7 @@ class Pref_Feeds extends Handler_Protected { array_push($root['items'], $feed); } - $root['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', count($cat['items'])), count($cat['items'])); + $root['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', count($root['items'])), count($root['items'])); } $fl = array(); diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php index e7e7a365e..2862a84b2 100644 --- a/classes/pref/prefs.php +++ b/classes/pref/prefs.php @@ -122,6 +122,11 @@ class Pref_Prefs extends Handler_Protected { $new_pw = clean($_POST["new_password"]); $con_pw = clean($_POST["confirm_password"]); + if ($old_pw == $new_pw) { + print "ERROR: ".format_error("New password must be different from the old one."); + return; + } + if ($old_pw == "") { print "ERROR: ".format_error("Old password cannot be blank."); return; @@ -194,6 +199,37 @@ class Pref_Prefs extends Handler_Protected { $full_name = clean($_POST["full_name"]); $active_uid = $_SESSION["uid"]; + $sth = $this->pdo->prepare("SELECT email, login, full_name FROM ttrss_users WHERE id = ?"); + $sth->execute([$active_uid]); + + if ($row = $sth->fetch()) { + $old_email = $row["email"]; + + if ($old_email != $email) { + $mailer = new Mailer(); + + require_once "lib/MiniTemplator.class.php"; + + $tpl = new MiniTemplator; + + $tpl->readTemplateFromFile("templates/mail_change_template.txt"); + + $tpl->setVariable('LOGIN', $row["login"]); + $tpl->setVariable('NEWMAIL', $email); + $tpl->setVariable('TTRSS_HOST', SELF_URL_PATH); + + $tpl->addBlock('message'); + + $tpl->generateOutputToString($message); + + $mailer->mail(["to_name" => $row["login"], + "to_address" => $row["email"], + "subject" => "[tt-rss] Mail address change notification", + "message" => $message]); + + } + } + $sth = $this->pdo->prepare("UPDATE ttrss_users SET email = ?, full_name = ? WHERE id = ?"); $sth->execute([$email, $full_name, $active_uid]); @@ -972,6 +1008,31 @@ class Pref_Prefs extends Handler_Protected { if ($authenticator->check_password($_SESSION["uid"], $password)) { + $sth = $this->pdo->prepare("SELECT email, login FROM ttrss_users WHERE id = ?"); + $sth->execute([$_SESSION['uid']]); + + if ($row = $sth->fetch()) { + $mailer = new Mailer(); + + require_once "lib/MiniTemplator.class.php"; + + $tpl = new MiniTemplator; + + $tpl->readTemplateFromFile("templates/otp_disabled_template.txt"); + + $tpl->setVariable('LOGIN', $row["login"]); + $tpl->setVariable('TTRSS_HOST', SELF_URL_PATH); + + $tpl->addBlock('message'); + + $tpl->generateOutputToString($message); + + $mailer->mail(["to_name" => $row["login"], + "to_address" => $row["email"], + "subject" => "[tt-rss] OTP change notification", + "message" => $message]); + } + $sth = $this->pdo->prepare("UPDATE ttrss_users SET otp_enabled = false WHERE id = ?"); $sth->execute([$_SESSION['uid']]); diff --git a/classes/rpc.php b/classes/rpc.php index 8736cbb65..84c9cfe92 100755 --- a/classes/rpc.php +++ b/classes/rpc.php @@ -572,7 +572,7 @@ class RPC extends Handler_Protected { function log() { $msg = clean($_REQUEST['msg']); - $file = basename(clean($_REQUEST['file'])); + $file = clean_filename($_REQUEST['file']); $line = (int) clean($_REQUEST['line']); $context = clean($_REQUEST['context']); diff --git a/classes/rssutils.php b/classes/rssutils.php index fe4c0a8a3..aad45a1aa 100755 --- a/classes/rssutils.php +++ b/classes/rssutils.php @@ -559,18 +559,10 @@ class RSSUtils { Debug::log("guid $entry_guid / $entry_guid_hashed", Debug::$LOG_VERBOSE); - $entry_timestamp = strip_tags($item->get_date()); + $entry_timestamp = (int)$item->get_date(); Debug::log("orig date: " . $item->get_date(), Debug::$LOG_VERBOSE); - if ($entry_timestamp == -1 || !$entry_timestamp || $entry_timestamp > time()) { - $entry_timestamp = time(); - } - - $entry_timestamp_fmt = strftime("%Y/%m/%d %H:%M:%S", $entry_timestamp); - - Debug::log("date $entry_timestamp [$entry_timestamp_fmt]", Debug::$LOG_VERBOSE); - $entry_title = strip_tags($item->get_title()); $entry_link = rewrite_relative_url($site_url, clean($item->get_link())); @@ -599,7 +591,6 @@ class RSSUtils { $entry_guid = mb_substr($entry_guid, 0, 245); Debug::log("author $entry_author", Debug::$LOG_VERBOSE); - Debug::log("num_comments: $num_comments", Debug::$LOG_VERBOSE); Debug::log("looking for tags...", Debug::$LOG_VERBOSE); // parse <category> entries into tags @@ -656,7 +647,8 @@ class RSSUtils { "force_catchup" => false, // ugly hack for the time being "score_modifier" => 0, // no previous value, plugin should recalculate score modifier based on content if needed "language" => $entry_language, - "num_comments" => $num_comments, // read only + "timestamp" => $entry_timestamp, + "num_comments" => $num_comments, "feed" => array("id" => $feed, "fetch_url" => $fetch_url, "site_url" => $site_url, @@ -797,6 +789,17 @@ class RSSUtils { $article_labels = $article["labels"]; $entry_score_modifier = (int) $article["score_modifier"]; $entry_language = $article["language"]; + $entry_timestamp = $article["timestamp"]; + $num_comments = $article["num_comments"]; + + if ($entry_timestamp == -1 || !$entry_timestamp || $entry_timestamp > time()) { + $entry_timestamp = time(); + } + + $entry_timestamp_fmt = strftime("%Y/%m/%d %H:%M:%S", $entry_timestamp); + + Debug::log("date $entry_timestamp [$entry_timestamp_fmt]", Debug::$LOG_VERBOSE); + Debug::log("num_comments: $num_comments", Debug::$LOG_VERBOSE); if (Debug::get_loglevel() >= Debug::$LOG_EXTENDED) { Debug::log("article labels:", Debug::$LOG_VERBOSE); |