diff options
Diffstat (limited to 'classes')
31 files changed, 3201 insertions, 3348 deletions
diff --git a/classes/api.php b/classes/api.php index bb4d33247..4c321d77e 100644 --- a/classes/api.php +++ b/classes/api.php @@ -8,6 +8,10 @@ class API extends Handler { private $seq; + static function param_to_bool($p) { + return $p && ($p !== "f" && $p !== "false"); + } + function before($method) { if (parent::before($method)) { header("Content-Type: text/json"); @@ -22,7 +26,7 @@ class API extends Handler { return false; } - $this->seq = (int) $_REQUEST['seq']; + $this->seq = (int) clean($_REQUEST['seq']); return true; } @@ -49,16 +53,17 @@ class API extends Handler { @session_destroy(); @session_start(); - $login = $this->dbh->escape_string($_REQUEST["user"]); - $password = $_REQUEST["password"]; - $password_base64 = base64_decode($_REQUEST["password"]); + $login = clean($_REQUEST["user"]); + $password = clean($_REQUEST["password"]); + $password_base64 = base64_decode(clean($_REQUEST["password"])); if (SINGLE_USER_MODE) $login = "admin"; - $result = $this->dbh->query("SELECT id FROM ttrss_users WHERE login = '$login'"); + $sth = $this->pdo->prepare("SELECT id FROM ttrss_users WHERE login = ?"); + $sth->execute([$login]); - if ($this->dbh->num_rows($result) != 0) { - $uid = $this->dbh->fetch_result($result, 0, "id"); + if ($row = $sth->fetch()) { + $uid = $row["id"]; } else { $uid = 0; } @@ -95,8 +100,8 @@ class API extends Handler { } function getUnread() { - $feed_id = $this->dbh->escape_string($_REQUEST["feed_id"]); - $is_cat = $this->dbh->escape_string($_REQUEST["is_cat"]); + $feed_id = clean($_REQUEST["feed_id"]); + $is_cat = clean($_REQUEST["is_cat"]); if ($feed_id) { $this->wrap(self::STATUS_OK, array("unread" => getFeedUnread($feed_id, $is_cat))); @@ -111,11 +116,11 @@ class API extends Handler { } function getFeeds() { - $cat_id = $this->dbh->escape_string($_REQUEST["cat_id"]); - $unread_only = sql_bool_to_bool($_REQUEST["unread_only"]); - $limit = (int) $this->dbh->escape_string($_REQUEST["limit"]); - $offset = (int) $this->dbh->escape_string($_REQUEST["offset"]); - $include_nested = sql_bool_to_bool($_REQUEST["include_nested"]); + $cat_id = clean($_REQUEST["cat_id"]); + $unread_only = API::param_to_bool(clean($_REQUEST["unread_only"])); + $limit = (int) clean($_REQUEST["limit"]); + $offset = (int) clean($_REQUEST["offset"]); + $include_nested = API::param_to_bool(clean($_REQUEST["include_nested"])); $feeds = $this->api_get_feeds($cat_id, $unread_only, $limit, $offset, $include_nested); @@ -123,9 +128,9 @@ class API extends Handler { } function getCategories() { - $unread_only = sql_bool_to_bool($_REQUEST["unread_only"]); - $enable_nested = sql_bool_to_bool($_REQUEST["enable_nested"]); - $include_empty = sql_bool_to_bool($_REQUEST['include_empty']); + $unread_only = API::param_to_bool(clean($_REQUEST["unread_only"])); + $enable_nested = API::param_to_bool(clean($_REQUEST["enable_nested"])); + $include_empty = API::param_to_bool(clean($_REQUEST['include_empty'])); // TODO do not return empty categories, return Uncategorized and standard virtual cats @@ -134,7 +139,7 @@ class API extends Handler { else $nested_qpart = "true"; - $result = $this->dbh->query("SELECT + $sth = $this->pdo->prepare("SELECT id, title, order_id, (SELECT COUNT(id) FROM ttrss_feeds WHERE ttrss_feed_categories.id IS NOT NULL AND cat_id = ttrss_feed_categories.id) AS num_feeds, @@ -142,12 +147,12 @@ class API extends Handler { ttrss_feed_categories AS c2 WHERE c2.parent_cat = ttrss_feed_categories.id) AS num_cats FROM ttrss_feed_categories - WHERE $nested_qpart AND owner_uid = " . - $_SESSION["uid"]); + WHERE $nested_qpart AND owner_uid = ?"); + $sth->execute([$_SESSION['uid']]); $cats = array(); - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $sth->fetch()) { if ($include_empty || $line["num_feeds"] > 0 || $line["num_cats"] > 0) { $unread = getFeedUnread($line["id"], true); @@ -180,39 +185,39 @@ class API extends Handler { } function getHeadlines() { - $feed_id = $this->dbh->escape_string($_REQUEST["feed_id"]); + $feed_id = clean($_REQUEST["feed_id"]); if ($feed_id != "") { if (is_numeric($feed_id)) $feed_id = (int) $feed_id; - $limit = (int)$this->dbh->escape_string($_REQUEST["limit"]); + $limit = (int)clean($_REQUEST["limit"]); if (!$limit || $limit >= 200) $limit = 200; - $offset = (int)$this->dbh->escape_string($_REQUEST["skip"]); - $filter = $this->dbh->escape_string($_REQUEST["filter"]); - $is_cat = sql_bool_to_bool($_REQUEST["is_cat"]); - $show_excerpt = sql_bool_to_bool($_REQUEST["show_excerpt"]); - $show_content = sql_bool_to_bool($_REQUEST["show_content"]); + $offset = (int)clean($_REQUEST["skip"]); + $filter = clean($_REQUEST["filter"]); + $is_cat = API::param_to_bool(clean($_REQUEST["is_cat"])); + $show_excerpt = API::param_to_bool(clean($_REQUEST["show_excerpt"])); + $show_content = API::param_to_bool(clean($_REQUEST["show_content"])); /* all_articles, unread, adaptive, marked, updated */ - $view_mode = $this->dbh->escape_string($_REQUEST["view_mode"]); - $include_attachments = sql_bool_to_bool($_REQUEST["include_attachments"]); - $since_id = (int)$this->dbh->escape_string($_REQUEST["since_id"]); - $include_nested = sql_bool_to_bool($_REQUEST["include_nested"]); + $view_mode = clean($_REQUEST["view_mode"]); + $include_attachments = API::param_to_bool(clean($_REQUEST["include_attachments"])); + $since_id = (int)clean($_REQUEST["since_id"]); + $include_nested = API::param_to_bool(clean($_REQUEST["include_nested"])); $sanitize_content = !isset($_REQUEST["sanitize"]) || - sql_bool_to_bool($_REQUEST["sanitize"]); - $force_update = sql_bool_to_bool($_REQUEST["force_update"]); - $has_sandbox = sql_bool_to_bool($_REQUEST["has_sandbox"]); - $excerpt_length = (int)$this->dbh->escape_string($_REQUEST["excerpt_length"]); - $check_first_id = (int)$this->dbh->escape_string($_REQUEST["check_first_id"]); - $include_header = sql_bool_to_bool($_REQUEST["include_header"]); + API::param_to_bool($_REQUEST["sanitize"]); + $force_update = API::param_to_bool(clean($_REQUEST["force_update"])); + $has_sandbox = API::param_to_bool(clean($_REQUEST["has_sandbox"])); + $excerpt_length = (int)clean($_REQUEST["excerpt_length"]); + $check_first_id = (int)clean($_REQUEST["check_first_id"]); + $include_header = API::param_to_bool(clean($_REQUEST["include_header"])); $_SESSION['hasSandbox'] = $has_sandbox; $skip_first_id_check = false; $override_order = false; - switch ($_REQUEST["order_by"]) { + switch (clean($_REQUEST["order_by"])) { case "title": $override_order = "ttrss_entries.title, date_entered, updated"; break; @@ -227,7 +232,7 @@ class API extends Handler { /* do not rely on params below */ - $search = $this->dbh->escape_string($_REQUEST["search"]); + $search = clean($_REQUEST["search"]); list($headlines, $headlines_header) = $this->api_get_headlines($feed_id, $limit, $offset, $filter, $is_cat, $show_excerpt, $show_content, $view_mode, $override_order, @@ -245,10 +250,10 @@ class API extends Handler { } function updateArticle() { - $article_ids = array_filter(explode(",", $this->dbh->escape_string($_REQUEST["article_ids"])), is_numeric); - $mode = (int) $this->dbh->escape_string($_REQUEST["mode"]); - $data = $this->dbh->escape_string($_REQUEST["data"]); - $field_raw = (int)$this->dbh->escape_string($_REQUEST["field"]); + $article_ids = explode(",", clean($_REQUEST["article_ids"])); + $mode = (int) clean($_REQUEST["mode"]); + $data = clean($_REQUEST["data"]); + $field_raw = (int)clean($_REQUEST["field"]); $field = ""; $set_to = ""; @@ -282,21 +287,25 @@ class API extends Handler { break; } - if ($field == "note") $set_to = "'$data'"; + if ($field == "note") $set_to = $this->pdo->quote($data); if ($field && $set_to && count($article_ids) > 0) { - $article_ids = join(", ", $article_ids); + $article_qmarks = arr_qmarks($article_ids); - $result = $this->dbh->query("UPDATE ttrss_user_entries SET $field = $set_to $additional_fields WHERE ref_id IN ($article_ids) AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET + $field = $set_to $additional_fields + WHERE ref_id IN ($article_qmarks) AND owner_uid = ?"); + $sth->execute(array_merge($article_ids, [$_SESSION['uid']])); - $num_updated = $this->dbh->affected_rows($result); + $num_updated = $sth->rowCount(); if ($num_updated > 0 && $field == "unread") { - $result = $this->dbh->query("SELECT DISTINCT feed_id FROM ttrss_user_entries - WHERE ref_id IN ($article_ids)"); + $sth = $this->pdo->prepare("SELECT DISTINCT feed_id FROM ttrss_user_entries + WHERE ref_id IN ($article_qmarks)"); + $sth->execute($article_ids); - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $sth->fetch()) { CCache::update($line["feed_id"], $_SESSION["uid"]); } } @@ -312,69 +321,66 @@ class API extends Handler { function getArticle() { - $article_id = join(",", array_filter(explode(",", $this->dbh->escape_string($_REQUEST["article_id"])), is_numeric)); + $article_ids = explode(",", clean($_REQUEST["article_id"])); $sanitize_content = !isset($_REQUEST["sanitize"]) || - sql_bool_to_bool($_REQUEST["sanitize"]); + API::param_to_bool($_REQUEST["sanitize"]); + + if ($article_ids) { - if ($article_id) { + $article_qmarks = arr_qmarks($article_ids); - $query = "SELECT id,guid,title,link,content,feed_id,comments,int_id, + $sth = $this->pdo->prepare("SELECT id,guid,title,link,content,feed_id,comments,int_id, marked,unread,published,score,note,lang, ".SUBSTRING_FOR_DATE."(updated,1,16) as updated, author,(SELECT title FROM ttrss_feeds WHERE id = feed_id) AS feed_title, (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 FROM ttrss_entries,ttrss_user_entries - WHERE id IN ($article_id) AND ref_id = id AND owner_uid = " . - $_SESSION["uid"] ; + WHERE id IN ($article_qmarks) AND ref_id = id AND owner_uid = ?"); - $result = $this->dbh->query($query); + $sth->execute(array_merge($article_ids, [$_SESSION['uid']])); $articles = array(); - if ($this->dbh->num_rows($result) != 0) { - - while ($line = $this->dbh->fetch_assoc($result)) { - - $attachments = Article::get_article_enclosures($line['id']); - - $article = array( - "id" => $line["id"], - "guid" => $line["guid"], - "title" => $line["title"], - "link" => $line["link"], - "labels" => Article::get_article_labels($line['id']), - "unread" => sql_bool_to_bool($line["unread"]), - "marked" => sql_bool_to_bool($line["marked"]), - "published" => sql_bool_to_bool($line["published"]), - "comments" => $line["comments"], - "author" => $line["author"], - "updated" => (int) strtotime($line["updated"]), - "feed_id" => $line["feed_id"], - "attachments" => $attachments, - "score" => (int)$line["score"], - "feed_title" => $line["feed_title"], - "note" => $line["note"], - "lang" => $line["lang"] - ); - - if ($sanitize_content) { - $article["content"] = sanitize( - $line["content"], - sql_bool_to_bool($line['hide_images']), - false, $line["site_url"], false, $line["id"]); - } else { - $article["content"] = $line["content"]; - } - - foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_RENDER_ARTICLE_API) as $p) { - $article = $p->hook_render_article_api(array("article" => $article)); - } + while ($line = $sth->fetch()) { + + $attachments = Article::get_article_enclosures($line['id']); + + $article = array( + "id" => $line["id"], + "guid" => $line["guid"], + "title" => $line["title"], + "link" => $line["link"], + "labels" => Article::get_article_labels($line['id']), + "unread" => API::param_to_bool($line["unread"]), + "marked" => API::param_to_bool($line["marked"]), + "published" => API::param_to_bool($line["published"]), + "comments" => $line["comments"], + "author" => $line["author"], + "updated" => (int) strtotime($line["updated"]), + "feed_id" => $line["feed_id"], + "attachments" => $attachments, + "score" => (int)$line["score"], + "feed_title" => $line["feed_title"], + "note" => $line["note"], + "lang" => $line["lang"] + ); + + if ($sanitize_content) { + $article["content"] = sanitize( + $line["content"], + API::param_to_bool($line['hide_images']), + false, $line["site_url"], false, $line["id"]); + } else { + $article["content"] = $line["content"]; + } + foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_RENDER_ARTICLE_API) as $p) { + $article = $p->hook_render_article_api(array("article" => $article)); + } - array_push($articles, $article); + array_push($articles, $article); - } } $this->wrap(self::STATUS_OK, $articles); @@ -390,18 +396,18 @@ class API extends Handler { $config["daemon_is_running"] = file_is_locked("update_daemon.lock"); - $result = $this->dbh->query("SELECT COUNT(*) AS cf FROM - ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]); - - $num_feeds = $this->dbh->fetch_result($result, 0, "cf"); + $sth = $this->pdo->prepare("SELECT COUNT(*) AS cf FROM + ttrss_feeds WHERE owner_uid = ?"); + $sth->execute([$_SESSION['uid']]); + $row = $sth->fetch(); - $config["num_feeds"] = (int)$num_feeds; + $config["num_feeds"] = $row["cf"]; $this->wrap(self::STATUS_OK, $config); } function updateFeed() { - $feed_id = (int) $this->dbh->escape_string($_REQUEST["feed_id"]); + $feed_id = (int) clean($_REQUEST["feed_id"]); if (!ini_get("open_basedir")) { RSSUtils::update_rss_feed($feed_id); @@ -411,8 +417,8 @@ class API extends Handler { } function catchupFeed() { - $feed_id = $this->dbh->escape_string($_REQUEST["feed_id"]); - $is_cat = $this->dbh->escape_string($_REQUEST["is_cat"]); + $feed_id = clean($_REQUEST["feed_id"]); + $is_cat = clean($_REQUEST["is_cat"]); Feeds::catchup_feed($feed_id, $is_cat); @@ -420,28 +426,27 @@ class API extends Handler { } function getPref() { - $pref_name = $this->dbh->escape_string($_REQUEST["pref_name"]); + $pref_name = clean($_REQUEST["pref_name"]); $this->wrap(self::STATUS_OK, array("value" => get_pref($pref_name))); } function getLabels() { - //$article_ids = array_filter(explode(",", $this->dbh->escape_string($_REQUEST["article_ids"])), is_numeric); - - $article_id = (int)$_REQUEST['article_id']; + $article_id = (int)clean($_REQUEST['article_id']); $rv = array(); - $result = $this->dbh->query("SELECT id, caption, fg_color, bg_color + $sth = $this->pdo->prepare("SELECT id, caption, fg_color, bg_color FROM ttrss_labels2 - WHERE owner_uid = '".$_SESSION['uid']."' ORDER BY caption"); + WHERE owner_uid = ? ORDER BY caption"); + $sth->execute([$_SESSION['uid']]); if ($article_id) $article_labels = Article::get_article_labels($article_id); else $article_labels = array(); - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $sth->fetch()) { $checked = false; foreach ($article_labels as $al) { @@ -464,12 +469,11 @@ class API extends Handler { function setArticleLabel() { - $article_ids = array_filter(explode(",", $this->dbh->escape_string($_REQUEST["article_ids"])), is_numeric); - $label_id = (int) $this->dbh->escape_string($_REQUEST['label_id']); - $assign = sql_bool_to_bool($_REQUEST['assign']); + $article_ids = explode(",", clean($_REQUEST["article_ids"])); + $label_id = (int) clean($_REQUEST['label_id']); + $assign = API::param_to_bool(clean($_REQUEST['assign'])); - $label = $this->dbh->escape_string(Labels::find_caption( - Labels::feed_to_label_id($label_id), $_SESSION["uid"])); + $label = Labels::find_caption(Labels::feed_to_label_id($label_id), $_SESSION["uid"]); $num_updated = 0; @@ -506,9 +510,9 @@ class API extends Handler { } function shareToPublished() { - $title = $this->dbh->escape_string(strip_tags($_REQUEST["title"])); - $url = $this->dbh->escape_string(strip_tags($_REQUEST["url"])); - $content = $this->dbh->escape_string(strip_tags($_REQUEST["content"])); + $title = strip_tags(clean($_REQUEST["title"])); + $url = strip_tags(clean($_REQUEST["url"])); + $content = strip_tags(clean($_REQUEST["content"])); if (Article::create_published_article($title, $url, $content, "", $_SESSION["uid"])) { $this->wrap(self::STATUS_OK, array("status" => 'OK')); @@ -521,6 +525,12 @@ class API extends Handler { $feeds = array(); + $pdo = Db::pdo(); + + $limit = (int) $limit; + $offset = (int) $offset; + $cat_id = (int) $cat_id; + /* Labels */ if ($cat_id == -4 || $cat_id == -2) { @@ -568,12 +578,13 @@ class API extends Handler { /* Child cats */ if ($include_nested && $cat_id) { - $result = db_query("SELECT + $sth = $pdo->prepare("SELECT id, title, order_id FROM ttrss_feed_categories - WHERE parent_cat = '$cat_id' AND owner_uid = " . $_SESSION["uid"] . - " ORDER BY id, title"); + WHERE parent_cat = ? AND owner_uid = ? ORDER BY id, title"); - while ($line = db_fetch_assoc($result)) { + $sth->execute([$cat_id, $_SESSION['uid']]); + + while ($line = $sth->fetch()) { $unread = getFeedUnread($line["id"], true) + Feeds::getCategoryChildrenUnread($line["id"]); @@ -599,31 +610,30 @@ class API extends Handler { } if ($cat_id == -4 || $cat_id == -3) { - $result = db_query("SELECT + $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 = " . $_SESSION["uid"] . - " ORDER BY cat_id, title " . $limit_qpart); - } else { + FROM ttrss_feeds WHERE owner_uid = ? + ORDER BY cat_id, title " . $limit_qpart); + $sth->execute([$_SESSION['uid']]); - if ($cat_id) - $cat_qpart = "cat_id = '$cat_id'"; - else - $cat_qpart = "cat_id IS NULL"; + } else { - $result = db_query("SELECT + $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 - $cat_qpart AND owner_uid = " . $_SESSION["uid"] . - " ORDER BY cat_id, title " . $limit_qpart); + (cat_id = :cat OR (:cat = 0 AND cat_id IS NULL)) + AND owner_uid = :uid + ORDER BY cat_id, title " . $limit_qpart); + $sth->execute([":uid" => $_SESSION['uid'], ":cat" => $cat_id]); } - while ($line = db_fetch_assoc($result)) { + while ($line = $sth->fetch()) { $unread = getFeedUnread($line["id"]); - $has_icon = feed_has_icon($line['id']); + $has_icon = Feeds::feedHasIcon($line['id']); if ($unread || !$unread_only) { @@ -654,22 +664,26 @@ class API extends Handler { $search = "", $include_nested = false, $sanitize_content = true, $force_update = false, $excerpt_length = 100, $check_first_id = false, $skip_first_id_check = false) { + $pdo = Db::pdo(); + if ($force_update && $feed_id > 0 && is_numeric($feed_id)) { // Update the feed if required with some basic flood control - $result = db_query( + $sth = $pdo->prepare( "SELECT cache_images,".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated - FROM ttrss_feeds WHERE id = '$feed_id'"); + FROM ttrss_feeds WHERE id = ?"); + $sth->execute([$feed_id]); - if (db_num_rows($result) != 0) { - $last_updated = strtotime(db_fetch_result($result, 0, "last_updated")); - $cache_images = sql_bool_to_bool(db_fetch_result($result, 0, "cache_images")); + if ($row = $sth->fetch()) { + $last_updated = strtotime($row["last_updated"]); + $cache_images = API::param_to_bool($row["cache_images"]); if (!$cache_images && time() - $last_updated > 120) { RSSUtils::update_rss_feed($feed_id, true); } else { - db_query("UPDATE ttrss_feeds SET last_updated = '1970-01-01', last_update_started = '1970-01-01' - WHERE id = '$feed_id'"); + $sth = $pdo->prepare("UPDATE ttrss_feeds SET last_updated = '1970-01-01', last_update_started = '1970-01-01' + WHERE id = ?"); + $sth->execute([$feed_id]); } } } @@ -702,7 +716,7 @@ class API extends Handler { 'is_cat' => $is_cat); if (!is_numeric($result)) { - while ($line = db_fetch_assoc($result)) { + while ($line = $result->fetch()) { $line["content_preview"] = truncate_string(strip_tags($line["content"]), $excerpt_length); foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_QUERY_HEADLINES) as $p) { $line = $p->hook_query_headlines($line, $excerpt_length, true); @@ -732,9 +746,9 @@ class API extends Handler { $headline_row = array( "id" => (int)$line["id"], "guid" => $line["guid"], - "unread" => sql_bool_to_bool($line["unread"]), - "marked" => sql_bool_to_bool($line["marked"]), - "published" => sql_bool_to_bool($line["published"]), + "unread" => API::param_to_bool($line["unread"]), + "marked" => API::param_to_bool($line["marked"]), + "published" => API::param_to_bool($line["published"]), "updated" => (int)strtotime($line["updated"]), "is_updated" => $is_updated, "title" => $line["title"], @@ -755,7 +769,7 @@ class API extends Handler { if ($sanitize_content) { $headline_row["content"] = sanitize( $line["content"], - sql_bool_to_bool($line['hide_images']), + API::param_to_bool($line['hide_images']), false, $line["site_url"], false, $line["id"]); } else { $headline_row["content"] = $line["content"]; @@ -773,7 +787,7 @@ class API extends Handler { $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"]); + $headline_row["always_display_attachments"] = API::param_to_bool($line["always_display_enclosures"]); $headline_row["author"] = $line["author"]; @@ -795,12 +809,13 @@ class API extends Handler { } function unsubscribeFeed() { - $feed_id = (int) $this->dbh->escape_string($_REQUEST["feed_id"]); + $feed_id = (int) clean($_REQUEST["feed_id"]); - $result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE - id = '$feed_id' AND owner_uid = ".$_SESSION["uid"]); + $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE + id = ? AND owner_uid = ?"); + $sth->execute([$feed_id, $_SESSION['uid']]); - if ($this->dbh->num_rows($result) != 0) { + if ($row = $sth->fetch()) { Pref_Feeds::remove_feed($feed_id, $_SESSION["uid"]); $this->wrap(self::STATUS_OK, array("status" => "OK")); } else { @@ -809,10 +824,10 @@ class API extends Handler { } function subscribeToFeed() { - $feed_url = $this->dbh->escape_string($_REQUEST["feed_url"]); - $category_id = (int) $this->dbh->escape_string($_REQUEST["category_id"]); - $login = $this->dbh->escape_string($_REQUEST["login"]); - $password = $this->dbh->escape_string($_REQUEST["password"]); + $feed_url = clean($_REQUEST["feed_url"]); + $category_id = (int) clean($_REQUEST["category_id"]); + $login = clean($_REQUEST["login"]); + $password = clean($_REQUEST["password"]); if ($feed_url) { $rc = Feeds::subscribe_to_feed($feed_url, $category_id, $login, $password); @@ -824,7 +839,7 @@ class API extends Handler { } function getFeedTree() { - $include_empty = sql_bool_to_bool($_REQUEST['include_empty']); + $include_empty = API::param_to_bool(clean($_REQUEST['include_empty'])); $pf = new Pref_Feeds($_REQUEST); @@ -845,16 +860,20 @@ class API extends Handler { private function isCategoryEmpty($id) { if ($id == -2) { - $result = $this->dbh->query("SELECT COUNT(*) AS count FROM ttrss_labels2 - WHERE owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("SELECT COUNT(id) AS count FROM ttrss_labels2 + WHERE owner_uid = ?"); + $sth->execute([$_SESSION['uid']]); + $row = $sth->fetch(); - return $this->dbh->fetch_result($result, 0, "count") == 0; + return $row["count"] == 0; } else if ($id == 0) { - $result = $this->dbh->query("SELECT COUNT(*) AS count FROM ttrss_feeds - WHERE cat_id IS NULL AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("SELECT COUNT(id) AS count FROM ttrss_feeds + WHERE cat_id IS NULL AND owner_uid = ?"); + $sth->execute([$_SESSION['uid']]); + $row = $sth->fetch(); - return $this->dbh->fetch_result($result, 0, "count") == 0; + return $row["count"] == 0; } diff --git a/classes/article.php b/classes/article.php index 1d5f06e70..edf95b743 100644 --- a/classes/article.php +++ b/classes/article.php @@ -8,14 +8,15 @@ class Article extends Handler_Protected { } function redirect() { - $id = $this->dbh->escape_string($_REQUEST['id']); + $id = clean($_REQUEST['id']); - $result = $this->dbh->query("SELECT link FROM ttrss_entries, ttrss_user_entries - WHERE id = '$id' AND id = ref_id AND owner_uid = '".$_SESSION['uid']."' + $sth = $this->pdo->prepare("SELECT link FROM ttrss_entries, ttrss_user_entries + WHERE id = ? AND id = ref_id AND owner_uid = ? LIMIT 1"); + $sth->execute([$id, $_SESSION['uid']]); - if ($this->dbh->num_rows($result) == 1) { - $article_url = $this->dbh->fetch_result($result, 0, 'link'); + if ($row = $sth->fetch()) { + $article_url = $row['link']; $article_url = str_replace("\n", "", $article_url); header("Location: $article_url"); @@ -27,9 +28,9 @@ class Article extends Handler_Protected { } function view() { - $id = $this->dbh->escape_string($_REQUEST["id"]); - $cids = explode(",", $this->dbh->escape_string($_REQUEST["cids"])); - $mode = $this->dbh->escape_string($_REQUEST["mode"]); + $id = clean($_REQUEST["id"]); + $cids = explode(",", clean($_REQUEST["cids"])); + $mode = clean($_REQUEST["mode"]); // in prefetch mode we only output requested cids, main article // just gets marked as read (it already exists in client cache) @@ -67,19 +68,21 @@ class Article extends Handler_Protected { private function catchupArticleById($id, $cmode) { if ($cmode == 0) { - $this->dbh->query("UPDATE ttrss_user_entries SET + $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET unread = false,last_read = NOW() - WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); + WHERE ref_id = ? AND owner_uid = ?"); } else if ($cmode == 1) { - $this->dbh->query("UPDATE ttrss_user_entries SET + $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET unread = true - WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); + WHERE ref_id = ? AND owner_uid = ?"); } else { - $this->dbh->query("UPDATE ttrss_user_entries SET + $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET unread = NOT unread,last_read = NOW() - WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); + WHERE ref_id = ? AND owner_uid = ?"); } + $sth->execute([$id, $_SESSION['uid']]); + $feed_id = $this->getArticleFeed($id); CCache::update($feed_id, $_SESSION["uid"]); } @@ -102,7 +105,7 @@ class Article extends Handler_Protected { if ($enable_share_anything) { $extracted_content = $af_readability->extract_content($url); - if ($extracted_content) $content = db_escape_string($extracted_content); + if ($extracted_content) $content = $extracted_content; } } } @@ -122,34 +125,42 @@ class Article extends Handler_Protected { if (filter_var($url, FILTER_VALIDATE_URL) === FALSE) return false; - db_query("BEGIN"); + $pdo = Db::pdo(); + + $pdo->beginTransaction(); // only check for our user data here, others might have shared this with different content etc - $result = db_query("SELECT id FROM ttrss_entries, ttrss_user_entries WHERE - guid = '$guid' AND ref_id = id AND owner_uid = '$owner_uid' LIMIT 1"); + $sth = $pdo->prepare("SELECT id FROM ttrss_entries, ttrss_user_entries WHERE + guid = ? AND ref_id = id AND owner_uid = ? LIMIT 1"); + $sth->execute([$guid, $owner_uid]); - if (db_num_rows($result) != 0) { - $ref_id = db_fetch_result($result, 0, "id"); + if ($row = $sth->fetch()) { + $ref_id = $row['id']; - $result = db_query("SELECT int_id FROM ttrss_user_entries WHERE - ref_id = '$ref_id' AND owner_uid = '$owner_uid' LIMIT 1"); + $sth = $pdo->prepare("SELECT int_id FROM ttrss_user_entries WHERE + ref_id = ? AND owner_uid = ? LIMIT 1"); + $sth->execute([$ref_id, $owner_uid]); - if (db_num_rows($result) != 0) { - $int_id = db_fetch_result($result, 0, "int_id"); + if ($row = $sth->fetch()) { + $int_id = $row['int_id']; - db_query("UPDATE ttrss_entries SET - content = '$content', content_hash = '$content_hash' WHERE id = '$ref_id'"); + $sth = $pdo->prepare("UPDATE ttrss_entries SET + content = ?, content_hash = ? WHERE id = ?"); + $sth->execute([$content, $content_hash, $ref_id]); - db_query("UPDATE ttrss_user_entries SET published = true, + $sth = $pdo->prepare("UPDATE ttrss_user_entries SET published = true, last_published = NOW() WHERE - int_id = '$int_id' AND owner_uid = '$owner_uid'"); + int_id = ? AND owner_uid = ?"); + $sth->execute([$int_id, $owner_uid]); + } else { - db_query("INSERT INTO ttrss_user_entries + $sth = $pdo->prepare("INSERT INTO ttrss_user_entries (ref_id, uuid, feed_id, orig_feed_id, owner_uid, published, tag_cache, label_cache, last_read, note, unread, last_published) VALUES - ('$ref_id', '', NULL, NULL, $owner_uid, true, '', '', NOW(), '', false, NOW())"); + (?, '', NULL, NULL, ?, true, '', '', NOW(), '', false, NOW())"); + $sth->execute([$ref_id, $owner_uid]); } if (count($labels) != 0) { @@ -161,21 +172,24 @@ class Article extends Handler_Protected { $rc = true; } else { - $result = db_query("INSERT INTO ttrss_entries + $sth = $pdo->prepare("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())"); + (?, ?, ?, NOW(), ?, ?, NOW(), NOW())"); + $sth->execute([$title, $guid, $url, $content, $content_hash]); - $result = db_query("SELECT id FROM ttrss_entries WHERE guid = '$guid'"); + $sth = $pdo->prepare("SELECT id FROM ttrss_entries WHERE guid = ?"); + $sth->execute([$guid]); - if (db_num_rows($result) != 0) { - $ref_id = db_fetch_result($result, 0, "id"); + if ($row = $sth->fetch()) { + $ref_id = $row["id"]; - db_query("INSERT INTO ttrss_user_entries + $sth = $pdo->prepare("INSERT INTO ttrss_user_entries (ref_id, uuid, feed_id, orig_feed_id, owner_uid, published, tag_cache, label_cache, last_read, note, unread, last_published) VALUES - ('$ref_id', '', NULL, NULL, $owner_uid, true, '', '', NOW(), '', false, NOW())"); + (?, '', NULL, NULL, ?, true, '', '', NOW(), '', false, NOW())"); + $sth->execute([$ref_id, $owner_uid]); if (count($labels) != 0) { foreach ($labels as $label) { @@ -187,7 +201,7 @@ class Article extends Handler_Protected { } } - db_query("COMMIT"); + $pdo->commit(); return $rc; } @@ -196,9 +210,9 @@ class Article extends Handler_Protected { print __("Tags for this article (separated by commas):")."<br>"; - $param = $this->dbh->escape_string($_REQUEST['param']); + $param = clean($_REQUEST['param']); - $tags = Article::get_article_tags($this->dbh->escape_string($param)); + $tags = Article::get_article_tags($param); $tags_str = join(", ", $tags); @@ -227,11 +241,15 @@ class Article extends Handler_Protected { } function setScore() { - $ids = $this->dbh->escape_string($_REQUEST['id']); - $score = (int)$this->dbh->escape_string($_REQUEST['score']); + $ids = explode(",", clean($_REQUEST['id'])); + $score = (int)clean($_REQUEST['score']); + + $ids_qmarks = arr_qmarks($ids); + + $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET + score = ? WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?"); - $this->dbh->query("UPDATE ttrss_user_entries SET - score = '$score' WHERE ref_id IN ($ids) AND owner_uid = " . $_SESSION["uid"]); + $sth->execute(array_merge([$score], $ids, [$_SESSION['uid']])); print json_encode(array("id" => $ids, "score" => (int)$score, @@ -239,10 +257,13 @@ class Article extends Handler_Protected { } function getScore() { - $id = $this->dbh->escape_string($_REQUEST['id']); + $id = clean($_REQUEST['id']); - $result = $this->dbh->query("SELECT score FROM ttrss_user_entries WHERE ref_id = $id AND owner_uid = " . $_SESSION["uid"]); - $score = $this->dbh->fetch_result($result, 0, "score"); + $sth = $this->pdo->prepare("SELECT score FROM ttrss_user_entries WHERE ref_id = ? AND owner_uid = ?"); + $sth->execute([$id, $_SESSION['uid']]); + $row = $sth->fetch(); + + $score = $row['score']; print json_encode(array("id" => $id, "score" => (int)$score, @@ -252,24 +273,26 @@ class Article extends Handler_Protected { function setArticleTags() { - $id = $this->dbh->escape_string($_REQUEST["id"]); + $id = clean($_REQUEST["id"]); - $tags_str = $this->dbh->escape_string($_REQUEST["tags_str"]); + $tags_str = clean($_REQUEST["tags_str"]); $tags = array_unique(trim_array(explode(",", $tags_str))); - $this->dbh->query("BEGIN"); + $this->pdo->beginTransaction(); - $result = $this->dbh->query("SELECT int_id FROM ttrss_user_entries WHERE - ref_id = '$id' AND owner_uid = '".$_SESSION["uid"]."' LIMIT 1"); + $sth = $this->pdo->prepare("SELECT int_id FROM ttrss_user_entries WHERE + ref_id = ? AND owner_uid = ? LIMIT 1"); + $sth->execute([$id, $_SESSION['uid']]); - if ($this->dbh->num_rows($result) == 1) { + if ($row = $sth->fetch()) { $tags_to_cache = array(); - $int_id = $this->dbh->fetch_result($result, 0, "int_id"); + $int_id = $row['int_id']; - $this->dbh->query("DELETE FROM ttrss_tags WHERE - post_int_id = $int_id AND owner_uid = '".$_SESSION["uid"]."'"); + $sth = $this->pdo->prepare("DELETE FROM ttrss_tags WHERE + post_int_id = ? AND owner_uid = ?"); + $sth->execute([$int_id, $_SESSION['uid']]); foreach ($tags as $tag) { $tag = sanitize_tag($tag); @@ -285,8 +308,11 @@ class Article extends Handler_Protected { // print "<!-- $id : $int_id : $tag -->"; if ($tag != '') { - $this->dbh->query("INSERT INTO ttrss_tags - (post_int_id, owner_uid, tag_name) VALUES ('$int_id', '".$_SESSION["uid"]."', '$tag')"); + $sth = $this->pdo->prepare("INSERT INTO ttrss_tags + (post_int_id, owner_uid, tag_name) + VALUES (?, ?, ?)"); + + $sth->execute([$int_id, $_SESSION['uid'], $tag]); } array_push($tags_to_cache, $tag); @@ -297,12 +323,12 @@ class Article extends Handler_Protected { sort($tags_to_cache); $tags_str = join(",", $tags_to_cache); - $this->dbh->query("UPDATE ttrss_user_entries - SET tag_cache = '$tags_str' WHERE ref_id = '$id' - AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_user_entries + SET tag_cache = ? WHERE ref_id = ? AND owner_uid = ?"); + $sth->execute([$tags_str, $id, $_SESSION['uid']]); } - $this->dbh->query("COMMIT"); + $this->pdo->commit(); $tags = Article::get_article_tags($id); $tags_str = $this->format_tags_string($tags, $id); @@ -316,15 +342,17 @@ class Article extends Handler_Protected { function completeTags() { - $search = $this->dbh->escape_string($_REQUEST["search"]); + $search = clean($_REQUEST["search"]); - $result = $this->dbh->query("SELECT DISTINCT tag_name FROM ttrss_tags - WHERE owner_uid = '".$_SESSION["uid"]."' AND - tag_name LIKE '$search%' ORDER BY tag_name + $sth = $this->pdo->prepare("SELECT DISTINCT tag_name FROM ttrss_tags + WHERE owner_uid = ? AND + tag_name LIKE ? ORDER BY tag_name LIMIT 10"); + $sth->execute([$_SESSION['uid'], "$search%"]); + print "<ul>"; - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $sth->fetch()) { print "<li>" . $line["tag_name"] . "</li>"; } print "</ul>"; @@ -341,10 +369,10 @@ class Article extends Handler_Protected { private function labelops($assign) { $reply = array(); - $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); - $label_id = $this->dbh->escape_string($_REQUEST["lid"]); + $ids = explode(",", clean($_REQUEST["ids"])); + $label_id = clean($_REQUEST["lid"]); - $label = $this->dbh->escape_string(Labels::find_caption($label_id, + $label = db_escape_string(Labels::find_caption($label_id, $_SESSION["uid"])); $reply["info-for-headlines"] = array(); @@ -372,11 +400,12 @@ class Article extends Handler_Protected { } function getArticleFeed($id) { - $result = db_query("SELECT feed_id FROM ttrss_user_entries - WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("SELECT feed_id FROM ttrss_user_entries + WHERE ref_id = ? AND owner_uid = ?"); + $sth->execute([$id, $_SESSION['uid']]); - if (db_num_rows($result) != 0) { - return db_fetch_result($result, 0, "feed_id"); + if ($row = $sth->fetch()) { + return $row["feed_id"]; } else { return 0; } @@ -530,24 +559,29 @@ class Article extends Handler_Protected { /* we can figure out feed_id from article id anyway, why do we * pass feed_id here? let's ignore the argument :(*/ - $result = db_query("SELECT feed_id FROM ttrss_user_entries - WHERE ref_id = '$id'"); + $pdo = Db::pdo(); - $feed_id = (int) db_fetch_result($result, 0, "feed_id"); + $sth = $pdo->prepare("SELECT feed_id FROM ttrss_user_entries + WHERE ref_id = ?"); + $sth->execute([$id]); + $row = $sth->fetch(); + + $feed_id = (int) $row["feed_id"]; $rv['feed_id'] = $feed_id; //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; }; if ($mark_as_read) { - $result = db_query("UPDATE ttrss_user_entries + $sth = $pdo->prepare("UPDATE ttrss_user_entries SET unread = false,last_read = NOW() - WHERE ref_id = '$id' AND owner_uid = $owner_uid"); + WHERE ref_id = ? AND owner_uid = ?"); + $sth->execute([$id, $owner_uid]); CCache::update($feed_id, $owner_uid); } - $result = db_query("SELECT id,title,link,content,feed_id,comments,int_id,lang, + $sth = $pdo->prepare("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 title FROM ttrss_feeds WHERE id = feed_id) as feed_title, @@ -560,17 +594,16 @@ class Article extends Handler_Protected { orig_feed_id, note FROM ttrss_entries,ttrss_user_entries - WHERE id = '$id' AND ref_id = id AND owner_uid = $owner_uid"); - - if ($result) { + WHERE id = ? AND ref_id = id AND owner_uid = ?"); + $sth->execute([$id, $owner_uid]); - $line = db_fetch_assoc($result); + if ($line = $sth->fetch()) { $line["tags"] = Article::get_article_tags($id, $owner_uid, $line["tag_cache"]); unset($line["tag_cache"]); $line["content"] = sanitize($line["content"], - sql_bool_to_bool($line['hide_images']), + $line['hide_images'], $owner_uid, $line["site_url"], false, $line["id"]); foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_RENDER_ARTICLE) as $p) { @@ -601,12 +634,11 @@ class Article extends Handler_Protected { $rv['content'] .= "<html><head> <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/> <title>".$line["title"]."</title>". - stylesheet_tag("css/default.css")." - + stylesheet_tag("css/default.css")." <link rel=\"shortcut icon\" type=\"image/png\" href=\"images/favicon.png\"> <link rel=\"icon\" type=\"image/png\" sizes=\"72x72\" href=\"images/favicon-72px.png\"> - </head><body id=\"ttrssZoom\">"; + </head><body class=\"claro ttrss_utility ttrss_zoom\">"; } $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">"; @@ -683,18 +715,17 @@ class Article extends Handler_Protected { if ($line["orig_feed_id"]) { - $tmp_result = db_query("SELECT * FROM ttrss_archived_feeds - WHERE id = ".$line["orig_feed_id"] . " AND owner_uid = " . $_SESSION["uid"]); + $of_sth = $pdo->prepare("SELECT * FROM ttrss_archived_feeds + WHERE id = ? AND owner_uid = ?"); + $of_sth->execute([$line["orig_feed_id"], $owner_uid]); - if (db_num_rows($tmp_result) != 0) { + if ($tmp_line = $of_sth->fetch()) { $rv['content'] .= "<div clear='both'>"; $rv['content'] .= __("Originally from:"); $rv['content'] .= " "; - $tmp_line = db_fetch_assoc($tmp_result); - $rv['content'] .= "<a target='_blank' rel='noopener noreferrer' href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" . $tmp_line['title'] . "</a>"; @@ -724,9 +755,9 @@ class Article extends Handler_Protected { if (!$zoom_mode) { $rv['content'] .= Article::format_article_enclosures($id, - sql_bool_to_bool($line["always_display_enclosures"]), + $line["always_display_enclosures"], $line["content"], - sql_bool_to_bool($line["hide_images"])); + $line["hide_images"]); } $rv['content'] .= "</div>"; @@ -753,25 +784,27 @@ class Article extends Handler_Protected { static function get_article_tags($id, $owner_uid = 0, $tag_cache = false) { - $a_id = db_escape_string($id); + $a_id = $id; if (!$owner_uid) $owner_uid = $_SESSION["uid"]; - $query = "SELECT DISTINCT tag_name, - owner_uid as owner FROM - ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE - ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name"; + $pdo = Db::pdo(); + + $sth = $pdo->prepare("SELECT DISTINCT tag_name, + owner_uid as owner FROM ttrss_tags + WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE + ref_id = ? AND owner_uid = ? LIMIT 1) ORDER BY tag_name"); $tags = array(); /* check cache first */ if ($tag_cache === false) { - $result = db_query("SELECT tag_cache FROM ttrss_user_entries - WHERE ref_id = '$id' AND owner_uid = $owner_uid"); + $csth = $pdo->prepare("SELECT tag_cache FROM ttrss_user_entries + WHERE ref_id = ? AND owner_uid = ?"); + $csth->execute([$id, $owner_uid]); - if (db_num_rows($result) != 0) - $tag_cache = db_fetch_result($result, 0, "tag_cache"); + if ($row = $csth->fetch()) $tag_cache = $row["tag_cache"]; } if ($tag_cache) { @@ -780,19 +813,20 @@ class Article extends Handler_Protected { /* do it the hard way */ - $tmp_result = db_query($query); + $sth->execute([$a_id, $owner_uid]); - while ($tmp_line = db_fetch_assoc($tmp_result)) { + while ($tmp_line = $sth->fetch()) { array_push($tags, $tmp_line["tag_name"]); } /* update the cache */ - $tags_str = db_escape_string(join(",", $tags)); + $tags_str = join(",", $tags); - db_query("UPDATE ttrss_user_entries - SET tag_cache = '$tags_str' WHERE ref_id = '$id' - AND owner_uid = $owner_uid"); + $sth = $pdo->prepare("UPDATE ttrss_user_entries + SET tag_cache = ? WHERE ref_id = ? + AND owner_uid = ?"); + $sth->execute([$tags_str, $id, $owner_uid]); } return $tags; @@ -845,22 +879,21 @@ class Article extends Handler_Protected { static function get_article_enclosures($id) { - $query = "SELECT * FROM ttrss_enclosures - WHERE post_id = '$id' AND content_url != ''"; - - $rv = array(); + $pdo = Db::pdo(); - $result = db_query($query); + $sth = $pdo->prepare("SELECT * FROM ttrss_enclosures + WHERE post_id = ? AND content_url != ''"); + $sth->execute([$id]); - if (db_num_rows($result) > 0) { - while ($line = db_fetch_assoc($result)) { + $rv = array(); - if (file_exists(CACHE_DIR . '/images/' . sha1($line["content_url"]))) { - $line["content_url"] = get_self_url_prefix() . '/public.php?op=cached_url&hash=' . sha1($line["content_url"]); - } + while ($line = $sth->fetch()) { - array_push($rv, $line); + if (file_exists(CACHE_DIR . '/images/' . sha1($line["content_url"]))) { + $line["content_url"] = get_self_url_prefix() . '/public.php?op=cached_url&hash=' . sha1($line["content_url"]); } + + array_push($rv, $line); } return $rv; @@ -869,11 +902,18 @@ class Article extends Handler_Protected { static function purge_orphans($do_output = false) { // purge orphaned posts in main content table - $result = db_query("DELETE FROM ttrss_entries WHERE - NOT EXISTS (SELECT ref_id FROM ttrss_user_entries WHERE ref_id = id)"); + + if (DB_TYPE == "mysql") + $limit_qpart = "LIMIT 5000"; + else + $limit_qpart = ""; + + $pdo = Db::pdo(); + $res = $pdo->query("DELETE FROM ttrss_entries WHERE + NOT EXISTS (SELECT ref_id FROM ttrss_user_entries WHERE ref_id = id) $limit_qpart"); if ($do_output) { - $rows = db_affected_rows($result); + $rows = $res->rowCount(); _debug("Purged $rows orphaned posts."); } } @@ -881,46 +921,47 @@ class Article extends Handler_Protected { static function catchupArticlesById($ids, $cmode, $owner_uid = false) { if (!$owner_uid) $owner_uid = $_SESSION["uid"]; - if (count($ids) == 0) return; - $tmp_ids = array(); + $pdo = Db::pdo(); - foreach ($ids as $id) { - array_push($tmp_ids, "ref_id = '$id'"); - } - - $ids_qpart = join(" OR ", $tmp_ids); + $ids_qmarks = arr_qmarks($ids); if ($cmode == 0) { - db_query("UPDATE ttrss_user_entries SET + $sth = $pdo->prepare("UPDATE ttrss_user_entries SET unread = false,last_read = NOW() - WHERE ($ids_qpart) AND owner_uid = $owner_uid"); + WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?"); } else if ($cmode == 1) { - db_query("UPDATE ttrss_user_entries SET + $sth = $pdo->prepare("UPDATE ttrss_user_entries SET unread = true - WHERE ($ids_qpart) AND owner_uid = $owner_uid"); + WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?"); } else { - db_query("UPDATE ttrss_user_entries SET - unread = NOT unread,last_read = NOW() - WHERE ($ids_qpart) AND owner_uid = $owner_uid"); + $sth = $pdo->prepare("UPDATE ttrss_user_entries SET + unread = NOT unread,last_read = NOW() + WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?"); } + $sth->execute(array_merge($ids, [$owner_uid])); + /* update ccache */ - $result = db_query("SELECT DISTINCT feed_id FROM ttrss_user_entries - WHERE ($ids_qpart) AND owner_uid = $owner_uid"); + $sth = $pdo->prepare("SELECT DISTINCT feed_id FROM ttrss_user_entries + WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?"); + $sth->execute(array_merge($ids, [$owner_uid])); - while ($line = db_fetch_assoc($result)) { + while ($line = $sth->fetch()) { CCache::update($line["feed_id"], $owner_uid); } } static function getLastArticleId() { - $result = db_query("SELECT ref_id AS id FROM ttrss_user_entries - WHERE owner_uid = " . $_SESSION["uid"] . " ORDER BY ref_id DESC LIMIT 1"); + $pdo = DB::pdo(); + + $sth = $pdo->prepare("SELECT ref_id AS id FROM ttrss_user_entries + WHERE owner_uid = ? ORDER BY ref_id DESC LIMIT 1"); + $sth->execute([$_SESSION['uid']]); - if (db_num_rows($result) == 1) { - return db_fetch_result($result, 0, "id"); + if ($row = $sth->fetch()) { + return $row['id']; } else { return -1; } @@ -931,32 +972,34 @@ class Article extends Handler_Protected { if (!$owner_uid) $owner_uid = $_SESSION["uid"]; - $result = db_query("SELECT label_cache FROM - ttrss_user_entries WHERE ref_id = '$id' AND owner_uid = " . - $owner_uid); + $pdo = Db::pdo(); + + $sth = $pdo->prepare("SELECT label_cache FROM + ttrss_user_entries WHERE ref_id = ? AND owner_uid = ?"); + $sth->execute([$id, $owner_uid]); - if (db_num_rows($result) > 0) { - $label_cache = db_fetch_result($result, 0, "label_cache"); + if ($row = $sth->fetch()) { + $label_cache = $row["label_cache"]; if ($label_cache) { - $label_cache = json_decode($label_cache, true); + $tmp = json_decode($label_cache, true); - if ($label_cache["no-labels"] == 1) + if (!$tmp || $tmp["no-labels"] == 1) return $rv; else - return $label_cache; + return $tmp; } } - $result = db_query( - "SELECT DISTINCT label_id,caption,fg_color,bg_color + $sth = $pdo->prepare("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 . " + AND article_id = ? + AND owner_uid = ? ORDER BY caption"); + $sth->execute([$id, $owner_uid]); - while ($line = db_fetch_assoc($result)) { + while ($line = $sth->fetch()) { $rk = array(Labels::label_to_feed_id($line["label_id"]), $line["caption"], $line["fg_color"], $line["bg_color"]); diff --git a/classes/auth/base.php b/classes/auth/base.php index 304431213..dbc77f8cd 100644 --- a/classes/auth/base.php +++ b/classes/auth/base.php @@ -1,9 +1,9 @@ <?php class Auth_Base { - private $dbh; + private $pdo; function __construct() { - $this->dbh = Db::get(); + $this->pdo = Db::pdo(); } /** @@ -29,15 +29,13 @@ class Auth_Base { if (!$password) $password = make_password(); if (!$user_id) { - $login = $this->dbh->escape_string($login); $salt = substr(bin2hex(get_random_bytes(125)), 0, 250); $pwd_hash = encrypt_password($password, $salt, true); - $query = "INSERT INTO ttrss_users + $sth = $this->pdo->prepare("INSERT INTO ttrss_users (login,access_level,last_login,created,pwd_hash,salt) - VALUES ('$login', 0, null, NOW(), '$pwd_hash','$salt')"; - - $this->dbh->query($query); + VALUES (?, 0, null, NOW(), ?,?)"); + $sth->execute([$login, $pwd_hash, $salt]); return $this->find_user_by_login($login); @@ -50,13 +48,12 @@ class Auth_Base { } function find_user_by_login($login) { - $login = $this->dbh->escape_string($login); - - $result = $this->dbh->query("SELECT id FROM ttrss_users WHERE - login = '$login'"); + $sth = $this->pdo->prepare("SELECT id FROM ttrss_users WHERE + login = ?"); + $sth->execute([$login]); - if ($this->dbh->num_rows($result) > 0) { - return $this->dbh->fetch_result($result, 0, "id"); + if ($row = $sth->fetch()) { + return $row["id"]; } else { return false; } diff --git a/classes/backend.php b/classes/backend.php index c9a595b86..d5d0f5a01 100644 --- a/classes/backend.php +++ b/classes/backend.php @@ -84,7 +84,7 @@ class Backend extends Handler { } function help() { - $topic = basename($_REQUEST["topic"]); + $topic = basename(clean($_REQUEST["topic"])); switch ($topic) { case "main": diff --git a/classes/ccache.php b/classes/ccache.php index 4efb96d97..87c14e78e 100644 --- a/classes/ccache.php +++ b/classes/ccache.php @@ -1,40 +1,46 @@ <?php class CCache { - /* function ccache_zero($feed_id, $owner_uid) { - db_query("UPDATE ttrss_counters_cache SET - value = 0, updated = NOW() WHERE - feed_id = '$feed_id' AND owner_uid = '$owner_uid'"); - } */ - static function zero_all($owner_uid) { - db_query("UPDATE ttrss_counters_cache SET - value = 0 WHERE owner_uid = '$owner_uid'"); + $pdo = Db::pdo(); + + $sth = $pdo->prepare("UPDATE ttrss_counters_cache SET + value = 0 WHERE owner_uid = ?"); + $sth->execute([$owner_uid]); - db_query("UPDATE ttrss_cat_counters_cache SET - value = 0 WHERE owner_uid = '$owner_uid'"); + $sth = $pdo->prepare("UPDATE ttrss_cat_counters_cache SET + value = 0 WHERE owner_uid = ?"); + $sth->execute([$owner_uid]); } static function remove($feed_id, $owner_uid, $is_cat = false) { + $feed_id = (int) $feed_id; + if (!$is_cat) { $table = "ttrss_counters_cache"; } else { $table = "ttrss_cat_counters_cache"; } - db_query("DELETE FROM $table WHERE - feed_id = '$feed_id' AND owner_uid = '$owner_uid'"); + $pdo = Db::pdo(); + + $sth = $pdo->prepare("DELETE FROM $table WHERE + feed_id = ? AND owner_uid = ?"); + $sth->execute([$feed_id, $owner_uid]); } static function update_all($owner_uid) { + $pdo = Db::pdo(); + if (get_pref('ENABLE_FEED_CATS', $owner_uid)) { - $result = db_query("SELECT feed_id FROM ttrss_cat_counters_cache - WHERE feed_id > 0 AND owner_uid = '$owner_uid'"); + $sth = $pdo->prepare("SELECT feed_id FROM ttrss_cat_counters_cache + WHERE feed_id > 0 AND owner_uid = ?"); + $sth->execute([$owner_uid]); - while ($line = db_fetch_assoc($result)) { + while ($line = $sth->fetch()) { CCache::update($line["feed_id"], $owner_uid, true); } @@ -43,10 +49,11 @@ class CCache { CCache::update(0, $owner_uid, true); } else { - $result = db_query("SELECT feed_id FROM ttrss_counters_cache - WHERE feed_id > 0 AND owner_uid = '$owner_uid'"); + $sth = $pdo->prepare("SELECT feed_id FROM ttrss_counters_cache + WHERE feed_id > 0 AND owner_uid = ?"); + $sth->execute([$owner_uid]); - while ($line = db_fetch_assoc($result)) { + while ($line = $sth->fetch()) { print CCache::update($line["feed_id"], $owner_uid); } @@ -57,31 +64,29 @@ class CCache { static function find($feed_id, $owner_uid, $is_cat = false, $no_update = false) { - if (!is_numeric($feed_id)) return; + // "" (null) is valid and should be cast to 0 (uncategorized) + // everything else i.e. tags are not + if (!is_numeric($feed_id) && $feed_id) + return; + + $feed_id = (int) $feed_id; if (!$is_cat) { $table = "ttrss_counters_cache"; - /* if ($feed_id > 0) { - $tmp_result = db_query("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)"; - } + $pdo = Db::pdo(); - $result = db_query("SELECT value FROM $table - WHERE owner_uid = '$owner_uid' AND feed_id = '$feed_id' + $sth = $pdo->prepare("SELECT value FROM $table + WHERE owner_uid = ? AND feed_id = ? LIMIT 1"); - if (db_num_rows($result) == 1) { - return db_fetch_result($result, 0, "value"); + $sth->execute([$owner_uid, $feed_id]); + + if ($row = $sth->fetch()) { + return $row["value"]; } else { if ($no_update) { return -1; @@ -95,13 +100,12 @@ class CCache { static function update($feed_id, $owner_uid, $is_cat = false, $update_pcat = true, $pcat_fast = false) { - if (!is_numeric($feed_id)) return; + // "" (null) is valid and should be cast to 0 (uncategorized) + // everything else i.e. tags are not + if (!is_numeric($feed_id) && $feed_id) + return; - /* if (!$is_cat && $feed_id > 0) { - $tmp_result = db_query("SELECT owner_uid FROM ttrss_feeds - WHERE id = '$feed_id'"); - $owner_uid = db_fetch_result($tmp_result, 0, "owner_uid"); - } */ + $feed_id = (int) $feed_id; $prev_unread = CCache::find($feed_id, $owner_uid, $is_cat, true); @@ -119,54 +123,66 @@ class CCache { $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"; - } + $pdo = Db::pdo(); + if ($is_cat && $feed_id >= 0) { /* Recalculate counters for child feeds */ if (!$pcat_fast) { - $result = db_query("SELECT id FROM ttrss_feeds - WHERE owner_uid = '$owner_uid' AND $cat_qpart"); + $sth = $pdo->prepare("SELECT id FROM ttrss_feeds + WHERE owner_uid = :uid AND + (cat_id = :cat OR (:cat = 0 AND cat_id IS NULL))"); + $sth->execute([":uid" => $owner_uid, ":cat" => $feed_id]); - while ($line = db_fetch_assoc($result)) { - CCache::update($line["id"], $owner_uid, false, false); + while ($line = $sth->fetch()) { + CCache::update((int)$line["id"], $owner_uid, false, false); } } - $result = db_query("SELECT SUM(value) AS sv + $sth = $pdo->prepare("SELECT SUM(value) AS sv FROM ttrss_counters_cache, ttrss_feeds - WHERE id = feed_id AND $cat_qpart AND - ttrss_counters_cache.owner_uid = $owner_uid AND - ttrss_feeds.owner_uid = '$owner_uid'"); + WHERE id = feed_id AND + (cat_id = :cat OR (:cat = 0 AND cat_id IS NULL)) AND + ttrss_counters_cache.owner_uid = :uid AND + ttrss_feeds.owner_uid = :uid"); + $sth->execute([":uid" => $owner_uid, ":cat" => $feed_id]); + $row = $sth->fetch(); - $unread = (int) db_fetch_result($result, 0, "sv"); + $unread = (int) $row["sv"]; } else { $unread = (int) Feeds::getFeedArticles($feed_id, $is_cat, true, $owner_uid); } - db_query("BEGIN"); + $tr_in_progress = false; - $result = db_query("SELECT feed_id FROM $table - WHERE owner_uid = '$owner_uid' AND feed_id = '$feed_id' LIMIT 1"); + try { + $pdo->beginTransaction(); + } catch (Exception $e) { + $tr_in_progress = true; + } + + $sth = $pdo->prepare("SELECT feed_id FROM $table + WHERE owner_uid = ? AND feed_id = ? LIMIT 1"); + $sth->execute([$owner_uid, $feed_id]); + + if ($sth->fetch()) { + + $sth = $pdo->prepare("UPDATE $table SET + value = ?, updated = NOW() WHERE + feed_id = ? AND owner_uid = ?"); - if (db_num_rows($result) == 1) { - db_query("UPDATE $table SET - value = '$unread', updated = NOW() WHERE - feed_id = '$feed_id' AND owner_uid = '$owner_uid'"); + $sth->execute([$unread, $feed_id, $owner_uid]); } else { - db_query("INSERT INTO $table + $sth = $pdo->prepare("INSERT INTO $table (feed_id, value, owner_uid, updated) VALUES - ($feed_id, $unread, $owner_uid, NOW())"); + (?, ?, ?, NOW())"); + $sth->execute([$feed_id, $unread, $owner_uid]); } - db_query("COMMIT"); + if (!$tr_in_progress) $pdo->commit(); if ($feed_id > 0 && $prev_unread != $unread) { @@ -176,13 +192,13 @@ class CCache { if ($update_pcat) { - $result = db_query("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($cat_id, $owner_uid, true, true, true); + $sth = $pdo->prepare("SELECT cat_id FROM ttrss_feeds + WHERE owner_uid = ? AND id = ?"); + $sth->execute([$owner_uid, $feed_id]); + if ($row = $sth->fetch()) { + CCache::update((int)$row["cat_id"], $owner_uid, true, true, true); + } } } } else if ($feed_id < 0) { @@ -192,37 +208,4 @@ class CCache { return $unread; } - /* function ccache_cleanup($owner_uid) { - - if (DB_TYPE == "pgsql") { - db_query("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("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("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("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"); - - } - } */ - - }
\ No newline at end of file diff --git a/classes/counters.php b/classes/counters.php index c608acf72..2de7d5a3a 100644 --- a/classes/counters.php +++ b/classes/counters.php @@ -22,15 +22,18 @@ class Counters { array_push($ret_arr, $cv); - $result = db_query("SELECT id AS cat_id, value AS unread, + $pdo = DB::pdo(); + + $sth = $pdo->prepare("SELECT id AS cat_id, value AS unread, (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE c2.parent_cat = ttrss_feed_categories.id) AS num_children FROM ttrss_feed_categories, ttrss_cat_counters_cache WHERE ttrss_cat_counters_cache.feed_id = id AND ttrss_cat_counters_cache.owner_uid = ttrss_feed_categories.owner_uid AND - ttrss_feed_categories.owner_uid = " . $_SESSION["uid"]); + ttrss_feed_categories.owner_uid = ?"); + $sth->execute([$_SESSION['uid']]); - while ($line = db_fetch_assoc($result)) { + while ($line = $sth->fetch()) { $line["cat_id"] = (int) $line["cat_id"]; if ($line["num_children"] > 0) { @@ -67,10 +70,14 @@ class Counters { array_push($ret_arr, $cv); - $result = db_query("SELECT COUNT(id) AS fn FROM - ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]); + $pdo = Db::pdo(); + + $sth = $pdo->prepare("SELECT COUNT(id) AS fn FROM + ttrss_feeds WHERE owner_uid = ?"); + $sth->execute([$_SESSION['uid']]); + $row = $sth->fetch(); - $subscribed_feeds = db_fetch_result($result, 0, "fn"); + $subscribed_feeds = $row["fn"]; $cv = array("id" => "subscribed-feeds", "counter" => (int) $subscribed_feeds); @@ -124,17 +131,18 @@ class Counters { $ret_arr = array(); - $owner_uid = $_SESSION["uid"]; + $pdo = Db::pdo(); - $result = db_query("SELECT id,caption,SUM(CASE WHEN u1.unread = true THEN 1 ELSE 0 END) AS unread, COUNT(u1.unread) AS total + $sth = $pdo->prepare("SELECT id,caption,SUM(CASE WHEN u1.unread = true THEN 1 ELSE 0 END) AS unread, COUNT(u1.unread) AS total FROM ttrss_labels2 LEFT JOIN ttrss_user_labels2 ON (ttrss_labels2.id = label_id) LEFT JOIN ttrss_user_entries AS u1 ON u1.ref_id = article_id - WHERE ttrss_labels2.owner_uid = $owner_uid AND u1.owner_uid = $owner_uid + WHERE ttrss_labels2.owner_uid = :uid AND u1.owner_uid = :uid GROUP BY ttrss_labels2.id, ttrss_labels2.caption"); + $sth->execute([":uid" => $_SESSION['uid']]); - while ($line = db_fetch_assoc($result)) { + while ($line = $sth->fetch()) { $id = Labels::label_to_feed_id($line["id"]); @@ -155,18 +163,19 @@ class Counters { $ret_arr = array(); - $query = "SELECT ttrss_feeds.id, + $pdo = Db::pdo(); + + $sth = $pdo->prepare("SELECT ttrss_feeds.id, ttrss_feeds.title, ".SUBSTRING_FOR_DATE."(ttrss_feeds.last_updated,1,19) AS last_updated, last_error, value AS count FROM ttrss_feeds, ttrss_counters_cache - WHERE ttrss_feeds.owner_uid = ".$_SESSION["uid"]." + WHERE ttrss_feeds.owner_uid = ? AND ttrss_counters_cache.owner_uid = ttrss_feeds.owner_uid - AND ttrss_counters_cache.feed_id = id"; + AND ttrss_counters_cache.feed_id = id"); + $sth->execute([$_SESSION['uid']]); - $result = db_query($query); - - while ($line = db_fetch_assoc($result)) { + while ($line = $sth->fetch()) { $id = $line["id"]; $count = $line["count"]; @@ -174,7 +183,11 @@ class Counters { $last_updated = make_local_datetime($line['last_updated'], false); - $has_img = feed_has_icon($id); + if (Feeds::feedHasIcon($id)) { + $has_img = filemtime(Feeds::getIconFile($id)); + } else { + $has_img = false; + } if (date('Y') - date('Y', strtotime($line['last_updated'])) > 2) $last_updated = ''; diff --git a/classes/db.php b/classes/db.php index 3b71f3c8f..96a888275 100644 --- a/classes/db.php +++ b/classes/db.php @@ -1,17 +1,29 @@ <?php -class Db implements IDb { +class Db +{ + + /* @var Db $instance */ private static $instance; + + /* @var IDb $adapter */ private $adapter; + private $link; - private function __construct() { + /* @var PDO $pdo */ + private $pdo; + + private function __clone() { + // + } + + private function legacy_connect() { + + user_error("Legacy connect requested to " . DB_TYPE, E_USER_NOTICE); $er = error_reporting(E_ALL); - if (defined('_ENABLE_PDO') && _ENABLE_PDO && class_exists("PDO")) { - $this->adapter = new Db_PDO(); - } else { - switch (DB_TYPE) { + switch (DB_TYPE) { case "mysql": $this->adapter = new Db_Mysqli(); break; @@ -20,7 +32,6 @@ class Db implements IDb { break; default: die("Unknown DB_TYPE: " . DB_TYPE); - } } if (!$this->adapter) { @@ -38,63 +49,57 @@ class Db implements IDb { error_reporting($er); } - private function __clone() { - // - } - - public static function get() { - if (self::$instance == null) - self::$instance = new self(); + private function pdo_connect() { - return self::$instance; - } + $db_port = defined('DB_PORT') && DB_PORT ? ';port=' . DB_PORT : ''; + $db_host = defined('DB_HOST') && DB_HOST ? ';host=' . DB_HOST : ''; - static function quote($str){ - return("'$str'"); - } + try { + $this->pdo = new PDO(DB_TYPE . ':dbname=' . DB_NAME . $db_host . $db_port, + DB_USER, + DB_PASS); + } catch (Exception $e) { + print "<pre>Exception while creating PDO object:" . $e->getMessage() . "</pre>"; + exit(101); + } - function reconnect() { - $this->link = $this->adapter->connect(DB_HOST, DB_USER, DB_PASS, DB_NAME, defined('DB_PORT') ? DB_PORT : ""); - } + $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); - function connect($host, $user, $pass, $db, $port) { - //return $this->adapter->connect($host, $user, $pass, $db, $port); - return ; - } + if (DB_TYPE == "pgsql") { - function escape_string($s, $strip_tags = true) { - return $this->adapter->escape_string($s, $strip_tags); - } + $this->pdo->query("set client_encoding = 'UTF-8'"); + $this->pdo->query("set datestyle = 'ISO, european'"); + $this->pdo->query("set TIME ZONE 0"); + $this->pdo->query("set cpu_tuple_cost = 0.5"); - function query($query, $die_on_error = true) { - return $this->adapter->query($query, $die_on_error); - } + } else if (DB_TYPE == "mysql") { + $this->pdo->query("SET time_zone = '+0:0'"); - function fetch_assoc($result) { - return $this->adapter->fetch_assoc($result); + if (defined('MYSQL_CHARSET') && MYSQL_CHARSET) { + $this->pdo->query("SET NAMES " . MYSQL_CHARSET); + } + } } - function num_rows($result) { - return $this->adapter->num_rows($result); - } + public static function get() { + if (self::$instance == null) + self::$instance = new self(); - function fetch_result($result, $row, $param) { - return $this->adapter->fetch_result($result, $row, $param); - } + if (!self::$instance->adapter) { + self::$instance->legacy_connect(); + } - function close() { - return $this->adapter->close(); + return self::$instance->adapter; } - function affected_rows($result) { - return $this->adapter->affected_rows($result); - } + public static function pdo() { + if (self::$instance == null) + self::$instance = new self(); - function last_error() { - return $this->adapter->last_error(); - } + if (!self::$instance->pdo) { + self::$instance->pdo_connect(); + } - function last_query_error() { - return $this->adapter->last_query_error(); + return self::$instance->pdo; } }
\ No newline at end of file diff --git a/classes/db/mysqli.php b/classes/db/mysqli.php index 817b48c0c..a05b121fc 100644 --- a/classes/db/mysqli.php +++ b/classes/db/mysqli.php @@ -65,7 +65,7 @@ class Db_Mysqli implements IDb { } function last_error() { - return mysqli_error(); + return mysqli_error($this->link); } function last_query_error() { diff --git a/classes/db/pdo.php b/classes/db/pdo.php deleted file mode 100644 index d3070fac4..000000000 --- a/classes/db/pdo.php +++ /dev/null @@ -1,100 +0,0 @@ -<?php -class Db_PDO implements IDb { - private $pdo; - - function connect($host, $user, $pass, $db, $port) { - $connstr = DB_TYPE . ":host=$host;dbname=$db"; - - if (DB_TYPE == "mysql") $connstr .= ";charset=utf8"; - - try { - $this->pdo = new PDO($connstr, $user, $pass); - $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - $this->init(); - } catch (PDOException $e) { - die($e->getMessage()); - } - - return $this->pdo; - } - - function escape_string($s, $strip_tags = true) { - if ($strip_tags) $s = strip_tags($s); - - $qs = $this->pdo->quote($s); - - return mb_substr($qs, 1, mb_strlen($qs)-2); - } - - function query($query, $die_on_error = true) { - try { - return new Db_Stmt($this->pdo->query($query)); - } catch (PDOException $e) { - user_error($e->getMessage(), $die_on_error ? E_USER_ERROR : E_USER_WARNING); - } - } - - function fetch_assoc($result) { - try { - if ($result) { - return $result->fetch(); - } else { - return null; - } - } catch (PDOException $e) { - user_error($e->getMessage(), E_USER_WARNING); - } - } - - function num_rows($result) { - try { - if ($result) { - return $result->rowCount(); - } else { - return false; - } - } catch (PDOException $e) { - user_error($e->getMessage(), E_USER_WARNING); - } - } - - function fetch_result($result, $row, $param) { - return $result->fetch_result($row, $param); - } - - function close() { - $this->pdo = null; - } - - function affected_rows($result) { - try { - if ($result) { - return $result->rowCount(); - } else { - return null; - } - } catch (PDOException $e) { - user_error($e->getMessage(), E_USER_WARNING); - } - } - - function last_error() { - return join(" ", $this->pdo->errorInfo()); - } - - function init() { - switch (DB_TYPE) { - case "pgsql": - $this->query("set client_encoding = 'UTF-8'"); - $this->query("set datestyle = 'ISO, european'"); - $this->query("set TIME ZONE 0"); - return; - case "mysql": - $this->query("SET time_zone = '+0:0'"); - return; - } - - return true; - } - -}
\ No newline at end of file diff --git a/classes/db/prefs.php b/classes/db/prefs.php index d61cc107b..64238bc78 100644 --- a/classes/db/prefs.php +++ b/classes/db/prefs.php @@ -1,11 +1,11 @@ <?php class Db_Prefs { - private $dbh; + private $pdo; private static $instance; private $cache; function __construct() { - $this->dbh = Db::get(); + $this->pdo = Db::pdo(); $this->cache = array(); if ($_SESSION["uid"]) $this->cache(); @@ -26,26 +26,22 @@ class Db_Prefs { $user_id = $_SESSION["uid"]; @$profile = $_SESSION["profile"]; - if ($profile) { - $profile_qpart = "profile = '$profile' AND"; - } else { - $profile_qpart = "profile IS NULL AND"; - } - - if (get_schema_version() < 63) $profile_qpart = ""; + if (!$profile || get_schema_version() < 63) $profile = null; - $result = db_query("SELECT + $sth = $this->pdo->prepare("SELECT value,ttrss_prefs_types.type_name as type_name,ttrss_prefs.pref_name AS pref_name FROM ttrss_user_prefs,ttrss_prefs,ttrss_prefs_types WHERE - $profile_qpart + (profile = :profile OR (:profile IS NULL AND profile IS NULL)) AND ttrss_prefs.pref_name NOT LIKE '_MOBILE%' AND ttrss_prefs_types.id = type_id AND - owner_uid = '$user_id' AND + owner_uid = :uid AND ttrss_user_prefs.pref_name = ttrss_prefs.pref_name"); - while ($line = db_fetch_assoc($result)) { + $sth->execute([":profile" => $profile, ":uid" => $user_id]); + + while ($line = $sth->fetch()) { if ($user_id == $_SESSION["uid"]) { $pref_name = $line["pref_name"]; @@ -57,7 +53,6 @@ class Db_Prefs { function read($pref_name, $user_id = false, $die_on_error = false) { - $pref_name = db_escape_string($pref_name); $profile = false; if (!$user_id) { @@ -72,28 +67,23 @@ class Db_Prefs { return $this->convert($tuple["value"], $tuple["type"]); } - if ($profile) { - $profile_qpart = "profile = '$profile' AND"; - } else { - $profile_qpart = "profile IS NULL AND"; - } - - if (get_schema_version() < 63) $profile_qpart = ""; + if (!$profile || get_schema_version() < 63) $profile = null; - $result = db_query("SELECT + $sth = $this->pdo->prepare("SELECT value,ttrss_prefs_types.type_name as type_name FROM ttrss_user_prefs,ttrss_prefs,ttrss_prefs_types WHERE - $profile_qpart - ttrss_user_prefs.pref_name = '$pref_name' AND + (profile = :profile OR (:profile IS NULL AND profile IS NULL)) AND + ttrss_user_prefs.pref_name = :pref_name AND ttrss_prefs_types.id = type_id AND - owner_uid = '$user_id' AND + owner_uid = :uid AND ttrss_user_prefs.pref_name = ttrss_prefs.pref_name"); + $sth->execute([":uid" => $user_id, ":profile" => $profile, ":pref_name" => $pref_name]); - if (db_num_rows($result) > 0) { - $value = db_fetch_result($result, 0, "value"); - $type_name = db_fetch_result($result, 0, "type_name"); + if ($row = $sth->fetch()) { + $value = $row["value"]; + $type_name = $row["type_name"]; if ($user_id == $_SESSION["uid"]) { $this->cache[$pref_name]["type"] = $type_name; @@ -119,8 +109,7 @@ class Db_Prefs { } function write($pref_name, $value, $user_id = false, $strip_tags = true) { - $pref_name = db_escape_string($pref_name); - $value = db_escape_string($value, $strip_tags); + if ($strip_tags) $value = strip_tags($value); if (!$user_id) { $user_id = $_SESSION["uid"]; @@ -129,13 +118,7 @@ class Db_Prefs { $user_id = sprintf("%d", $user_id); } - if ($profile) { - $profile_qpart = "AND profile = '$profile'"; - } else { - $profile_qpart = "AND profile IS NULL"; - } - - if (get_schema_version() < 63) $profile_qpart = ""; + if (!$profile || get_schema_version() < 63) $profile = null; $type_name = ""; $current_value = ""; @@ -146,12 +129,14 @@ class Db_Prefs { } if (!$type_name) { - $result = db_query("SELECT type_name + $sth = $this->pdo->prepare("SELECT type_name FROM ttrss_prefs,ttrss_prefs_types - WHERE pref_name = '$pref_name' AND type_id = ttrss_prefs_types.id"); + WHERE pref_name = ? AND type_id = ttrss_prefs_types.id"); + $sth->execute([$pref_name]); + + if ($row = $sth->fetch()) + $type_name = $row["type_name"]; - if (db_num_rows($result) > 0) - $type_name = db_fetch_result($result, 0, "type_name"); } else if ($current_value == $value) { return; } @@ -171,10 +156,12 @@ class Db_Prefs { $value = 'UTC'; } - db_query("UPDATE ttrss_user_prefs SET - value = '$value' WHERE pref_name = '$pref_name' - $profile_qpart - AND owner_uid = " . $user_id); + $sth = $this->pdo->prepare("UPDATE ttrss_user_prefs SET + value = :value WHERE pref_name = :pref_name + AND (profile = :profile OR (:profile IS NULL AND profile IS NULL)) + AND owner_uid = :uid"); + + $sth->execute([":pref_name" => $pref_name, ":value" => $value, ":uid" => $user_id, ":profile" => $profile]); if ($user_id == $_SESSION["uid"]) { $this->cache[$pref_name]["type"] = $type_name; diff --git a/classes/db/stmt.php b/classes/db/stmt.php deleted file mode 100644 index 7d6bbb30a..000000000 --- a/classes/db/stmt.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php -class Db_Stmt { - private $stmt; - private $cache; - - function __construct($stmt) { - $this->stmt = $stmt; - $this->cache = false; - } - - function fetch_result($row, $param) { - if (!$this->cache) { - $this->cache = $this->stmt->fetchAll(); - } - - if (isset($this->cache[$row])) { - return $this->cache[$row][$param]; - } else { - user_error("Unable to jump to row $row", E_USER_WARNING); - return false; - } - } - - function rowCount() { - return $this->stmt->rowCount(); - } - - function fetch() { - return $this->stmt->fetch(); - } -}
\ No newline at end of file diff --git a/classes/dbupdater.php b/classes/dbupdater.php index 2d131fde7..1014fa5a5 100644 --- a/classes/dbupdater.php +++ b/classes/dbupdater.php @@ -1,19 +1,19 @@ <?php class DbUpdater { - private $dbh; + private $pdo; private $db_type; private $need_version; - function __construct($dbh, $db_type, $need_version) { - $this->dbh = $dbh; + function __construct($pdo, $db_type, $need_version) { + $this->pdo = Db::pdo(); //$pdo; $this->db_type = $db_type; $this->need_version = (int) $need_version; } function getSchemaVersion() { - $result = db_query("SELECT schema_version FROM ttrss_version"); - return (int) db_fetch_result($result, 0, "schema_version"); + $row = $this->pdo->query("SELECT schema_version FROM ttrss_version")->fetch(); + return (int) $row['schema_version']; } function isUpdateRequired() { @@ -26,6 +26,7 @@ class DbUpdater { if (file_exists($filename)) { return explode(";", preg_replace("/[\r\n]/", "", file_get_contents($filename))); } else { + user_error("DB Updater: schema file for version $version is not found."); return false; } } @@ -37,17 +38,17 @@ class DbUpdater { if (is_array($lines)) { - db_query("BEGIN"); + $this->pdo->beginTransaction(); foreach ($lines as $line) { if (strpos($line, "--") !== 0 && $line) { - if (!db_query($line, false)) { + if (!$this->pdo->query($line)) { if ($html_output) { print_notice("Query: $line"); - print_error("Error: " . db_last_query_error()); + print_error("Error: " . implode(", ", $this->pdo->errorInfo())); } else { _debug("Query: $line"); - _debug("Error: " . db_last_query_error()); + _debug("Error: " . implode(", ", $this->pdo->errorInfo())); } return false; @@ -58,10 +59,10 @@ class DbUpdater { $db_version = $this->getSchemaVersion(); if ($db_version == $version) { - db_query("COMMIT"); + $this->pdo->commit(); return true; } else { - db_query("ROLLBACK"); + $this->pdo->rollBack(); return false; } } else { diff --git a/classes/digest.php b/classes/digest.php index 5a50eb071..83f39a865 100644 --- a/classes/digest.php +++ b/classes/digest.php @@ -19,15 +19,17 @@ class Digest if ($debug) _debug("Sending digests, batch of max $user_limit users, headline limit = $limit"); if (DB_TYPE == "pgsql") { - $interval_query = "last_digest_sent < NOW() - INTERVAL '1 days'"; + $interval_qpart = "last_digest_sent < NOW() - INTERVAL '1 days'"; } else if (DB_TYPE == "mysql") { - $interval_query = "last_digest_sent < DATE_SUB(NOW(), INTERVAL 1 DAY)"; + $interval_qpart = "last_digest_sent < DATE_SUB(NOW(), INTERVAL 1 DAY)"; } - $result = db_query("SELECT id,email FROM ttrss_users - WHERE email != '' AND (last_digest_sent IS NULL OR $interval_query)"); + $pdo = Db::pdo(); - while ($line = db_fetch_assoc($result)) { + $res = $pdo->query("SELECT id,email FROM ttrss_users + WHERE email != '' AND (last_digest_sent IS NULL OR $interval_qpart)"); + + while ($line = $res->fetch()) { if (@get_pref('DIGEST_ENABLE', $line['id'], false)) { $preferred_ts = strtotime(get_pref('DIGEST_PREFERRED_TIME', $line['id'], '00:00')); @@ -70,8 +72,9 @@ class Digest if ($debug) _debug("No headlines"); } - db_query("UPDATE ttrss_users SET last_digest_sent = NOW() - WHERE id = " . $line["id"]); + $sth = $pdo->prepare("UPDATE ttrss_users SET last_digest_sent = NOW() + WHERE id = ?"); + $sth->execute([$line["id"]]); } } @@ -102,13 +105,17 @@ class Digest $affected_ids = array(); + $days = (int) $days; + if (DB_TYPE == "pgsql") { - $interval_query = "ttrss_entries.date_updated > NOW() - INTERVAL '$days days'"; + $interval_qpart = "ttrss_entries.date_updated > NOW() - INTERVAL '$days days'"; } else if (DB_TYPE == "mysql") { - $interval_query = "ttrss_entries.date_updated > DATE_SUB(NOW(), INTERVAL $days DAY)"; + $interval_qpart = "ttrss_entries.date_updated > DATE_SUB(NOW(), INTERVAL $days DAY)"; } - $result = db_query("SELECT ttrss_entries.title, + $pdo = Db::pdo(); + + $sth = $pdo->prepare("SELECT ttrss_entries.title, ttrss_feeds.title AS feed_title, COALESCE(ttrss_feed_categories.title, '" . __('Uncategorized') . "') AS cat_title, date_updated, @@ -124,19 +131,20 @@ class Digest WHERE ref_id = ttrss_entries.id AND feed_id = ttrss_feeds.id AND include_in_digest = true - AND $interval_query - AND ttrss_user_entries.owner_uid = $user_id + AND $interval_qpart + AND ttrss_user_entries.owner_uid = ? AND unread = true AND score >= 0 ORDER BY ttrss_feed_categories.title, ttrss_feeds.title, score DESC, date_updated DESC - LIMIT $limit"); - - $headlines_count = db_num_rows($result); + LIMIT ?"); + $sth->execute([$user_id, $limit]); + $headlines_count = 0; $headlines = array(); - while ($line = db_fetch_assoc($result)) { + while ($line = $sth->fetch()) { array_push($headlines, $line); + $headlines_count++; } for ($i = 0; $i < sizeof($headlines); $i++) { @@ -148,12 +156,6 @@ class Digest $updated = make_local_datetime($line['last_updated'], false, $user_id); - /* if ($line["score"] != 0) { - if ($line["score"] > 0) $line["score"] = '+' . $line["score"]; - - $line["title"] .= " (".$line['score'].")"; - } */ - if (get_pref('ENABLE_FEED_CATS', $user_id)) { $line['feed_title'] = $line['cat_title'] . " / " . $line['feed_title']; } diff --git a/classes/dlg.php b/classes/dlg.php index 53fa60280..9ac5cd12f 100644 --- a/classes/dlg.php +++ b/classes/dlg.php @@ -7,7 +7,7 @@ class Dlg extends Handler_Protected { if (parent::before($method)) { header("Content-Type: text/html"); # required for iframe - $this->param = $this->dbh->escape_string($_REQUEST["param"]); + $this->param = $_REQUEST["param"]; return true; } return false; @@ -18,16 +18,12 @@ class Dlg extends Handler_Protected { print "<div class=\"prefFeedOPMLHolder\">"; - $this->dbh->query("BEGIN"); - print "<ul class='nomarks'>"; $opml = new Opml($_REQUEST); $opml->opml_import($_SESSION["uid"]); - $this->dbh->query("COMMIT"); - print "</ul>"; print "</div>"; @@ -102,15 +98,14 @@ class Dlg extends Handler_Protected { // from here: http://www.roscripts.com/Create_tag_cloud-71.html - $query = "SELECT tag_name, COUNT(post_int_id) AS count - FROM ttrss_tags WHERE owner_uid = ".$_SESSION["uid"]." - GROUP BY tag_name ORDER BY count DESC LIMIT 50"; - - $result = $this->dbh->query($query); + $sth = $this->pdo->prepare("SELECT tag_name, COUNT(post_int_id) AS count + FROM ttrss_tags WHERE owner_uid = ? + GROUP BY tag_name ORDER BY count DESC LIMIT 50"); + $sth->execute([$_SESSION['uid']]); $tags = array(); - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $sth->fetch()) { $tags[$line["tag_name"]] = $line["count"]; } @@ -164,7 +159,7 @@ class Dlg extends Handler_Protected { function generatedFeed() { $this->params = explode(":", $this->param, 3); - $feed_id = $this->dbh->escape_string($this->params[0]); + $feed_id = $this->params[0]; $is_cat = (bool) $this->params[1]; $key = get_feed_access_key($feed_id, $is_cat); @@ -190,4 +185,16 @@ class Dlg extends Handler_Protected { //return; } + function defaultPasswordWarning() { + + print_warning(__("You are using default tt-rss password. Please change it in the Preferences (Personal data / Authentication).")); + + print "<div align='center'>"; + print "<button dojoType=\"dijit.form.Button\" onclick=\"gotoPreferences()\">". + __('Open Preferences')."</button> "; + print "<button dojoType=\"dijit.form.Button\" + onclick=\"return closeInfoBox()\">". + __('Close this window')."</button>"; + print "</div>"; + } }
\ No newline at end of file diff --git a/classes/feeds.php b/classes/feeds.php index 70271802a..e875eeb7f 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -39,7 +39,7 @@ class Feeds extends Handler_Protected { $search_q = ""; } - $reply .= "<span class=\"holder\">"; + $reply = "<span class=\"holder\">"; $rss_link = htmlspecialchars(get_self_url_prefix() . "/public.php?op=rss&id=$feed_id$cat_q$search_q"); @@ -64,7 +64,7 @@ class Feeds extends Handler_Protected { $target = "target=\"_blank\""; $reply .= "<a title=\"$last_updated\" $target href=\"$feed_site_url\">". - truncate_string($feed_title, 30)."</a>"; + truncate_string(strip_tags($feed_title), 30)."</a>"; if ($error) { $error = htmlspecialchars($error); @@ -72,7 +72,7 @@ class Feeds extends Handler_Protected { } } else { - $reply .= $feed_title; + $reply .= strip_tags($feed_title); } $reply .= "</span>"; @@ -193,24 +193,32 @@ class Feeds extends Handler_Protected { if (!$any_needs_curl) { - $result = $this->dbh->query( - "SELECT cache_images," . SUBSTRING_FOR_DATE . "(last_updated,1,19) AS last_updated - FROM ttrss_feeds WHERE id = '$feed'"); + $sth = $this->pdo->prepare("SELECT cache_images," . SUBSTRING_FOR_DATE . "(last_updated,1,19) AS last_updated + FROM ttrss_feeds WHERE id = ?"); + $sth->execute([$feed]); - if ($this->dbh->num_rows($result) != 0) { - $last_updated = strtotime($this->dbh->fetch_result($result, 0, "last_updated")); - $cache_images = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "cache_images")); + if ($row = $sth->fetch()) { + $last_updated = strtotime($row["last_updated"]); + $cache_images = $row["cache_images"]; if (!$cache_images && time() - $last_updated > 120) { - RSSUtils::update_rss_feed($feed, true); + try { + RSSUtils::update_rss_feed($feed, true); + } catch (PDOException $e) { + user_error("PDO Exception while doing on-demand feed update for $feed: " . $e->getMessage(), E_USER_NOTICE); + } } else { - $this->dbh->query("UPDATE ttrss_feeds SET last_updated = '1970-01-01', last_update_started = '1970-01-01' - WHERE id = '$feed'"); + $sth = $this->pdo->prepare("UPDATE ttrss_feeds + SET last_updated = '1970-01-01', last_update_started = '1970-01-01' + WHERE id = ?"); + $sth->execute([$feed]); } } } else { - $this->dbh->query("UPDATE ttrss_feeds SET last_updated = '1970-01-01', last_update_started = '1970-01-01' - WHERE id = '$feed'"); + $sth = $this->pdo->prepare("UPDATE ttrss_feeds + SET last_updated = '1970-01-01', last_update_started = '1970-01-01' + WHERE id = ?"); + $sth->execute([$feed]); } } @@ -221,16 +229,16 @@ class Feeds extends Handler_Protected { // FIXME: might break tag display? if (is_numeric($feed) && $feed > 0 && !$cat_view) { - $result = $this->dbh->query( - "SELECT id FROM ttrss_feeds WHERE id = '$feed' LIMIT 1"); + $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE id = ? LIMIT 1"); + $sth->execute([$feed]); - if ($this->dbh->num_rows($result) == 0) { + if (!$sth->fetch()) { $reply['content'] = "<div align='center'>".__('Feed not found.')."</div>"; } } - @$search = $this->dbh->escape_string($_REQUEST["query"]); - @$search_language = $this->dbh->escape_string($_REQUEST["search_language"]); // PGSQL only + @$search = $_REQUEST["query"]; + @$search_language = $_REQUEST["search_language"]; // PGSQL only if ($search) { $disable_cache = true; @@ -238,7 +246,6 @@ class Feeds extends Handler_Protected { if ($_REQUEST["debug"]) $timing_info = print_checkpoint("H0", $timing_info); - if (!$cat_view && is_numeric($feed) && $feed < PLUGIN_FEED_BASE_INDEX && $feed > LABEL_BASE_INDEX) { $handler = PluginHost::getInstance()->get_feed_handler( PluginHost::feed_to_pfeed_id($feed)); @@ -283,7 +290,7 @@ class Feeds extends Handler_Protected { if ($_REQUEST["debug"]) $timing_info = print_checkpoint("H1", $timing_info); - $result = $qfh_ret[0]; + $result = $qfh_ret[0]; // this could be either a PDO query result or a -1 if first id changed $feed_title = $qfh_ret[1]; $feed_site_url = $qfh_ret[2]; $last_error = $qfh_ret[3]; @@ -300,8 +307,6 @@ class Feeds extends Handler_Protected { $feed, $cat_view, $search, $last_error, $last_updated); - $headlines_count = is_numeric($result) ? 0 : $this->dbh->num_rows($result); - if ($offset == 0) { foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_HEADLINES_BEFORE) as $p) { $reply['content'] .= $p->hook_headlines_before($feed, $cat_view, $qfh_ret); @@ -310,18 +315,18 @@ class Feeds extends Handler_Protected { $reply['content'] = ''; - if ($headlines_count > 0) { + $headlines_count = 0; - $lnum = $offset; + $lnum = $offset; + $num_unread = 0; + if ($_REQUEST["debug"]) $timing_info = print_checkpoint("PS", $timing_info); + $expand_cdm = get_pref('CDM_EXPANDED'); - $num_unread = 0; - $cur_feed_title = ''; + if (is_object($result)) { - if ($_REQUEST["debug"]) $timing_info = print_checkpoint("PS", $timing_info); + while ($line = $result->fetch()) { - $expand_cdm = get_pref('CDM_EXPANDED'); - - while ($line = $this->dbh->fetch_assoc($result)) { + ++$headlines_count; $line["content_preview"] = "— " . truncate_string(strip_tags($line["content"]), 250); @@ -338,6 +343,8 @@ class Feeds extends Handler_Protected { $label_cache = $line["label_cache"]; $labels = false; + $mouseover_attrs = "onmouseover='postMouseIn(event, $id)' onmouseout='postMouseOut($id)'"; + if ($label_cache) { $label_cache = json_decode($label_cache, true); @@ -361,45 +368,18 @@ class Feeds extends Handler_Protected { $class = ""; - if (sql_bool_to_bool($line["unread"])) { + if ($line["unread"]) { $class .= " Unread"; ++$num_unread; } - if (sql_bool_to_bool($line["marked"])) { - $marked_pic = "<img - src=\"images/mark_set.png\" - class=\"markedPic\" alt=\"Unstar article\" - onclick='toggleMark($id)'>"; - $class .= " marked"; - } else { - $marked_pic = "<img - src=\"images/mark_unset.png\" - class=\"markedPic\" alt=\"Star article\" - onclick='toggleMark($id)'>"; - } + $marked_pic_src = $line["marked"] ? "mark_set.png" : "mark_unset.png"; + $class .= $line["marked"] ? " marked" : ""; + $marked_pic = "<img src=\"images/$marked_pic_src\" class=\"markedPic\" onclick='toggleMark($id)'>"; - if (sql_bool_to_bool($line["published"])) { - $published_pic = "<img src=\"images/pub_set.png\" - class=\"pubPic\" - alt=\"Unpublish article\" onclick='togglePub($id)'>"; - $class .= " published"; - } else { - $published_pic = "<img src=\"images/pub_unset.png\" - class=\"pubPic\" - alt=\"Publish article\" onclick='togglePub($id)'>"; - } - -# $content_link = "<a target=\"_blank\" rel=\"noopener noreferrer\" href=\"".$line["link"]."\">" . -# $line["title"] . "</a>"; - -# $content_link = "<a -# href=\"" . htmlspecialchars($line["link"]) . "\" -# onclick=\"view($id,$feed_id);\">" . -# $line["title"] . "</a>"; - -# $content_link = "<a href=\"javascript:viewContentUrl('".$line["link"]."');\">" . -# $line["title"] . "</a>"; + $published_pic_src = $line["published"] ? "pub_set.png" : "pub_unset.png"; + $class .= $line["published"] ? " published" : ""; + $published_pic = "<img src=\"images/$published_pic_src\" class=\"pubPic\" onclick='togglePub($id)'>"; $updated_fmt = make_local_datetime($line["updated"], false, false, false, true); $date_entered_fmt = T_sprintf("Imported at %s", @@ -409,12 +389,8 @@ class Feeds extends Handler_Protected { $score_pic = "images/" . get_score_pic($score); -/* $score_title = __("(Click to change)"); - $score_pic = "<img class='hlScorePic' src=\"images/$score_pic\" - onclick=\"adjustArticleScore($id, $score)\" title=\"$score $score_title\">"; */ - $score_pic = "<img class='hlScorePic' score='$score' onclick='changeScore($id, this)' src=\"$score_pic\" - title=\"$score\">"; + title=\"$score\">"; if ($score > 500) { $hlc_suffix = "high"; @@ -430,7 +406,7 @@ class Feeds extends Handler_Protected { $entry_author = " — $entry_author"; } - $has_feed_icon = feed_has_icon($feed_id); + $has_feed_icon = feeds::feedHasIcon($feed_id); if ($has_feed_icon) { $feed_icon_img = "<img class=\"tinyFeedIcon\" src=\"".ICONS_URL."/$feed_id.ico\" alt=\"\">"; @@ -456,33 +432,27 @@ class Feeds extends Handler_Protected { if ($vfeed_group_enabled) { if ($feed_id != $vgroup_last_feed && $line["feed_title"]) { - $cur_feed_title = $line["feed_title"]; $vgroup_last_feed = $feed_id; - $cur_feed_title = htmlspecialchars($cur_feed_title); - $vf_catchup_link = "<a class='catchup' onclick='catchupFeedInGroup($feed_id);' href='#'>".__('mark feed as read')."</a>"; $reply['content'] .= "<div data-feed-id='$feed_id' id='FTITLE-$feed_id' class='cdmFeedTitle'>". "<div style='float : right'>$feed_icon_img</div>". "<a class='title' href=\"#\" onclick=\"viewfeed({feed:$feed_id})\">". $line["feed_title"]."</a> - $vf_catchup_link</div>"; + $vf_catchup_link</div>"; } } - $mouseover_attrs = "onmouseover='postMouseIn(event, $id)' - onmouseout='postMouseOut($id)'"; - $reply['content'] .= "<div class='hl hlMenuAttach $class' data-orig-feed-id='$feed_id' data-article-id='$id' id='RROW-$id' $mouseover_attrs>"; $reply['content'] .= "<div class='hlLeft'>"; $reply['content'] .= "<input dojoType=\"dijit.form.CheckBox\" - type=\"checkbox\" onclick=\"toggleSelectRow2(this)\" - class='rchk'>"; + type=\"checkbox\" onclick=\"toggleSelectRow2(this)\" + class='rchk'>"; $reply['content'] .= "$marked_pic"; $reply['content'] .= "$published_pic"; @@ -490,14 +460,14 @@ class Feeds extends Handler_Protected { $reply['content'] .= "</div>"; $reply['content'] .= "<div onclick='return hlClicked(event, $id)' - class=\"hlTitle\"><span class='hlContent $hlc_suffix'>"; + class=\"hlTitle\"><span class='hlContent $hlc_suffix'>"; $reply['content'] .= "<a id=\"RTITLE-$id\" class=\"title $hlc_suffix\" - href=\"" . htmlspecialchars($line["link"]) . "\" - onclick=\"\">" . + href=\"" . htmlspecialchars($line["link"]) . "\" + onclick=\"\">" . truncate_string($line["title"], 200); if (get_pref('SHOW_CONTENT_PREVIEW')) { - $reply['content'] .= "<span class=\"contentPreview\">" . $line["content_preview"] . "</span>"; + $reply['content'] .= "<span class=\"contentPreview\">" . $line["content_preview"] . "</span>"; } $reply['content'] .= "</a></span>"; @@ -519,7 +489,7 @@ class Feeds extends Handler_Protected { $reply['content'] .= "<span class=\"hlUpdated\">"; $reply['content'] .= "<div title='$date_entered_fmt'>$updated_fmt</div> - </span>"; + </span>"; $reply['content'] .= "<div class=\"hlRight\">"; @@ -528,9 +498,9 @@ class Feeds extends Handler_Protected { if ($line["feed_title"] && !$vfeed_group_enabled) { $reply['content'] .= "<span onclick=\"viewfeed({feed:$feed_id})\" - style=\"cursor : pointer\" - title=\"".htmlspecialchars($line['feed_title'])."\"> - $feed_icon_img</span>"; + style=\"cursor : pointer\" + title=\"".htmlspecialchars($line['feed_title'])."\"> + $feed_icon_img</span>"; } $reply['content'] .= "</div>"; @@ -544,7 +514,7 @@ class Feeds extends Handler_Protected { $tags = false; $line["content"] = sanitize($line["content"], - sql_bool_to_bool($line['hide_images']), false, $entry_site_url, $highlight_words, $line["id"]); + $line['hide_images'], false, $entry_site_url, $highlight_words, $line["id"]); foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_RENDER_ARTICLE_CDM) as $p) { $line = $p->hook_render_article_cdm($line); @@ -553,20 +523,12 @@ class Feeds extends Handler_Protected { if ($vfeed_group_enabled && $line["feed_title"]) { if ($feed_id != $vgroup_last_feed) { - $cur_feed_title = $line["feed_title"]; $vgroup_last_feed = $feed_id; - $cur_feed_title = htmlspecialchars($cur_feed_title); - $vf_catchup_link = "<a class='catchup' onclick='catchupFeedInGroup($feed_id);' href='#'>".__('mark feed as read')."</a>"; - $has_feed_icon = feed_has_icon($feed_id); - - if ($has_feed_icon) { - $feed_icon_img = "<img class=\"tinyFeedIcon\" src=\"".ICONS_URL."/$feed_id.ico\" alt=\"\">"; - } else { - //$feed_icon_img = "<img class=\"tinyFeedIcon\" src=\"images/blank_icon.gif\" alt=\"\">"; - } + $feed_icon_src = Feeds::getFeedIcon($feed_id); + $feed_icon_img = "<img class=\"tinyFeedIcon\" src=\"$feed_icon_src\">"; $reply['content'] .= "<div data-feed-id='$feed_id' id='FTITLE-$feed_id' class='cdmFeedTitle'>". "<div style=\"float : right\">$feed_icon_img</div>". @@ -576,27 +538,24 @@ class Feeds extends Handler_Protected { } } - $mouseover_attrs = "onmouseover='postMouseIn(event, $id)' - onmouseout='postMouseOut($id)'"; - $expanded_class = $expand_cdm ? "expanded" : "expandable"; - + $tmp_content = "<div class=\"cdm $hlc_suffix $expanded_class $class\" - id=\"RROW-$id\" data-article-id='$id' data-orig-feed-id='$feed_id' $mouseover_attrs>"; + id=\"RROW-$id\" data-article-id='$id' data-orig-feed-id='$feed_id' $mouseover_attrs>"; $tmp_content .= "<div class=\"cdmHeader\">"; $tmp_content .= "<div style=\"vertical-align : middle\">"; $tmp_content .= "<input dojoType=\"dijit.form.CheckBox\" - type=\"checkbox\" onclick=\"toggleSelectRow2(this, false, true)\" - class='rchk'>"; + type=\"checkbox\" onclick=\"toggleSelectRow2(this, false, true)\" + class='rchk'>"; $tmp_content .= "$marked_pic"; $tmp_content .= "$published_pic"; $tmp_content .= "</div>"; - if ($highlight_words && count($highlight_words > 0)) { + if ($highlight_words && count($highlight_words) > 0) { foreach ($highlight_words as $word) { $line["title"] = preg_replace("/(\Q$word\E)/i", "<span class=\"highlight\">$1</span>", $line["title"]); @@ -605,12 +564,12 @@ class Feeds extends Handler_Protected { // data-article-id included for context menu $tmp_content .= "<span id=\"RTITLE-$id\" - onclick=\"return cdmClicked(event, $id);\" - data-article-id=\"$id\" - class=\"titleWrap hlMenuAttach $hlc_suffix\"> - <a class=\"title $hlc_suffix\" - title=\"".htmlspecialchars($line["title"])."\" - target=\"_blank\" rel=\"noopener noreferrer\" href=\"". + onclick=\"return cdmClicked(event, $id);\" + data-article-id=\"$id\" + class=\"titleWrap hlMenuAttach $hlc_suffix\"> + <a class=\"title $hlc_suffix\" + title=\"".htmlspecialchars($line["title"])."\" + target=\"_blank\" rel=\"noopener noreferrer\" href=\"". htmlspecialchars($line["link"])."\">". $line["title"] . "</a> <span class=\"author\">$entry_author</span>"; @@ -618,8 +577,8 @@ class Feeds extends Handler_Protected { $tmp_content .= $labels_str; $tmp_content .= "<span class='collapseBtn' style='display : none'> - <img src=\"images/collapse.png\" onclick=\"cdmCollapseArticle(event, $id)\" - title=\"".__("Collapse article")."\"/></span>"; + <img src=\"images/collapse.png\" onclick=\"cdmCollapseArticle(event, $id)\" + title=\"".__("Collapse article")."\"/></span>"; if (!$expand_cdm) $content_hidden = "style=\"display : none\""; @@ -635,10 +594,10 @@ class Feeds extends Handler_Protected { $rgba = @$rgba_cache[$feed_id]; $tmp_content .= "<div class=\"hlFeed\"> - <a href=\"#\" style=\"background-color: rgba($rgba,0.3)\" - onclick=\"viewfeed({feed:$feed_id})\">". + <a href=\"#\" style=\"background-color: rgba($rgba,0.3)\" + onclick=\"viewfeed({feed:$feed_id})\">". truncate_string($line["feed_title"],30)."</a> - </div>"; + </div>"; } } @@ -649,16 +608,16 @@ class Feeds extends Handler_Protected { if (!get_pref("VFEED_GROUP_BY_FEED") && $line["feed_title"]) { $tmp_content .= "<span style=\"cursor : pointer\" - title=\"".htmlspecialchars($line["feed_title"])."\" - onclick=\"viewfeed({feed:$feed_id})\">$feed_icon_img</span>"; + title=\"".htmlspecialchars($line["feed_title"])."\" + onclick=\"viewfeed({feed:$feed_id})\">$feed_icon_img</span>"; } $tmp_content .= "</div>"; //scoreWrap $tmp_content .= "</div>"; //cdmHeader $tmp_content .= "<div class=\"cdmContent\" $content_hidden - onclick=\"return cdmClicked(event, $id, true);\" - id=\"CICD-$id\">"; + onclick=\"return cdmClicked(event, $id, true);\" + id=\"CICD-$id\">"; $tmp_content .= "<div id=\"POSTNOTE-$id\">"; if ($line['note']) { @@ -672,20 +631,19 @@ class Feeds extends Handler_Protected { if ($line["orig_feed_id"]) { - $tmp_result = $this->dbh->query("SELECT * FROM ttrss_archived_feeds - WHERE id = ".$line["orig_feed_id"] . " AND owner_uid = " . $_SESSION["uid"]); + $ofgh = $this->pdo->prepare("SELECT * FROM ttrss_archived_feeds + WHERE id = ? AND owner_uid = ?"); + $ofgh->execute([$line["orig_feed_id"], $_SESSION['uid']]); - if ($this->dbh->num_rows($tmp_result) != 0) { + if ($tmp_line = $ofgh->fetch()) { $tmp_content .= "<div clear='both'>"; $tmp_content .= __("Originally from:"); $tmp_content .= " "; - $tmp_line = $this->dbh->fetch_assoc($tmp_result); - $tmp_content .= "<a target='_blank' rel='noopener noreferrer' - href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" . + href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" . $tmp_line['title'] . "</a>"; $tmp_content .= " "; @@ -707,8 +665,9 @@ class Feeds extends Handler_Protected { $tmp_content .= "<div class=\"cdmIntermediate\">"; - $always_display_enclosures = sql_bool_to_bool($line["always_display_enclosures"]); - $tmp_content .= Article::format_article_enclosures($id, $always_display_enclosures, $line["content"], sql_bool_to_bool($line["hide_images"])); + $always_display_enclosures = $line["always_display_enclosures"]; + $tmp_content .= Article::format_article_enclosures($id, $always_display_enclosures, + $line["content"], $line["hide_images"]); $tmp_content .= "</div>"; // cdmIntermediate @@ -723,9 +682,9 @@ class Feeds extends Handler_Protected { $tmp_content .= "<span class='left'>"; $tmp_content .= "<img src='images/tag.png' alt='Tags' title='Tags'> - <span id=\"ATSTR-$id\">$tags_str</span> - <a title=\"".__('Edit tags for this article')."\" - href=\"#\" onclick=\"editArticleTags($id)\">(+)</a>"; + <span id=\"ATSTR-$id\">$tags_str</span> + <a title=\"".__('Edit tags for this article')."\" + href=\"#\" onclick=\"editArticleTags($id)\">(+)</a>"; $num_comments = (int) $line["num_comments"]; $entry_comments = ""; @@ -737,7 +696,7 @@ class Feeds extends Handler_Protected { $comments_url = htmlspecialchars($line["link"]); } $entry_comments = "<a class=\"postComments\" - target='_blank' rel='noopener noreferrer' href=\"$comments_url\">$num_comments ". + target='_blank' rel='noopener noreferrer' href=\"$comments_url\">$num_comments ". _ngettext("comment", "comments", $num_comments)."</a>"; } else { @@ -751,9 +710,6 @@ class Feeds extends Handler_Protected { $tmp_content .= "</span>"; $tmp_content .= "<div>"; -// $tmp_content .= "$marked_pic"; -// $tmp_content .= "$published_pic"; - foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ARTICLE_BUTTON) as $p) { $tmp_content .= $p->hook_article_button($line); } @@ -773,58 +729,64 @@ class Feeds extends Handler_Protected { ++$lnum; } + } - if ($_REQUEST["debug"]) $timing_info = print_checkpoint("PE", $timing_info); - - } else if (!is_numeric($result)) { - $message = ""; - - switch ($view_mode) { - case "unread": - $message = __("No unread articles found to display."); - break; - case "updated": - $message = __("No updated articles found to display."); - break; - case "marked": - $message = __("No starred articles found to display."); - break; - default: - if ($feed < LABEL_BASE_INDEX) { - $message = __("No articles found to display. You can assign articles to labels manually from article header context menu (applies to all selected articles) or use a filter."); - } else { - $message = __("No articles found to display."); - } - } + if ($_REQUEST["debug"]) $timing_info = print_checkpoint("PE", $timing_info); + + if (!$headlines_count) { + + if (!is_numeric($result)) { - if (!$offset && $message) { - $reply['content'] = "<div class='whiteBox'>$message"; + switch ($view_mode) { + case "unread": + $message = __("No unread articles found to display."); + break; + case "updated": + $message = __("No updated articles found to display."); + break; + case "marked": + $message = __("No starred articles found to display."); + break; + default: + if ($feed < LABEL_BASE_INDEX) { + $message = __("No articles found to display. You can assign articles to labels manually from article header context menu (applies to all selected articles) or use a filter."); + } else { + $message = __("No articles found to display."); + } + } - $reply['content'] .= "<p><span class=\"insensitive\">"; + if (!$offset && $message) { + $reply['content'] = "<div class='whiteBox'>$message"; - $result = $this->dbh->query("SELECT ".SUBSTRING_FOR_DATE."(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds - WHERE owner_uid = " . $_SESSION['uid']); + $reply['content'] .= "<p><span class=\"insensitive\">"; - $last_updated = $this->dbh->fetch_result($result, 0, "last_updated"); - $last_updated = make_local_datetime($last_updated, false); + $sth = $this->pdo->prepare("SELECT " . SUBSTRING_FOR_DATE . "(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds + WHERE owner_uid = ?"); + $sth->execute([$_SESSION['uid']]); + $row = $sth->fetch(); - $reply['content'] .= sprintf(__("Feeds last updated at %s"), $last_updated); + $last_updated = make_local_datetime($row["last_updated"], false); - $result = $this->dbh->query("SELECT COUNT(id) AS num_errors - FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ".$_SESSION["uid"]); + $reply['content'] .= sprintf(__("Feeds last updated at %s"), $last_updated); - $num_errors = $this->dbh->fetch_result($result, 0, "num_errors"); + $sth = $this->pdo->prepare("SELECT COUNT(id) AS num_errors + FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ?"); + $sth->execute([$_SESSION['uid']]); + $row = $sth->fetch(); - if ($num_errors > 0) { - $reply['content'] .= "<br/>"; - $reply['content'] .= "<a class=\"insensitive\" href=\"#\" onclick=\"showFeedsWithErrors()\">". - __('Some feeds have update errors (click for details)')."</a>"; - } - $reply['content'] .= "</span></p></div>"; + $num_errors = $row["num_errors"]; + if ($num_errors > 0) { + $reply['content'] .= "<br/>"; + $reply['content'] .= "<a class=\"insensitive\" href=\"#\" onclick=\"showFeedsWithErrors()\">" . + __('Some feeds have update errors (click for details)') . "</a>"; + } + $reply['content'] .= "</span></p></div>"; + + } + } else if (is_numeric($result) && $result == -1) { + $reply['first_id_changed'] = true; } - } else if (is_numeric($result) && $result == -1) { - $reply['first_id_changed'] = true; } if ($_REQUEST["debug"]) $timing_info = print_checkpoint("H2", $timing_info); @@ -834,8 +796,10 @@ class Feeds extends Handler_Protected { } function catchupAll() { - $this->dbh->query("UPDATE ttrss_user_entries SET - last_read = NOW(), unread = false WHERE unread = true AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET + last_read = NOW(), unread = false WHERE unread = true AND owner_uid = ?"); + $sth->execute([$_SESSION['uid']]); + CCache::zero_all($_SESSION["uid"]); } @@ -846,16 +810,16 @@ class Feeds extends Handler_Protected { if ($_REQUEST["debug"]) $timing_info = print_checkpoint("0", $timing_info); - $feed = $this->dbh->escape_string($_REQUEST["feed"]); - $method = $this->dbh->escape_string($_REQUEST["m"]); - $view_mode = $this->dbh->escape_string($_REQUEST["view_mode"]); + $feed = $_REQUEST["feed"]; + $method = $_REQUEST["m"]; + $view_mode = $_REQUEST["view_mode"]; $limit = 30; @$cat_view = $_REQUEST["cat"] == "true"; - @$next_unread_feed = $this->dbh->escape_string($_REQUEST["nuf"]); - @$offset = $this->dbh->escape_string($_REQUEST["skip"]); - @$vgroup_last_feed = $this->dbh->escape_string($_REQUEST["vgrlf"]); - $order_by = $this->dbh->escape_string($_REQUEST["order_by"]); - $check_first_id = $this->dbh->escape_string($_REQUEST["fid"]); + @$next_unread_feed = $_REQUEST["nuf"]; + @$offset = $_REQUEST["skip"]; + @$vgroup_last_feed = $_REQUEST["vgrlf"]; + $order_by = $_REQUEST["order_by"]; + $check_first_id = $_REQUEST["fid"]; if (is_numeric($feed)) $feed = (int) $feed; @@ -867,21 +831,30 @@ class Feeds extends Handler_Protected { return; } - $result = false; - + $sth = false; if ($feed < LABEL_BASE_INDEX) { + $label_feed = Labels::feed_to_label_id($feed); - $result = $this->dbh->query("SELECT id FROM ttrss_labels2 WHERE - id = '$label_feed' AND owner_uid = " . $_SESSION['uid']); + + $sth = $this->pdo->prepare("SELECT id FROM ttrss_labels2 WHERE + id = ? AND owner_uid = ?"); + $sth->execute([$label_feed, $_SESSION['uid']]); + } else if (!$cat_view && is_numeric($feed) && $feed > 0) { - $result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE - id = '$feed' AND owner_uid = " . $_SESSION['uid']); + + $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE + id = ? AND owner_uid = ?"); + $sth->execute([$feed, $_SESSION['uid']]); + } else if ($cat_view && is_numeric($feed) && $feed > 0) { - $result = $this->dbh->query("SELECT id FROM ttrss_feed_categories WHERE - id = '$feed' AND owner_uid = " . $_SESSION['uid']); + + $sth = $this->pdo->prepare("SELECT id FROM ttrss_feed_categories WHERE + id = ? AND owner_uid = ?"); + + $sth->execute([$feed, $_SESSION['uid']]); } - if ($result && $this->dbh->num_rows($result) == 0) { + if ($sth && !$sth->fetch()) { print json_encode($this->generate_error_feed(__("Feed not found."))); return; } @@ -898,14 +871,16 @@ class Feeds extends Handler_Protected { /* bump login timestamp if needed */ if (time() - $_SESSION["last_login_update"] > 3600) { - $this->dbh->query("UPDATE ttrss_users SET last_login = NOW() WHERE id = " . - $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_users SET last_login = NOW() WHERE id = ?"); + $sth->execute([$_SESSION['uid']]); + $_SESSION["last_login_update"] = time(); } if (!$cat_view && is_numeric($feed) && $feed > 0) { - $this->dbh->query("UPDATE ttrss_feeds SET last_viewed = NOW() - WHERE id = '$feed' AND owner_uid = ".$_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_feeds SET last_viewed = NOW() + WHERE id = ? AND owner_uid = ?"); + $sth->execute([$feed, $_SESSION['uid']]); } $reply['headlines'] = array(); @@ -976,18 +951,21 @@ class Feeds extends Handler_Protected { $reply['headlines']['content'] .= "<p><span class=\"insensitive\">"; - $result = $this->dbh->query("SELECT ".SUBSTRING_FOR_DATE."(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds - WHERE owner_uid = " . $_SESSION['uid']); + $sth = $this->pdo->prepare("SELECT ".SUBSTRING_FOR_DATE."(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds + WHERE owner_uid = ?"); + $sth->execute([$_SESSION['uid']]); + $row = $sth->fetch(); - $last_updated = $this->dbh->fetch_result($result, 0, "last_updated"); - $last_updated = make_local_datetime($last_updated, false); + $last_updated = make_local_datetime($row["last_updated"], false); $reply['headlines']['content'] .= sprintf(__("Feeds last updated at %s"), $last_updated); - $result = $this->dbh->query("SELECT COUNT(id) AS num_errors - FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ".$_SESSION["uid"]); + $sth = $this->pdo->prepare("SELECT COUNT(id) AS num_errors + FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ?"); + $sth->execute([$_SESSION['uid']]); + $row = $sth->fetch(); - $num_errors = $this->dbh->fetch_result($result, 0, "num_errors"); + $num_errors = $row["num_errors"]; if ($num_errors > 0) { $reply['headlines']['content'] .= "<br/>"; @@ -1104,7 +1082,7 @@ class Feeds extends Handler_Protected { function feedBrowser() { if (defined('_DISABLE_FEED_BROWSER') && _DISABLE_FEED_BROWSER) return; - $browser_search = $this->dbh->escape_string($_REQUEST["search"]); + $browser_search = $_REQUEST["search"]; print_hidden("op", "rpc"); print_hidden("method", "updateFeedBrowser"); @@ -1150,7 +1128,7 @@ class Feeds extends Handler_Protected { } function search() { - $this->params = explode(":", $this->dbh->escape_string($_REQUEST["param"]), 2); + $this->params = explode(":", $_REQUEST["param"], 2); $active_feed_id = sprintf("%d", $this->params[0]); $is_cat = $this->params[1] != "false"; @@ -1193,16 +1171,24 @@ class Feeds extends Handler_Protected { @$do_update = $_REQUEST["action"] == "do_update"; $csrf_token = $_REQUEST["csrf_token"]; + $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE id = ? AND owner_uid = ?"); + $sth->execute([$feed_id, $_SESSION['uid']]); + + if (!$sth->fetch()) { + print "Access denied."; + return; + } + $refetch_checked = isset($_REQUEST["force_refetch"]) ? "checked" : ""; $rehash_checked = isset($_REQUEST["force_rehash"]) ? "checked" : ""; ?> <html> <head> - <link rel="stylesheet" type="text/css" href="css/utility.css"> + <?php echo stylesheet_tag("css/default.css") ?> <title>Feed Debugger</title> </head> - <body class="small_margins"> + <body class="small_margins ttrss_utility claro"> <h1>Feed Debugger: <?php echo "$feed_id: " . $this->getFeedTitle($feed_id) ?></h1> <form method="GET" action=""> <input type="hidden" name="op" value="feeds"> @@ -1237,9 +1223,10 @@ class Feeds extends Handler_Protected { if (!$owner_uid) $owner_uid = $_SESSION['uid']; + $pdo = Db::pdo(); + // Todo: all this interval stuff needs some generic generator function - $date_qpart = "false"; $search_qpart = is_array($search) && $search[0] ? search_to_sql($search[0], $search[1])[0] : 'true'; switch ($mode) { @@ -1284,50 +1271,55 @@ class Feeds extends Handler_Protected { $cat_qpart = "cat_id IS NULL"; } - db_query("UPDATE ttrss_user_entries + $sth = $pdo->prepare("UPDATE ttrss_user_entries SET unread = false, last_read = NOW() WHERE ref_id IN (SELECT id FROM (SELECT DISTINCT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id - AND owner_uid = $owner_uid AND unread = true AND feed_id IN + AND owner_uid = ? AND unread = true AND feed_id IN (SELECT id FROM ttrss_feeds WHERE $cat_qpart) AND $date_qpart AND $search_qpart) as tmp)"); + $sth->execute([$owner_uid]); } else if ($feed == -2) { - db_query("UPDATE ttrss_user_entries + $sth = $pdo->prepare("UPDATE ttrss_user_entries SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*) FROM ttrss_user_labels2, ttrss_entries WHERE article_id = ref_id AND id = ref_id AND $date_qpart AND $search_qpart) > 0 - AND unread = true AND owner_uid = $owner_uid"); + AND unread = true AND owner_uid = ?"); + $sth->execute([$owner_uid]); } } else if ($feed > 0) { - db_query("UPDATE ttrss_user_entries + $sth = $pdo->prepare("UPDATE ttrss_user_entries SET unread = false, last_read = NOW() WHERE ref_id IN (SELECT id FROM (SELECT DISTINCT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id - AND owner_uid = $owner_uid AND unread = true AND feed_id = $feed AND $date_qpart AND $search_qpart) as tmp)"); + AND owner_uid = ? AND unread = true AND feed_id = ? AND $date_qpart AND $search_qpart) as tmp)"); + $sth->execute([$owner_uid, $feed]); } else if ($feed < 0 && $feed > LABEL_BASE_INDEX) { // special, like starred if ($feed == -1) { - db_query("UPDATE ttrss_user_entries + $sth = $pdo->prepare("UPDATE ttrss_user_entries SET unread = false, last_read = NOW() WHERE ref_id IN (SELECT id FROM (SELECT DISTINCT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id - AND owner_uid = $owner_uid AND unread = true AND marked = true AND $date_qpart AND $search_qpart) as tmp)"); + AND owner_uid = ? AND unread = true AND marked = true AND $date_qpart AND $search_qpart) as tmp)"); + $sth->execute([$owner_uid]); } if ($feed == -2) { - db_query("UPDATE ttrss_user_entries + $sth = $pdo->prepare("UPDATE ttrss_user_entries SET unread = false, last_read = NOW() WHERE ref_id IN (SELECT id FROM (SELECT DISTINCT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id - AND owner_uid = $owner_uid AND unread = true AND published = true AND $date_qpart AND $search_qpart) as tmp)"); + AND owner_uid = ? AND unread = true AND published = true AND $date_qpart AND $search_qpart) as tmp)"); + $sth->execute([$owner_uid]); } if ($feed == -3) { - $intl = get_pref("FRESH_ARTICLE_MAX_AGE"); + $intl = (int) get_pref("FRESH_ARTICLE_MAX_AGE"); if (DB_TYPE == "pgsql") { $match_part = "date_entered > NOW() - INTERVAL '$intl hour' "; @@ -1336,43 +1328,47 @@ class Feeds extends Handler_Protected { INTERVAL $intl HOUR) "; } - db_query("UPDATE ttrss_user_entries + $sth = $pdo->prepare("UPDATE ttrss_user_entries SET unread = false, last_read = NOW() WHERE ref_id IN (SELECT id FROM (SELECT DISTINCT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id - AND owner_uid = $owner_uid AND score >= 0 AND unread = true AND $date_qpart AND $match_part AND $search_qpart) as tmp)"); + AND owner_uid = ? AND score >= 0 AND unread = true AND $date_qpart AND $match_part AND $search_qpart) as tmp)"); + $sth->execute([$owner_uid]); } if ($feed == -4) { - db_query("UPDATE ttrss_user_entries + $sth = $pdo->prepare("UPDATE ttrss_user_entries SET unread = false, last_read = NOW() WHERE ref_id IN (SELECT id FROM (SELECT DISTINCT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id - AND owner_uid = $owner_uid AND unread = true AND $date_qpart AND $search_qpart) as tmp)"); + AND owner_uid = ? AND unread = true AND $date_qpart AND $search_qpart) as tmp)"); + $sth->execute([$owner_uid]); } } else if ($feed < LABEL_BASE_INDEX) { // label $label_id = Labels::feed_to_label_id($feed); - db_query("UPDATE ttrss_user_entries + $sth = $pdo->prepare("UPDATE ttrss_user_entries SET unread = false, last_read = NOW() WHERE ref_id IN (SELECT id FROM (SELECT DISTINCT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_user_labels2 WHERE ref_id = id - AND label_id = '$label_id' AND ref_id = article_id - AND owner_uid = $owner_uid AND unread = true AND $date_qpart AND $search_qpart) as tmp)"); + AND label_id = ? AND ref_id = article_id + AND owner_uid = ? AND unread = true AND $date_qpart AND $search_qpart) as tmp)"); + $sth->execute([$label_id, $owner_uid]); } CCache::update($feed, $owner_uid, $cat_view); } else { // tag - db_query("UPDATE ttrss_user_entries + $sth = $pdo->prepare("UPDATE ttrss_user_entries SET unread = false, last_read = NOW() WHERE ref_id IN (SELECT id FROM (SELECT DISTINCT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_tags WHERE ref_id = ttrss_entries.id - AND post_int_id = int_id AND tag_name = '$feed' - AND ttrss_user_entries.owner_uid = $owner_uid AND unread = true AND $date_qpart AND $search_qpart) as tmp)"); + AND post_int_id = int_id AND tag_name = ? + AND ttrss_user_entries.owner_uid = ? AND unread = true AND $date_qpart AND $search_qpart) as tmp)"); + $sth->execute([$feed, $owner_uid]); } } @@ -1383,6 +1379,8 @@ class Feeds extends Handler_Protected { $n_feed = (int) $feed; $need_entries = false; + $pdo = Db::pdo(); + if (!$owner_uid) $owner_uid = $_SESSION["uid"]; if ($unread_only) { @@ -1391,19 +1389,23 @@ class Feeds extends Handler_Protected { $unread_qpart = "true"; } + $match_part = ""; + if ($is_cat) { return Feeds::getCategoryUnread($n_feed, $owner_uid); } else if ($n_feed == -6) { return 0; } else if ($feed != "0" && $n_feed == 0) { - $feed = db_escape_string($feed); - - $result = db_query("SELECT SUM((SELECT COUNT(int_id) + $sth = $pdo->prepare("SELECT SUM((SELECT COUNT(int_id) FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id AND ref_id = id AND $unread_qpart)) AS count FROM ttrss_tags - WHERE owner_uid = $owner_uid AND tag_name = '$feed'"); - return db_fetch_result($result, 0, "count"); + WHERE owner_uid = ? AND tag_name = ?"); + + $sth->execute([$owner_uid, $feed]); + $row = $sth->fetch(); + + return $row["count"]; } else if ($n_feed == -1) { $match_part = "marked = true"; @@ -1412,7 +1414,7 @@ class Feeds extends Handler_Protected { } else if ($n_feed == -3) { $match_part = "unread = true AND score >= 0"; - $intl = get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid); + $intl = (int) get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid); if (DB_TYPE == "pgsql") { $match_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' "; @@ -1437,7 +1439,6 @@ class Feeds extends Handler_Protected { $label_id = Labels::feed_to_label_id($feed); return Feeds::getLabelUnread($label_id, $owner_uid); - } if ($match_part) { @@ -1450,25 +1451,26 @@ class Feeds extends Handler_Protected { $from_where = ""; } - $query = "SELECT count(int_id) AS unread + $sth = $pdo->prepare("SELECT count(int_id) AS unread FROM $from_qpart WHERE - $unread_qpart AND $from_where ($match_part) AND ttrss_user_entries.owner_uid = $owner_uid"; + $unread_qpart AND $from_where ($match_part) AND ttrss_user_entries.owner_uid = ?"); + $sth->execute([$owner_uid]); + $row = $sth->fetch(); - //echo "[$feed/$query]\n"; - - $result = db_query($query); + return $row["unread"]; } else { - $result = db_query("SELECT COUNT(post_int_id) AS unread + $sth = $pdo->prepare("SELECT COUNT(post_int_id) AS unread FROM ttrss_tags,ttrss_user_entries,ttrss_entries - WHERE tag_name = '$feed' AND post_int_id = int_id AND ref_id = ttrss_entries.id - AND $unread_qpart AND ttrss_tags.owner_uid = " . $owner_uid); - } + WHERE tag_name = ? AND post_int_id = int_id AND ref_id = ttrss_entries.id + AND $unread_qpart AND ttrss_tags.owner_uid = ,"); - $unread = db_fetch_result($result, 0, "unread"); + $sth->execute([$feed, $owner_uid]); + $row = $sth->fetch(); - return $unread; + return $row["unread"]; + } } /** @@ -1490,6 +1492,8 @@ class Feeds extends Handler_Protected { global $fetch_last_error; global $fetch_last_error_content; + $pdo = Db::pdo(); + $url = fix_url($url); if (!$url || !validate_feed_url($url)) return array("code" => 2); @@ -1520,41 +1524,46 @@ class Feeds extends Handler_Protected { $url = key($feedUrls); } - if ($cat_id == "0" || !$cat_id) { - $cat_qpart = "NULL"; - } else { - $cat_qpart = "'$cat_id'"; - } - - $result = db_query( - "SELECT id FROM ttrss_feeds - WHERE feed_url = '$url' AND owner_uid = ".$_SESSION["uid"]); + if (!$cat_id) $cat_id = null; - $auth_pass = db_escape_string($auth_pass); + $sth = $pdo->prepare("SELECT id FROM ttrss_feeds + WHERE feed_url = ? AND owner_uid = ?"); + $sth->execute([$url, $_SESSION['uid']]); - if (db_num_rows($result) == 0) { - $result = db_query( + if ($row = $sth->fetch()) { + return array("code" => 0, "feed_id" => (int) $row["id"]); + } else { + $sth = $pdo->prepare( "INSERT INTO ttrss_feeds (owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method,auth_pass_encrypted) - VALUES ('".$_SESSION["uid"]."', '$url', - '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', 0, false)"); + VALUES (?, ?, ?, ?, ?, ?, 0, false)"); - $result = db_query( - "SELECT id FROM ttrss_feeds WHERE feed_url = '$url' - AND owner_uid = " . $_SESSION["uid"]); + $sth->execute([$_SESSION['uid'], $url, "[Unknown]", $cat_id, $auth_login, $auth_pass]); - $feed_id = db_fetch_result($result, 0, "id"); + $sth = $pdo->prepare("SELECT id FROM ttrss_feeds WHERE feed_url = ? + AND owner_uid = ?"); + $sth->execute([$url, $_SESSION['uid']]); + $row = $sth->fetch(); + + $feed_id = $row["id"]; if ($feed_id) { RSSUtils::set_basic_feed_info($feed_id); } return array("code" => 1, "feed_id" => (int) $feed_id); - } else { - return array("code" => 0, "feed_id" => (int) db_fetch_result($result, 0, "id")); + } } + static function getIconFile($feed_id) { + return ICONS_DIR . "/$feed_id.ico"; + } + + static function feedHasIcon($id) { + return is_file(ICONS_DIR . "/$id.ico") && filesize(ICONS_DIR . "/$id.ico") > 0; + } + static function getFeedIcon($id) { switch ($id) { case 0: @@ -1579,8 +1588,11 @@ class Feeds extends Handler_Protected { if ($id < LABEL_BASE_INDEX) { return "images/label.png"; } else { - if (file_exists(ICONS_DIR . "/$id.ico")) - return ICONS_URL . "/$id.ico"; + $icon = self::getIconFile($id); + + if ($icon && file_exists($icon)) { + return ICONS_URL . "/" . basename($icon) . "?" . filemtime($icon); + } } break; } @@ -1589,6 +1601,8 @@ class Feeds extends Handler_Protected { } static function getFeedTitle($id, $cat = false) { + $pdo = Db::pdo(); + if ($cat) { return Feeds::getCategoryTitle($id); } else if ($id == -1) { @@ -1604,21 +1618,29 @@ class Feeds extends Handler_Protected { } else if ($id == -6) { return __("Recently read"); } else if ($id < LABEL_BASE_INDEX) { + $label_id = Labels::feed_to_label_id($id); - $result = db_query("SELECT caption FROM ttrss_labels2 WHERE id = '$label_id'"); - if (db_num_rows($result) == 1) { - return db_fetch_result($result, 0, "caption"); + + $sth = $pdo->prepare("SELECT caption FROM ttrss_labels2 WHERE id = ?"); + $sth->execute([$label_id]); + + if ($row = $sth->fetch()) { + return $row["caption"]; } else { return "Unknown label ($label_id)"; } } else if (is_numeric($id) && $id > 0) { - $result = db_query("SELECT title FROM ttrss_feeds WHERE id = '$id'"); - if (db_num_rows($result) == 1) { - return db_fetch_result($result, 0, "title"); + + $sth = $pdo->prepare("SELECT title FROM ttrss_feeds WHERE id = ?"); + $sth->execute([$id]); + + if ($row = $sth->fetch()) { + return $row["title"]; } else { return "Unknown feed ($id)"; } + } else { return $id; } @@ -1628,19 +1650,20 @@ class Feeds extends Handler_Protected { if (!$owner_uid) $owner_uid = $_SESSION["uid"]; + $pdo = Db::pdo(); + if ($cat >= 0) { - if ($cat != 0) { - $cat_query = "cat_id = '$cat'"; - } else { - $cat_query = "cat_id IS NULL"; - } + if (!$cat) $cat = null; - $result = db_query("SELECT id FROM ttrss_feeds WHERE $cat_query - AND owner_uid = " . $owner_uid); + $sth = $pdo->prepare("SELECT id FROM ttrss_feeds + WHERE (cat_id = :cat OR (:cat IS NULL AND cat_id IS NULL)) + AND owner_uid = :uid"); + + $sth->execute([":cat" => $cat, ":uid" => $owner_uid]); $cat_feeds = array(); - while ($line = db_fetch_assoc($result)) { + while ($line = $sth->fetch()) { array_push($cat_feeds, "feed_id = " . $line["id"]); } @@ -1648,15 +1671,16 @@ class Feeds extends Handler_Protected { $match_part = implode(" OR ", $cat_feeds); - $result = db_query("SELECT COUNT(int_id) AS unread + $sth = $pdo->prepare("SELECT COUNT(int_id) AS unread FROM ttrss_user_entries WHERE unread = true AND ($match_part) - AND owner_uid = " . $owner_uid); + AND owner_uid = ?"); + $sth->execute([$owner_uid]); $unread = 0; # this needs to be rewritten - while ($line = db_fetch_assoc($result)) { + while ($line = $sth->fetch()) { $unread += $line["unread"]; } @@ -1665,16 +1689,14 @@ class Feeds extends Handler_Protected { return getFeedUnread(-1) + getFeedUnread(-2) + getFeedUnread(-3) + getFeedUnread(0); } else if ($cat == -2) { - $result = db_query(" - SELECT COUNT(unread) AS unread FROM + $sth = $pdo->prepare("SELECT COUNT(unread) AS unread FROM ttrss_user_entries, ttrss_user_labels2 WHERE article_id = ref_id AND unread = true - AND ttrss_user_entries.owner_uid = '$owner_uid'"); - - $unread = db_fetch_result($result, 0, "unread"); - - return $unread; + AND ttrss_user_entries.owner_uid = ?"); + $sth->execute([$owner_uid]); + $row = $sth->fetch(); + return $row["unread"]; } } @@ -1682,12 +1704,15 @@ class Feeds extends Handler_Protected { static function getCategoryChildrenUnread($cat, $owner_uid = false) { if (!$owner_uid) $owner_uid = $_SESSION["uid"]; - $result = db_query("SELECT id FROM ttrss_feed_categories WHERE parent_cat = '$cat' - AND owner_uid = $owner_uid"); + $pdo = Db::pdo(); + + $sth = $pdo->prepare("SELECT id FROM ttrss_feed_categories WHERE parent_cat = ? + AND owner_uid = ?"); + $sth->execute([$cat, $owner_uid]); $unread = 0; - while ($line = db_fetch_assoc($result)) { + while ($line = $sth->fetch()) { $unread += Feeds::getCategoryUnread($line["id"], $owner_uid); $unread += Feeds::getCategoryChildrenUnread($line["id"], $owner_uid); } @@ -1697,16 +1722,16 @@ class Feeds extends Handler_Protected { static function getGlobalUnread($user_id = false) { - if (!$user_id) { - $user_id = $_SESSION["uid"]; - } + if (!$user_id) $user_id = $_SESSION["uid"]; - $result = db_query("SELECT SUM(value) AS c_id FROM ttrss_counters_cache - WHERE owner_uid = '$user_id' AND feed_id > 0"); + $pdo = Db::pdo(); - $c_id = db_fetch_result($result, 0, "c_id"); + $sth = $pdo->prepare("SELECT SUM(value) AS c_id FROM ttrss_counters_cache + WHERE owner_uid = ? AND feed_id > 0"); + $sth->execute([$user_id]); + $row = $sth->fetch(); - return $c_id; + return $row["c_id"]; } static function getCategoryTitle($cat_id) { @@ -1717,11 +1742,14 @@ class Feeds extends Handler_Protected { return __("Labels"); } else { - $result = db_query("SELECT title FROM ttrss_feed_categories WHERE - id = '$cat_id'"); + $pdo = Db::pdo(); - if (db_num_rows($result) == 1) { - return db_fetch_result($result, 0, "title"); + $sth = $pdo->prepare("SELECT title FROM ttrss_feed_categories WHERE + id = ?"); + $sth->execute([$cat_id]); + + if ($row = $sth->fetch()) { + return $row["title"]; } else { return __("Uncategorized"); } @@ -1731,11 +1759,15 @@ class Feeds extends Handler_Protected { static function getLabelUnread($label_id, $owner_uid = false) { if (!$owner_uid) $owner_uid = $_SESSION["uid"]; - $result = db_query("SELECT COUNT(ref_id) AS unread FROM ttrss_user_entries, ttrss_user_labels2 - WHERE owner_uid = '$owner_uid' AND unread = true AND label_id = '$label_id' AND article_id = ref_id"); + $pdo = Db::pdo(); + + $sth = $pdo->prepare("SELECT COUNT(ref_id) AS unread FROM ttrss_user_entries, ttrss_user_labels2 + WHERE owner_uid = ? AND unread = true AND label_id = ? AND article_id = ref_id"); + + $sth->execute([$owner_uid, $label_id]); - if (db_num_rows($result) != 0) { - return db_fetch_result($result, 0, "unread"); + if ($row = $sth->fetch()) { + return $row["unread"]; } else { return 0; } @@ -1743,6 +1775,11 @@ class Feeds extends Handler_Protected { static function queryFeedHeadlines($params) { + $pdo = Db::pdo(); + + // WARNING: due to highly dynamic nature of this query its going to quote parameters + // right before adding them to SQL part + $feed = $params["feed"]; $limit = isset($params["limit"]) ? $params["limit"] : 30; $view_mode = $params["view_mode"]; @@ -1762,7 +1799,7 @@ class Feeds extends Handler_Protected { $skip_first_id_check = isset($params["skip_first_id_check"]) ? $params["skip_first_id_check"] : false; $ext_tables_part = ""; - $query_strategy_part = ""; + $limit_query_part = ""; $search_words = array(); @@ -1782,7 +1819,7 @@ class Feeds extends Handler_Protected { } if ($since_id) { - $since_id_part = "ttrss_entries.id > $since_id AND "; + $since_id_part = "ttrss_entries.id > ".$pdo->quote($since_id)." AND "; } else { $since_id_part = ""; } @@ -1822,7 +1859,7 @@ class Feeds extends Handler_Protected { } if ($limit > 0) { - $limit_query_part = "LIMIT " . $limit; + $limit_query_part = "LIMIT " . (int)$limit; } $allow_archived = false; @@ -1848,7 +1885,7 @@ class Feeds extends Handler_Protected { implode(",", $subcats).")"; } else { - $query_strategy_part = "cat_id = '$feed'"; + $query_strategy_part = "cat_id = " . $pdo->quote($feed); } } else { @@ -1858,7 +1895,7 @@ class Feeds extends Handler_Protected { $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; } else { - $query_strategy_part = "feed_id = '$feed'"; + $query_strategy_part = "feed_id = " . $pdo->quote($feed); } } else if ($feed == 0 && !$cat_view) { // archive virtual feed $query_strategy_part = "feed_id IS NULL"; @@ -1913,7 +1950,7 @@ class Feeds extends Handler_Protected { } else if ($feed == -3) { // fresh virtual feed $query_strategy_part = "unread = true AND score >= 0"; - $intl = get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid); + $intl = (int) get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid); if (DB_TYPE == "pgsql") { $query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' "; @@ -1929,7 +1966,7 @@ class Feeds extends Handler_Protected { } else if ($feed <= LABEL_BASE_INDEX) { // labels $label_id = Labels::feed_to_label_id($feed); - $query_strategy_part = "label_id = '$label_id' AND + $query_strategy_part = "label_id = ".$pdo->quote($label_id)." AND ttrss_labels2.id = ttrss_user_labels2.label_id AND ttrss_user_labels2.article_id = ref_id"; @@ -1955,8 +1992,6 @@ class Feeds extends Handler_Protected { $vfeed_query_part = $override_vfeed; } - $feed_title = ""; - if ($search) { $feed_title = T_sprintf("Search results: %s", $search); } else { @@ -1964,24 +1999,25 @@ class Feeds extends Handler_Protected { $feed_title = Feeds::getCategoryTitle($feed); } else { if (is_numeric($feed) && $feed > 0) { - $result = db_query("SELECT title,site_url,last_error,last_updated - FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid"); - - $feed_title = db_fetch_result($result, 0, "title"); - $feed_site_url = db_fetch_result($result, 0, "site_url"); - $last_error = db_fetch_result($result, 0, "last_error"); - $last_updated = db_fetch_result($result, 0, "last_updated"); + $ssth = $pdo->prepare("SELECT title,site_url,last_error,last_updated + FROM ttrss_feeds WHERE id = ? AND owner_uid = ?"); + $ssth->execute([$feed, $owner_uid]); + $row = $ssth->fetch(); + + $feed_title = $row["title"]; + $feed_site_url = $row["site_url"]; + $last_error = $row["last_error"]; + $last_updated = $row["last_updated"]; } else { $feed_title = Feeds::getFeedTitle($feed); } } } - $content_query_part = "content, "; if ($limit_query_part) { - $offset_query_part = "OFFSET $offset"; + $offset_query_part = "OFFSET " . (int)$offset; } else { $offset_query_part = ""; } @@ -1990,9 +2026,9 @@ class Feeds extends Handler_Protected { // proper override_order applied above if ($vfeed_query_part && !$ignore_vfeed_group && get_pref('VFEED_GROUP_BY_FEED', $owner_uid)) { if (!$override_order) { - $order_by = "ttrss_feeds.title, $order_by"; + $order_by = "ttrss_feeds.title, ".$order_by; } else { - $order_by = "ttrss_feeds.title, $override_order"; + $order_by = "ttrss_feeds.title, ".$override_order; } } @@ -2045,7 +2081,7 @@ class Feeds extends Handler_Protected { $from_qpart WHERE $feed_check_qpart - ttrss_user_entries.owner_uid = '$owner_uid' AND + ttrss_user_entries.owner_uid = ".$pdo->quote($owner_uid)." AND $search_query_part $start_ts_query_part $since_id_part @@ -2056,9 +2092,10 @@ class Feeds extends Handler_Protected { print $query; } - $result = db_query($query); - if ($result && db_num_rows($result) > 0) { - $first_id = (int)db_fetch_result($result, 0, "id"); + $res = $pdo->query($query); + + if ($row = $res->fetch()) { + $first_id = (int)$row["id"]; if ($offset > 0 && $first_id && $check_first_id && $first_id != $check_first_id) { return array(-1, $feed_title, $feed_site_url, $last_error, $last_updated, $search_words, $first_id); @@ -2091,7 +2128,7 @@ class Feeds extends Handler_Protected { $from_qpart WHERE $feed_check_qpart - ttrss_user_entries.owner_uid = '$owner_uid' AND + ttrss_user_entries.owner_uid = ".$pdo->quote($owner_uid)." AND $search_query_part $start_ts_query_part $view_query_part @@ -2101,7 +2138,7 @@ class Feeds extends Handler_Protected { if ($_REQUEST["debug"]) print $query; - $result = db_query($query); + $res = $pdo->query($query); } else { // browsing by tag @@ -2135,9 +2172,9 @@ class Feeds extends Handler_Protected { FROM ttrss_entries, ttrss_user_entries, ttrss_tags WHERE ref_id = ttrss_entries.id AND - ttrss_user_entries.owner_uid = $owner_uid AND + ttrss_user_entries.owner_uid = ".$pdo->quote($owner_uid)." AND post_int_id = int_id AND - tag_name = '$feed' AND + tag_name = ".$pdo->quote($feed)." AND $view_query_part $search_query_part $query_strategy_part ORDER BY $order_by @@ -2145,20 +2182,23 @@ class Feeds extends Handler_Protected { if ($_REQUEST["debug"]) print $query; - $result = db_query($query); + $res = $pdo->query($query); } - return array($result, $feed_title, $feed_site_url, $last_error, $last_updated, $search_words, $first_id); + return array($res, $feed_title, $feed_site_url, $last_error, $last_updated, $search_words, $first_id); } static function getParentCategories($cat, $owner_uid) { $rv = array(); - $result = db_query("SELECT parent_cat FROM ttrss_feed_categories - WHERE id = '$cat' AND parent_cat IS NOT NULL AND owner_uid = $owner_uid"); + $pdo = Db::pdo(); - while ($line = db_fetch_assoc($result)) { + $sth = $pdo->prepare("SELECT parent_cat FROM ttrss_feed_categories + WHERE id = ? AND parent_cat IS NOT NULL AND owner_uid = ?"); + $sth->execute([$cat, $owner_uid]); + + while ($line = $sth->fetch()) { array_push($rv, $line["parent_cat"]); $rv = array_merge($rv, Feeds::getParentCategories($line["parent_cat"], $owner_uid)); } @@ -2169,10 +2209,13 @@ class Feeds extends Handler_Protected { static function getChildCategories($cat, $owner_uid) { $rv = array(); - $result = db_query("SELECT id FROM ttrss_feed_categories - WHERE parent_cat = '$cat' AND owner_uid = $owner_uid"); + $pdo = Db::pdo(); + + $sth = $pdo->prepare("SELECT id FROM ttrss_feed_categories + WHERE parent_cat = ? AND owner_uid = ?"); + $sth->execute([$cat, $owner_uid]); - while ($line = db_fetch_assoc($result)) { + while ($line = $sth->fetch()) { array_push($rv, $line["id"]); $rv = array_merge($rv, Feeds::getChildCategories($line["id"], $owner_uid)); } @@ -2181,11 +2224,14 @@ class Feeds extends Handler_Protected { } static function getFeedCategory($feed) { - $result = db_query("SELECT cat_id FROM ttrss_feeds - WHERE id = '$feed'"); + $pdo = Db::pdo(); + + $sth = $pdo->prepare("SELECT cat_id FROM ttrss_feeds + WHERE id = ?"); + $sth->execute([$feed]); - if (db_num_rows($result) > 0) { - return db_fetch_result($result, 0, "cat_id"); + if ($row = $sth->fetch()) { + return $row["cat_id"]; } else { return false; } diff --git a/classes/handler.php b/classes/handler.php index 16c209607..5b1109492 100644 --- a/classes/handler.php +++ b/classes/handler.php @@ -1,10 +1,10 @@ <?php class Handler implements IHandler { - protected $dbh; + protected $pdo; protected $args; function __construct($args) { - $this->dbh = Db::get(); + $this->pdo = Db::pdo(); $this->args = $args; } diff --git a/classes/handler/public.php b/classes/handler/public.php index a98ea0193..8440bc355 100644 --- a/classes/handler/public.php +++ b/classes/handler/public.php @@ -15,14 +15,11 @@ class Handler_Public extends Handler { if (!$limit) $limit = 60; $date_sort_field = "date_entered DESC, updated DESC"; - $date_check_field = "date_entered"; if ($feed == -2 && !$is_cat) { $date_sort_field = "last_published DESC"; - $date_check_field = "last_published"; } else if ($feed == -1 && !$is_cat) { $date_sort_field = "last_marked DESC"; - $date_check_field = "last_marked"; } switch ($order) { @@ -36,39 +33,6 @@ class Handler_Public extends Handler { $date_sort_field = "updated DESC"; break; } - - $params = array( - "owner_uid" => $owner_uid, - "feed" => $feed, - "limit" => 1, - "view_mode" => $view_mode, - "cat_view" => $is_cat, - "search" => $search, - "override_order" => $date_sort_field, - "include_children" => true, - "ignore_vfeed_group" => true, - "offset" => $offset, - "start_ts" => $start_ts - ); - - $qfh_ret = Feeds::queryFeedHeadlines($params); - - $result = $qfh_ret[0]; - - if ($this->dbh->num_rows($result) != 0) { - - $ts = strtotime($this->dbh->fetch_result($result, 0, $date_check_field)); - - if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && - strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $ts) { - header('HTTP/1.0 304 Not Modified'); - return; - } - - $last_modified = gmdate("D, d M Y H:i:s", $ts) . " GMT"; - header("Last-Modified: $last_modified", true); - } - $params = array( "owner_uid" => $owner_uid, "feed" => $feed, @@ -106,7 +70,7 @@ class Handler_Public extends Handler { $tpl->setVariable('FEED_URL', htmlspecialchars($feed_self_url), true); $tpl->setVariable('SELF_URL', htmlspecialchars(get_self_url_prefix()), true); - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $result->fetch()) { $line["content_preview"] = sanitize(truncate_string(strip_tags($line["content"]), 100, '...')); @@ -175,7 +139,7 @@ class Handler_Public extends Handler { $tpl->addBlock('feed'); $tpl->generateOutputToString($tmp); - if (@!$_REQUEST["noxml"]) { + if (@!clean($_REQUEST["noxml"])) { header("Content-Type: text/xml; charset=utf-8"); } else { header("Content-Type: text/plain; charset=utf-8"); @@ -194,7 +158,7 @@ class Handler_Public extends Handler { $feed['articles'] = array(); - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $result->fetch()) { $line["content_preview"] = sanitize(truncate_string(strip_tags($line["content_preview"]), 100, '...')); @@ -255,13 +219,14 @@ class Handler_Public extends Handler { } function getUnread() { - $login = $this->dbh->escape_string($_REQUEST["login"]); - $fresh = $_REQUEST["fresh"] == "1"; + $login = clean($_REQUEST["login"]); + $fresh = clean($_REQUEST["fresh"]) == "1"; - $result = $this->dbh->query("SELECT id FROM ttrss_users WHERE login = '$login'"); + $sth = $this->pdo->prepare("SELECT id FROM ttrss_users WHERE login = ?"); + $sth->execute([$login]); - if ($this->dbh->num_rows($result) == 1) { - $uid = $this->dbh->fetch_result($result, 0, "id"); + if ($row = $sth->fetch()) { + $uid = $row["id"]; print Feeds::getGlobalUnread($uid); @@ -273,20 +238,20 @@ class Handler_Public extends Handler { } else { print "-1;User not found"; } - } function getProfiles() { - $login = $this->dbh->escape_string($_REQUEST["login"]); + $login = clean($_REQUEST["login"]); - $result = $this->dbh->query("SELECT ttrss_settings_profiles.* FROM ttrss_settings_profiles,ttrss_users - WHERE ttrss_users.id = ttrss_settings_profiles.owner_uid AND login = '$login' ORDER BY title"); + $sth = $this->pdo->prepare("SELECT ttrss_settings_profiles.* FROM ttrss_settings_profiles,ttrss_users + WHERE ttrss_users.id = ttrss_settings_profiles.owner_uid AND login = ? ORDER BY title"); + $sth->execute([$login]); print "<select dojoType='dijit.form.Select' style='width : 220px; margin : 0px' name='profile'>"; print "<option value='0'>" . __("Default profile") . "</option>"; - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $sth->fetch()) { $id = $line["id"]; $title = $line["title"]; @@ -302,16 +267,17 @@ class Handler_Public extends Handler { } function share() { - $uuid = $this->dbh->escape_string($_REQUEST["key"]); + $uuid = clean($_REQUEST["key"]); - $result = $this->dbh->query("SELECT ref_id, owner_uid FROM ttrss_user_entries WHERE - uuid = '$uuid'"); + $sth = $this->pdo->prepare("SELECT ref_id, owner_uid FROM ttrss_user_entries WHERE + uuid = ?"); + $sth->execute([$uuid]); - if ($this->dbh->num_rows($result) != 0) { + if ($row = $sth->fetch()) { header("Content-Type: text/html"); - $id = $this->dbh->fetch_result($result, 0, "ref_id"); - $owner_uid = $this->dbh->fetch_result($result, 0, "owner_uid"); + $id = $row["ref_id"]; + $owner_uid = $row["owner_uid"]; $article = Article::format_article($id, false, true, $owner_uid); @@ -324,19 +290,19 @@ class Handler_Public extends Handler { } function rss() { - $feed = $this->dbh->escape_string($_REQUEST["id"]); - $key = $this->dbh->escape_string($_REQUEST["key"]); - $is_cat = sql_bool_to_bool($_REQUEST["is_cat"]); - $limit = (int)$this->dbh->escape_string($_REQUEST["limit"]); - $offset = (int)$this->dbh->escape_string($_REQUEST["offset"]); + $feed = clean($_REQUEST["id"]); + $key = clean($_REQUEST["key"]); + $is_cat = clean($_REQUEST["is_cat"]); + $limit = (int)clean($_REQUEST["limit"]); + $offset = (int)clean($_REQUEST["offset"]); - $search = $this->dbh->escape_string($_REQUEST["q"]); - $view_mode = $this->dbh->escape_string($_REQUEST["view-mode"]); - $order = $this->dbh->escape_string($_REQUEST["order"]); - $start_ts = $this->dbh->escape_string($_REQUEST["ts"]); + $search = clean($_REQUEST["q"]); + $view_mode = clean($_REQUEST["view-mode"]); + $order = clean($_REQUEST["order"]); + $start_ts = clean($_REQUEST["ts"]); - $format = $this->dbh->escape_string($_REQUEST['format']); - $orig_guid = sql_bool_to_bool($_REQUEST["orig_guid"]); + $format = clean($_REQUEST['format']); + $orig_guid = clean($_REQUEST["orig_guid"]); if (!$format) $format = 'atom'; @@ -347,11 +313,12 @@ class Handler_Public extends Handler { $owner_id = false; if ($key) { - $result = $this->dbh->query("SELECT owner_uid FROM - ttrss_access_keys WHERE access_key = '$key' AND feed_id = '$feed'"); + $sth = $this->pdo->prepare("SELECT owner_uid FROM + ttrss_access_keys WHERE access_key = ? AND feed_id = ?"); + $sth->execute([$key, $feed]); - if ($this->dbh->num_rows($result) == 1) - $owner_id = $this->dbh->fetch_result($result, 0, "owner_uid"); + if ($row = $sth->fetch()) + $owner_id = $row["owner_uid"]; } if ($owner_id) { @@ -371,7 +338,7 @@ class Handler_Public extends Handler { } function globalUpdateFeeds() { - RPC::updaterandomfeed_real($this->dbh); + RPC::updaterandomfeed_real(); PluginHost::getInstance()->run_hooks(PluginHost::HOOK_UPDATE_TASK, "hook_update_task", false); } @@ -386,23 +353,22 @@ class Handler_Public extends Handler { <link rel=\"shortcut icon\" type=\"image/png\" href=\"images/favicon.png\"> <link rel=\"icon\" type=\"image/png\" sizes=\"72x72\" href=\"images/favicon-72px.png\">"; - echo stylesheet_tag("css/utility.css"); echo stylesheet_tag("css/default.css"); echo javascript_tag("lib/prototype.js"); echo javascript_tag("lib/scriptaculous/scriptaculous.js?load=effects,controls"); print "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/> - </head><body id='sharepopup'>"; + </head><body id='sharepopup' class='ttrss_utility'>"; - $action = $_REQUEST["action"]; + $action = clean($_REQUEST["action"]); if ($_SESSION["uid"]) { if ($action == 'share') { - $title = $this->dbh->escape_string(strip_tags($_REQUEST["title"])); - $url = $this->dbh->escape_string(strip_tags($_REQUEST["url"])); - $content = $this->dbh->escape_string(strip_tags($_REQUEST["content"])); - $labels = $this->dbh->escape_string(strip_tags($_REQUEST["labels"])); + $title = strip_tags(clean($_REQUEST["title"])); + $url = strip_tags(clean($_REQUEST["url"])); + $content = strip_tags(clean($_REQUEST["content"])); + $labels = strip_tags(clean($_REQUEST["labels"])); Article::create_published_article($title, $url, $content, $labels, $_SESSION["uid"]); @@ -412,8 +378,8 @@ class Handler_Public extends Handler { print "</script>"; } else { - $title = htmlspecialchars($_REQUEST["title"]); - $url = htmlspecialchars($_REQUEST["url"]); + $title = htmlspecialchars(clean($_REQUEST["title"])); + $url = htmlspecialchars(clean($_REQUEST["url"])); ?> @@ -500,9 +466,9 @@ class Handler_Public extends Handler { function login() { if (!SINGLE_USER_MODE) { - $login = $this->dbh->escape_string($_POST["login"]); - $password = $_POST["password"]; - $remember_me = $_POST["remember_me"]; + $login = clean($_POST["login"]); + $password = clean($_POST["password"]); + $remember_me = clean($_POST["remember_me"]); if ($remember_me) { session_set_cookie_params(SESSION_COOKIE_LIFETIME); @@ -520,16 +486,17 @@ class Handler_Public extends Handler { } $_SESSION["ref_schema_version"] = get_schema_version(true); - $_SESSION["bw_limit"] = !!$_POST["bw_limit"]; + $_SESSION["bw_limit"] = !!clean($_POST["bw_limit"]); - if ($_POST["profile"]) { + if (clean($_POST["profile"])) { - $profile = $this->dbh->escape_string($_POST["profile"]); + $profile = clean($_POST["profile"]); - $result = $this->dbh->query("SELECT id FROM ttrss_settings_profiles - WHERE id = '$profile' AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("SELECT id FROM ttrss_settings_profiles + WHERE id = ? AND owner_uid = ?"); + $sth->execute([$profile, $_SESSION['uid']]); - if ($this->dbh->num_rows($result) != 0) { + if ($sth->fetch()) { $_SESSION["profile"] = $profile; } } @@ -538,8 +505,8 @@ class Handler_Public extends Handler { user_error("Failed login attempt for $login from {$_SERVER['REMOTE_ADDR']}", E_USER_WARNING); } - if ($_REQUEST['return']) { - header("Location: " . $_REQUEST['return']); + if (clean($_REQUEST['return'])) { + header("Location: " . clean($_REQUEST['return'])); } else { header("Location: " . get_self_url_prefix()); } @@ -549,7 +516,7 @@ class Handler_Public extends Handler { /* function subtest() { header("Content-type: text/plain; charset=utf-8"); - $url = $_REQUEST["url"]; + $url = clean($_REQUEST["url"]); print "$url\n\n"; @@ -565,19 +532,20 @@ class Handler_Public extends Handler { if ($_SESSION["uid"]) { - $feed_url = $this->dbh->escape_string(trim($_REQUEST["feed_url"])); + $feed_url = trim(clean($_REQUEST["feed_url"])); header('Content-Type: text/html; charset=utf-8'); print "<html> <head> - <title>Tiny Tiny RSS</title> - <link rel=\"stylesheet\" type=\"text/css\" href=\"css/utility.css\"> - <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/> - <link rel=\"shortcut icon\" type=\"image/png\" href=\"images/favicon.png\"> - <link rel=\"icon\" type=\"image/png\" sizes=\"72x72\" href=\"images/favicon-72px.png\"> + <title>Tiny Tiny RSS</title>"; + print stylesheet_tag("css/default.css"); + + print "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/> + <link rel=\"shortcut icon\" type=\"image/png\" href=\"images/favicon.png\"> + <link rel=\"icon\" type=\"image/png\" sizes=\"72x72\" href=\"images/favicon-72px.png\"> </head> - <body> + <body class='claro ttrss_utility'> <img class=\"floatingLogo\" src=\"images/logo_small.png\" alt=\"Tiny Tiny RSS\"/> <h1>".__("Subscribe to feed...")."</h1><div class='content'>"; @@ -630,10 +598,12 @@ class Handler_Public extends Handler { $tt_uri = get_self_url_prefix(); if ($rc['code'] <= 2){ - $result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE - feed_url = '$feed_url' AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE + feed_url = ? AND owner_uid = ?"); + $sth->execute([$feed_url, $_SESSION['uid']]); + $row = $sth->fetch(); - $feed_id = $this->dbh->fetch_result($result, 0, "id"); + $feed_id = $row["id"]; } else { $feed_id = 0; } @@ -668,43 +638,46 @@ class Handler_Public extends Handler { function forgotpass() { startup_gettext(); - @$hash = $_REQUEST["hash"]; + @$hash = clean($_REQUEST["hash"]); header('Content-Type: text/html; charset=utf-8'); print "<html><head><title>Tiny Tiny RSS</title> <link rel=\"shortcut icon\" type=\"image/png\" href=\"images/favicon.png\"> <link rel=\"icon\" type=\"image/png\" sizes=\"72x72\" href=\"images/favicon-72px.png\">"; - echo stylesheet_tag("css/utility.css"); + echo stylesheet_tag("lib/dijit/themes/claro/claro.css"); + echo stylesheet_tag("css/default.css"); echo javascript_tag("lib/prototype.js"); print "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/> - </head><body id='forgotpass'>"; + </head><body class='claro ttrss_utility'>"; print '<div class="floatingLogo"><img src="images/logo_small.png"></div>'; print "<h1>".__("Password recovery")."</h1>"; print "<div class='content'>"; - @$method = $_POST['method']; + @$method = clean($_POST['method']); if ($hash) { - $login = $this->dbh->escape_string($_REQUEST["login"]); + $login = clean($_REQUEST["login"]); if ($login) { - $result = $this->dbh->query("SELECT id, resetpass_token FROM ttrss_users - WHERE login = '$login'"); + $sth = $this->pdo->prepare("SELECT id, resetpass_token FROM ttrss_users + WHERE login = ?"); + $sth->execute([$login]); - if ($this->dbh->num_rows($result) != 0) { - $id = $this->dbh->fetch_result($result, 0, "id"); - $resetpass_token_full = $this->dbh->fetch_result($result, 0, "resetpass_token"); + if ($row = $sth->fetch()) { + $id = $row["id"]; + $resetpass_token_full = $row["resetpass_token"]; list($timestamp, $resetpass_token) = explode(":", $resetpass_token_full); if ($timestamp && $resetpass_token && $timestamp >= time() - 15*60*60 && $resetpass_token == $hash) { - $result = $this->dbh->query("UPDATE ttrss_users SET resetpass_token = NULL - WHERE id = $id"); + $sth = $this->pdo->prepare("UPDATE ttrss_users SET resetpass_token = NULL + WHERE id = ?"); + $sth->execute([$id]); Pref_Users::resetUserPassword($id, true); @@ -733,17 +706,17 @@ class Handler_Public extends Handler { print "<fieldset>"; print "<label>".__("Login:")."</label>"; - print "<input type='text' name='login' value='' required>"; + print "<input class='input input-text' type='text' name='login' value='' required>"; print "</fieldset>"; print "<fieldset>"; print "<label>".__("Email:")."</label>"; - print "<input type='email' name='email' value='' required>"; + print "<input class='input input-text' type='email' name='email' value='' required>"; print "</fieldset>"; print "<fieldset>"; print "<label>".__("How much is two plus two:")."</label>"; - print "<input type='text' name='test' value='' required>"; + print "<input class='input input-text' type='text' name='test' value='' required>"; print "</fieldset>"; print "<p/>"; @@ -752,9 +725,9 @@ class Handler_Public extends Handler { print "</form>"; } else if ($method == 'do') { - $login = $this->dbh->escape_string($_POST["login"]); - $email = $this->dbh->escape_string($_POST["email"]); - $test = $this->dbh->escape_string($_POST["test"]); + $login = clean($_POST["login"]); + $email = clean($_POST["email"]); + $test = clean($_POST["test"]); if (($test != 4 && $test != 'four') || !$email || !$login) { print_error(__('Some of the required form parameters are missing or incorrect.')); @@ -768,11 +741,12 @@ class Handler_Public extends Handler { print_notice("Password reset instructions are being sent to your email address."); - $result = $this->dbh->query("SELECT id FROM ttrss_users - WHERE login = '$login' AND email = '$email'"); + $sth = $this->pdo->prepare("SELECT id FROM ttrss_users + WHERE login = ? AND email = ?"); + $sth->execute([$login, $email]); - if ($this->dbh->num_rows($result) != 0) { - $id = $this->dbh->fetch_result($result, 0, "id"); + if ($row = $sth->fetch()) { + $id = $row["id"]; if ($id) { $resetpass_token = sha1(get_random_bytes(128)); @@ -803,11 +777,13 @@ class Handler_Public extends Handler { if (!$rc) print_error($mail->ErrorInfo); - $resetpass_token_full = $this->dbh->escape_string(time() . ":" . $resetpass_token); + $resetpass_token_full = time() . ":" . $resetpass_token; + + $sth = $this->pdo->prepare("UPDATE ttrss_users + SET resetpass_token = ? + WHERE login = ? AND email = ?"); - $result = $this->dbh->query("UPDATE ttrss_users - SET resetpass_token = '$resetpass_token_full' - WHERE login = '$login' AND email = '$email'"); + $sth->execute([$resetpass_token_full, $login, $email]); //Pref_Users::resetUserPassword($id, false); @@ -854,7 +830,7 @@ class Handler_Public extends Handler { <head> <title>Database Updater</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> - <link rel="stylesheet" type="text/css" href="css/utility.css"/> + <link rel="stylesheet" type="text/css" href="css/default.css"/> <link rel=\"shortcut icon\" type=\"image/png\" href=\"images/favicon.png\"> <link rel=\"icon\" type=\"image/png\" sizes=\"72x72\" href=\"images/favicon-72px.png\"> </head> @@ -862,7 +838,7 @@ class Handler_Public extends Handler { span.ok { color : #009000; font-weight : bold; } span.err { color : #ff0000; font-weight : bold; } </style> - <body> + <body class="claro ttrss_utility"> <script type='text/javascript'> function confirmOP() { return confirm("Update the database?"); @@ -876,8 +852,8 @@ class Handler_Public extends Handler { <div class="content"> <?php - @$op = $_REQUEST["subop"]; - $updater = new DbUpdater(Db::get(), DB_TYPE, SCHEMA_VERSION); + @$op = clean($_REQUEST["subop"]); + $updater = new DbUpdater(Db::pdo(), DB_TYPE, SCHEMA_VERSION); if ($op == "performupdate") { if ($updater->isUpdateRequired()) { @@ -992,8 +968,8 @@ class Handler_Public extends Handler { public function pluginhandler() { $host = new PluginHost(); - $plugin = basename($_REQUEST["plugin"]); - $method = $_REQUEST["pmethod"]; + $plugin = basename(clean($_REQUEST["plugin"])); + $method = clean($_REQUEST["pmethod"]); $host->load($plugin, PluginHost::KIND_USER, 0); $host->load_data(); diff --git a/classes/labels.php b/classes/labels.php index c46f70c94..4061de57e 100644 --- a/classes/labels.php +++ b/classes/labels.php @@ -10,24 +10,28 @@ class Labels } static function find_id($label, $owner_uid) { - $result = db_query( - "SELECT id FROM ttrss_labels2 WHERE caption = '$label' - AND owner_uid = '$owner_uid' LIMIT 1"); + $pdo = Db::pdo(); - if (db_num_rows($result) == 1) { - return db_fetch_result($result, 0, "id"); + $sth = $pdo->prepare("SELECT id FROM ttrss_labels2 WHERE caption = ? + AND owner_uid = ? LIMIT 1"); + $sth->execute([$label, $owner_uid]); + + if ($row = $sth->fetch()) { + return $row['id']; } else { return 0; } } static function find_caption($label, $owner_uid) { - $result = db_query( - "SELECT caption FROM ttrss_labels2 WHERE id = '$label' - AND owner_uid = '$owner_uid' LIMIT 1"); + $pdo = Db::pdo(); + + $sth = $pdo->prepare("SELECT caption FROM ttrss_labels2 WHERE id = ? + AND owner_uid = ? LIMIT 1"); + $sth->execute([$label, $owner_uid]); - if (db_num_rows($result) == 1) { - return db_fetch_result($result, 0, "caption"); + if ($row = $sth->fetch()) { + return $row['caption']; } else { return ""; } @@ -36,9 +40,13 @@ class Labels static function get_all_labels($owner_uid) { $rv = array(); - $result = db_query("SELECT id, fg_color, bg_color, caption FROM ttrss_labels2 WHERE owner_uid = '$owner_uid' ORDER BY caption"); + $pdo = Db::pdo(); - while ($line = db_fetch_assoc($result)) { + $sth = $pdo->prepare("SELECT id, fg_color, bg_color, caption FROM ttrss_labels2 + WHERE owner_uid = ? ORDER BY caption"); + $sth->execute([$owner_uid]); + + while ($line = $sth->fetch()) { array_push($rv, $line); } @@ -46,6 +54,7 @@ class Labels } static function update_cache($owner_uid, $id, $labels = false, $force = false) { + $pdo = Db::pdo(); if ($force) Labels::clear_cache($id); @@ -53,17 +62,21 @@ class Labels if (!$labels) $labels = Article::get_article_labels($id); - $labels = db_escape_string(json_encode($labels)); + $labels = json_encode($labels); - db_query("UPDATE ttrss_user_entries SET - label_cache = '$labels' WHERE ref_id = '$id' AND owner_uid = '$owner_uid'"); + $sth = $pdo->prepare("UPDATE ttrss_user_entries SET + label_cache = ? WHERE ref_id = ? AND owner_uid = ?"); + $sth->execute([$labels, $id, $owner_uid]); } static function clear_cache($id) { - db_query("UPDATE ttrss_user_entries SET - label_cache = '' WHERE ref_id = '$id'"); + $pdo = Db::pdo(); + + $sth = $pdo->prepare("UPDATE ttrss_user_entries SET + label_cache = '' WHERE ref_id = ?"); + $sth->execute([$id]); } @@ -73,11 +86,14 @@ class Labels if (!$label_id) return; - db_query( - "DELETE FROM ttrss_user_labels2 + $pdo = Db::pdo(); + + $sth = $pdo->prepare("DELETE FROM ttrss_user_labels2 WHERE - label_id = '$label_id' AND - article_id = '$id'"); + label_id = ? AND + article_id = ?"); + + $sth->execute([$label_id, $id]); Labels::clear_cache($id); } @@ -88,18 +104,23 @@ class Labels if (!$label_id) return; - $result = db_query( - "SELECT + $pdo = Db::pdo(); + + $sth = $pdo->prepare("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' + label_id = ? AND + article_id = ? AND owner_uid = ? LIMIT 1"); - if (db_num_rows($result) == 0) { - db_query("INSERT INTO ttrss_user_labels2 - (label_id, article_id) VALUES ('$label_id', '$id')"); + $sth->execute([$label_id, $id, $owner_uid]); + + if (!$sth->fetch()) { + $sth = $pdo->prepare("INSERT INTO ttrss_user_labels2 + (label_id, article_id) VALUES (?, ?)"); + + $sth->execute([$label_id, $id]); } Labels::clear_cache($id); @@ -109,53 +130,75 @@ class Labels static function remove($id, $owner_uid) { if (!$owner_uid) $owner_uid = $_SESSION["uid"]; - db_query("BEGIN"); + $pdo = Db::pdo(); + $tr_in_progress = false; + + try { + $pdo->beginTransaction(); + } catch (Exception $e) { + $tr_in_progress = true; + } - $result = db_query("SELECT caption FROM ttrss_labels2 - WHERE id = '$id'"); + $sth = $pdo->prepare("SELECT caption FROM ttrss_labels2 + WHERE id = ?"); + $sth->execute([$id]); - $caption = db_fetch_result($result, 0, "caption"); + $row = $sth->fetch(); + $caption = $row['caption']; - $result = db_query("DELETE FROM ttrss_labels2 WHERE id = '$id' - AND owner_uid = " . $owner_uid); + $sth = $pdo->prepare("DELETE FROM ttrss_labels2 WHERE id = ? + AND owner_uid = ?"); + $sth->execute([$id, $owner_uid]); - if (db_affected_rows($result) != 0 && $caption) { + if ($sth->rowCount() != 0 && $caption) { /* Remove access key for the label */ $ext_id = LABEL_BASE_INDEX - 1 - $id; - db_query("DELETE FROM ttrss_access_keys WHERE - feed_id = '$ext_id' AND owner_uid = $owner_uid"); + $sth = $pdo->prepare("DELETE FROM ttrss_access_keys WHERE + feed_id = ? AND owner_uid = ?"); + $sth->execute([$ext_id, $owner_uid]); /* Remove cached data */ - db_query("UPDATE ttrss_user_entries SET label_cache = '' - WHERE label_cache LIKE '%$caption%' AND owner_uid = " . $owner_uid); + $sth = $pdo->prepare("UPDATE ttrss_user_entries SET label_cache = '' + WHERE label_cache LIKE ? AND owner_uid = ?"); + $sth->execute(["%$caption%", $owner_uid]); } - db_query("COMMIT"); + if (!$tr_in_progress) $pdo->commit(); } static function create($caption, $fg_color = '', $bg_color = '', $owner_uid = false) { if (!$owner_uid) $owner_uid = $_SESSION['uid']; - db_query("BEGIN"); + $pdo = Db::pdo(); + + $tr_in_progress = false; + + try { + $pdo->beginTransaction(); + } catch (Exception $e) { + $tr_in_progress = true; + } + + $sth = $pdo->prepare("SELECT id FROM ttrss_labels2 + WHERE caption = ? AND owner_uid = ?"); + $sth->execute([$caption, $owner_uid]); - $result = db_query("SELECT id FROM ttrss_labels2 - WHERE caption = '$caption' AND owner_uid = $owner_uid"); + if (!$sth->fetch()) { + $sth = $pdo->prepare("INSERT INTO ttrss_labels2 + (caption,owner_uid,fg_color,bg_color) VALUES (?, ?, ?, ?)"); - if (db_num_rows($result) == 0) { - $result = db_query( - "INSERT INTO ttrss_labels2 (caption,owner_uid,fg_color,bg_color) - VALUES ('$caption', '$owner_uid', '$fg_color', '$bg_color')"); + $sth->execute([$caption, $owner_uid, $fg_color, $bg_color]); - $result = db_affected_rows($result) != 0; + $result = $sth->rowCount(); } - db_query("COMMIT"); + if (!$tr_in_progress) $pdo->commit(); return $result; } diff --git a/classes/logger/sql.php b/classes/logger/sql.php index bd192ae78..120584014 100644 --- a/classes/logger/sql.php +++ b/classes/logger/sql.php @@ -2,22 +2,25 @@ class Logger_SQL { function log_error($errno, $errstr, $file, $line, $context) { - if (Db::get() && get_schema_version() > 117) { + + $pdo = Db::pdo(); + + if ($pdo && get_schema_version() > 117) { - $errno = Db::get()->escape_string($errno); - $errstr = Db::get()->escape_string($errstr); - $file = Db::get()->escape_string($file); - $line = Db::get()->escape_string($line); - $context = DB::get()->escape_string($context); + try { + $pdo->rollBack(); + } catch (Exception $e) { + // + } - $owner_uid = $_SESSION["uid"] ? $_SESSION["uid"] : "NULL"; + $owner_uid = $_SESSION["uid"] ? $_SESSION["uid"] : null; - $result = Db::get()->query( - "INSERT INTO ttrss_error_log + $sth = $pdo->prepare("INSERT INTO ttrss_error_log (errno, errstr, filename, lineno, context, owner_uid, created_at) VALUES - ($errno, '$errstr', '$file', '$line', '$context', $owner_uid, NOW())"); + (?, ?, ?, ?, ?, ?, NOW())"); + $sth->execute([$errno, $errstr, $file, $line, $context, $owner_uid]); - return Db::get()->affected_rows($result) != 0; + return $sth->rowCount(); } return false; diff --git a/classes/opml.php b/classes/opml.php index 2c2c2ac0e..ac9e30f3d 100644 --- a/classes/opml.php +++ b/classes/opml.php @@ -14,7 +14,10 @@ class Opml extends Handler_Protected { $show_settings = $_REQUEST["settings"]; $owner_uid = $_SESSION["uid"]; - return $this->opml_export($output_name, $owner_uid, false, ($show_settings == 1)); + + $rc = $this->opml_export($output_name, $owner_uid, false, ($show_settings == 1)); + + return $rc; } function import() { @@ -24,17 +27,18 @@ class Opml extends Handler_Protected { print "<html> <head> - <link rel=\"stylesheet\" href=\"css/utility.css\" type=\"text/css\"> + ".stylesheet_tag("css/default.css")." <title>".__("OPML Utility")."</title> <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/> </head> - <body> + <body class='claro ttrss_utility'> <div class=\"floatingLogo\"><img src=\"images/logo_small.png\"></div> <h1>".__('OPML Utility')."</h1><div class='content'>"; add_feed_category("Imported feeds"); $this->opml_notice(__("Importing OPML...")); + $this->opml_import($owner_uid); print "<br><form method=\"GET\" action=\"prefs.php\"> @@ -50,13 +54,7 @@ class Opml extends Handler_Protected { private function opml_export_category($owner_uid, $cat_id, $hide_private_feeds=false) { - if ($cat_id) { - $cat_qpart = "parent_cat = '$cat_id'"; - $feed_cat_qpart = "cat_id = '$cat_id'"; - } else { - $cat_qpart = "parent_cat IS NULL"; - $feed_cat_qpart = "cat_id IS NULL"; - } + $cat_id = (int) $cat_id; if ($hide_private_feeds) $hide_qpart = "(private IS false AND auth_login = '' AND auth_pass = '')"; @@ -66,27 +64,34 @@ class Opml extends Handler_Protected { $out = ""; if ($cat_id) { - $result = $this->dbh->query("SELECT title FROM ttrss_feed_categories WHERE id = '$cat_id' - AND owner_uid = '$owner_uid'"); - $cat_title = htmlspecialchars($this->dbh->fetch_result($result, 0, "title")); + $sth = $this->pdo->prepare("SELECT title FROM ttrss_feed_categories WHERE id = ? + AND owner_uid = ?"); + $sth->execute([$cat_id, $owner_uid]); + $row = $sth->fetch(); + $cat_title = htmlspecialchars($row['title']); } if ($cat_title) $out .= "<outline text=\"$cat_title\">\n"; - $result = $this->dbh->query("SELECT id,title + $sth = $this->pdo->prepare("SELECT id,title FROM ttrss_feed_categories WHERE - $cat_qpart AND owner_uid = '$owner_uid' ORDER BY order_id, title"); + (parent_cat = :cat OR (:cat = 0 AND parent_cat IS NULL)) AND + owner_uid = :uid ORDER BY order_id, title"); - while ($line = $this->dbh->fetch_assoc($result)) { - $title = htmlspecialchars($line["title"]); + $sth->execute([':cat' => $cat_id, ':uid' => $owner_uid]); + + while ($line = $sth->fetch()) { $out .= $this->opml_export_category($owner_uid, $line["id"], $hide_private_feeds); } - $feeds_result = $this->dbh->query("select title, feed_url, site_url - from ttrss_feeds where $feed_cat_qpart AND owner_uid = '$owner_uid' AND $hide_qpart - order by order_id, title"); + $fsth = $this->pdo->prepare("select title, feed_url, site_url + FROM ttrss_feeds WHERE + (cat_id = :cat OR (:cat = 0 AND cat_id IS NULL)) AND owner_uid = :uid AND $hide_qpart + ORDER BY order_id, title"); + + $fsth->execute([':cat' => $cat_id, ':uid' => $owner_uid]); - while ($fline = $this->dbh->fetch_assoc($feeds_result)) { + while ($fline = $fsth->fetch()) { $title = htmlspecialchars($fline["title"]); $url = htmlspecialchars($fline["feed_url"]); $site_url = htmlspecialchars($fline["site_url"]); @@ -124,17 +129,18 @@ class Opml extends Handler_Protected { </head>"; $out .= "<body>"; - $out .= $this->opml_export_category($owner_uid, false, $hide_private_feeds); + $out .= $this->opml_export_category($owner_uid, 0, $hide_private_feeds); # export tt-rss settings if ($include_settings) { $out .= "<outline text=\"tt-rss-prefs\" schema-version=\"".SCHEMA_VERSION."\">"; - $result = $this->dbh->query("SELECT pref_name, value FROM ttrss_user_prefs WHERE - profile IS NULL AND owner_uid = " . $_SESSION["uid"] . " ORDER BY pref_name"); + $sth = $this->pdo->prepare("SELECT pref_name, value FROM ttrss_user_prefs WHERE + profile IS NULL AND owner_uid = ? ORDER BY pref_name"); + $sth->execute([$owner_uid]); - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $sth->fetch()) { $name = $line["pref_name"]; $value = htmlspecialchars($line["value"]); @@ -145,10 +151,11 @@ class Opml extends Handler_Protected { $out .= "<outline text=\"tt-rss-labels\" schema-version=\"".SCHEMA_VERSION."\">"; - $result = $this->dbh->query("SELECT * FROM ttrss_labels2 WHERE - owner_uid = " . $_SESSION['uid']); + $sth = $this->pdo->prepare("SELECT * FROM ttrss_labels2 WHERE + owner_uid = ?"); + $sth->execute([$owner_uid]); - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $sth->fetch()) { $name = htmlspecialchars($line['caption']); $fg_color = htmlspecialchars($line['fg_color']); $bg_color = htmlspecialchars($line['bg_color']); @@ -161,25 +168,23 @@ class Opml extends Handler_Protected { $out .= "<outline text=\"tt-rss-filters\" schema-version=\"".SCHEMA_VERSION."\">"; - $result = $this->dbh->query("SELECT * FROM ttrss_filters2 - WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY id"); - - while ($line = $this->dbh->fetch_assoc($result)) { - foreach (array('enabled', 'match_any_rule', 'inverse') as $b) { - $line[$b] = sql_bool_to_bool($line[$b]); - } + $sth = $this->pdo->prepare("SELECT * FROM ttrss_filters2 + WHERE owner_uid = ? ORDER BY id"); + $sth->execute([$owner_uid]); + while ($line = $sth->fetch()) { $line["rules"] = array(); $line["actions"] = array(); - $tmp_result = $this->dbh->query("SELECT * FROM ttrss_filters2_rules - WHERE filter_id = ".$line["id"]); + $tmph = $this->pdo->prepare("SELECT * FROM ttrss_filters2_rules + WHERE filter_id = ?"); + $tmph->execute([$line['id']]); - while ($tmp_line = $this->dbh->fetch_assoc($tmp_result)) { + while ($tmp_line = $tmph->fetch()) { unset($tmp_line["id"]); unset($tmp_line["filter_id"]); - $cat_filter = sql_bool_to_bool($tmp_line["cat_filter"]); + $cat_filter = $tmp_line["cat_filter"]; if (!$tmp_line["match_on"]) { if ($cat_filter && $tmp_line["cat_id"] || $tmp_line["feed_id"]) { @@ -213,19 +218,17 @@ class Opml extends Handler_Protected { unset($tmp_line["match_on"]); } - $tmp_line["cat_filter"] = sql_bool_to_bool($tmp_line["cat_filter"]); - $tmp_line["inverse"] = sql_bool_to_bool($tmp_line["inverse"]); - unset($tmp_line["feed_id"]); unset($tmp_line["cat_id"]); array_push($line["rules"], $tmp_line); } - $tmp_result = $this->dbh->query("SELECT * FROM ttrss_filters2_actions - WHERE filter_id = ".$line["id"]); + $tmph = $this->pdo->prepare("SELECT * FROM ttrss_filters2_actions + WHERE filter_id = ?"); + $tmph->execute([$line['id']]); - while ($tmp_line = $this->dbh->fetch_assoc($tmp_result)) { + while ($tmp_line = $tmph->fetch()) { unset($tmp_line["id"]); unset($tmp_line["filter_id"]); @@ -278,29 +281,30 @@ class Opml extends Handler_Protected { private function opml_import_feed($node, $cat_id, $owner_uid) { $attrs = $node->attributes; - $feed_title = $this->dbh->escape_string(mb_substr($attrs->getNamedItem('text')->nodeValue, 0, 250)); - if (!$feed_title) $feed_title = $this->dbh->escape_string(mb_substr($attrs->getNamedItem('title')->nodeValue, 0, 250)); + $feed_title = mb_substr($attrs->getNamedItem('text')->nodeValue, 0, 250); + if (!$feed_title) $feed_title = mb_substr($attrs->getNamedItem('title')->nodeValue, 0, 250); - $feed_url = $this->dbh->escape_string($attrs->getNamedItem('xmlUrl')->nodeValue); - if (!$feed_url) $feed_url = $this->dbh->escape_string($attrs->getNamedItem('xmlURL')->nodeValue); + $feed_url = $attrs->getNamedItem('xmlUrl')->nodeValue; + if (!$feed_url) $feed_url = $attrs->getNamedItem('xmlURL')->nodeValue; - $site_url = $this->dbh->escape_string(mb_substr($attrs->getNamedItem('htmlUrl')->nodeValue, 0, 250)); + $site_url = mb_substr($attrs->getNamedItem('htmlUrl')->nodeValue, 0, 250); if ($feed_url && $feed_title) { - $result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE - feed_url = '$feed_url' AND owner_uid = '$owner_uid'"); + $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE + feed_url = ? AND owner_uid = ?"); + $sth->execute([$feed_url, $owner_uid]); - if ($this->dbh->num_rows($result) == 0) { + if (!$sth->fetch()) { #$this->opml_notice("[FEED] [$feed_title/$feed_url] dst_CAT=$cat_id"); $this->opml_notice(T_sprintf("Adding feed: %s", $feed_title)); - if (!$cat_id) $cat_id = 'NULL'; + if (!$cat_id) $cat_id = null; - $query = "INSERT INTO ttrss_feeds + $sth = $this->pdo->prepare("INSERT INTO ttrss_feeds (title, feed_url, owner_uid, cat_id, site_url, order_id) VALUES - ('$feed_title', '$feed_url', '$owner_uid', - $cat_id, '$site_url', 0)"; - $this->dbh->query($query); + (?, ?, ?, ?, ?, 0)"); + + $sth->execute([$feed_title, $feed_url, $owner_uid, $cat_id, $site_url]); } else { $this->opml_notice(T_sprintf("Duplicate feed: %s", $feed_title)); @@ -310,11 +314,11 @@ class Opml extends Handler_Protected { private function opml_import_label($node, $owner_uid) { $attrs = $node->attributes; - $label_name = $this->dbh->escape_string($attrs->getNamedItem('label-name')->nodeValue); + $label_name = $attrs->getNamedItem('label-name')->nodeValue; if ($label_name) { - $fg_color = $this->dbh->escape_string($attrs->getNamedItem('label-fg-color')->nodeValue); - $bg_color = $this->dbh->escape_string($attrs->getNamedItem('label-bg-color')->nodeValue); + $fg_color = $attrs->getNamedItem('label-fg-color')->nodeValue; + $bg_color = $attrs->getNamedItem('label-bg-color')->nodeValue; if (!Labels::find_id($label_name, $_SESSION['uid'])) { $this->opml_notice(T_sprintf("Adding label %s", htmlspecialchars($label_name))); @@ -327,10 +331,10 @@ class Opml extends Handler_Protected { private function opml_import_preference($node) { $attrs = $node->attributes; - $pref_name = $this->dbh->escape_string($attrs->getNamedItem('pref-name')->nodeValue); + $pref_name = $attrs->getNamedItem('pref-name')->nodeValue; if ($pref_name) { - $pref_value = $this->dbh->escape_string($attrs->getNamedItem('value')->nodeValue); + $pref_value = $attrs->getNamedItem('value')->nodeValue; $this->opml_notice(T_sprintf("Setting preference key %s to %s", $pref_name, $pref_value)); @@ -342,7 +346,7 @@ class Opml extends Handler_Protected { private function opml_import_filter($node) { $attrs = $node->attributes; - $filter_type = $this->dbh->escape_string($attrs->getNamedItem('filter-type')->nodeValue); + $filter_type = $attrs->getNamedItem('filter-type')->nodeValue; if ($filter_type == '2') { $filter = json_decode($node->nodeValue, true); @@ -351,24 +355,28 @@ class Opml extends Handler_Protected { $match_any_rule = bool_to_sql_bool($filter["match_any_rule"]); $enabled = bool_to_sql_bool($filter["enabled"]); $inverse = bool_to_sql_bool($filter["inverse"]); - $title = db_escape_string($filter["title"]); + $title = $filter["title"]; - $this->dbh->query("BEGIN"); + //print "F: $title, $inverse, $enabled, $match_any_rule"; - $this->dbh->query("INSERT INTO ttrss_filters2 (match_any_rule,enabled,inverse,title,owner_uid) - VALUES ($match_any_rule, $enabled, $inverse, '$title', - ".$_SESSION["uid"].")"); + $sth = $this->pdo->prepare("INSERT INTO ttrss_filters2 (match_any_rule,enabled,inverse,title,owner_uid) + VALUES (?, ?, ?, ?, ?)"); - $result = $this->dbh->query("SELECT MAX(id) AS id FROM ttrss_filters2 WHERE - owner_uid = ".$_SESSION["uid"]); - $filter_id = $this->dbh->fetch_result($result, 0, "id"); + $sth->execute([$match_any_rule, $enabled, $inverse, $title, $_SESSION['uid']]); + + $sth = $this->pdo->prepare("SELECT MAX(id) AS id FROM ttrss_filters2 WHERE + owner_uid = ?"); + $sth->execute([$_SESSION['uid']]); + + $row = $sth->fetch(); + $filter_id = $row['id']; if ($filter_id) { $this->opml_notice(T_sprintf("Adding filter...")); foreach ($filter["rules"] as $rule) { - $feed_id = "NULL"; - $cat_id = "NULL"; + $feed_id = null; + $cat_id = null; if ($rule["match"]) { @@ -384,70 +392,86 @@ class Opml extends Handler_Protected { $match_id = false; if (!$is_cat) { - $tmp_result = $this->dbh->query("SELECT id FROM ttrss_feeds - WHERE title = '" . $this->dbh->escape_string($name) . "' AND owner_uid = " . $_SESSION["uid"]); - if ($this->dbh->num_rows($tmp_result) > 0) { - $match_id = $this->dbh->fetch_result($tmp_result, 0, "id"); + $tsth = $this->pdo->prepare("SELECT id FROM ttrss_feeds + WHERE title = ? AND owner_uid = ?"); + + $tsth->execute([$name, $_SESSION['uid']]); + + if ($row = $tsth->fetch()) { + $match_id = $row['id']; } } else { - $tmp_result = $this->dbh->query("SELECT id FROM ttrss_feed_categories - WHERE title = '" . $this->dbh->escape_string($name) . "' AND owner_uid = " . $_SESSION["uid"]); + $tsth = $this->pdo->prepare("SELECT id FROM ttrss_feed_categories + WHERE title = ? AND owner_uid = ?"); + $tsth->execute([$name, $_SESSION['uid']]); - if ($this->dbh->num_rows($tmp_result) > 0) { - $match_id = 'CAT:' . $this->dbh->fetch_result($tmp_result, 0, "id"); - } + if ($row = $tsth->fetch()) { + $match_id = $row['id']; + } } if ($match_id) array_push($match_on, $match_id); } } - $reg_exp = $this->dbh->escape_string($rule["reg_exp"]); + $reg_exp = $rule["reg_exp"]; $filter_type = (int)$rule["filter_type"]; $inverse = bool_to_sql_bool($rule["inverse"]); - $match_on = $this->dbh->escape_string(json_encode($match_on)); + $match_on = json_encode($match_on); - $this->dbh->query("INSERT INTO ttrss_filters2_rules (feed_id,cat_id,match_on,filter_id,filter_type,reg_exp,cat_filter,inverse) - VALUES (NULL, NULL, '$match_on',$filter_id, $filter_type, '$reg_exp', false, $inverse)"); + $usth = $this->pdo->prepare("INSERT INTO ttrss_filters2_rules + (feed_id,cat_id,match_on,filter_id,filter_type,reg_exp,cat_filter,inverse) + VALUES + (NULL, NULL, ?, ?, ?, ?, false, ?)"); + $usth->execute([$match_on, $filter_id, $filter_type, $reg_exp, $inverse]); } else { if (!$rule["cat_filter"]) { - $tmp_result = $this->dbh->query("SELECT id FROM ttrss_feeds - WHERE title = '" . $this->dbh->escape_string($rule["feed"]) . "' AND owner_uid = " . $_SESSION["uid"]); - if ($this->dbh->num_rows($tmp_result) > 0) { - $feed_id = $this->dbh->fetch_result($tmp_result, 0, "id"); + $tsth = $this->pdo->prepare("SELECT id FROM ttrss_feeds + WHERE title = ? AND owner_uid = ?"); + + $tsth->execute([$rule['feed'], $_SESSION['uid']]); + + if ($row = $tsth->fetch()) { + $feed_id = $row['id']; } } else { - $tmp_result = $this->dbh->query("SELECT id FROM ttrss_feed_categories - WHERE title = '" . $this->dbh->escape_string($rule["feed"]) . "' AND owner_uid = " . $_SESSION["uid"]); + $tsth = $this->pdo->prepare("SELECT id FROM ttrss_feed_categories + WHERE title = ? AND owner_uid = ?"); - if ($this->dbh->num_rows($tmp_result) > 0) { - $cat_id = $this->dbh->fetch_result($tmp_result, 0, "id"); - } + $tsth->execute([$rule['feed'], $_SESSION['uid']]); + + if ($row = $tsth->fetch()) { + $feed_id = $row['id']; + } } $cat_filter = bool_to_sql_bool($rule["cat_filter"]); - $reg_exp = $this->dbh->escape_string($rule["reg_exp"]); + $reg_exp = $rule["reg_exp"]; $filter_type = (int)$rule["filter_type"]; $inverse = bool_to_sql_bool($rule["inverse"]); - $this->dbh->query("INSERT INTO ttrss_filters2_rules (feed_id,cat_id,filter_id,filter_type,reg_exp,cat_filter,inverse) - VALUES ($feed_id, $cat_id, $filter_id, $filter_type, '$reg_exp', $cat_filter,$inverse)"); + $usth = $this->pdo->prepare("INSERT INTO ttrss_filters2_rules + (feed_id,cat_id,filter_id,filter_type,reg_exp,cat_filter,inverse) + VALUES + (?, ?, ?, ?, ?, ?, ?)"); + $usth->execute([$feed_id, $cat_id, $filter_id, $filter_type, $reg_exp, $cat_filter, $inverse]); } } foreach ($filter["actions"] as $action) { $action_id = (int)$action["action_id"]; - $action_param = $this->dbh->escape_string($action["action_param"]); + $action_param = $action["action_param"]; - $this->dbh->query("INSERT INTO ttrss_filters2_actions (filter_id,action_id,action_param) - VALUES ($filter_id, $action_id, '$action_param')"); + $usth = $this->pdo->prepare("INSERT INTO ttrss_filters2_actions + (filter_id,action_id,action_param) + VALUES + (?, ?, ?)"); + $usth->execute([$filter_id, $action_id, $action_param]); } } - - $this->dbh->query("COMMIT"); } } } @@ -456,19 +480,19 @@ class Opml extends Handler_Protected { $default_cat_id = (int) $this->get_feed_category('Imported feeds', false); if ($root_node) { - $cat_title = $this->dbh->escape_string(mb_substr($root_node->attributes->getNamedItem('text')->nodeValue, 0, 250)); + $cat_title = mb_substr($root_node->attributes->getNamedItem('text')->nodeValue, 0, 250); if (!$cat_title) - $cat_title = $this->dbh->escape_string(mb_substr($root_node->attributes->getNamedItem('title')->nodeValue, 0, 250)); + $cat_title = mb_substr($root_node->attributes->getNamedItem('title')->nodeValue, 0, 250); if (!in_array($cat_title, array("tt-rss-filters", "tt-rss-labels", "tt-rss-prefs"))) { $cat_id = $this->get_feed_category($cat_title, $parent_id); - $this->dbh->query("BEGIN"); + if ($cat_id === false) { add_feed_category($cat_title, $parent_id); $cat_id = $this->get_feed_category($cat_title, $parent_id); } - $this->dbh->query("COMMIT"); + } else { $cat_id = 0; } @@ -488,12 +512,12 @@ class Opml extends Handler_Protected { foreach ($outlines as $node) { if ($node->hasAttributes() && strtolower($node->tagName) == "outline") { $attrs = $node->attributes; - $node_cat_title = $this->dbh->escape_string($attrs->getNamedItem('text')->nodeValue); + $node_cat_title = $attrs->getNamedItem('text')->nodeValue; if (!$node_cat_title) - $node_cat_title = $this->dbh->escape_string($attrs->getNamedItem('title')->nodeValue); + $node_cat_title = $attrs->getNamedItem('title')->nodeValue; - $node_feed_url = $this->dbh->escape_string($attrs->getNamedItem('xmlUrl')->nodeValue); + $node_feed_url = $attrs->getNamedItem('xmlUrl')->nodeValue; if ($node_cat_title && !$node_feed_url) { $this->opml_import_category($doc, $node, $owner_uid, $cat_id); @@ -561,7 +585,9 @@ class Opml extends Handler_Protected { } if ($doc) { + $this->pdo->beginTransaction(); $this->opml_import_category($doc, false, $owner_uid, false); + $this->pdo->commit(); } else { print_error(__('Error while parsing document.')); } @@ -581,22 +607,20 @@ class Opml extends Handler_Protected { } function get_feed_category($feed_cat, $parent_cat_id = false) { - if ($parent_cat_id) { - $parent_qpart = "parent_cat = '$parent_cat_id'"; - $parent_insert = "'$parent_cat_id'"; - } else { - $parent_qpart = "parent_cat IS NULL"; - $parent_insert = "NULL"; - } - $result = db_query( - "SELECT id FROM ttrss_feed_categories - WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]); + $parent_cat_id = (int) $parent_cat_id; - if (db_num_rows($result) == 0) { - return false; + $sth = $this->pdo->prepare("SELECT id FROM ttrss_feed_categories + WHERE title = :title + AND (parent_cat = :parent OR (:parent = 0 AND parent_cat IS NULL)) + AND owner_uid = :uid"); + + $sth->execute([':title' => $feed_cat, ':parent' => $parent_cat_id, ':uid' => $_SESSION['uid']]); + + if ($row = $sth->fetch()) { + return $row['id']; } else { - return db_fetch_result($result, 0, "id"); + return false; } } diff --git a/classes/plugin.php b/classes/plugin.php index fcf329ca1..b90c603b7 100644 --- a/classes/plugin.php +++ b/classes/plugin.php @@ -2,11 +2,19 @@ abstract class Plugin { const API_VERSION_COMPAT = 1; + /** @var PDO */ + protected $pdo; + + /* @var PluginHost $host */ abstract function init($host); abstract function about(); // return array(1.0, "plugin", "No description", "No author", false); + function __construct() { + $this->pdo = Db::pdo(); + } + function flags() { /* associative array, possible keys: needs_curl = boolean diff --git a/classes/pluginhandler.php b/classes/pluginhandler.php index 1c9e7aef6..d10343e09 100644 --- a/classes/pluginhandler.php +++ b/classes/pluginhandler.php @@ -5,7 +5,7 @@ class PluginHandler extends Handler_Protected { } function catchall($method) { - $plugin = PluginHost::getInstance()->get_plugin($_REQUEST["plugin"]); + $plugin = PluginHost::getInstance()->get_plugin(clean($_REQUEST["plugin"])); if ($plugin) { if (method_exists($plugin, $method)) { diff --git a/classes/pluginhost.php b/classes/pluginhost.php index f56343c5f..4224893c2 100644 --- a/classes/pluginhost.php +++ b/classes/pluginhost.php @@ -1,6 +1,6 @@ <?php class PluginHost { - private $dbh; + private $pdo; private $hooks = array(); private $plugins = array(); private $handlers = array(); @@ -63,7 +63,7 @@ class PluginHost { const KIND_USER = 3; function __construct() { - $this->dbh = Db::get(); + $this->pdo = Db::pdo(); $this->storage = array(); } @@ -90,9 +90,13 @@ class PluginHost { } function get_dbh() { - return $this->dbh; + return Db::get(); } + function get_pdo() { + return $this->pdo; + } + function get_plugin_names() { $names = array(); @@ -276,8 +280,6 @@ class PluginHost { } else { return false; } - - return false; } function get_commands() { @@ -295,10 +297,11 @@ class PluginHost { function load_data() { if ($this->owner_uid) { - $result = $this->dbh->query("SELECT name, content FROM ttrss_plugin_storage - WHERE owner_uid = '".$this->owner_uid."'"); + $sth = $this->pdo->prepare("SELECT name, content FROM ttrss_plugin_storage + WHERE owner_uid = ?"); + $sth->execute([$this->owner_uid]); - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $sth->fetch()) { $this->storage[$line["name"]] = unserialize($line["content"]); } } @@ -306,30 +309,30 @@ class PluginHost { private function save_data($plugin) { if ($this->owner_uid) { - $plugin = $this->dbh->escape_string($plugin); - - $this->dbh->query("BEGIN"); + $this->pdo->beginTransaction(); - $result = $this->dbh->query("SELECT id FROM ttrss_plugin_storage WHERE - owner_uid= '".$this->owner_uid."' AND name = '$plugin'"); + $sth = $this->pdo->prepare("SELECT id FROM ttrss_plugin_storage WHERE + owner_uid= ? AND name = ?"); + $sth->execute([$this->owner_uid, $plugin]); if (!isset($this->storage[$plugin])) $this->storage[$plugin] = array(); - $content = $this->dbh->escape_string(serialize($this->storage[$plugin]), - false); + $content = serialize($this->storage[$plugin]); - if ($this->dbh->num_rows($result) != 0) { - $this->dbh->query("UPDATE ttrss_plugin_storage SET content = '$content' - WHERE owner_uid= '".$this->owner_uid."' AND name = '$plugin'"); + if ($sth->fetch()) { + $sth = $this->pdo->prepare("UPDATE ttrss_plugin_storage SET content = ? + WHERE owner_uid= ? AND name = ?"); + $sth->execute([(string)$content, $this->owner_uid, $plugin]); } else { - $this->dbh->query("INSERT INTO ttrss_plugin_storage + $sth = $this->pdo->prepare("INSERT INTO ttrss_plugin_storage (name,owner_uid,content) VALUES - ('$plugin','".$this->owner_uid."','$content')"); + (?, ?, ?)"); + $sth->execute([$plugin, $this->owner_uid, (string)$content]); } - $this->dbh->query("COMMIT"); + $this->pdo->commit(); } } @@ -366,8 +369,9 @@ class PluginHost { unset($this->storage[$idx]); - $this->dbh->query("DELETE FROM ttrss_plugin_storage WHERE name = '$idx' - AND owner_uid = " . $this->owner_uid); + $sth = $this->pdo->prepare("DELETE FROM ttrss_plugin_storage WHERE name = ? + AND owner_uid = ?"); + $sth->execute([$idx, $this->owner_uid]); } } diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php index 2a9c57e79..846e814ee 100644 --- a/classes/pref/feeds.php +++ b/classes/pref/feeds.php @@ -17,35 +17,34 @@ class Pref_Feeds extends Handler_Protected { } function renamecat() { - $title = $this->dbh->escape_string($_REQUEST['title']); - $id = $this->dbh->escape_string($_REQUEST['id']); + $title = clean($_REQUEST['title']); + $id = clean($_REQUEST['id']); if ($title) { - $this->dbh->query("UPDATE ttrss_feed_categories SET - title = '$title' WHERE id = '$id' AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_feed_categories SET + title = ? WHERE id = ? AND owner_uid = ?"); + $sth->execute([$title, $id, $_SESSION['uid']]); } - return; } private function get_category_items($cat_id) { - if ($_REQUEST['mode'] != 2) + if (clean($_REQUEST['mode']) != 2) $search = $_SESSION["prefs_feed_search"]; else $search = ""; - if ($search) $search_qpart = " AND (LOWER(title) LIKE LOWER('%$search%') OR LOWER(feed_url) LIKE LOWER('%$search%'))"; - // first one is set by API - $show_empty_cats = $_REQUEST['force_show_empty'] || - ($_REQUEST['mode'] != 2 && !$search); + $show_empty_cats = clean($_REQUEST['force_show_empty']) || + (clean($_REQUEST['mode']) != 2 && !$search); $items = array(); - $result = $this->dbh->query("SELECT id, title FROM ttrss_feed_categories - WHERE owner_uid = " . $_SESSION["uid"] . " AND parent_cat = '$cat_id' ORDER BY order_id, title"); + $sth = $this->pdo->prepare("SELECT id, title FROM ttrss_feed_categories + WHERE owner_uid = ? AND parent_cat = ? ORDER BY order_id, title"); + $sth->execute([$_SESSION['uid'], $cat_id]); - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $sth->fetch()) { $cat = array(); $cat['id'] = 'CAT:' . $line['id']; @@ -69,13 +68,17 @@ class Pref_Feeds extends Handler_Protected { } - $feed_result = $this->dbh->query("SELECT id, title, last_error, + $fsth = $this->pdo->prepare("SELECT id, title, last_error, ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated FROM ttrss_feeds - WHERE cat_id = '$cat_id' AND owner_uid = ".$_SESSION["uid"]. - "$search_qpart ORDER BY order_id, title"); + WHERE cat_id = :cat AND + owner_uid = :uid AND + (:search = '' OR (LOWER(title) LIKE :search OR LOWER(feed_url) LIKE :search)) + ORDER BY order_id, title"); - while ($feed_line = $this->dbh->fetch_assoc($feed_result)) { + $fsth->execute([":cat" => $cat_id, ":uid" => $_SESSION['uid'], ":search" => $search ? "%$search%" : ""]); + + while ($feed_line = $fsth->fetch()) { $feed = array(); $feed['id'] = 'FEED:' . $feed_line['id']; $feed['bare_id'] = (int)$feed_line['id']; @@ -100,13 +103,11 @@ class Pref_Feeds extends Handler_Protected { function makefeedtree() { - if ($_REQUEST['mode'] != 2) + if (clean($_REQUEST['mode']) != 2) $search = $_SESSION["prefs_feed_search"]; else $search = ""; - if ($search) $search_qpart = " AND LOWER(title) LIKE LOWER('%$search%')"; - $root = array(); $root['id'] = 'root'; $root['name'] = __('Feeds'); @@ -115,7 +116,7 @@ class Pref_Feeds extends Handler_Protected { $enable_cats = get_pref('ENABLE_FEED_CATS'); - if ($_REQUEST['mode'] == 2) { + if (clean($_REQUEST['mode']) == 2) { if ($enable_cats) { $cat = $this->feedlist_init_cat(-1); @@ -158,29 +159,31 @@ class Pref_Feeds extends Handler_Protected { $root['items'] = array_merge($root['items'], $cat['items']); } - $result = $this->dbh->query("SELECT * FROM - ttrss_labels2 WHERE owner_uid = ".$_SESSION['uid']." ORDER by caption"); + $sth = $this->pdo->prepare("SELECT * FROM + ttrss_labels2 WHERE owner_uid = ? ORDER by caption"); + $sth->execute([$_SESSION['uid']]); - if ($this->dbh->num_rows($result) > 0) { - - if (get_pref('ENABLE_FEED_CATS')) { - $cat = $this->feedlist_init_cat(-2); - } else { - $cat['items'] = array(); - } + if (get_pref('ENABLE_FEED_CATS')) { + $cat = $this->feedlist_init_cat(-2); + } else { + $cat['items'] = array(); + } - while ($line = $this->dbh->fetch_assoc($result)) { + $num_labels = 0; + while ($line = $sth->fetch()) { + ++$num_labels; - $label_id = Labels::label_to_feed_id($line['id']); + $label_id = Labels::label_to_feed_id($line['id']); - $feed = $this->feedlist_init_feed($label_id, false, 0); + $feed = $this->feedlist_init_feed($label_id, false, 0); - $feed['fg_color'] = $line['fg_color']; - $feed['bg_color'] = $line['bg_color']; + $feed['fg_color'] = $line['fg_color']; + $feed['bg_color'] = $line['bg_color']; - array_push($cat['items'], $feed); - } + array_push($cat['items'], $feed); + } + if ($num_labels) { if ($enable_cats) { array_push($root['items'], $cat); } else { @@ -190,13 +193,14 @@ class Pref_Feeds extends Handler_Protected { } if ($enable_cats) { - $show_empty_cats = $_REQUEST['force_show_empty'] || - ($_REQUEST['mode'] != 2 && !$search); + $show_empty_cats = clean($_REQUEST['force_show_empty']) || + (clean($_REQUEST['mode']) != 2 && !$search); - $result = $this->dbh->query("SELECT id, title FROM ttrss_feed_categories - WHERE owner_uid = " . $_SESSION["uid"] . " AND parent_cat IS NULL ORDER BY order_id, title"); + $sth = $this->pdo->prepare("SELECT id, title FROM ttrss_feed_categories + WHERE owner_uid = ? AND parent_cat IS NULL ORDER BY order_id, title"); + $sth->execute([$_SESSION['uid']]); - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $sth->fetch()) { $cat = array(); $cat['id'] = 'CAT:' . $line['id']; $cat['bare_id'] = (int)$line['id']; @@ -232,13 +236,16 @@ class Pref_Feeds extends Handler_Protected { $cat['unread'] = 0; $cat['child_unread'] = 0; - $feed_result = $this->dbh->query("SELECT id, title,last_error, + $fsth = $this->pdo->prepare("SELECT id, title,last_error, ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated FROM ttrss_feeds - WHERE cat_id IS NULL AND owner_uid = ".$_SESSION["uid"]. - "$search_qpart ORDER BY order_id, title"); + WHERE cat_id IS NULL AND + owner_uid = :uid AND + (:search = '' OR (LOWER(title) LIKE :search OR LOWER(feed_url) LIKE :search)) + ORDER BY order_id, title"); + $fsth->execute([":uid" => $_SESSION['uid'], ":search" => $search ? "%$search%" : ""]); - while ($feed_line = $this->dbh->fetch_assoc($feed_result)) { + while ($feed_line = $fsth->fetch()) { $feed = array(); $feed['id'] = 'FEED:' . $feed_line['id']; $feed['bare_id'] = (int)$feed_line['id']; @@ -264,13 +271,15 @@ class Pref_Feeds extends Handler_Protected { $root['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', (int) $num_children), $num_children); } else { - $feed_result = $this->dbh->query("SELECT id, title, last_error, + $fsth = $this->pdo->prepare("SELECT id, title, last_error, ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated FROM ttrss_feeds - WHERE owner_uid = ".$_SESSION["uid"]. - "$search_qpart ORDER BY order_id, title"); + WHERE owner_uid = :uid AND + (:search = '' OR (LOWER(title) LIKE :search OR LOWER(feed_url) LIKE :search)) + ORDER BY order_id, title"); + $fsth->execute([":uid" => $_SESSION['uid'], ":search" => $search ? "%$search%" : ""]); - while ($feed_line = $this->dbh->fetch_assoc($feed_result)) { + while ($feed_line = $fsth->fetch()) { $feed = array(); $feed['id'] = 'FEED:' . $feed_line['id']; $feed['bare_id'] = (int)$feed_line['id']; @@ -294,7 +303,7 @@ class Pref_Feeds extends Handler_Protected { $fl['identifier'] = 'id'; $fl['label'] = 'name'; - if ($_REQUEST['mode'] != 2) { + if (clean($_REQUEST['mode']) != 2) { $fl['items'] = array($root); } else { $fl['items'] = $root['items']; @@ -304,15 +313,15 @@ class Pref_Feeds extends Handler_Protected { } function catsortreset() { - $this->dbh->query("UPDATE ttrss_feed_categories - SET order_id = 0 WHERE owner_uid = " . $_SESSION["uid"]); - return; + $sth = $this->pdo->prepare("UPDATE ttrss_feed_categories + SET order_id = 0 WHERE owner_uid = ?"); + $sth->execute([$_SESSION['uid']]); } function feedsortreset() { - $this->dbh->query("UPDATE ttrss_feeds - SET order_id = 0 WHERE owner_uid = " . $_SESSION["uid"]); - return; + $sth = $this->pdo->prepare("UPDATE ttrss_feeds + SET order_id = 0 WHERE owner_uid = ?"); + $sth->execute([$_SESSION['uid']]); } private function process_category_order(&$data_map, $item_id, $parent_id = false, $nest_level = 0) { @@ -324,19 +333,20 @@ class Pref_Feeds extends Handler_Protected { if ($debug) _debug("$prefix C: $item_id P: $parent_id"); - $bare_item_id = $this->dbh->escape_string(substr($item_id, strpos($item_id, ':')+1)); + $bare_item_id = substr($item_id, strpos($item_id, ':')+1); if ($item_id != 'root') { if ($parent_id && $parent_id != 'root') { $parent_bare_id = substr($parent_id, strpos($parent_id, ':')+1); - $parent_qpart = $this->dbh->escape_string($parent_bare_id); + $parent_qpart = $parent_bare_id; } else { - $parent_qpart = 'NULL'; + $parent_qpart = null; } - $this->dbh->query("UPDATE ttrss_feed_categories - SET parent_cat = $parent_qpart WHERE id = '$bare_item_id' AND - owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_feed_categories + SET parent_cat = ? WHERE id = ? AND + owner_uid = ?"); + $sth->execute([$parent_qpart, $bare_item_id, $_SESSION['uid']]); } $order_id = 1; @@ -346,7 +356,7 @@ class Pref_Feeds extends Handler_Protected { if ($cat && is_array($cat)) { foreach ($cat as $item) { $id = $item['_reference']; - $bare_id = $this->dbh->escape_string(substr($id, strpos($id, ':')+1)); + $bare_id = substr($id, strpos($id, ':')+1); if ($debug) _debug("$prefix [$order_id] $id/$bare_id"); @@ -354,30 +364,22 @@ class Pref_Feeds extends Handler_Protected { if (strpos($id, "FEED") === 0) { - $cat_id = ($item_id != "root") ? - $this->dbh->escape_string($bare_item_id) : "NULL"; + $cat_id = ($item_id != "root") ? $bare_item_id : null; - $cat_qpart = ($cat_id != 0) ? "cat_id = '$cat_id'" : - "cat_id = NULL"; + $sth = $this->pdo->prepare("UPDATE ttrss_feeds + SET order_id = ?, cat_id = ? + WHERE id = ? AND owner_uid = ?"); - $this->dbh->query("UPDATE ttrss_feeds - SET order_id = $order_id, $cat_qpart - WHERE id = '$bare_id' AND - owner_uid = " . $_SESSION["uid"]); + $sth->execute([$order_id, $cat_id ? $cat_id : null, $bare_id, $_SESSION['uid']]); } else if (strpos($id, "CAT:") === 0) { $this->process_category_order($data_map, $item['_reference'], $item_id, $nest_level+1); - if ($item_id != 'root') { - $parent_qpart = $this->dbh->escape_string($bare_id); - } else { - $parent_qpart = 'NULL'; - } - - $this->dbh->query("UPDATE ttrss_feed_categories - SET order_id = '$order_id' WHERE id = '$bare_id' AND - owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_feed_categories + SET order_id = ? WHERE id = ? AND + owner_uid = ?"); + $sth->execute([$order_id, $bare_id, $_SESSION['uid']]); } } @@ -387,9 +389,9 @@ class Pref_Feeds extends Handler_Protected { } function savefeedorder() { - $data = json_decode($_POST['payload'], true); + $data = json_decode(clean($_POST['payload']), true); - #file_put_contents("/tmp/saveorder.json", $_POST['payload']); + #file_put_contents("/tmp/saveorder.json", clean($_POST['payload'])); #$data = json_decode(file_get_contents("/tmp/saveorder.json"), true); if (!is_array($data['items'])) @@ -419,67 +421,28 @@ class Pref_Feeds extends Handler_Protected { } $this->process_category_order($data_map, $root_item); - - /* foreach ($data['items'][0]['items'] as $item) { - $id = $item['_reference']; - $bare_id = substr($id, strpos($id, ':')+1); - - ++$cat_order_id; - - if ($bare_id > 0) { - $this->dbh->query("UPDATE ttrss_feed_categories - SET order_id = '$cat_order_id' WHERE id = '$bare_id' AND - owner_uid = " . $_SESSION["uid"]); - } - - $feed_order_id = 0; - - if (is_array($data_map[$id])) { - foreach ($data_map[$id] as $feed) { - $id = $feed['_reference']; - $feed_id = substr($id, strpos($id, ':')+1); - - if ($bare_id != 0) - $cat_query = "cat_id = '$bare_id'"; - else - $cat_query = "cat_id = NULL"; - - $this->dbh->query("UPDATE ttrss_feeds - SET order_id = '$feed_order_id', - $cat_query - WHERE id = '$feed_id' AND - owner_uid = " . $_SESSION["uid"]); - - ++$feed_order_id; - } - } - } */ } - - return; } function removeicon() { - $feed_id = $this->dbh->escape_string($_REQUEST["feed_id"]); + $feed_id = clean($_REQUEST["feed_id"]); - $result = $this->dbh->query("SELECT id FROM ttrss_feeds - WHERE id = '$feed_id' AND owner_uid = ". $_SESSION["uid"]); + $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds + WHERE id = ? AND owner_uid = ?"); + $sth->execute([$feed_id, $_SESSION['uid']]); - if ($this->dbh->num_rows($result) != 0) { + if ($row = $sth->fetch()) { @unlink(ICONS_DIR . "/$feed_id.ico"); - $this->dbh->query("UPDATE ttrss_feeds SET favicon_avg_color = NULL - where id = '$feed_id'"); + $sth = $this->pdo->prepare("UPDATE ttrss_feeds SET favicon_avg_color = NULL + where id = ?"); + $sth->execute([$feed_id]); } - - return; } function uploadicon() { header("Content-type: text/html"); - $tmp_file = false; - if (is_uploaded_file($_FILES['icon_file']['tmp_name'])) { $tmp_file = tempnam(CACHE_DIR . '/upload', 'icon'); @@ -494,20 +457,23 @@ class Pref_Feeds extends Handler_Protected { } $icon_file = $tmp_file; - $feed_id = $this->dbh->escape_string($_REQUEST["feed_id"]); + $feed_id = clean($_REQUEST["feed_id"]); if (is_file($icon_file) && $feed_id) { if (filesize($icon_file) < 65535) { - $result = $this->dbh->query("SELECT id FROM ttrss_feeds - WHERE id = '$feed_id' AND owner_uid = ". $_SESSION["uid"]); + $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds + WHERE id = ? AND owner_uid = ?"); + $sth->execute([$feed_id, $_SESSION['uid']]); - if ($this->dbh->num_rows($result) != 0) { + if ($row = $sth->fetch()) { @unlink(ICONS_DIR . "/$feed_id.ico"); if (rename($icon_file, ICONS_DIR . "/$feed_id.ico")) { - $this->dbh->query("UPDATE ttrss_feeds SET + + $sth = $this->pdo->prepare("UPDATE ttrss_feeds SET favicon_avg_color = '' - WHERE id = '$feed_id'"); + WHERE id = ?"); + $sth->execute([$feed_id]); $rc = 0; } @@ -533,283 +499,283 @@ class Pref_Feeds extends Handler_Protected { global $purge_intervals; global $update_intervals; - print '<div dojoType="dijit.layout.TabContainer" style="height : 450px"> - <div dojoType="dijit.layout.ContentPane" title="'.__('General').'">'; - $feed_id = $this->dbh->escape_string($_REQUEST["id"]); + $feed_id = clean($_REQUEST["id"]); - $result = $this->dbh->query( - "SELECT * FROM ttrss_feeds WHERE id = '$feed_id' AND - owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("SELECT * FROM ttrss_feeds WHERE id = ? AND + owner_uid = ?"); + $sth->execute([$feed_id, $_SESSION['uid']]); - $auth_pass_encrypted = sql_bool_to_bool($this->dbh->fetch_result($result, 0, - "auth_pass_encrypted")); + if ($row = $sth->fetch()) { + print '<div dojoType="dijit.layout.TabContainer" style="height : 450px"> + <div dojoType="dijit.layout.ContentPane" title="'.__('General').'">'; - $title = htmlspecialchars($this->dbh->fetch_result($result, - 0, "title")); + $auth_pass_encrypted = $row["auth_pass_encrypted"]; - print_hidden("id", "$feed_id"); - print_hidden("op", "pref-feeds"); - print_hidden("method", "editSave"); + $title = htmlspecialchars($row["title"]); - print "<div class=\"dlgSec\">".__("Feed")."</div>"; - print "<div class=\"dlgSecCont\">"; + print_hidden("id", "$feed_id"); + print_hidden("op", "pref-feeds"); + print_hidden("method", "editSave"); + + print "<div class=\"dlgSec\">".__("Feed")."</div>"; + print "<div class=\"dlgSecCont\">"; - /* Title */ + /* Title */ - print "<input dojoType=\"dijit.form.ValidationTextBox\" required=\"1\" + print "<input dojoType=\"dijit.form.ValidationTextBox\" required=\"1\" placeHolder=\"".__("Feed Title")."\" style=\"font-size : 16px; width: 20em\" name=\"title\" value=\"$title\">"; - /* Feed URL */ + /* Feed URL */ - $feed_url = $this->dbh->fetch_result($result, 0, "feed_url"); - $feed_url = htmlspecialchars($this->dbh->fetch_result($result, - 0, "feed_url")); + $feed_url = htmlspecialchars($row["feed_url"]); - print "<hr/>"; + print "<hr/>"; - print __('URL:') . " "; - print "<input dojoType=\"dijit.form.ValidationTextBox\" required=\"1\" + print __('URL:') . " "; + print "<input dojoType=\"dijit.form.ValidationTextBox\" required=\"1\" placeHolder=\"".__("Feed URL")."\" regExp='^(http|https)://.*' style=\"width : 20em\" name=\"feed_url\" value=\"$feed_url\">"; - $last_error = $this->dbh->fetch_result($result, 0, "last_error"); + $last_error = $row["last_error"]; - if ($last_error) { - print " <img src=\"images/error.png\" alt=\"(error)\" + if ($last_error) { + print " <img src=\"images/error.png\" alt=\"(error)\" style=\"vertical-align : middle\" title=\"".htmlspecialchars($last_error)."\">"; - } + } - /* Category */ + /* Category */ - if (get_pref('ENABLE_FEED_CATS')) { + if (get_pref('ENABLE_FEED_CATS')) { - $cat_id = $this->dbh->fetch_result($result, 0, "cat_id"); + $cat_id = $row["cat_id"]; - print "<hr/>"; + print "<hr/>"; - print __('Place in category:') . " "; + print __('Place in category:') . " "; - print_feed_cat_select("cat_id", $cat_id, - 'dojoType="dijit.form.Select"'); - } + print_feed_cat_select("cat_id", $cat_id, + 'dojoType="dijit.form.Select"'); + } - /* FTS Stemming Language */ + /* FTS Stemming Language */ - if (DB_TYPE == "pgsql") { - $feed_language = $this->dbh->fetch_result($result, 0, "feed_language"); + if (DB_TYPE == "pgsql") { + $feed_language = $row["feed_language"]; - print "<hr/>"; + print "<hr/>"; - print __('Language:') . " "; - print_select("feed_language", $feed_language, $this::$feed_languages, - 'dojoType="dijit.form.Select"'); - } + print __('Language:') . " "; + print_select("feed_language", $feed_language, $this::$feed_languages, + 'dojoType="dijit.form.Select"'); + } - print "</div>"; + print "</div>"; - print "<div class=\"dlgSec\">".__("Update")."</div>"; - print "<div class=\"dlgSecCont\">"; + print "<div class=\"dlgSec\">".__("Update")."</div>"; + print "<div class=\"dlgSecCont\">"; - /* Update Interval */ + /* Update Interval */ - $update_interval = $this->dbh->fetch_result($result, 0, "update_interval"); + $update_interval = $row["update_interval"]; - print_select_hash("update_interval", $update_interval, $update_intervals, - 'dojoType="dijit.form.Select"'); + print_select_hash("update_interval", $update_interval, $update_intervals, + 'dojoType="dijit.form.Select"'); - /* Purge intl */ + /* Purge intl */ - $purge_interval = $this->dbh->fetch_result($result, 0, "purge_interval"); + $purge_interval = $row["purge_interval"]; - print "<hr/>"; - print __('Article purging:') . " "; + print "<hr/>"; + print __('Article purging:') . " "; - print_select_hash("purge_interval", $purge_interval, $purge_intervals, - 'dojoType="dijit.form.Select" ' . + print_select_hash("purge_interval", $purge_interval, $purge_intervals, + 'dojoType="dijit.form.Select" ' . ((FORCE_ARTICLE_PURGE == 0) ? "" : 'disabled="1"')); - print "</div>"; + print "</div>"; - $auth_login = htmlspecialchars($this->dbh->fetch_result($result, 0, "auth_login")); - $auth_pass = $this->dbh->fetch_result($result, 0, "auth_pass"); + $auth_login = htmlspecialchars($row["auth_login"]); + $auth_pass = $row["auth_pass"]; - if ($auth_pass_encrypted && function_exists("mcrypt_decrypt")) { - require_once "crypt.php"; - $auth_pass = decrypt_string($auth_pass); - } + if ($auth_pass_encrypted && function_exists("mcrypt_decrypt")) { + require_once "crypt.php"; + $auth_pass = decrypt_string($auth_pass); + } - $auth_pass = htmlspecialchars($auth_pass); - $auth_enabled = $auth_login !== '' || $auth_pass !== ''; + $auth_pass = htmlspecialchars($auth_pass); + $auth_enabled = $auth_login !== '' || $auth_pass !== ''; - $auth_style = $auth_enabled ? '' : 'display: none'; - print "<div id='feedEditDlg_loginContainer' style='$auth_style'>"; - print "<div class=\"dlgSec\">".__("Authentication")."</div>"; - print "<div class=\"dlgSecCont\">"; + $auth_style = $auth_enabled ? '' : 'display: none'; + print "<div id='feedEditDlg_loginContainer' style='$auth_style'>"; + print "<div class=\"dlgSec\">".__("Authentication")."</div>"; + print "<div class=\"dlgSecCont\">"; - print "<input dojoType=\"dijit.form.TextBox\" id=\"feedEditDlg_login\" + print "<input dojoType=\"dijit.form.TextBox\" id=\"feedEditDlg_login\" placeHolder=\"".__("Login")."\" autocomplete=\"new-password\" name=\"auth_login\" value=\"$auth_login\"><hr/>"; - print "<input dojoType=\"dijit.form.TextBox\" type=\"password\" name=\"auth_pass\" + print "<input dojoType=\"dijit.form.TextBox\" type=\"password\" name=\"auth_pass\" autocomplete=\"new-password\" placeHolder=\"".__("Password")."\" value=\"$auth_pass\">"; - print "<div dojoType=\"dijit.Tooltip\" connectId=\"feedEditDlg_login\" position=\"below\"> + print "<div dojoType=\"dijit.Tooltip\" connectId=\"feedEditDlg_login\" position=\"below\"> ".__('<b>Hint:</b> you need to fill in your login information if your feed requires authentication, except for Twitter feeds.')." </div>"; - print "</div></div>"; + print "</div></div>"; - $auth_checked = $auth_enabled ? 'checked' : ''; - print "<div style=\"clear : both\"> + $auth_checked = $auth_enabled ? 'checked' : ''; + print "<div style=\"clear : both\"> <input type=\"checkbox\" $auth_checked name=\"need_auth\" dojoType=\"dijit.form.CheckBox\" id=\"feedEditDlg_loginCheck\" onclick='checkboxToggleElement(this, \"feedEditDlg_loginContainer\")'> <label for=\"feedEditDlg_loginCheck\">". - __('This feed requires authentication.')."</div>"; + __('This feed requires authentication.')."</div>"; - print '</div><div dojoType="dijit.layout.ContentPane" title="'.__('Options').'">'; + print '</div><div dojoType="dijit.layout.ContentPane" title="'.__('Options').'">'; - //print "<div class=\"dlgSec\">".__("Options")."</div>"; - print "<div class=\"dlgSecSimple\">"; + //print "<div class=\"dlgSec\">".__("Options")."</div>"; + print "<div class=\"dlgSecSimple\">"; - $private = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "private")); + $private = $row["private"]; - if ($private) { - $checked = "checked=\"1\""; - } else { - $checked = ""; - } + if ($private) { + $checked = "checked=\"1\""; + } else { + $checked = ""; + } - print "<input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"private\" id=\"private\" + print "<input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"private\" id=\"private\" $checked> <label for=\"private\">".__('Hide from Popular feeds')."</label>"; - $include_in_digest = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "include_in_digest")); + $include_in_digest = $row["include_in_digest"]; - if ($include_in_digest) { - $checked = "checked=\"1\""; - } else { - $checked = ""; - } + if ($include_in_digest) { + $checked = "checked=\"1\""; + } else { + $checked = ""; + } - print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"include_in_digest\" + print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"include_in_digest\" name=\"include_in_digest\" $checked> <label for=\"include_in_digest\">".__('Include in e-mail digest')."</label>"; - $always_display_enclosures = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "always_display_enclosures")); + $always_display_enclosures = $row["always_display_enclosures"]; - if ($always_display_enclosures) { - $checked = "checked"; - } else { - $checked = ""; - } + if ($always_display_enclosures) { + $checked = "checked"; + } else { + $checked = ""; + } - print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"always_display_enclosures\" + print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"always_display_enclosures\" name=\"always_display_enclosures\" $checked> <label for=\"always_display_enclosures\">".__('Always display image attachments')."</label>"; - $hide_images = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "hide_images")); + $hide_images = $row["hide_images"]; - if ($hide_images) { - $checked = "checked=\"1\""; - } else { - $checked = ""; - } + if ($hide_images) { + $checked = "checked=\"1\""; + } else { + $checked = ""; + } - print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"hide_images\" + print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"hide_images\" name=\"hide_images\" $checked> <label for=\"hide_images\">". - __('Do not embed images')."</label>"; + __('Do not embed images')."</label>"; - $cache_images = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "cache_images")); + $cache_images = $row["cache_images"]; - if ($cache_images) { - $checked = "checked=\"1\""; - } else { - $checked = ""; - } + if ($cache_images) { + $checked = "checked=\"1\""; + } else { + $checked = ""; + } - print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"cache_images\" + print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"cache_images\" name=\"cache_images\" $checked> <label for=\"cache_images\">". - __('Cache media')."</label>"; + __('Cache media')."</label>"; - $mark_unread_on_update = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "mark_unread_on_update")); + $mark_unread_on_update = $row["mark_unread_on_update"]; - if ($mark_unread_on_update) { - $checked = "checked"; - } else { - $checked = ""; - } + if ($mark_unread_on_update) { + $checked = "checked"; + } else { + $checked = ""; + } - print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"mark_unread_on_update\" + print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"mark_unread_on_update\" name=\"mark_unread_on_update\" $checked> <label for=\"mark_unread_on_update\">".__('Mark updated articles as unread')."</label>"; - print "</div>"; + print "</div>"; - print '</div><div dojoType="dijit.layout.ContentPane" title="'.__('Icon').'">'; + print '</div><div dojoType="dijit.layout.ContentPane" title="'.__('Icon').'">'; - /* Icon */ + /* Icon */ - print "<div class=\"dlgSecSimple\">"; + print "<div class=\"dlgSecSimple\">"; - print "<iframe name=\"icon_upload_iframe\" - style=\"width: 400px; height: 100px; display: none;\"></iframe>"; + print "<img class=\"feedIcon\" src=\"".Feeds::getFeedIcon($feed_id)."\">"; - print "<form style='display : block' target=\"icon_upload_iframe\" + print "<iframe name=\"icon_upload_iframe\" + style=\"width: 400px; height: 100px; display: none;\"></iframe>"; + + print "<form style='display : block' target=\"icon_upload_iframe\" enctype=\"multipart/form-data\" method=\"POST\" action=\"backend.php\"> - <input id=\"icon_file\" size=\"10\" name=\"icon_file\" type=\"file\"> + <label class=\"dijitButton\">".__("Choose file...")." + <input style=\"display: none\" id=\"icon_file\" size=\"10\" name=\"icon_file\" type=\"file\"> + </label> <input type=\"hidden\" name=\"op\" value=\"pref-feeds\"> <input type=\"hidden\" name=\"feed_id\" value=\"$feed_id\"> - <input type=\"hidden\" name=\"method\" value=\"uploadicon\"><p> + <input type=\"hidden\" name=\"method\" value=\"uploadicon\"> <button class=\"\" dojoType=\"dijit.form.Button\" onclick=\"return uploadFeedIcon();\" type=\"submit\">".__('Replace')."</button> - <button class=\"\" dojoType=\"dijit.form.Button\" onclick=\"return removeFeedIcon($feed_id);\" + <button class=\"danger\" dojoType=\"dijit.form.Button\" onclick=\"return removeFeedIcon($feed_id);\" type=\"submit\">".__('Remove')."</button> </form>"; - print "</div>"; + print "</div>"; - print '</div><div dojoType="dijit.layout.ContentPane" title="'.__('Plugins').'">'; + print '</div><div dojoType="dijit.layout.ContentPane" title="'.__('Plugins').'">'; - PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_EDIT_FEED, - "hook_prefs_edit_feed", $feed_id); + PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_EDIT_FEED, + "hook_prefs_edit_feed", $feed_id); - print "</div></div>"; + print "</div></div>"; - $title = htmlspecialchars($title, ENT_QUOTES); + $title = htmlspecialchars($title, ENT_QUOTES); - print "<div class='dlgButtons'> + print "<div class='dlgButtons'> <div style=\"float : left\"> <button class=\"danger\" dojoType=\"dijit.form.Button\" onclick='return unsubscribeFeed($feed_id, \"$title\")'>". __('Unsubscribe')."</button>"; - print "</div>"; + print "</div>"; - print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('feedEditDlg').execute()\">".__('Save')."</button> - <button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('feedEditDlg').hide()\">".__('Cancel')."</button> - </div>"; - - - return; + print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('feedEditDlg').execute()\">".__('Save')."</button> + <button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('feedEditDlg').hide()\">".__('Cancel')."</button> + </div>"; + } } function editfeeds() { global $purge_intervals; global $update_intervals; - $feed_ids = $this->dbh->escape_string($_REQUEST["ids"]); + $feed_ids = clean($_REQUEST["ids"]); print_notice("Enable the options you wish to apply using checkboxes on the right:"); @@ -958,72 +924,77 @@ class Pref_Feeds extends Handler_Protected { function editsaveops($batch) { - $feed_title = $this->dbh->escape_string(trim($_POST["title"])); - $feed_link = $this->dbh->escape_string(trim($_POST["feed_url"])); - $upd_intl = (int) $this->dbh->escape_string($_POST["update_interval"]); - $purge_intl = (int) $this->dbh->escape_string($_POST["purge_interval"]); - $feed_id = (int) $this->dbh->escape_string($_POST["id"]); /* editSave */ - $feed_ids = $this->dbh->escape_string($_POST["ids"]); /* batchEditSave */ - $cat_id = (int) $this->dbh->escape_string($_POST["cat_id"]); - $auth_login = $this->dbh->escape_string(trim($_POST["auth_login"])); - $auth_pass = trim($_POST["auth_pass"]); - $private = checkbox_to_sql_bool($this->dbh->escape_string($_POST["private"])); + $feed_title = trim(clean($_POST["title"])); + $feed_url = trim(clean($_POST["feed_url"])); + $upd_intl = (int) clean($_POST["update_interval"]); + $purge_intl = (int) clean($_POST["purge_interval"]); + $feed_id = (int) clean($_POST["id"]); /* editSave */ + $feed_ids = explode(",", clean($_POST["ids"])); /* batchEditSave */ + $cat_id = (int) clean($_POST["cat_id"]); + $auth_login = trim(clean($_POST["auth_login"])); + $auth_pass = trim(clean($_POST["auth_pass"])); + $private = checkbox_to_sql_bool(clean($_POST["private"])); $include_in_digest = checkbox_to_sql_bool( - $this->dbh->escape_string($_POST["include_in_digest"])); + clean($_POST["include_in_digest"])); $cache_images = checkbox_to_sql_bool( - $this->dbh->escape_string($_POST["cache_images"])); + clean($_POST["cache_images"])); $hide_images = checkbox_to_sql_bool( - $this->dbh->escape_string($_POST["hide_images"])); + clean($_POST["hide_images"])); $always_display_enclosures = checkbox_to_sql_bool( - $this->dbh->escape_string($_POST["always_display_enclosures"])); + clean($_POST["always_display_enclosures"])); $mark_unread_on_update = checkbox_to_sql_bool( - $this->dbh->escape_string($_POST["mark_unread_on_update"])); + clean($_POST["mark_unread_on_update"])); - $feed_language = $this->dbh->escape_string(trim($_POST["feed_language"])); - - $auth_pass = $this->dbh->escape_string($auth_pass); - - if (get_pref('ENABLE_FEED_CATS')) { - if ($cat_id && $cat_id != 0) { - $category_qpart = "cat_id = '$cat_id',"; - $category_qpart_nocomma = "cat_id = '$cat_id'"; - } else { - $category_qpart = 'cat_id = NULL,'; - $category_qpart_nocomma = 'cat_id = NULL'; - } - } else { - $category_qpart = ""; - $category_qpart_nocomma = ""; - } + $feed_language = trim(clean($_POST["feed_language"])); if (!$batch) { - if ($_POST["need_auth"] !== 'on') { + if (clean($_POST["need_auth"]) !== 'on') { $auth_login = ''; $auth_pass = ''; } - $result = db_query("SELECT feed_url FROM ttrss_feeds WHERE id = " . $feed_id); - $orig_feed_url = db_fetch_result($result, 0, "feed_url"); - - $reset_basic_info = $orig_feed_url != $feed_link; - - $this->dbh->query("UPDATE ttrss_feeds SET - $category_qpart - title = '$feed_title', feed_url = '$feed_link', - update_interval = '$upd_intl', - purge_interval = '$purge_intl', - auth_login = '$auth_login', - auth_pass = '$auth_pass', + $sth = $this->pdo->prepare("SELECT feed_url FROM ttrss_feeds WHERE id = ?"); + $sth->execute([$feed_id]); + $row = $sth->fetch(); + $orig_feed_url = $row["feed_url"]; + + $reset_basic_info = $orig_feed_url != $feed_url; + + $sth = $this->pdo->prepare("UPDATE ttrss_feeds SET + cat_id = :cat_id, + title = :title, + feed_url = :feed_url, + update_interval = :upd_intl, + purge_interval = :purge_intl, + auth_login = :auth_login, + auth_pass = :auth_pass, auth_pass_encrypted = false, - private = $private, - cache_images = $cache_images, - hide_images = $hide_images, - include_in_digest = $include_in_digest, - always_display_enclosures = $always_display_enclosures, - mark_unread_on_update = $mark_unread_on_update, - feed_language = '$feed_language' - WHERE id = '$feed_id' AND owner_uid = " . $_SESSION["uid"]); + private = :private, + cache_images = :cache_images, + hide_images = :hide_images, + include_in_digest = :include_in_digest, + always_display_enclosures = :always_display_enclosures, + mark_unread_on_update = :mark_unread_on_update, + feed_language = :feed_language + WHERE id = :id AND owner_uid = :uid"); + + $sth->execute([":title" => $feed_title, + ":cat_id" => $cat_id ? $cat_id : null, + ":feed_url" => $feed_url, + ":upd_intl" => $upd_intl, + ":purge_intl" => $purge_intl, + ":auth_login" => $auth_login, + ":auth_pass" => $auth_pass, + ":private" => (int)$private, + ":cache_images" => (int)$cache_images, + ":hide_images" => (int)$hide_images, + ":include_in_digest" => (int)$include_in_digest, + ":always_display_enclosures" => (int)$always_display_enclosures, + ":mark_unread_on_update" => (int)$mark_unread_on_update, + ":feed_language" => $feed_language, + ":id" => $feed_id, + ":uid" => $_SESSION['uid']]); if ($reset_basic_info) { RSSUtils::set_basic_feed_info($feed_id); @@ -1037,11 +1008,13 @@ class Pref_Feeds extends Handler_Protected { foreach (array_keys($_POST) as $k) { if ($k != "op" && $k != "method" && $k != "ids") { - $feed_data[$k] = $_POST[$k]; + $feed_data[$k] = clean($_POST[$k]); } } - $this->dbh->query("BEGIN"); + $this->pdo->beginTransaction(); + + $feed_ids_qmarks = arr_qmarks($feed_ids); foreach (array_keys($feed_data) as $k) { @@ -1049,79 +1022,87 @@ class Pref_Feeds extends Handler_Protected { switch ($k) { case "title": - $qpart = "title = '$feed_title'"; + $qpart = "title = " . $this->pdo->quote($feed_title); break; case "feed_url": - $qpart = "feed_url = '$feed_link'"; + $qpart = "feed_url = " . $this->pdo->quote($feed_url); break; case "update_interval": - $qpart = "update_interval = '$upd_intl'"; + $qpart = "update_interval = " . $this->pdo->quote($upd_intl); break; case "purge_interval": - $qpart = "purge_interval = '$purge_intl'"; + $qpart = "purge_interval =" . $this->pdo->quote($purge_intl); break; case "auth_login": - $qpart = "auth_login = '$auth_login'"; + $qpart = "auth_login = " . $this->pdo->quote($auth_login); break; case "auth_pass": - $qpart = "auth_pass = '$auth_pass', auth_pass_encrypted = false"; + $qpart = "auth_pass =" . $this->pdo->quote($auth_pass). ", auth_pass_encrypted = false"; break; case "private": - $qpart = "private = $private"; + $qpart = "private = " . $this->pdo->quote($private); break; case "include_in_digest": - $qpart = "include_in_digest = $include_in_digest"; + $qpart = "include_in_digest = " . $this->pdo->quote($include_in_digest); break; case "always_display_enclosures": - $qpart = "always_display_enclosures = $always_display_enclosures"; + $qpart = "always_display_enclosures = " . $this->pdo->quote($always_display_enclosures); break; case "mark_unread_on_update": - $qpart = "mark_unread_on_update = $mark_unread_on_update"; + $qpart = "mark_unread_on_update = " . $this->pdo->quote($mark_unread_on_update); break; case "cache_images": - $qpart = "cache_images = $cache_images"; + $qpart = "cache_images = " . $this->pdo->quote($cache_images); break; case "hide_images": - $qpart = "hide_images = $hide_images"; + $qpart = "hide_images = " . $this->pdo->quote($hide_images); break; case "cat_id": - $qpart = $category_qpart_nocomma; + if (get_pref('ENABLE_FEED_CATS')) { + if ($cat_id) { + $qpart = "cat_id = " . $this->pdo->quote($cat_id); + } else { + $qpart = 'cat_id = NULL'; + } + } else { + $qpart = ""; + } + break; case "feed_language": - $qpart = "feed_language = '$feed_language'"; + $qpart = "feed_language = " . $this->pdo->quote($feed_language); break; } if ($qpart) { - $this->dbh->query( - "UPDATE ttrss_feeds SET $qpart WHERE id IN ($feed_ids) - AND owner_uid = " . $_SESSION["uid"]); - print "<br/>"; + $sth = $this->pdo->prepare("UPDATE ttrss_feeds SET $qpart WHERE id IN ($feed_ids_qmarks) + AND owner_uid = ?"); + $sth->execute(array_merge($feed_ids, [$_SESSION['uid']])); } } - $this->dbh->query("COMMIT"); + $this->pdo->commit(); } return; } function remove() { - $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); + $ids = explode(",", clean($_REQUEST["ids"])); foreach ($ids as $id) { Pref_Feeds::remove_feed($id, $_SESSION["uid"]); @@ -1130,150 +1111,15 @@ class Pref_Feeds extends Handler_Protected { return; } - function clear() { - $id = $this->dbh->escape_string($_REQUEST["id"]); - $this->clear_feed_articles($id); - } - - function rescore() { - $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); - - foreach ($ids as $id) { - - $filters = load_filters($id, $_SESSION["uid"], 6); - - $result = $this->dbh->query("SELECT - title, content, link, ref_id, author,". - SUBSTRING_FOR_DATE."(updated, 1, 19) AS updated - FROM - ttrss_user_entries, ttrss_entries - WHERE ref_id = id AND feed_id = '$id' AND - owner_uid = " .$_SESSION['uid']." - "); - - $scores = array(); - - while ($line = $this->dbh->fetch_assoc($result)) { - - $tags = Article::get_article_tags($line["ref_id"]); - - $article_filters = RSSUtils::get_article_filters($filters, $line['title'], - $line['content'], $line['link'], strtotime($line['updated']), - $line['author'], $tags); - - $new_score = RSSUtils::calculate_article_score($article_filters); - - if (!$scores[$new_score]) $scores[$new_score] = array(); - - array_push($scores[$new_score], $line['ref_id']); - } - - foreach (array_keys($scores) as $s) { - if ($s > 1000) { - $this->dbh->query("UPDATE ttrss_user_entries SET score = '$s', - marked = true WHERE - ref_id IN (" . join(',', $scores[$s]) . ")"); - } else if ($s < -500) { - $this->dbh->query("UPDATE ttrss_user_entries SET score = '$s', - unread = false WHERE - ref_id IN (" . join(',', $scores[$s]) . ")"); - } else { - $this->dbh->query("UPDATE ttrss_user_entries SET score = '$s' WHERE - ref_id IN (" . join(',', $scores[$s]) . ")"); - } - } - } - - print __("All done."); - - } - - function rescoreAll() { - - $result = $this->dbh->query( - "SELECT id FROM ttrss_feeds WHERE owner_uid = " . $_SESSION['uid']); - - while ($feed_line = $this->dbh->fetch_assoc($result)) { - - $id = $feed_line["id"]; - - $filters = load_filters($id, $_SESSION["uid"], 6); - - $tmp_result = $this->dbh->query("SELECT - title, content, link, ref_id, author,". - SUBSTRING_FOR_DATE."(updated, 1, 19) AS updated - FROM - ttrss_user_entries, ttrss_entries - WHERE ref_id = id AND feed_id = '$id' AND - owner_uid = " .$_SESSION['uid']." - "); - - $scores = array(); - - while ($line = $this->dbh->fetch_assoc($tmp_result)) { - - $tags = Article::get_article_tags($line["ref_id"]); - - $article_filters = RSSUtils::get_article_filters($filters, $line['title'], - $line['content'], $line['link'], strtotime($line['updated']), - $line['author'], $tags); - - $new_score = RSSUtils::calculate_article_score($article_filters); - - if (!$scores[$new_score]) $scores[$new_score] = array(); - - array_push($scores[$new_score], $line['ref_id']); - } - - foreach (array_keys($scores) as $s) { - if ($s > 1000) { - $this->dbh->query("UPDATE ttrss_user_entries SET score = '$s', - marked = true WHERE - ref_id IN (" . join(',', $scores[$s]) . ")"); - } else { - $this->dbh->query("UPDATE ttrss_user_entries SET score = '$s' WHERE - ref_id IN (" . join(',', $scores[$s]) . ")"); - } - } - } - - print __("All done."); - - } - - function categorize() { - $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); - - $cat_id = $this->dbh->escape_string($_REQUEST["cat_id"]); - - if ($cat_id == 0) { - $cat_id_qpart = 'NULL'; - } else { - $cat_id_qpart = "'$cat_id'"; - } - - $this->dbh->query("BEGIN"); - - foreach ($ids as $id) { - - $this->dbh->query("UPDATE ttrss_feeds SET cat_id = $cat_id_qpart - WHERE id = '$id' - AND owner_uid = " . $_SESSION["uid"]); - - } - - $this->dbh->query("COMMIT"); - } - function removeCat() { - $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); + $ids = explode(",", clean($_REQUEST["ids"])); foreach ($ids as $id) { $this->remove_feed_category($id, $_SESSION["uid"]); } } function addCat() { - $feed_cat = $this->dbh->escape_string(trim($_REQUEST["cat"])); + $feed_cat = trim(clean($_REQUEST["cat"])); add_feed_category($feed_cat); } @@ -1283,10 +1129,15 @@ class Pref_Feeds extends Handler_Protected { print "<div dojoType=\"dijit.layout.AccordionContainer\" region=\"center\">"; print "<div id=\"pref-feeds-feeds\" dojoType=\"dijit.layout.AccordionPane\" title=\"".__('Feeds')."\">"; - $result = $this->dbh->query("SELECT COUNT(id) AS num_errors - FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ".$_SESSION["uid"]); + $sth = $this->pdo->prepare("SELECT COUNT(id) AS num_errors + FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ?"); + $sth->execute([$_SESSION['uid']]); - $num_errors = $this->dbh->fetch_result($result, 0, "num_errors"); + if ($row = $sth->fetch()) { + $num_errors = $row["num_errors"]; + } else { + $num_errors = 0; + } if ($num_errors > 0) { @@ -1301,7 +1152,7 @@ class Pref_Feeds extends Handler_Protected { onclick=\"showInactiveFeeds()\">" . __("Inactive feeds") . "</button>"; - $feed_search = $this->dbh->escape_string($_REQUEST["search"]); + $feed_search = clean($_REQUEST["search"]); if (array_key_exists("search", $_REQUEST)) { $_SESSION["prefs_feed_search"] = $feed_search; @@ -1361,24 +1212,6 @@ class Pref_Feeds extends Handler_Protected { print $error_button; print $inactive_button; - if (defined('_ENABLE_FEED_DEBUGGING')) { - - print "<select id=\"feedActionChooser\" onchange=\"feedActionChange()\"> - <option value=\"facDefault\" selected>".__('More actions...')."</option>"; - - if (FORCE_ARTICLE_PURGE == 0) { - print - "<option value=\"facPurge\">".__('Manual purge')."</option>"; - } - - print " - <option value=\"facClear\">".__('Clear feed data')."</option> - <option value=\"facRescore\">".__('Rescore articles')."</option>"; - - print "</select>"; - - } - print "</div>"; # toolbar //print '</div>'; @@ -1438,7 +1271,9 @@ class Pref_Feeds extends Handler_Protected { print "<form name=\"opml_form\" style='display : block' target=\"upload_iframe\" enctype=\"multipart/form-data\" method=\"POST\" action=\"backend.php\"> - <input id=\"opml_file\" name=\"opml_file\" type=\"file\"> + <label class=\"dijitButton\">".__("Choose file...")." + <input style=\"display : none\" id=\"opml_file\" name=\"opml_file\" type=\"file\"> + </label> <input type=\"hidden\" name=\"op\" value=\"dlg\"> <input type=\"hidden\" name=\"method\" value=\"importOpml\"> <button dojoType=\"dijit.form.Button\" onclick=\"return opmlImport();\" type=\"submit\">" . @@ -1449,7 +1284,7 @@ class Pref_Feeds extends Handler_Protected { $opml_export_filename = "TinyTinyRSS_".date("Y-m-d").".opml"; print "<p>" . __('Filename:') . - " <input type=\"text\" id=\"filename\" value=\"$opml_export_filename\" /> " . + " <input class=\"input input-text\" type=\"text\" id=\"filename\" value=\"$opml_export_filename\" /> " . __('Include settings') . "<input type=\"checkbox\" id=\"settings\" checked=\"1\"/>"; print "</p><button dojoType=\"dijit.form.Button\" @@ -1568,17 +1403,18 @@ class Pref_Feeds extends Handler_Protected { $interval_qpart = "DATE_SUB(NOW(), INTERVAL 3 MONTH)"; } - $result = $this->dbh->query("SELECT ttrss_feeds.title, ttrss_feeds.site_url, + $sth = $this->pdo->prepare("SELECT ttrss_feeds.title, ttrss_feeds.site_url, ttrss_feeds.feed_url, ttrss_feeds.id, MAX(updated) AS last_article FROM ttrss_feeds, ttrss_entries, ttrss_user_entries WHERE (SELECT MAX(updated) FROM ttrss_entries, ttrss_user_entries WHERE ttrss_entries.id = ref_id AND ttrss_user_entries.feed_id = ttrss_feeds.id) < $interval_qpart - AND ttrss_feeds.owner_uid = ".$_SESSION["uid"]." AND + AND ttrss_feeds.owner_uid = ? AND ttrss_user_entries.feed_id = ttrss_feeds.id AND ttrss_entries.id = ref_id GROUP BY ttrss_feeds.title, ttrss_feeds.id, ttrss_feeds.site_url, ttrss_feeds.feed_url ORDER BY last_article"); + $sth->execute([$_SESSION['uid']]); print "<p" .__("These feeds have not been updated with new content for 3 months (oldest first):") . "</p>"; @@ -1599,7 +1435,7 @@ class Pref_Feeds extends Handler_Protected { $lnum = 1; - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $sth->fetch()) { $feed_id = $line["id"]; $this_row_id = "id=\"FUPDD-$feed_id\""; @@ -1643,8 +1479,9 @@ class Pref_Feeds extends Handler_Protected { } function feedsWithErrors() { - $result = $this->dbh->query("SELECT id,title,feed_url,last_error,site_url - FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ".$_SESSION["uid"]); + $sth = $this->pdo->prepare("SELECT id,title,feed_url,last_error,site_url + FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ?"); + $sth->execute([$_SESSION['uid']]); print "<div dojoType=\"dijit.Toolbar\">"; print "<div dojoType=\"dijit.form.DropDownButton\">". @@ -1663,7 +1500,7 @@ class Pref_Feeds extends Handler_Protected { $lnum = 1; - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $sth->fetch()) { $feed_id = $line["id"]; $this_row_id = "id=\"FERDD-$feed_id\""; @@ -1707,33 +1544,11 @@ class Pref_Feeds extends Handler_Protected { print "</div>"; } - /** - * 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 - */ - private function clear_feed_articles($id) { - - if ($id != 0) { - $result = $this->dbh->query("DELETE FROM ttrss_user_entries - WHERE feed_id = '$id' AND marked = false AND owner_uid = " . $_SESSION["uid"]); - } else { - $result = $this->dbh->query("DELETE FROM ttrss_user_entries - WHERE feed_id IS NULL AND marked = false AND owner_uid = " . $_SESSION["uid"]); - } - - $result = $this->dbh->query("DELETE FROM ttrss_entries WHERE - (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0"); - - CCache::update($id, $_SESSION['uid']); - } // function clear_feed_articles - private function remove_feed_category($id, $owner_uid) { - $this->dbh->query("DELETE FROM ttrss_feed_categories - WHERE id = '$id' AND owner_uid = $owner_uid"); + $sth = $this->pdo->prepare("DELETE FROM ttrss_feed_categories + WHERE id = ? AND owner_uid = ?"); + $sth->execute([$id, $owner_uid]); CCache::remove($id, $owner_uid, true); } @@ -1748,51 +1563,63 @@ class Pref_Feeds extends Handler_Protected { } } + $pdo = Db::pdo(); + if ($id > 0) { + $pdo->beginTransaction(); /* save starred articles in Archived feed */ - db_query("BEGIN"); - /* prepare feed if necessary */ - $result = db_query("SELECT feed_url FROM ttrss_feeds WHERE id = $id - AND owner_uid = $owner_uid"); + $sth = $pdo->prepare("SELECT feed_url FROM ttrss_feeds WHERE id = ? + AND owner_uid = ?"); + $sth->execute([$id, $owner_uid]); - $feed_url = db_escape_string(db_fetch_result($result, 0, "feed_url")); + if ($row = $sth->fetch()) { + $feed_url = $row["feed_url"]; - $result = db_query("SELECT id FROM ttrss_archived_feeds - WHERE feed_url = '$feed_url' AND owner_uid = $owner_uid"); + $sth = $pdo->prepare("SELECT id FROM ttrss_archived_feeds + WHERE feed_url = ? AND owner_uid = ?"); + $sth->execute([$feed_url, $owner_uid]); - if (db_num_rows($result) == 0) { - $result = db_query("SELECT MAX(id) AS id FROM ttrss_archived_feeds"); - $new_feed_id = (int)db_fetch_result($result, 0, "id") + 1; + if ($row = $sth->fetch()) { + $archive_id = $row["id"]; + } else { + $res = $pdo->query("SELECT MAX(id) AS id FROM ttrss_archived_feeds"); + $row = $res->fetch(); - db_query("INSERT INTO ttrss_archived_feeds - (id, owner_uid, title, feed_url, site_url) - SELECT $new_feed_id, owner_uid, title, feed_url, site_url from ttrss_feeds - WHERE id = '$id'"); + $new_feed_id = (int)$row['id'] + 1; - $archive_id = $new_feed_id; - } else { - $archive_id = db_fetch_result($result, 0, "id"); - } + $sth = $pdo->prepare("INSERT INTO ttrss_archived_feeds + (id, owner_uid, title, feed_url, site_url) + SELECT ?, owner_uid, title, feed_url, site_url from ttrss_feeds + WHERE id = ?"); + $sth->execute([$new_feed_id, $id]); - db_query("UPDATE ttrss_user_entries SET feed_id = NULL, - orig_feed_id = '$archive_id' WHERE feed_id = '$id' AND - marked = true AND owner_uid = $owner_uid"); + $archive_id = $new_feed_id; + } + + $sth = $pdo->prepare("UPDATE ttrss_user_entries SET feed_id = NULL, + orig_feed_id = ? WHERE feed_id = ? AND + marked = true AND owner_uid = ?"); - /* Remove access key for the feed */ + $sth->execute([$archive_id, $id, $owner_uid]); - db_query("DELETE FROM ttrss_access_keys WHERE - feed_id = '$id' AND owner_uid = $owner_uid"); + /* Remove access key for the feed */ - /* remove the feed */ + $sth = $pdo->prepare("DELETE FROM ttrss_access_keys WHERE + feed_id = ? AND owner_uid = ?"); + $sth->execute([$id, $owner_uid]); - db_query("DELETE FROM ttrss_feeds - WHERE id = '$id' AND owner_uid = $owner_uid"); + /* remove the feed */ - db_query("COMMIT"); + $sth = $pdo->prepare("DELETE FROM ttrss_feeds + WHERE id = ? AND owner_uid = ?"); + $sth->execute([$id, $owner_uid]); + } + + $pdo->commit(); if (file_exists(ICONS_DIR . "/$id.ico")) { unlink(ICONS_DIR . "/$id.ico"); @@ -1856,39 +1683,31 @@ class Pref_Feeds extends Handler_Protected { } function batchAddFeeds() { - $cat_id = $this->dbh->escape_string($_REQUEST['cat']); - $feeds = explode("\n", $_REQUEST['feeds']); - $login = $this->dbh->escape_string($_REQUEST['login']); - $pass = trim($_REQUEST['pass']); + $cat_id = clean($_REQUEST['cat']); + $feeds = explode("\n", clean($_REQUEST['feeds'])); + $login = clean($_REQUEST['login']); + $pass = trim(clean($_REQUEST['pass'])); foreach ($feeds as $feed) { - $feed = $this->dbh->escape_string(trim($feed)); + $feed = trim($feed); if (validate_feed_url($feed)) { - $this->dbh->query("BEGIN"); - - if ($cat_id == "0" || !$cat_id) { - $cat_qpart = "NULL"; - } else { - $cat_qpart = "'$cat_id'"; - } + $this->pdo->beginTransaction(); - $result = $this->dbh->query( - "SELECT id FROM ttrss_feeds - WHERE feed_url = '$feed' AND owner_uid = ".$_SESSION["uid"]); + $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds + WHERE feed_url = ? AND owner_uid = ?"); + $sth->execute([$feed, $_SESSION['uid']]); - $pass = $this->dbh->escape_string($pass); - - if ($this->dbh->num_rows($result) == 0) { - $result = $this->dbh->query( - "INSERT INTO ttrss_feeds + if (!$sth->fetch()) { + $sth = $this->pdo->prepare("INSERT INTO ttrss_feeds (owner_uid,feed_url,title,cat_id,auth_login,auth_pass,update_method,auth_pass_encrypted) - VALUES ('".$_SESSION["uid"]."', '$feed', - '[Unknown]', $cat_qpart, '$login', '$pass', 0, false)"); + VALUES (?, ?, '[Unknown]', ?, ?, ?, 0, false)"); + + $sth->execute([$_SESSION['uid'], $feed, $cat_id ? $cat_id : null, $login, $pass]); } - $this->dbh->query("COMMIT"); + $this->pdo->commit(); } } } @@ -1903,8 +1722,8 @@ class Pref_Feeds extends Handler_Protected { } function regenFeedKey() { - $feed_id = $this->dbh->escape_string($_REQUEST['id']); - $is_cat = $this->dbh->escape_string($_REQUEST['is_cat']) == "true"; + $feed_id = clean($_REQUEST['id']); + $is_cat = clean($_REQUEST['is_cat']) == "true"; $new_key = $this->update_feed_access_key($feed_id, $is_cat); @@ -1915,30 +1734,19 @@ class Pref_Feeds extends Handler_Protected { private function update_feed_access_key($feed_id, $is_cat, $owner_uid = false) { if (!$owner_uid) $owner_uid = $_SESSION["uid"]; - $sql_is_cat = bool_to_sql_bool($is_cat); - - $result = $this->dbh->query("SELECT access_key FROM ttrss_access_keys - WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat - AND owner_uid = " . $owner_uid); + // clear old value and generate new one + $sth = $this->pdo->prepare("DELETE FROM ttrss_access_keys + WHERE feed_id = ? AND is_cat = ? AND owner_uid = ?"); + $sth->execute([$feed_id, $is_cat, $owner_uid]); - if ($this->dbh->num_rows($result) == 1) { - $key = $this->dbh->escape_string(uniqid_short()); - - $this->dbh->query("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($feed_id, $is_cat, $owner_uid); - } + return get_feed_access_key($feed_id, $is_cat, $owner_uid); } // Silent function clearKeys() { - $this->dbh->query("DELETE FROM ttrss_access_keys WHERE - owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("DELETE FROM ttrss_access_keys WHERE + owner_uid = ?"); + $sth->execute([$_SESSION['uid']]); } private function calculate_children_count($cat) { @@ -1962,13 +1770,16 @@ class Pref_Feeds extends Handler_Protected { $interval_qpart = "DATE_SUB(NOW(), INTERVAL 3 MONTH)"; } - $result = $this->dbh->query("SELECT COUNT(*) AS num_inactive FROM ttrss_feeds WHERE + $sth = $this->pdo->prepare("SELECT COUNT(id) AS num_inactive FROM ttrss_feeds WHERE (SELECT MAX(updated) FROM ttrss_entries, ttrss_user_entries WHERE ttrss_entries.id = ref_id AND ttrss_user_entries.feed_id = ttrss_feeds.id) < $interval_qpart AND - ttrss_feeds.owner_uid = ".$_SESSION["uid"]); + ttrss_feeds.owner_uid = ?"); + $sth->execute([$_SESSION['uid']]); - print (int) $this->dbh->fetch_result($result, 0, "num_inactive"); + if ($row = $sth->fetch()) { + print (int)$row["num_inactive"]; + } } static function subscribe_to_feed_url() { diff --git a/classes/pref/filters.php b/classes/pref/filters.php index 6ea233cb6..74aecd309 100755 --- a/classes/pref/filters.php +++ b/classes/pref/filters.php @@ -9,15 +9,16 @@ class Pref_Filters extends Handler_Protected { } function filtersortreset() { - $this->dbh->query("UPDATE ttrss_filters2 - SET order_id = 0 WHERE owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_filters2 + SET order_id = 0 WHERE owner_uid = ?"); + $sth->execute([$_SESSION['uid']]); return; } function savefilterorder() { - $data = json_decode($_POST['payload'], true); + $data = json_decode(clean($_POST['payload']), true); - #file_put_contents("/tmp/saveorder.json", $_POST['payload']); + #file_put_contents("/tmp/saveorder.json", clean($_POST['payload'])); #$data = json_decode(file_get_contents("/tmp/saveorder.json"), true); if (!is_array($data['items'])) @@ -26,15 +27,16 @@ class Pref_Filters extends Handler_Protected { $index = 0; if (is_array($data) && is_array($data['items'])) { + + $sth = $this->pdo->prepare("UPDATE ttrss_filters2 SET + order_id = ? WHERE id = ? AND + owner_uid = ?"); + foreach ($data['items'][0]['items'] as $item) { $filter_id = (int) str_replace("FILTER:", "", $item['_reference']); if ($filter_id > 0) { - - $this->dbh->query("UPDATE ttrss_filters2 SET - order_id = $index WHERE id = '$filter_id' AND - owner_uid = " .$_SESSION["uid"]); - + $sth->execute([$index, $filter_id, $_SESSION['uid']]); ++$index; } } @@ -44,31 +46,29 @@ class Pref_Filters extends Handler_Protected { } function testFilterDo() { - $offset = (int) db_escape_string($_REQUEST["offset"]); - $limit = (int) db_escape_string($_REQUEST["limit"]); + $offset = (int) clean($_REQUEST["offset"]); + $limit = (int) clean($_REQUEST["limit"]); $filter = array(); $filter["enabled"] = true; - $filter["match_any_rule"] = sql_bool_to_bool( - checkbox_to_sql_bool($this->dbh->escape_string($_REQUEST["match_any_rule"]))); - $filter["inverse"] = sql_bool_to_bool( - checkbox_to_sql_bool($this->dbh->escape_string($_REQUEST["inverse"]))); + $filter["match_any_rule"] = checkbox_to_sql_bool(clean($_REQUEST["match_any_rule"])); + $filter["inverse"] = checkbox_to_sql_bool(clean($_REQUEST["inverse"])); $filter["rules"] = array(); $filter["actions"] = array("dummy-action"); - $result = $this->dbh->query("SELECT id,name FROM ttrss_filter_types"); + $res = $this->pdo->query("SELECT id,name FROM ttrss_filter_types"); $filter_types = array(); - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $res->fetch()) { $filter_types[$line["id"]] = $line["name"]; } $scope_qparts = array(); $rctr = 0; - foreach ($_REQUEST["rule"] AS $r) { + foreach (clean($_REQUEST["rule"]) AS $r) { $rule = json_decode($r, true); if ($rule && $rctr < 5) { @@ -80,9 +80,9 @@ class Pref_Filters extends Handler_Protected { if (strpos($feed_id, "CAT:") === 0) { $cat_id = (int) substr($feed_id, 4); - array_push($scope_inner_qparts, "cat_id = " . $cat_id); + array_push($scope_inner_qparts, "cat_id = " . $this->pdo->quote($cat_id)); } else if ($feed_id > 0) { - array_push($scope_inner_qparts, "feed_id = " . $feed_id); + array_push($scope_inner_qparts, "feed_id = " . $this->pdo->quote($feed_id)); } } @@ -109,74 +109,76 @@ class Pref_Filters extends Handler_Protected { //while ($found < $limit && $offset < $limit * 1000 && time() - $started < ini_get("max_execution_time") * 0.7) { - $result = db_query("SELECT ttrss_entries.id, - ttrss_entries.title, - ttrss_feeds.id AS feed_id, - ttrss_feeds.title AS feed_title, - ttrss_feed_categories.id AS cat_id, - content, - date_entered, - link, - author, - tag_cache - FROM - ttrss_entries, ttrss_user_entries - LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id) - LEFT JOIN ttrss_feed_categories ON (ttrss_feeds.cat_id = ttrss_feed_categories.id) - WHERE - ref_id = ttrss_entries.id AND - ($scope_qpart) AND - ttrss_user_entries.owner_uid = " . $_SESSION["uid"] . " - ORDER BY date_entered DESC LIMIT $limit OFFSET $offset"); - - while ($line = db_fetch_assoc($result)) { - - $rc = RSSUtils::get_article_filters(array($filter), $line['title'], $line['content'], $line['link'], - $line['author'], explode(",", $line['tag_cache'])); - - if (count($rc) > 0) { - - $line["content_preview"] = truncate_string(strip_tags($line["content"]), 200, '…'); - - foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_QUERY_HEADLINES) as $p) { - $line = $p->hook_query_headlines($line, 100); - } + $sth = $this->pdo->prepare("SELECT ttrss_entries.id, + ttrss_entries.title, + ttrss_feeds.id AS feed_id, + ttrss_feeds.title AS feed_title, + ttrss_feed_categories.id AS cat_id, + content, + date_entered, + link, + author, + tag_cache + FROM + ttrss_entries, ttrss_user_entries + LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id) + LEFT JOIN ttrss_feed_categories ON (ttrss_feeds.cat_id = ttrss_feed_categories.id) + WHERE + ref_id = ttrss_entries.id AND + ($scope_qpart) AND + ttrss_user_entries.owner_uid = ? + ORDER BY date_entered DESC LIMIT $limit OFFSET $offset"); - $content_preview = $line["content_preview"]; + $sth->execute([$_SESSION['uid']]); - $tmp = "<tr style='margin-top : 5px'>"; + while ($line = $sth->fetch()) { - #$tmp .= "<td width='5%' align='center'><input dojoType=\"dijit.form.CheckBox\" - # checked=\"1\" disabled=\"1\" type=\"checkbox\"></td>"; + $rc = RSSUtils::get_article_filters(array($filter), $line['title'], $line['content'], $line['link'], + $line['author'], explode(",", $line['tag_cache'])); - $id = $line['id']; - $tmp .= "<td width='5%' align='center'><img style='cursor : pointer' title='".__("Preview article")."' - src='images/information.png' onclick='openArticlePopup($id)'></td><td>"; + if (count($rc) > 0) { - /*foreach ($filter['rules'] as $rule) { - $reg_exp = str_replace('/', '\/', $rule["reg_exp"]); + $line["content_preview"] = truncate_string(strip_tags($line["content"]), 200, '…'); - $line["title"] = preg_replace("/($reg_exp)/i", - "<span class=\"highlight\">$1</span>", $line["title"]); + foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_QUERY_HEADLINES) as $p) { + $line = $p->hook_query_headlines($line, 100); + } - $content_preview = preg_replace("/($reg_exp)/i", - "<span class=\"highlight\">$1</span>", $content_preview); - }*/ + $content_preview = $line["content_preview"]; - $tmp .= "<strong>" . $line["title"] . "</strong><br/>"; - $tmp .= $line['feed_title'] . ", " . mb_substr($line["date_entered"], 0, 16); - $tmp .= "<div class='insensitive'>" . $content_preview . "</div>"; - $tmp .= "</td></tr>"; + $tmp = "<tr style='margin-top : 5px'>"; - array_push($rv, $tmp); + #$tmp .= "<td width='5%' align='center'><input dojoType=\"dijit.form.CheckBox\" + # checked=\"1\" disabled=\"1\" type=\"checkbox\"></td>"; - /*array_push($rv, array("title" => $line["title"], - "content" => $content_preview, - "date" => $line["date_entered"], - "feed" => $line["feed_title"])); */ + $id = $line['id']; + $tmp .= "<td width='5%' align='center'><img style='cursor : pointer' title='".__("Preview article")."' + src='images/information.png' onclick='openArticlePopup($id)'></td><td>"; + + /*foreach ($filter['rules'] as $rule) { + $reg_exp = str_replace('/', '\/', $rule["reg_exp"]); + + $line["title"] = preg_replace("/($reg_exp)/i", + "<span class=\"highlight\">$1</span>", $line["title"]); + + $content_preview = preg_replace("/($reg_exp)/i", + "<span class=\"highlight\">$1</span>", $content_preview); + }*/ + + $tmp .= "<strong>" . $line["title"] . "</strong><br/>"; + $tmp .= $line['feed_title'] . ", " . mb_substr($line["date_entered"], 0, 16); + $tmp .= "<div class='insensitive'>" . $content_preview . "</div>"; + $tmp .= "</td></tr>"; + + array_push($rv, $tmp); + + /*array_push($rv, array("title" => $line["title"], + "content" => $content_preview, + "date" => $line["date_entered"], + "feed" => $line["feed_title"])); */ - } } + } //$offset += $limit; //} @@ -209,7 +211,7 @@ class Pref_Filters extends Handler_Protected { } private function getfilterrules_concise($filter_id) { - $result = $this->dbh->query("SELECT reg_exp, + $sth = $this->pdo->prepare("SELECT reg_exp, inverse, match_on, feed_id, @@ -219,12 +221,13 @@ class Pref_Filters extends Handler_Protected { FROM ttrss_filters2_rules, ttrss_filter_types WHERE - filter_id = '$filter_id' AND filter_type = ttrss_filter_types.id + filter_id = ? AND filter_type = ttrss_filter_types.id ORDER BY reg_exp"); + $sth->execute([$filter_id]); $rv = ""; - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $sth->fetch()) { if ($line["match_on"]) { $feeds = json_decode($line["match_on"], true); @@ -247,7 +250,7 @@ class Pref_Filters extends Handler_Protected { } else { - $where = sql_bool_to_bool($line["cat_filter"]) ? + $where = $line["cat_filter"] ? Feeds::getCategoryTitle($line["cat_id"]) : ($line["feed_id"] ? Feeds::getFeedTitle($line["feed_id"]) : __("All feeds")); @@ -255,13 +258,13 @@ class Pref_Filters extends Handler_Protected { # $where = $line["cat_id"] . "/" . $line["feed_id"]; - $inverse = sql_bool_to_bool($line["inverse"]) ? "inverse" : ""; + $inverse = $line["inverse"] ? "inverse" : ""; $rv .= "<span class='$inverse'>" . T_sprintf("%s on %s in %s %s", htmlspecialchars($line["reg_exp"]), $line["field"], $where, - sql_bool_to_bool($line["inverse"]) ? __("(inverse)") : "") . "</span>"; + $line["inverse"] ? __("(inverse)") : "") . "</span>"; } return $rv; @@ -275,7 +278,7 @@ class Pref_Filters extends Handler_Protected { $filter_search = $_SESSION["prefs_filter_search"]; - $result = $this->dbh->query("SELECT *, + $sth = $this->pdo->prepare("SELECT *, (SELECT action_param FROM ttrss_filters2_actions WHERE filter_id = ttrss_filters2.id ORDER BY id LIMIT 1) AS action_param, (SELECT action_id FROM ttrss_filters2_actions @@ -286,22 +289,23 @@ class Pref_Filters extends Handler_Protected { (SELECT reg_exp FROM ttrss_filters2_rules WHERE filter_id = ttrss_filters2.id ORDER BY id LIMIT 1) AS reg_exp FROM ttrss_filters2 WHERE - owner_uid = ".$_SESSION["uid"]." ORDER BY order_id, title"); - + owner_uid = ? ORDER BY order_id, title"); + $sth->execute([$_SESSION['uid']]); $folder = array(); $folder['items'] = array(); - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $sth->fetch()) { $name = $this->getFilterName($line["id"]); $match_ok = false; if ($filter_search) { - $rules_result = $this->dbh->query( - "SELECT reg_exp FROM ttrss_filters2_rules WHERE filter_id = ".$line["id"]); + $rules_sth = $this->pdo->prepare("SELECT reg_exp + FROM ttrss_filters2_rules WHERE filter_id = ?"); + $rules_sth->execute([$line['id']]); - while ($rule_line = $this->dbh->fetch_assoc($rules_result)) { + while ($rule_line = $rules_sth->fetch()) { if (mb_strpos($rule_line['reg_exp'], $filter_search) !== false) { $match_ok = true; break; @@ -310,13 +314,14 @@ class Pref_Filters extends Handler_Protected { } if ($line['action_id'] == 7) { - $label_result = $this->dbh->query("SELECT fg_color, bg_color - FROM ttrss_labels2 WHERE caption = '".$this->dbh->escape_string($line['action_param'])."' AND - owner_uid = " . $_SESSION["uid"]); + $label_sth = $this->pdo->prepare("SELECT fg_color, bg_color + FROM ttrss_labels2 WHERE caption = ? AND + owner_uid = ?"); + $label_sth->execute([$line['action_param'], $_SESSION['uid']]); - if ($this->dbh->num_rows($label_result) > 0) { - $fg_color = $this->dbh->fetch_result($label_result, 0, "fg_color"); - $bg_color = $this->dbh->fetch_result($label_result, 0, "bg_color"); + if ($label_row = $label_sth->fetch()) { + $fg_color = $label_row["fg_color"]; + $bg_color = $label_row["bg_color"]; $name[1] = "<span class=\"labelColorIndicator\" id=\"label-editor-indicator\" style='color : $fg_color; background-color : $bg_color; margin-right : 4px'>α</span>" . $name[1]; } @@ -328,7 +333,7 @@ class Pref_Filters extends Handler_Protected { $filter['name'] = $name[0]; $filter['param'] = $name[1]; $filter['checkbox'] = false; - $filter['enabled'] = sql_bool_to_bool($line["enabled"]); + $filter['enabled'] = $line["enabled"]; $filter['rules'] = $this->getfilterrules_concise($line['id']); if (!$filter_search || $match_ok) { @@ -336,10 +341,6 @@ class Pref_Filters extends Handler_Protected { } } - /* if (count($folder['items']) > 0) { - array_push($root['items'], $folder); - } */ - $root['items'] = $folder['items']; $fl = array(); @@ -353,179 +354,186 @@ class Pref_Filters extends Handler_Protected { function edit() { - $filter_id = $this->dbh->escape_string($_REQUEST["id"]); + $filter_id = clean($_REQUEST["id"]); - $result = $this->dbh->query( - "SELECT * FROM ttrss_filters2 WHERE id = '$filter_id' AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("SELECT * FROM ttrss_filters2 + WHERE id = ? AND owner_uid = ?"); + $sth->execute([$filter_id, $_SESSION['uid']]); - $enabled = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "enabled")); - $match_any_rule = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "match_any_rule")); - $inverse = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "inverse")); - $title = htmlspecialchars($this->dbh->fetch_result($result, 0, "title")); + if ($row = $sth->fetch()) { - print "<form id=\"filter_edit_form\" onsubmit='return false'>"; + $enabled = $row["enabled"]; + $match_any_rule = $row["match_any_rule"]; + $inverse = $row["inverse"]; + $title = htmlspecialchars($row["title"]); - print_hidden("op", "pref-filters"); - print_hidden("id", "$filter_id"); - print_hidden("method", "editSave"); - print_hidden("csrf_token", $_SESSION['csrf_token']); + print "<form id=\"filter_edit_form\" onsubmit='return false'>"; - print "<div class=\"dlgSec\">".__("Caption")."</div>"; + print_hidden("op", "pref-filters"); + print_hidden("id", "$filter_id"); + print_hidden("method", "editSave"); + print_hidden("csrf_token", $_SESSION['csrf_token']); - print "<input required=\"true\" dojoType=\"dijit.form.ValidationTextBox\" style=\"width : 20em;\" name=\"title\" value=\"$title\">"; + print "<div class=\"dlgSec\">".__("Caption")."</div>"; - print "</div>"; + print "<input required=\"true\" dojoType=\"dijit.form.ValidationTextBox\" style=\"width : 20em;\" name=\"title\" value=\"$title\">"; - print "<div class=\"dlgSec\">".__("Match")."</div>"; + print "</div>"; - print "<div dojoType=\"dijit.Toolbar\">"; + print "<div class=\"dlgSec\">".__("Match")."</div>"; - print "<div dojoType=\"dijit.form.DropDownButton\">". + print "<div dojoType=\"dijit.Toolbar\">"; + + print "<div dojoType=\"dijit.form.DropDownButton\">". "<span>" . __('Select')."</span>"; - print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">"; - print "<div onclick=\"dijit.byId('filterEditDlg').selectRules(true)\" + print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">"; + print "<div onclick=\"dijit.byId('filterEditDlg').selectRules(true)\" dojoType=\"dijit.MenuItem\">".__('All')."</div>"; - print "<div onclick=\"dijit.byId('filterEditDlg').selectRules(false)\" + print "<div onclick=\"dijit.byId('filterEditDlg').selectRules(false)\" dojoType=\"dijit.MenuItem\">".__('None')."</div>"; - print "</div></div>"; + print "</div></div>"; - print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').addRule()\">". - __('Add')."</button> "; + print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').addRule()\">". + __('Add')."</button> "; - print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').deleteRule()\">". - __('Delete')."</button> "; + print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').deleteRule()\">". + __('Delete')."</button> "; - print "</div>"; + print "</div>"; - print "<ul id='filterDlg_Matches'>"; + print "<ul id='filterDlg_Matches'>"; - $rules_result = $this->dbh->query("SELECT * FROM ttrss_filters2_rules - WHERE filter_id = '$filter_id' ORDER BY reg_exp, id"); + $rules_sth = $this->pdo->prepare("SELECT * FROM ttrss_filters2_rules + WHERE filter_id = ? ORDER BY reg_exp, id"); + $rules_sth->execute([$filter_id]); - while ($line = $this->dbh->fetch_assoc($rules_result)) { - if ($line["match_on"]) { - $line["feed_id"] = json_decode($line["match_on"], true); - } else { - if (sql_bool_to_bool($line["cat_filter"])) { - $feed_id = "CAT:" . (int)$line["cat_id"]; - } else { - $feed_id = (int)$line["feed_id"]; - } + while ($line = $rules_sth->fetch()) { + if ($line["match_on"]) { + $line["feed_id"] = json_decode($line["match_on"], true); + } else { + if ($line["cat_filter"]) { + $feed_id = "CAT:" . (int)$line["cat_id"]; + } else { + $feed_id = (int)$line["feed_id"]; + } - $line["feed_id"] = ["" . $feed_id]; // set item type to string for in_array() - } + $line["feed_id"] = ["" . $feed_id]; // set item type to string for in_array() + } - unset($line["cat_filter"]); - unset($line["cat_id"]); - unset($line["filter_id"]); - unset($line["id"]); - if (!sql_bool_to_bool($line["inverse"])) unset($line["inverse"]); - unset($line["match_on"]); + unset($line["cat_filter"]); + unset($line["cat_id"]); + unset($line["filter_id"]); + unset($line["id"]); + if (!$line["inverse"]) unset($line["inverse"]); + unset($line["match_on"]); - $data = htmlspecialchars(json_encode($line)); + $data = htmlspecialchars(json_encode($line)); - print "<li><input dojoType='dijit.form.CheckBox' type='checkbox' onclick='toggleSelectListRow2(this)'>". - "<span onclick=\"dijit.byId('filterEditDlg').editRule(this)\">".$this->getRuleName($line)."</span>". - "<input type='hidden' name='rule[]' value=\"$data\"/></li>"; - } + print "<li><input dojoType='dijit.form.CheckBox' type='checkbox' onclick='toggleSelectListRow2(this)'>". + "<span onclick=\"dijit.byId('filterEditDlg').editRule(this)\">".$this->getRuleName($line)."</span>". + "<input type='hidden' name='rule[]' value=\"$data\"/></li>"; + } - print "</ul>"; + print "</ul>"; - print "</div>"; + print "</div>"; - print "<div class=\"dlgSec\">".__("Apply actions")."</div>"; + print "<div class=\"dlgSec\">".__("Apply actions")."</div>"; - print "<div dojoType=\"dijit.Toolbar\">"; + print "<div dojoType=\"dijit.Toolbar\">"; - print "<div dojoType=\"dijit.form.DropDownButton\">". + print "<div dojoType=\"dijit.form.DropDownButton\">". "<span>" . __('Select')."</span>"; - print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">"; - print "<div onclick=\"dijit.byId('filterEditDlg').selectActions(true)\" + print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">"; + print "<div onclick=\"dijit.byId('filterEditDlg').selectActions(true)\" dojoType=\"dijit.MenuItem\">".__('All')."</div>"; - print "<div onclick=\"dijit.byId('filterEditDlg').selectActions(false)\" + print "<div onclick=\"dijit.byId('filterEditDlg').selectActions(false)\" dojoType=\"dijit.MenuItem\">".__('None')."</div>"; - print "</div></div>"; + print "</div></div>"; - print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').addAction()\">". - __('Add')."</button> "; + print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').addAction()\">". + __('Add')."</button> "; - print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').deleteAction()\">". - __('Delete')."</button> "; + print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').deleteAction()\">". + __('Delete')."</button> "; - print "</div>"; + print "</div>"; - print "<ul id='filterDlg_Actions'>"; + print "<ul id='filterDlg_Actions'>"; - $actions_result = $this->dbh->query("SELECT * FROM ttrss_filters2_actions - WHERE filter_id = '$filter_id' ORDER BY id"); + $actions_sth = $this->pdo->prepare("SELECT * FROM ttrss_filters2_actions + WHERE filter_id = ? ORDER BY id"); + $actions_sth->execute([$filter_id]); - while ($line = $this->dbh->fetch_assoc($actions_result)) { - $line["action_param_label"] = $line["action_param"]; + while ($line = $actions_sth->fetch()) { + $line["action_param_label"] = $line["action_param"]; - unset($line["filter_id"]); - unset($line["id"]); + unset($line["filter_id"]); + unset($line["id"]); - $data = htmlspecialchars(json_encode($line)); + $data = htmlspecialchars(json_encode($line)); - print "<li><input dojoType='dijit.form.CheckBox' type='checkbox' onclick='toggleSelectListRow2(this)'>". - "<span onclick=\"dijit.byId('filterEditDlg').editAction(this)\">".$this->getActionName($line)."</span>". - "<input type='hidden' name='action[]' value=\"$data\"/></li>"; - } + print "<li><input dojoType='dijit.form.CheckBox' type='checkbox' onclick='toggleSelectListRow2(this)'>". + "<span onclick=\"dijit.byId('filterEditDlg').editAction(this)\">".$this->getActionName($line)."</span>". + "<input type='hidden' name='action[]' value=\"$data\"/></li>"; + } - print "</ul>"; + print "</ul>"; - print "</div>"; + print "</div>"; - if ($enabled) { - $checked = "checked=\"1\""; - } else { - $checked = ""; - } + if ($enabled) { + $checked = "checked=\"1\""; + } else { + $checked = ""; + } - print "<input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"enabled\" id=\"enabled\" $checked> + print "<input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"enabled\" id=\"enabled\" $checked> <label for=\"enabled\">".__('Enabled')."</label>"; - if ($match_any_rule) { - $checked = "checked=\"1\""; - } else { - $checked = ""; - } + if ($match_any_rule) { + $checked = "checked=\"1\""; + } else { + $checked = ""; + } - print "<br/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"match_any_rule\" id=\"match_any_rule\" $checked> + print "<br/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"match_any_rule\" id=\"match_any_rule\" $checked> <label for=\"match_any_rule\">".__('Match any rule')."</label>"; - if ($inverse) { - $checked = "checked=\"1\""; - } else { - $checked = ""; - } + if ($inverse) { + $checked = "checked=\"1\""; + } else { + $checked = ""; + } - print "<br/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"inverse\" id=\"inverse\" $checked> + print "<br/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"inverse\" id=\"inverse\" $checked> <label for=\"inverse\">".__('Inverse matching')."</label>"; - print "<p/>"; + print "<p/>"; - print "<div class=\"dlgButtons\">"; + print "<div class=\"dlgButtons\">"; - print "<div style=\"float : left\">"; - print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').removeFilter()\">". - __('Remove')."</button>"; - print "</div>"; + print "<div style=\"float : left\">"; + print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').removeFilter()\">". + __('Remove')."</button>"; + print "</div>"; - print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').test()\">". - __('Test')."</button> "; + print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').test()\">". + __('Test')."</button> "; - print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').execute()\">". - __('Save')."</button> "; + print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').execute()\">". + __('Save')."</button> "; - print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').hide()\">". - __('Cancel')."</button>"; + print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').hide()\">". + __('Cancel')."</button>"; - print "</div>"; + print "</div>"; + + } } private function getRuleName($rule) { - if (!$rule) $rule = json_decode($_REQUEST["rule"], true); + if (!$rule) $rule = json_decode(clean($_REQUEST["rule"]), true); $feeds = $rule["feed_id"]; $feeds_fmt = []; @@ -547,9 +555,15 @@ class Pref_Filters extends Handler_Protected { $feed = implode(", ", $feeds_fmt); - $result = $this->dbh->query("SELECT description FROM ttrss_filter_types - WHERE id = ".(int)$rule["filter_type"]); - $filter_type = $this->dbh->fetch_result($result, 0, "description"); + $sth = $this->pdo->prepare("SELECT description FROM ttrss_filter_types + WHERE id = ?"); + $sth->execute([(int)$rule["filter_type"]]); + + if ($row = $sth->fetch()) { + $filter_type = $row["description"]; + } else { + $filter_type = "?UNKNOWN?"; + } $inverse = isset($rule["inverse"]) ? "inverse" : ""; @@ -559,29 +573,35 @@ class Pref_Filters extends Handler_Protected { } function printRuleName() { - print $this->getRuleName(json_decode($_REQUEST["rule"], true)); + print $this->getRuleName(json_decode(clean($_REQUEST["rule"]), true)); } private function getActionName($action) { - $result = $this->dbh->query("SELECT description FROM - ttrss_filter_actions WHERE id = " .(int)$action["action_id"]); + $sth = $this->pdo->prepare("SELECT description FROM + ttrss_filter_actions WHERE id = ?"); + $sth->execute([(int)$action["action_id"]]); + + $title = ""; - $title = __($this->dbh->fetch_result($result, 0, "description")); + if ($row = $sth->fetch()) { - if ($action["action_id"] == 4 || $action["action_id"] == 6 || - $action["action_id"] == 7) + $title = __($row["description"]); + + if ($action["action_id"] == 4 || $action["action_id"] == 6 || + $action["action_id"] == 7) $title .= ": " . $action["action_param"]; - if ($action["action_id"] == 9) { - list ($pfclass, $pfaction) = explode(":", $action["action_param"]); + if ($action["action_id"] == 9) { + list ($pfclass, $pfaction) = explode(":", $action["action_param"]); - $filter_actions = PluginHost::getInstance()->get_filter_actions(); + $filter_actions = PluginHost::getInstance()->get_filter_actions(); - foreach ($filter_actions as $fclass => $factions) { - foreach ($factions as $faction) { - if ($pfaction == $faction["action"] && $pfclass == $fclass) { - $title .= ": " . $fclass . ": " . $faction["description"]; - break; + foreach ($filter_actions as $fclass => $factions) { + foreach ($factions as $faction) { + if ($pfaction == $faction["action"] && $pfclass == $fclass) { + $title .= ": " . $fclass . ": " . $faction["description"]; + break; + } } } } @@ -591,54 +611,64 @@ class Pref_Filters extends Handler_Protected { } function printActionName() { - print $this->getActionName(json_decode($_REQUEST["action"], true)); + print $this->getActionName(json_decode(clean($_REQUEST["action"]), true)); } function editSave() { - if ($_REQUEST["savemode"] && $_REQUEST["savemode"] == "test") { + if (clean($_REQUEST["savemode"] && $_REQUEST["savemode"]) == "test") { return $this->testFilter(); } -# print_r($_REQUEST); + $filter_id = clean($_REQUEST["id"]); + $enabled = checkbox_to_sql_bool(clean($_REQUEST["enabled"])); + $match_any_rule = checkbox_to_sql_bool(clean($_REQUEST["match_any_rule"])); + $inverse = checkbox_to_sql_bool(clean($_REQUEST["inverse"])); + $title = clean($_REQUEST["title"]); - $filter_id = $this->dbh->escape_string($_REQUEST["id"]); - $enabled = checkbox_to_sql_bool($this->dbh->escape_string($_REQUEST["enabled"])); - $match_any_rule = checkbox_to_sql_bool($this->dbh->escape_string($_REQUEST["match_any_rule"])); - $inverse = checkbox_to_sql_bool($this->dbh->escape_string($_REQUEST["inverse"])); - $title = $this->dbh->escape_string($_REQUEST["title"]); + $this->pdo->beginTransaction(); - $this->dbh->query("UPDATE ttrss_filters2 SET enabled = $enabled, - match_any_rule = $match_any_rule, - inverse = $inverse, - title = '$title' - WHERE id = '$filter_id' - AND owner_uid = ". $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_filters2 SET enabled = ?, + match_any_rule = ?, + inverse = ?, + title = ? + WHERE id = ? AND owner_uid = ?"); + + $sth->execute([$enabled, $match_any_rule, $inverse, $title, $filter_id, $_SESSION['uid']]); $this->saveRulesAndActions($filter_id); + $this->pdo->commit(); } function remove() { - $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); + $ids = explode(",", clean($_REQUEST["ids"])); + $ids_qmarks = arr_qmarks($ids); - foreach ($ids as $id) { - $this->dbh->query("DELETE FROM ttrss_filters2 WHERE id = '$id' AND owner_uid = ". $_SESSION["uid"]); - } + $sth = $this->pdo->prepare("DELETE FROM ttrss_filters2 WHERE id IN ($ids_qmarks) + AND owner_uid = ?"); + $sth->execute(array_merge($ids, [$_SESSION['uid']])); } - private function saveRulesAndActions($filter_id) { + private function saveRulesAndActions($filter_id) + { - $this->dbh->query("DELETE FROM ttrss_filters2_rules WHERE filter_id = '$filter_id'"); - $this->dbh->query("DELETE FROM ttrss_filters2_actions WHERE filter_id = '$filter_id'"); + $sth = $this->pdo->prepare("DELETE FROM ttrss_filters2_rules WHERE filter_id = ?"); + $sth->execute([$filter_id]); + $sth = $this->pdo->prepare("DELETE FROM ttrss_filters2_actions WHERE filter_id = ?"); + $sth->execute([$filter_id]); + + if (!is_array(clean($_REQUEST["rule"]))) $_REQUEST["rule"] = []; + if (!is_array(clean($_REQUEST["action"]))) $_REQUEST["action"] = []; + if ($filter_id) { /* create rules */ $rules = array(); $actions = array(); - foreach ($_REQUEST["rule"] as $rule) { + foreach (clean($_REQUEST["rule"]) as $rule) { $rule = json_decode($rule, true); unset($rule["id"]); @@ -647,7 +677,7 @@ class Pref_Filters extends Handler_Protected { } } - foreach ($_REQUEST["action"] as $action) { + foreach (clean($_REQUEST["action"]) as $action) { $action = json_decode($action, true); unset($action["id"]); @@ -656,104 +686,83 @@ class Pref_Filters extends Handler_Protected { } } + $rsth = $this->pdo->prepare("INSERT INTO ttrss_filters2_rules + (filter_id, reg_exp,filter_type,feed_id,cat_id,match_on,inverse) VALUES + (?, ?, ?, NULL, NULL, ?, ?)"); + foreach ($rules as $rule) { if ($rule) { - $reg_exp = $this->dbh->escape_string(trim($rule["reg_exp"]), false); - $inverse = isset($rule["inverse"]) ? "true" : "false"; - - $filter_type = (int) $this->dbh->escape_string(trim($rule["filter_type"])); - $match_on = $this->dbh->escape_string(json_encode($rule["feed_id"])); - - /*if (strpos($feed_id, "CAT:") === 0) { - - $cat_filter = bool_to_sql_bool(true); - $cat_id = (int) substr($feed_id, 4); - $feed_id = "NULL"; + $reg_exp = trim($rule["reg_exp"]); + $inverse = isset($rule["inverse"]) ? 1 : 0; - if (!$cat_id) $cat_id = "NULL"; // Uncategorized - } else { - $cat_filter = bool_to_sql_bool(false); - $feed_id = (int) $feed_id; - $cat_id = "NULL"; - - if (!$feed_id) $feed_id = "NULL"; // Uncategorized - }*/ + $filter_type = (int)trim($rule["filter_type"]); + $match_on = json_encode($rule["feed_id"]); - $query = "INSERT INTO ttrss_filters2_rules - (filter_id, reg_exp,filter_type,feed_id,cat_id,match_on,inverse) VALUES - ('$filter_id', '$reg_exp', '$filter_type', NULL, NULL, '$match_on', $inverse)"; - - $this->dbh->query($query); + $rsth->execute([$filter_id, $reg_exp, $filter_type, $match_on, $inverse]); } } + $asth = $this->pdo->prepare("INSERT INTO ttrss_filters2_actions + (filter_id, action_id, action_param) VALUES + (?, ?, ?)"); + foreach ($actions as $action) { if ($action) { - $action_id = (int) $this->dbh->escape_string($action["action_id"]); - $action_param = $this->dbh->escape_string($action["action_param"]); - $action_param_label = $this->dbh->escape_string($action["action_param_label"]); + $action_id = (int)$action["action_id"]; + $action_param = $action["action_param"]; + $action_param_label = $action["action_param_label"]; if ($action_id == 7) { $action_param = $action_param_label; } if ($action_id == 6) { - $action_param = (int) str_replace("+", "", $action_param); + $action_param = (int)str_replace("+", "", $action_param); } - $query = "INSERT INTO ttrss_filters2_actions - (filter_id, action_id, action_param) VALUES - ('$filter_id', '$action_id', '$action_param')"; - - $this->dbh->query($query); + $asth->execute([$filter_id, $action_id, $action_param]); } } } - - } function add() { - if ($_REQUEST["savemode"] && $_REQUEST["savemode"] == "test") { + if (clean($_REQUEST["savemode"] && $_REQUEST["savemode"]) == "test") { return $this->testFilter(); } -# print_r($_REQUEST); - - $enabled = checkbox_to_sql_bool($_REQUEST["enabled"]); - $match_any_rule = checkbox_to_sql_bool($_REQUEST["match_any_rule"]); - $title = $this->dbh->escape_string($_REQUEST["title"]); - $inverse = checkbox_to_sql_bool($_REQUEST["inverse"]); + $enabled = checkbox_to_sql_bool(clean($_REQUEST["enabled"])); + $match_any_rule = checkbox_to_sql_bool(clean($_REQUEST["match_any_rule"])); + $title = clean($_REQUEST["title"]); + $inverse = checkbox_to_sql_bool(clean($_REQUEST["inverse"])); - $this->dbh->query("BEGIN"); + $this->pdo->beginTransaction(); /* create base filter */ - $result = $this->dbh->query("INSERT INTO ttrss_filters2 + $sth = $this->pdo->prepare("INSERT INTO ttrss_filters2 (owner_uid, match_any_rule, enabled, title, inverse) VALUES - (".$_SESSION["uid"].",$match_any_rule,$enabled, '$title', $inverse)"); + (?, ?, ?, ?, ?)"); - $result = $this->dbh->query("SELECT MAX(id) AS id FROM ttrss_filters2 - WHERE owner_uid = ".$_SESSION["uid"]); + $sth->execute([$_SESSION['uid'], $match_any_rule, $enabled, $title, $inverse]); - $filter_id = $this->dbh->fetch_result($result, 0, "id"); + $sth = $this->pdo->prepare("SELECT MAX(id) AS id FROM ttrss_filters2 + WHERE owner_uid = ?"); + $sth->execute([$_SESSION['uid']]); - $this->saveRulesAndActions($filter_id); + if ($row = $sth->fetch()) { + $filter_id = $row['id']; + $this->saveRulesAndActions($filter_id); + } - $this->dbh->query("COMMIT"); + $this->pdo->commit(); } function index() { - $sort = $this->dbh->escape_string($_REQUEST["sort"]); - - if (!$sort || $sort == "undefined") { - $sort = "reg_exp"; - } - - $filter_search = $this->dbh->escape_string($_REQUEST["search"]); + $filter_search = clean($_REQUEST["search"]); if (array_key_exists("search", $_REQUEST)) { $_SESSION["prefs_filter_search"] = $filter_search; @@ -765,8 +774,6 @@ class Pref_Filters extends Handler_Protected { print "<div id=\"pref-filter-header\" dojoType=\"dijit.layout.ContentPane\" region=\"top\">"; print "<div id=\"pref-filter-toolbar\" dojoType=\"dijit.Toolbar\">"; - $filter_search = $this->dbh->escape_string($_REQUEST["search"]); - if (array_key_exists("search", $_REQUEST)) { $_SESSION["prefs_filter_search"] = $filter_search; } else { @@ -805,11 +812,6 @@ class Pref_Filters extends Handler_Protected { print "<button dojoType=\"dijit.form.Button\" onclick=\"return removeSelectedFilters()\">". __('Remove')."</button> "; - if (defined('_ENABLE_FEED_DEBUGGING')) { - print "<button dojoType=\"dijit.form.Button\" onclick=\"rescore_all_feeds()\">". - __('Rescore articles')."</button> "; - } - print "</div>"; # toolbar print "</div>"; # toolbar-frame print "<div id=\"pref-filter-content\" dojoType=\"dijit.layout.ContentPane\" region=\"center\">"; @@ -946,7 +948,7 @@ class Pref_Filters extends Handler_Protected { } function newrule() { - $rule = json_decode($_REQUEST["rule"], true); + $rule = json_decode(clean($_REQUEST["rule"]), true); if ($rule) { $reg_exp = htmlspecialchars($rule["reg_exp"]); @@ -960,21 +962,14 @@ class Pref_Filters extends Handler_Protected { $inverse_checked = ""; } - /*if (strpos($feed_id, "CAT:") === 0) { - $feed_id = substr($feed_id, 4); - $cat_filter = true; - } else { - $cat_filter = false; - }*/ - print "<form name='filter_new_rule_form' id='filter_new_rule_form'>"; - $result = $this->dbh->query("SELECT id,description + $res = $this->pdo->query("SELECT id,description FROM ttrss_filter_types WHERE id != 5 ORDER BY description"); $filter_types = array(); - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $res->fetch()) { $filter_types[$line["id"]] = __($line["description"]); } @@ -1027,10 +1022,10 @@ class Pref_Filters extends Handler_Protected { } function newaction() { - $action = json_decode($_REQUEST["action"], true); + $action = json_decode(clean($_REQUEST["action"]), true); if ($action) { - $action_param = $this->dbh->escape_string($action["action_param"]); + $action_param = $action["action_param"]; $action_id = (int)$action["action_id"]; } else { $action_param = ""; @@ -1046,10 +1041,10 @@ class Pref_Filters extends Handler_Protected { print "<select name=\"action_id\" dojoType=\"dijit.form.Select\" onchange=\"filterDlgCheckAction(this)\">"; - $result = $this->dbh->query("SELECT id,description FROM ttrss_filter_actions + $res = $this->pdo->query("SELECT id,description FROM ttrss_filter_actions ORDER BY name"); - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $res->fetch()) { $is_selected = ($line["id"] == $action_id) ? "selected='1'" : ""; printf("<option $is_selected value='%d'>%s</option>", $line["id"], __($line["description"])); } @@ -1121,60 +1116,72 @@ class Pref_Filters extends Handler_Protected { private function getFilterName($id) { - $result = $this->dbh->query( + $sth = $this->pdo->prepare( "SELECT title,match_any_rule,COUNT(DISTINCT r.id) AS num_rules,COUNT(DISTINCT a.id) AS num_actions FROM ttrss_filters2 AS f LEFT JOIN ttrss_filters2_rules AS r ON (r.filter_id = f.id) LEFT JOIN ttrss_filters2_actions AS a - ON (a.filter_id = f.id) WHERE f.id = '$id' GROUP BY f.title, f.match_any_rule"); + ON (a.filter_id = f.id) WHERE f.id = ? GROUP BY f.title, f.match_any_rule"); + $sth->execute([$id]); - $title = $this->dbh->fetch_result($result, 0, "title"); - $num_rules = $this->dbh->fetch_result($result, 0, "num_rules"); - $num_actions = $this->dbh->fetch_result($result, 0, "num_actions"); - $match_any_rule = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "match_any_rule")); + if ($row = $sth->fetch()) { - if (!$title) $title = __("[No caption]"); + $title = $row["title"]; + $num_rules = $row["num_rules"]; + $num_actions = $row["num_actions"]; + $match_any_rule = $row["match_any_rule"]; - $title = sprintf(_ngettext("%s (%d rule)", "%s (%d rules)", (int) $num_rules), $title, $num_rules); + if (!$title) $title = __("[No caption]"); + $title = sprintf(_ngettext("%s (%d rule)", "%s (%d rules)", (int) $num_rules), $title, $num_rules); - $result = $this->dbh->query( - "SELECT * FROM ttrss_filters2_actions WHERE filter_id = '$id' ORDER BY id LIMIT 1"); + $sth = $this->pdo->prepare("SELECT * FROM ttrss_filters2_actions + WHERE filter_id = ? ORDER BY id LIMIT 1"); + $sth->execute([$id]); - $actions = ""; + $actions = ""; - if ($this->dbh->num_rows($result) > 0) { - $line = $this->dbh->fetch_assoc($result); - $actions = $this->getActionName($line); + if ($line = $sth->fetch()) { + $actions = $this->getActionName($line); - $num_actions -= 1; - } + $num_actions -= 1; + } + + if ($match_any_rule) $title .= " (" . __("matches any rule") . ")"; - if ($match_any_rule) $title .= " (" . __("matches any rule") . ")"; + if ($num_actions > 0) + $actions = sprintf(_ngettext("%s (+%d action)", "%s (+%d actions)", (int) $num_actions), $actions, $num_actions); - if ($num_actions > 0) - $actions = sprintf(_ngettext("%s (+%d action)", "%s (+%d actions)", (int) $num_actions), $actions, $num_actions); + return [$title, $actions]; + } - return array($title, $actions); + return []; } function join() { - $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); + $ids = explode(",", clean($_REQUEST["ids"])); if (count($ids) > 1) { $base_id = array_shift($ids); - $ids_str = join(",", $ids); + $ids_qmarks = arr_qmarks($ids); + + $this->pdo->beginTransaction(); + + $sth = $this->pdo->prepare("UPDATE ttrss_filters2_rules + SET filter_id = ? WHERE filter_id IN ($ids_qmarks)"); + $sth->execute(array_merge([$base_id], $ids)); + + $sth = $this->pdo->prepare("UPDATE ttrss_filters2_actions + SET filter_id = ? WHERE filter_id IN ($ids_qmarks)"); + $sth->execute(array_merge([$base_id], $ids)); - $this->dbh->query("BEGIN"); - $this->dbh->query("UPDATE ttrss_filters2_rules - SET filter_id = '$base_id' WHERE filter_id IN ($ids_str)"); - $this->dbh->query("UPDATE ttrss_filters2_actions - SET filter_id = '$base_id' WHERE filter_id IN ($ids_str)"); + $sth = $this->pdo->prepare("DELETE FROM ttrss_filters2 WHERE id IN ($ids_qmarks)"); + $sth->execute($ids); - $this->dbh->query("DELETE FROM ttrss_filters2 WHERE id IN ($ids_str)"); - $this->dbh->query("UPDATE ttrss_filters2 SET match_any_rule = true WHERE id = '$base_id'"); + $sth = $this->pdo->prepare("UPDATE ttrss_filters2 SET match_any_rule = true WHERE id = ?"); + $sth->execute([$base_id]); - $this->dbh->query("COMMIT"); + $this->pdo->commit(); $this->optimizeFilter($base_id); @@ -1182,14 +1189,17 @@ class Pref_Filters extends Handler_Protected { } private function optimizeFilter($id) { - $this->dbh->query("BEGIN"); - $result = $this->dbh->query("SELECT * FROM ttrss_filters2_actions - WHERE filter_id = '$id'"); + + $this->pdo->beginTransaction(); + + $sth = $this->pdo->prepare("SELECT * FROM ttrss_filters2_actions + WHERE filter_id = ?"); + $sth->execute([$id]); $tmp = array(); $dupe_ids = array(); - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $sth->fetch()) { $id = $line["id"]; unset($line["id"]); @@ -1202,17 +1212,18 @@ class Pref_Filters extends Handler_Protected { if (count($dupe_ids) > 0) { $ids_str = join(",", $dupe_ids); - $this->dbh->query("DELETE FROM ttrss_filters2_actions - WHERE id IN ($ids_str)"); + + $this->pdo->query("DELETE FROM ttrss_filters2_actions WHERE id IN ($ids_str)"); } - $result = $this->dbh->query("SELECT * FROM ttrss_filters2_rules - WHERE filter_id = '$id'"); + $sth = $this->pdo->prepare("SELECT * FROM ttrss_filters2_rules + WHERE filter_id = ?"); + $sth->execute([$id]); $tmp = array(); $dupe_ids = array(); - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $sth->fetch()) { $id = $line["id"]; unset($line["id"]); @@ -1225,10 +1236,10 @@ class Pref_Filters extends Handler_Protected { if (count($dupe_ids) > 0) { $ids_str = join(",", $dupe_ids); - $this->dbh->query("DELETE FROM ttrss_filters2_rules - WHERE id IN ($ids_str)"); + + $this->pdo->query("DELETE FROM ttrss_filters2_rules WHERE id IN ($ids_str)"); } - $this->dbh->query("COMMIT"); + $this->pdo->commit(); } }
\ No newline at end of file diff --git a/classes/pref/labels.php b/classes/pref/labels.php index 5720a1f4b..38ec850a6 100644 --- a/classes/pref/labels.php +++ b/classes/pref/labels.php @@ -8,80 +8,80 @@ class Pref_Labels extends Handler_Protected { } function edit() { - $label_id = $this->dbh->escape_string($_REQUEST['id']); + $label_id = clean($_REQUEST['id']); - $result = $this->dbh->query("SELECT * FROM ttrss_labels2 WHERE - id = '$label_id' AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("SELECT * FROM ttrss_labels2 WHERE + id = ? AND owner_uid = ?"); + $sth->execute([$label_id, $_SESSION['uid']]); - $line = $this->dbh->fetch_assoc($result); + if ($line = $sth->fetch()) { - print_hidden("id", "$label_id"); - print_hidden("op", "pref-labels"); - print_hidden("method", "save"); + print_hidden("id", "$label_id"); + print_hidden("op", "pref-labels"); + print_hidden("method", "save"); - print "<div class=\"dlgSec\">".__("Caption")."</div>"; + print "<div class=\"dlgSec\">".__("Caption")."</div>"; - print "<div class=\"dlgSecCont\">"; + print "<div class=\"dlgSecCont\">"; - $fg_color = $line['fg_color']; - $bg_color = $line['bg_color']; + $fg_color = $line['fg_color']; + $bg_color = $line['bg_color']; - print "<span class=\"labelColorIndicator\" id=\"label-editor-indicator\" style='color : $fg_color; background-color : $bg_color; margin-bottom : 4px; margin-right : 4px'>α</span>"; + print "<span class=\"labelColorIndicator\" id=\"label-editor-indicator\" style='color : $fg_color; background-color : $bg_color; margin-bottom : 4px; margin-right : 4px'>α</span>"; - print "<input style=\"font-size : 16px\" name=\"caption\" + print "<input style=\"font-size : 16px\" name=\"caption\" dojoType=\"dijit.form.ValidationTextBox\" required=\"true\" value=\"".htmlspecialchars($line['caption'])."\">"; - print "</div>"; - print "<div class=\"dlgSec\">" . __("Colors") . "</div>"; - print "<div class=\"dlgSecCont\">"; + print "</div>"; + print "<div class=\"dlgSec\">" . __("Colors") . "</div>"; + print "<div class=\"dlgSecCont\">"; - print "<table cellspacing=\"0\">"; + print "<table cellspacing=\"0\">"; - print "<tr><td>".__("Foreground:")."</td><td>".__("Background:"). - "</td></tr>"; + print "<tr><td>".__("Foreground:")."</td><td>".__("Background:"). + "</td></tr>"; - print "<tr><td style='padding-right : 10px'>"; + print "<tr><td style='padding-right : 10px'>"; - print "<input dojoType=\"dijit.form.TextBox\" + print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" id=\"labelEdit_fgColor\" name=\"fg_color\" value=\"$fg_color\">"; - print "<input dojoType=\"dijit.form.TextBox\" + print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" id=\"labelEdit_bgColor\" name=\"bg_color\" value=\"$bg_color\">"; - print "<div dojoType=\"dijit.ColorPalette\"> + print "<div dojoType=\"dijit.ColorPalette\"> <script type=\"dojo/method\" event=\"onChange\" args=\"fg_color\"> dijit.byId(\"labelEdit_fgColor\").attr('value', fg_color); $('label-editor-indicator').setStyle({color: fg_color}); </script> - </div>"; - print "</div>"; + </div>"; + print "</div>"; - print "</td><td>"; + print "</td><td>"; - print "<div dojoType=\"dijit.ColorPalette\"> + print "<div dojoType=\"dijit.ColorPalette\"> <script type=\"dojo/method\" event=\"onChange\" args=\"bg_color\"> dijit.byId(\"labelEdit_bgColor\").attr('value', bg_color); $('label-editor-indicator').setStyle({backgroundColor: bg_color}); </script> - </div>"; - print "</div>"; + </div>"; + print "</div>"; - print "</td></tr></table>"; - print "</div>"; + print "</td></tr></table>"; + print "</div>"; # print "</form>"; - print "<div class=\"dlgButtons\">"; - print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('labelEditDlg').execute()\">". - __('Save')."</button>"; - print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('labelEditDlg').hide()\">". - __('Cancel')."</button>"; - print "</div>"; - - return; + print "<div class=\"dlgButtons\">"; + print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('labelEditDlg').execute()\">". + __('Save')."</button>"; + print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('labelEditDlg').hide()\">". + __('Cancel')."</button>"; + print "</div>"; + } } function getlabeltree() { @@ -90,12 +90,13 @@ class Pref_Labels extends Handler_Protected { $root['name'] = __('Labels'); $root['items'] = array(); - $result = $this->dbh->query("SELECT * + $sth = $this->pdo->prepare("SELECT * FROM ttrss_labels2 - WHERE owner_uid = ".$_SESSION["uid"]." + WHERE owner_uid = ? ORDER BY caption"); + $sth->execute([$_SESSION['uid']]); - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $sth->fetch()) { $label = array(); $label['id'] = 'LABEL:' . $line['id']; $label['bare_id'] = $line['id']; @@ -118,86 +119,94 @@ class Pref_Labels extends Handler_Protected { } function colorset() { - $kind = $this->dbh->escape_string($_REQUEST["kind"]); - $ids = explode(',', $this->dbh->escape_string($_REQUEST["ids"])); - $color = $this->dbh->escape_string($_REQUEST["color"]); - $fg = $this->dbh->escape_string($_REQUEST["fg"]); - $bg = $this->dbh->escape_string($_REQUEST["bg"]); + $kind = clean($_REQUEST["kind"]); + $ids = explode(',', clean($_REQUEST["ids"])); + $color = clean($_REQUEST["color"]); + $fg = clean($_REQUEST["fg"]); + $bg = clean($_REQUEST["bg"]); foreach ($ids as $id) { if ($kind == "fg" || $kind == "bg") { - $this->dbh->query("UPDATE ttrss_labels2 SET - ${kind}_color = '$color' WHERE id = '$id' - AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_labels2 SET + ${kind}_color = ? WHERE id = ? + AND owner_uid = ?"); + + $sth->execute([$color, $id, $_SESSION['uid']]); + } else { - $this->dbh->query("UPDATE ttrss_labels2 SET - fg_color = '$fg', bg_color = '$bg' WHERE id = '$id' - AND owner_uid = " . $_SESSION["uid"]); + + $sth = $this->pdo->prepare("UPDATE ttrss_labels2 SET + fg_color = ?, bg_color = ? WHERE id = ? + AND owner_uid = ?"); + + $sth->execute([$fg, $bg, $id, $_SESSION['uid']]); } - $caption = $this->dbh->escape_string(Labels::find_caption($id, $_SESSION["uid"])); + $caption = Labels::find_caption($id, $_SESSION["uid"]); /* Remove cached data */ - $this->dbh->query("UPDATE ttrss_user_entries SET label_cache = '' - WHERE label_cache LIKE '%$caption%' AND owner_uid = " . $_SESSION["uid"]); - + $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET label_cache = '' + WHERE label_cache LIKE ? AND owner_uid = ?"); + $sth->execute(["%$caption%", $_SESSION['uid']]); } - - return; } function colorreset() { - $ids = explode(',', $this->dbh->escape_string($_REQUEST["ids"])); + $ids = explode(',', clean($_REQUEST["ids"])); foreach ($ids as $id) { - $this->dbh->query("UPDATE ttrss_labels2 SET - fg_color = '', bg_color = '' WHERE id = '$id' - AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_labels2 SET + fg_color = '', bg_color = '' WHERE id = ? + AND owner_uid = ?"); + $sth->execute([$id, $_SESSION['uid']]); - $caption = $this->dbh->escape_string(Labels::find_caption($id, $_SESSION["uid"])); + $caption = Labels::find_caption($id, $_SESSION["uid"]); /* Remove cached data */ - $this->dbh->query("UPDATE ttrss_user_entries SET label_cache = '' - WHERE label_cache LIKE '%$caption%' AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET label_cache = '' + WHERE label_cache LIKE ? AND owner_uid = ?"); + $sth->execute(["%$caption%", $_SESSION['uid']]); } - } function save() { - $id = $this->dbh->escape_string($_REQUEST["id"]); - $caption = $this->dbh->escape_string(trim($_REQUEST["caption"])); + $id = clean($_REQUEST["id"]); + $caption = trim(clean($_REQUEST["caption"])); - $this->dbh->query("BEGIN"); + $this->pdo->beginTransaction(); - $result = $this->dbh->query("SELECT caption FROM ttrss_labels2 - WHERE id = '$id' AND owner_uid = ". $_SESSION["uid"]); + $sth = $this->pdo->prepare("SELECT caption FROM ttrss_labels2 + WHERE id = ? AND owner_uid = ?"); + $sth->execute([$id, $_SESSION['uid']]); - if ($this->dbh->num_rows($result) != 0) { - $old_caption = $this->dbh->fetch_result($result, 0, "caption"); + if ($row = $sth->fetch()) { + $old_caption = $row["caption"]; - $result = $this->dbh->query("SELECT id FROM ttrss_labels2 - WHERE caption = '$caption' AND owner_uid = ". $_SESSION["uid"]); + $sth = $this->pdo->prepare("SELECT id FROM ttrss_labels2 + WHERE caption = ? AND owner_uid = ?"); + $sth->execute([$caption, $_SESSION['uid']]); - if ($this->dbh->num_rows($result) == 0) { + if (!$sth->fetch()) { if ($caption) { - $result = $this->dbh->query("UPDATE ttrss_labels2 SET - caption = '$caption' WHERE id = '$id' AND - owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_labels2 SET + caption = ? WHERE id = ? AND + owner_uid = ?"); + $sth->execute([$caption, $id, $_SESSION['uid']]); /* Update filters that reference label being renamed */ - $old_caption = $this->dbh->escape_string($old_caption); - - $this->dbh->query("UPDATE ttrss_filters2_actions SET - action_param = '$caption' WHERE action_param = '$old_caption' + $sth = $this->pdo->prepare("UPDATE ttrss_filters2_actions SET + action_param = ? WHERE action_param = ? AND action_id = 7 - AND filter_id IN (SELECT id FROM ttrss_filters2 WHERE owner_uid = ".$_SESSION["uid"].")"); + AND filter_id IN (SELECT id FROM ttrss_filters2 WHERE owner_uid = ?)"); - print $_REQUEST["value"]; + $sth->execute([$caption, $old_caption, $_SESSION['uid']]); + + print clean($_REQUEST["value"]); } else { print $old_caption; } @@ -206,14 +215,13 @@ class Pref_Labels extends Handler_Protected { } } - $this->dbh->query("COMMIT"); + $this->pdo->commit(); - return; } function remove() { - $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); + $ids = explode(",", clean($_REQUEST["ids"])); foreach ($ids as $id) { Labels::remove($id, $_SESSION["uid"]); @@ -222,8 +230,8 @@ class Pref_Labels extends Handler_Protected { } function add() { - $caption = $this->dbh->escape_string($_REQUEST["caption"]); - $output = $this->dbh->escape_string($_REQUEST["output"]); + $caption = clean($_REQUEST["caption"]); + $output = clean($_REQUEST["output"]); if ($caption) { diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php index cf27a72df..5fc76b32c 100644 --- a/classes/pref/prefs.php +++ b/classes/pref/prefs.php @@ -60,9 +60,9 @@ class Pref_Prefs extends Handler_Protected { function changepassword() { - $old_pw = $_POST["old_password"]; - $new_pw = $_POST["new_password"]; - $con_pw = $_POST["confirm_password"]; + $old_pw = clean($_POST["old_password"]); + $new_pw = clean($_POST["new_password"]); + $con_pw = clean($_POST["confirm_password"]); if ($old_pw == "") { print "ERROR: ".format_error("Old password cannot be blank."); @@ -89,7 +89,7 @@ class Pref_Prefs extends Handler_Protected { } function saveconfig() { - $boolean_prefs = explode(",", $_POST["boolean_prefs"]); + $boolean_prefs = explode(",", clean($_POST["boolean_prefs"])); foreach ($boolean_prefs as $pref) { if (!isset($_POST[$pref])) $_POST[$pref] = 'false'; @@ -99,14 +99,14 @@ class Pref_Prefs extends Handler_Protected { foreach (array_keys($_POST) as $pref_name) { - $pref_name = $this->dbh->escape_string($pref_name); - $value = $this->dbh->escape_string($_POST[$pref_name]); + $value = $_POST[$pref_name]; if ($pref_name == 'DIGEST_PREFERRED_TIME') { if (get_pref('DIGEST_PREFERRED_TIME') != $value) { - $this->dbh->query("UPDATE ttrss_users SET - last_digest_sent = NULL WHERE id = " . $_SESSION['uid']); + $sth = $this->pdo->prepare("UPDATE ttrss_users SET + last_digest_sent = NULL WHERE id = ?"); + $sth->execute([$_SESSION['uid']]); } } @@ -129,13 +129,13 @@ class Pref_Prefs extends Handler_Protected { function changeemail() { - $email = $this->dbh->escape_string($_POST["email"]); - $full_name = $this->dbh->escape_string($_POST["full_name"]); - + $email = clean($_POST["email"]); + $full_name = clean($_POST["full_name"]); $active_uid = $_SESSION["uid"]; - $this->dbh->query("UPDATE ttrss_users SET email = '$email', - full_name = '$full_name' WHERE id = '$active_uid'"); + $sth = $this->pdo->prepare("UPDATE ttrss_users SET email = ?, + full_name = ? WHERE id = ?"); + $sth->execute([$email, $full_name, $active_uid]); print __("Your personal data has been saved."); @@ -146,14 +146,10 @@ class Pref_Prefs extends Handler_Protected { $_SESSION["prefs_op_result"] = "reset-to-defaults"; - if ($_SESSION["profile"]) { - $profile_qpart = "profile = '" . $_SESSION["profile"] . "'"; - } else { - $profile_qpart = "profile IS NULL"; - } - - $this->dbh->query("DELETE FROM ttrss_user_prefs - WHERE $profile_qpart AND owner_uid = ".$_SESSION["uid"]); + $sth = $this->pdo->query("DELETE FROM ttrss_user_prefs + WHERE (profile = :profile OR (:profile IS NULL AND profile IS NULL)) + AND owner_uid = :uid"); + $sth->execute([":profile" => $_SESSION['profile'], ":uid" => $_SESSION['uid']]); initialize_user_prefs($_SESSION["uid"], $_SESSION["profile"]); @@ -202,13 +198,15 @@ class Pref_Prefs extends Handler_Protected { print "<h2>" . __("Personal data") . "</h2>"; - $result = $this->dbh->query("SELECT email,full_name,otp_enabled, + $sth = $this->pdo->prepare("SELECT email,full_name,otp_enabled, access_level FROM ttrss_users - WHERE id = ".$_SESSION["uid"]); + WHERE id = ?"); + $sth->execute([$_SESSION["uid"]]); + $row = $sth->fetch(); - $email = htmlspecialchars($this->dbh->fetch_result($result, 0, "email")); - $full_name = htmlspecialchars($this->dbh->fetch_result($result, 0, "full_name")); - $otp_enabled = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "otp_enabled")); + $email = htmlspecialchars($row["email"]); + $full_name = htmlspecialchars($row["full_name"]); + $otp_enabled = sql_bool_to_bool($row["otp_enabled"]); print "<tr><td width=\"40%\">".__('Full name')."</td>"; print "<td class=\"prefValue\"><input dojoType=\"dijit.form.ValidationTextBox\" name=\"full_name\" required=\"1\" @@ -219,7 +217,7 @@ class Pref_Prefs extends Handler_Protected { if (!SINGLE_USER_MODE && !$_SESSION["hide_hello"]) { - $access_level = $this->dbh->fetch_result($result, 0, "access_level"); + $access_level = $row["access_level"]; print "<tr><td width=\"40%\">".__('Access level')."</td>"; print "<td>" . $access_level_names[$access_level] . "</td></tr>"; } @@ -246,14 +244,6 @@ class Pref_Prefs extends Handler_Protected { print "<div style='display : none' id='pwd_change_infobox'></div>"; - $result = $this->dbh->query("SELECT id FROM ttrss_users - WHERE id = ".$_SESSION["uid"]." AND pwd_hash - = 'SHA1:5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8'"); - - if ($this->dbh->num_rows($result) != 0) { - print format_warning(__("Your password is at default value, please change it."), "default_pass_warning"); - } - print "<form dojoType=\"dijit.form.Form\">"; print "<script type=\"dojo/method\" event=\"onSubmit\" args=\"evt\"> @@ -468,31 +458,22 @@ class Pref_Prefs extends Handler_Protected { if ($_SESSION["profile"]) { initialize_user_prefs($_SESSION["uid"], $_SESSION["profile"]); - $profile_qpart = "profile = '" . $_SESSION["profile"] . "'"; } else { initialize_user_prefs($_SESSION["uid"]); - $profile_qpart = "profile IS NULL"; } - /* if ($_SESSION["prefs_show_advanced"]) - $access_query = "true"; - else - $access_query = "(access_level = 0 AND section_id != 3)"; */ - - $access_query = 'true'; - - $result = $this->dbh->query("SELECT DISTINCT + $sth = $this->pdo->prepare("SELECT DISTINCT ttrss_user_prefs.pref_name,value,type_name, ttrss_prefs_sections.order_id, def_value,section_id FROM ttrss_prefs,ttrss_prefs_types,ttrss_prefs_sections,ttrss_user_prefs WHERE type_id = ttrss_prefs_types.id AND - $profile_qpart AND + (profile = :profile OR (:profile IS NULL AND profile IS NULL)) AND section_id = ttrss_prefs_sections.id AND ttrss_user_prefs.pref_name = ttrss_prefs.pref_name AND - $access_query AND - owner_uid = ".$_SESSION["uid"]." + owner_uid = :uid ORDER BY ttrss_prefs_sections.order_id,pref_name"); + $sth->execute([":uid" => $_SESSION['uid'], ":profile" => $_SESSION['profile']]); $lnum = 0; @@ -500,7 +481,7 @@ class Pref_Prefs extends Handler_Protected { $listed_boolean_prefs = array(); - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $sth->fetch()) { if (in_array($line["pref_name"], $prefs_blacklist)) { continue; @@ -872,20 +853,25 @@ class Pref_Prefs extends Handler_Protected { require_once "lib/otphp/lib/totp.php"; require_once "lib/phpqrcode/phpqrcode.php"; - $result = $this->dbh->query("SELECT login,salt,otp_enabled + $sth = $this->pdo->prepare("SELECT login,salt,otp_enabled FROM ttrss_users - WHERE id = ".$_SESSION["uid"]); + WHERE id = ?"); + $sth->execute([$_SESSION['uid']]); + + if ($row = $sth->fetch()) { + + $base32 = new Base32(); - $base32 = new Base32(); + $login = $row["login"]; + $otp_enabled = sql_bool_to_bool($row["otp_enabled"]); - $login = $this->dbh->fetch_result($result, 0, "login"); - $otp_enabled = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "otp_enabled")); + if (!$otp_enabled) { + $secret = $base32->encode(sha1($row["salt"])); - if (!$otp_enabled) { - $secret = $base32->encode(sha1($this->dbh->fetch_result($result, 0, "salt"))); - print QRcode::png("otpauth://totp/".urlencode($login). - "?secret=$secret&issuer=".urlencode("Tiny Tiny RSS")); + QRcode::png("otpauth://totp/".urlencode($login). + "?secret=$secret&issuer=".urlencode("Tiny Tiny RSS")); + } } } @@ -894,47 +880,65 @@ class Pref_Prefs extends Handler_Protected { require_once "lib/otphp/lib/otp.php"; require_once "lib/otphp/lib/totp.php"; - $password = $_REQUEST["password"]; - $otp = $_REQUEST["otp"]; + $password = clean($_REQUEST["password"]); + $otp = clean($_REQUEST["otp"]); $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]); if ($authenticator->check_password($_SESSION["uid"], $password)) { - $result = $this->dbh->query("SELECT salt + $sth = $this->pdo->query("SELECT salt FROM ttrss_users - WHERE id = ".$_SESSION["uid"]); + WHERE id = ?"); + $sth->execute([$_SESSION['uid']]); - $base32 = new Base32(); + if ($row = $sth->fetch()) { - $secret = $base32->encode(sha1($this->dbh->fetch_result($result, 0, "salt"))); - $topt = new \OTPHP\TOTP($secret); + $base32 = new Base32(); - $otp_check = $topt->now(); + $secret = $base32->encode(sha1($row["salt"])); + $topt = new \OTPHP\TOTP($secret); - if ($otp == $otp_check) { - $this->dbh->query("UPDATE ttrss_users SET otp_enabled = true WHERE - id = " . $_SESSION["uid"]); + $otp_check = $topt->now(); - print "OK"; - } else { - print "ERROR:".__("Incorrect one time password"); + if ($otp == $otp_check) { + $sth = $this->pdo->prepare("UPDATE ttrss_users + SET otp_enabled = true WHERE id = ?"); + + $sth->execute([$_SESSION['uid']]); + + print "OK"; + } else { + print "ERROR:".__("Incorrect one time password"); + } } + } else { print "ERROR:".__("Incorrect password"); } } + static function isdefaultpassword() { + $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]); + + if ($authenticator->check_password($_SESSION["uid"], "password")) { + return true; + } + + return false; + } + function otpdisable() { - $password = $this->dbh->escape_string($_REQUEST["password"]); + $password = clean($_REQUEST["password"]); $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]); if ($authenticator->check_password($_SESSION["uid"], $password)) { - $this->dbh->query("UPDATE ttrss_users SET otp_enabled = false WHERE - id = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_users SET otp_enabled = false WHERE + id = ?"); + $sth->execute([$_SESSION['uid']]); print "OK"; } else { @@ -944,8 +948,8 @@ class Pref_Prefs extends Handler_Protected { } function setplugins() { - if (is_array($_REQUEST["plugins"])) - $plugins = join(",", $_REQUEST["plugins"]); + if (is_array(clean($_REQUEST["plugins"]))) + $plugins = join(",", clean($_REQUEST["plugins"])); else $plugins = ""; @@ -953,7 +957,7 @@ class Pref_Prefs extends Handler_Protected { } function clearplugindata() { - $name = $this->dbh->escape_string($_REQUEST["name"]); + $name = clean($_REQUEST["name"]); PluginHost::getInstance()->clear_data(PluginHost::getInstance()->get_plugin($name)); } @@ -1007,8 +1011,9 @@ class Pref_Prefs extends Handler_Protected { print "</div>"; - $result = $this->dbh->query("SELECT title,id FROM ttrss_settings_profiles - WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title"); + $sth = $this->pdo->prepare("SELECT title,id FROM ttrss_settings_profiles + WHERE owner_uid = ? ORDER BY title"); + $sth->execute([$_SESSION['uid']]); print "<div class=\"prefProfileHolder\">"; @@ -1038,7 +1043,7 @@ class Pref_Prefs extends Handler_Protected { $lnum = 1; - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $sth->fetch()) { $profile_id = $line["id"]; $this_row_id = "id=\"FCATR-$profile_id\""; diff --git a/classes/pref/system.php b/classes/pref/system.php index 56c0fec94..2099ebb9c 100644 --- a/classes/pref/system.php +++ b/classes/pref/system.php @@ -20,7 +20,7 @@ class Pref_System extends Handler_Protected { } function clearLog() { - $this->dbh->query("DELETE FROM ttrss_error_log"); + $this->pdo->query("DELETE FROM ttrss_error_log"); } function index() { @@ -30,7 +30,7 @@ class Pref_System extends Handler_Protected { if (LOG_DESTINATION == "sql") { - $result = $this->dbh->query("SELECT errno, errstr, filename, lineno, + $res = $this->pdo->query("SELECT errno, errstr, filename, lineno, created_at, login, context FROM ttrss_error_log LEFT JOIN ttrss_users ON (owner_uid = ttrss_users.id) ORDER BY ttrss_error_log.id DESC @@ -52,7 +52,7 @@ class Pref_System extends Handler_Protected { <td width='5%'>".__("Date")."</td> </tr>"; - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $res->fetch()) { print "<tr class=\"errrow\">"; foreach ($line as $k => $v) { diff --git a/classes/pref/users.php b/classes/pref/users.php index a937a2409..d65af1680 100644 --- a/classes/pref/users.php +++ b/classes/pref/users.php @@ -25,66 +25,71 @@ class Pref_Users extends Handler_Protected { print "<form id=\"user_edit_form\" onsubmit='return false' dojoType=\"dijit.form.Form\">"; - $id = (int) $this->dbh->escape_string($_REQUEST["id"]); + $id = (int) clean($_REQUEST["id"]); print_hidden("id", "$id"); print_hidden("op", "pref-users"); print_hidden("method", "editSave"); - $result = $this->dbh->query("SELECT * FROM ttrss_users WHERE id = '$id'"); + $sth = $this->pdo->prepare("SELECT * FROM ttrss_users WHERE id = ?"); + $sth->execute([$id]); - $login = $this->dbh->fetch_result($result, 0, "login"); - $access_level = $this->dbh->fetch_result($result, 0, "access_level"); - $email = $this->dbh->fetch_result($result, 0, "email"); + if ($row = $sth->fetch()) { - $sel_disabled = ($id == $_SESSION["uid"] || $login == "admin") ? "disabled" : ""; + $login = $row["login"]; + $access_level = $row["access_level"]; + $email = $row["email"]; - print "<div class=\"dlgSec\">".__("User")."</div>"; - print "<div class=\"dlgSecCont\">"; + $sel_disabled = ($id == $_SESSION["uid"] || $login == "admin") ? "disabled" : ""; - if ($sel_disabled) { - print_hidden("login", "$login"); - } + print "<div class=\"dlgSec\">".__("User")."</div>"; + print "<div class=\"dlgSecCont\">"; - print "<input size=\"30\" style=\"font-size : 16px\" - dojoType=\"dijit.form.ValidationTextBox\" required=\"1\" - $sel_disabled - name=\"login\" value=\"$login\">"; + if ($sel_disabled) { + print_hidden("login", "$login"); + } - print "</div>"; + print "<input size=\"30\" style=\"font-size : 16px\" + dojoType=\"dijit.form.ValidationTextBox\" required=\"1\" + $sel_disabled + name=\"login\" value=\"$login\">"; - print "<div class=\"dlgSec\">".__("Authentication")."</div>"; - print "<div class=\"dlgSecCont\">"; + print "</div>"; - print __('Access level: ') . " "; + print "<div class=\"dlgSec\">".__("Authentication")."</div>"; + print "<div class=\"dlgSecCont\">"; - if (!$sel_disabled) { - print_select_hash("access_level", $access_level, $access_level_names, - "dojoType=\"dijit.form.Select\" $sel_disabled"); - } else { - print_select_hash("", $access_level, $access_level_names, - "dojoType=\"dijit.form.Select\" $sel_disabled"); - print_hidden("access_level", "$access_level"); - } + print __('Access level: ') . " "; + + if (!$sel_disabled) { + print_select_hash("access_level", $access_level, $access_level_names, + "dojoType=\"dijit.form.Select\" $sel_disabled"); + } else { + print_select_hash("", $access_level, $access_level_names, + "dojoType=\"dijit.form.Select\" $sel_disabled"); + print_hidden("access_level", "$access_level"); + } - print "<hr/>"; + print "<hr/>"; - print "<input dojoType=\"dijit.form.TextBox\" type=\"password\" size=\"20\" placeholder=\"Change password\" + print "<input dojoType=\"dijit.form.TextBox\" type=\"password\" size=\"20\" placeholder=\"Change password\" name=\"password\">"; - print "</div>"; + print "</div>"; - print "<div class=\"dlgSec\">".__("Options")."</div>"; - print "<div class=\"dlgSecCont\">"; + print "<div class=\"dlgSec\">".__("Options")."</div>"; + print "<div class=\"dlgSecCont\">"; - print "<input dojoType=\"dijit.form.TextBox\" size=\"30\" name=\"email\" placeholder=\"E-mail\" + print "<input dojoType=\"dijit.form.TextBox\" size=\"30\" name=\"email\" placeholder=\"E-mail\" value=\"$email\">"; - print "</div>"; + print "</div>"; - print "</table>"; + print "</table>"; - print "</form>"; + print "</form>"; + + } print '</div>'; #tab print "<div href=\"backend.php?op=pref-users&method=userdetails&id=$id\" @@ -103,131 +108,138 @@ class Pref_Users extends Handler_Protected { } function userdetails() { - $id = (int) $this->dbh->escape_string($_REQUEST["id"]); + $id = (int) clean($_REQUEST["id"]); - $result = $this->dbh->query("SELECT login, + $sth = $this->pdo->prepare("SELECT login, ".SUBSTRING_FOR_DATE."(last_login,1,16) AS last_login, access_level, (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE owner_uid = id) AS stored_articles, ".SUBSTRING_FOR_DATE."(created,1,16) AS created FROM ttrss_users - WHERE id = '$id'"); + WHERE id = ?"); + $sth->execute([$id]); - if ($this->dbh->num_rows($result) == 0) { - print "<h1>".__('User not found')."</h1>"; - return; - } + if ($row = $sth->fetch()) { + print "<table width='100%'>"; - print "<table width='100%'>"; + $last_login = make_local_datetime( + $row["last_login"], true); - $last_login = make_local_datetime( - $this->dbh->fetch_result($result, 0, "last_login"), true); + $created = make_local_datetime( + $row["created"], true); - $created = make_local_datetime( - $this->dbh->fetch_result($result, 0, "created"), true); + $stored_articles = $row["stored_articles"]; - $stored_articles = $this->dbh->fetch_result($result, 0, "stored_articles"); + print "<tr><td>".__('Registered')."</td><td>$created</td></tr>"; + print "<tr><td>".__('Last logged in')."</td><td>$last_login</td></tr>"; - print "<tr><td>".__('Registered')."</td><td>$created</td></tr>"; - print "<tr><td>".__('Last logged in')."</td><td>$last_login</td></tr>"; + $sth = $this->pdo->prepare("SELECT COUNT(id) as num_feeds FROM ttrss_feeds + WHERE owner_uid = ?"); + $sth->execute([$id]); + $row = $sth->fetch(); + $num_feeds = $row["num_feeds"]; - $result = $this->dbh->query("SELECT COUNT(id) as num_feeds FROM ttrss_feeds - WHERE owner_uid = '$id'"); + print "<tr><td>".__('Subscribed feeds count')."</td><td>$num_feeds</td></tr>"; + print "<tr><td>".__('Stored articles')."</td><td>$stored_articles</td></tr>"; - $num_feeds = $this->dbh->fetch_result($result, 0, "num_feeds"); + print "</table>"; - print "<tr><td>".__('Subscribed feeds count')."</td><td>$num_feeds</td></tr>"; - print "<tr><td>".__('Stored articles')."</td><td>$stored_articles</td></tr>"; + print "<h1>".__('Subscribed feeds')."</h1>"; - print "</table>"; + $sth = $this->pdo->prepare("SELECT id,title,site_url FROM ttrss_feeds + WHERE owner_uid = ? ORDER BY title"); + $sth->execute([$id]); - print "<h1>".__('Subscribed feeds')."</h1>"; + print "<ul class=\"userFeedList\">"; - $result = $this->dbh->query("SELECT id,title,site_url FROM ttrss_feeds - WHERE owner_uid = '$id' ORDER BY title"); + while ($line = $sth->fetch()) { - print "<ul class=\"userFeedList\">"; + $icon_file = ICONS_URL."/".$line["id"].".ico"; - while ($line = $this->dbh->fetch_assoc($result)) { + if (file_exists($icon_file) && filesize($icon_file) > 0) { + $feed_icon = "<img class=\"tinyFeedIcon\" src=\"$icon_file\">"; + } else { + $feed_icon = "<img class=\"tinyFeedIcon\" src=\"images/blank_icon.gif\">"; + } - $icon_file = ICONS_URL."/".$line["id"].".ico"; + print "<li>$feed_icon <a href=\"".$line["site_url"]."\">".$line["title"]."</a></li>"; - if (file_exists($icon_file) && filesize($icon_file) > 0) { - $feed_icon = "<img class=\"tinyFeedIcon\" src=\"$icon_file\">"; - } else { - $feed_icon = "<img class=\"tinyFeedIcon\" src=\"images/blank_icon.gif\">"; } - print "<li>$feed_icon <a href=\"".$line["site_url"]."\">".$line["title"]."</a></li>"; - - } - - if ($this->dbh->num_rows($result) < $num_feeds) { - // FIXME - add link to show ALL subscribed feeds here somewhere - print "<li><img - class=\"tinyFeedIcon\" src=\"images/blank_icon.gif\"> ...</li>"; + print "</ul>"; + + + } else { + print "<h1>".__('User not found')."</h1>"; } - - print "</ul>"; + } function editSave() { - $login = $this->dbh->escape_string(trim($_REQUEST["login"])); - $uid = $this->dbh->escape_string($_REQUEST["id"]); - $access_level = (int) $_REQUEST["access_level"]; - $email = $this->dbh->escape_string(trim($_REQUEST["email"])); - $password = $_REQUEST["password"]; + $login = trim(clean($_REQUEST["login"])); + $uid = clean($_REQUEST["id"]); + $access_level = (int) clean($_REQUEST["access_level"]); + $email = trim(clean($_REQUEST["email"])); + $password = clean($_REQUEST["password"]); if ($password) { $salt = substr(bin2hex(get_random_bytes(125)), 0, 250); $pwd_hash = encrypt_password($password, $salt, true); - $pass_query_part = "pwd_hash = '$pwd_hash', salt = '$salt',"; + $pass_query_part = "pwd_hash = ".$this->pdo->quote($pwd_hash).", + salt = ".$this->pdo->quote($salt).","; } else { $pass_query_part = ""; } - $this->dbh->query("UPDATE ttrss_users SET $pass_query_part login = '$login', - access_level = '$access_level', email = '$email', otp_enabled = false - WHERE id = '$uid'"); + $sth = $this->pdo->prepare("UPDATE ttrss_users SET $pass_query_part login = ?, + access_level = ?, email = ?, otp_enabled = false WHERE id = ?"); + $sth->execute([$login, $access_level, $email, $uid]); } function remove() { - $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); + $ids = explode(",", clean($_REQUEST["ids"])); foreach ($ids as $id) { if ($id != $_SESSION["uid"] && $id != 1) { - $this->dbh->query("DELETE FROM ttrss_tags WHERE owner_uid = '$id'"); - $this->dbh->query("DELETE FROM ttrss_feeds WHERE owner_uid = '$id'"); - $this->dbh->query("DELETE FROM ttrss_users WHERE id = '$id'"); + $sth = $this->pdo->prepare("DELETE FROM ttrss_tags WHERE owner_uid = ?"); + $sth->execute([$id]); + + $sth = $this->pdo->prepare("DELETE FROM ttrss_feeds WHERE owner_uid = ?"); + $sth->execute([$id]); + + $sth = $this->pdo->prepare("DELETE FROM ttrss_users WHERE id = ?"); + $sth->execute([$id]); } } } function add() { - $login = $this->dbh->escape_string(trim($_REQUEST["login"])); + $login = trim(clean($_REQUEST["login"])); $tmp_user_pwd = make_password(8); $salt = substr(bin2hex(get_random_bytes(125)), 0, 250); $pwd_hash = encrypt_password($tmp_user_pwd, $salt, true); - $result = $this->dbh->query("SELECT id FROM ttrss_users WHERE - login = '$login'"); + $sth = $this->pdo->prepare("SELECT id FROM ttrss_users WHERE + login = ?"); + $sth->execute([$login]); - if ($this->dbh->num_rows($result) == 0) { + if (!$sth->fetch()) { - $this->dbh->query("INSERT INTO ttrss_users + $sth = $this->pdo->prepare("INSERT INTO ttrss_users (login,pwd_hash,access_level,last_login,created, salt) - VALUES ('$login', '$pwd_hash', 0, null, NOW(), '$salt')"); - + VALUES (?, ?, 0, null, NOW(), ?)"); + $sth->execute([$login, $pwd_hash, $salt]); - $result = $this->dbh->query("SELECT id FROM ttrss_users WHERE - login = '$login' AND pwd_hash = '$pwd_hash'"); + $sth = $this->pdo->prepare("SELECT id FROM ttrss_users WHERE + login = ? AND pwd_hash = ?"); + $sth->execute([$login, $pwd_hash]); - if ($this->dbh->num_rows($result) == 1) { + if ($row = $sth->fetch()) { - $new_uid = $this->dbh->fetch_result($result, 0, "id"); + $new_uid = $row['id']; print format_notice(T_sprintf("Added user <b>%s</b> with password <b>%s</b>", $login, $tmp_user_pwd)); @@ -246,56 +258,65 @@ class Pref_Users extends Handler_Protected { static function resetUserPassword($uid, $show_password) { - $result = db_query("SELECT login,email - FROM ttrss_users WHERE id = '$uid'"); + $pdo = Db::pdo(); - $login = db_fetch_result($result, 0, "login"); - $email = db_fetch_result($result, 0, "email"); + $sth = $pdo->prepare("SELECT login, email + FROM ttrss_users WHERE id = ?"); + $sth->execute([$uid]); + + if ($row = $sth->fetch()) { - $new_salt = substr(bin2hex(get_random_bytes(125)), 0, 250); - $tmp_user_pwd = make_password(8); + $login = $row["login"]; + $email = $row["email"]; - $pwd_hash = encrypt_password($tmp_user_pwd, $new_salt, true); + $new_salt = substr(bin2hex(get_random_bytes(125)), 0, 250); + $tmp_user_pwd = make_password(8); - db_query("UPDATE ttrss_users SET pwd_hash = '$pwd_hash', salt = '$new_salt', otp_enabled = false - WHERE id = '$uid'"); + $pwd_hash = encrypt_password($tmp_user_pwd, $new_salt, true); - if ($show_password) { - print T_sprintf("Changed password of user <b>%s</b> to <b>%s</b>", $login, $tmp_user_pwd); - } else { - print_notice(T_sprintf("Sending new password of user <b>%s</b> to <b>%s</b>", $login, $email)); - } + $sth = $pdo->prepare("UPDATE ttrss_users + SET pwd_hash = ?, salt = ?, otp_enabled = false + WHERE id = ?"); + $sth->execute([$pwd_hash, $new_salt, $uid]); - require_once 'classes/ttrssmailer.php'; + if ($show_password) { + print T_sprintf("Changed password of user <b>%s</b> to <b>%s</b>", $login, $tmp_user_pwd); + } else { + print_notice(T_sprintf("Sending new password of user <b>%s</b> to <b>%s</b>", $login, $email)); + } + + require_once 'classes/ttrssmailer.php'; - if ($email) { - require_once "lib/MiniTemplator.class.php"; + if ($email) { + require_once "lib/MiniTemplator.class.php"; - $tpl = new MiniTemplator; + $tpl = new MiniTemplator; - $tpl->readTemplateFromFile("templates/resetpass_template.txt"); + $tpl->readTemplateFromFile("templates/resetpass_template.txt"); - $tpl->setVariable('LOGIN', $login); - $tpl->setVariable('NEWPASS', $tmp_user_pwd); + $tpl->setVariable('LOGIN', $login); + $tpl->setVariable('NEWPASS', $tmp_user_pwd); - $tpl->addBlock('message'); + $tpl->addBlock('message'); - $message = ""; + $message = ""; - $tpl->generateOutputToString($message); + $tpl->generateOutputToString($message); - $mail = new ttrssMailer(); + $mail = new ttrssMailer(); - $rc = $mail->quickMail($email, $login, - __("[tt-rss] Password change notification"), - $message, false); + $rc = $mail->quickMail($email, $login, + __("[tt-rss] Password change notification"), + $message, false); - if (!$rc) print_error($mail->ErrorInfo); + if (!$rc) print_error($mail->ErrorInfo); + } + } } function resetPass() { - $uid = $this->dbh->escape_string($_REQUEST["id"]); + $uid = clean($_REQUEST["id"]); Pref_Users::resetUserPassword($uid, true); } @@ -308,7 +329,7 @@ class Pref_Users extends Handler_Protected { print "<div id=\"pref-user-toolbar\" dojoType=\"dijit.Toolbar\">"; - $user_search = $this->dbh->escape_string($_REQUEST["search"]); + $user_search = trim(clean($_REQUEST["search"])); if (array_key_exists("search", $_REQUEST)) { $_SESSION["prefs_user_search"] = $user_search; @@ -323,7 +344,7 @@ class Pref_Users extends Handler_Protected { __('Search')."</button> </div>"; - $sort = $this->dbh->escape_string($_REQUEST["sort"]); + $sort = clean($_REQUEST["sort"]); if (!$sort || $sort == "undefined") { $sort = "login"; @@ -355,25 +376,12 @@ class Pref_Users extends Handler_Protected { print "</div>"; #pane print "<div id=\"pref-user-content\" dojoType=\"dijit.layout.ContentPane\" region=\"center\">"; - print "<div id=\"sticky-status-msg\"></div>"; - - if ($user_search) { + $sort = validate_field($sort, + ["login", "access_level", "created", "num_feeds", "created", "last_login"], "login"); - $user_search = explode(" ", $user_search); - $tokens = array(); + if ($sort != "login") $sort = "$sort DESC"; - foreach ($user_search as $token) { - $token = trim($token); - array_push($tokens, "(UPPER(login) LIKE UPPER('%$token%'))"); - } - - $user_search_query = "(" . join($tokens, " AND ") . ") AND "; - - } else { - $user_search_query = ""; - } - - $result = $this->dbh->query("SELECT + $sth = $this->pdo->prepare("SELECT tu.id, login,access_level,email, ".SUBSTRING_FOR_DATE."(last_login,1,16) as last_login, @@ -382,11 +390,9 @@ class Pref_Users extends Handler_Protected { FROM ttrss_users tu WHERE - $user_search_query - tu.id > 0 + (:search = '' OR login LIKE :search) AND tu.id > 0 ORDER BY $sort"); - - if ($this->dbh->num_rows($result) > 0) { + $sth->execute([":search" => $user_search ? "%$user_search%" : ""]); print "<p><table width=\"100%\" cellspacing=\"0\" class=\"prefUserList\" id=\"prefUserList\">"; @@ -401,7 +407,7 @@ class Pref_Users extends Handler_Protected { $lnum = 0; - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $sth->fetch()) { $uid = $line["id"]; @@ -434,15 +440,12 @@ class Pref_Users extends Handler_Protected { print "</table>"; - } else { - print "<p>"; + if ($lnum == 0) { if (!$user_search) { print_warning(__('No users defined.')); } else { print_warning(__('No matching users found.')); } - print "</p>"; - } print "</div>"; #pane diff --git a/classes/rpc.php b/classes/rpc.php index 7d39261b1..f8cf7a828 100755 --- a/classes/rpc.php +++ b/classes/rpc.php @@ -2,47 +2,54 @@ class RPC extends Handler_Protected { function csrf_ignore($method) { - $csrf_ignored = array("sanitycheck", "completelabels"); + $csrf_ignored = array("sanitycheck", "completelabels", "saveprofile"); return array_search($method, $csrf_ignored) !== false; } function setprofile() { - $id = $this->dbh->escape_string($_REQUEST["id"]); + $_SESSION["profile"] = clean($_REQUEST["id"]); - $_SESSION["profile"] = $id; + // default value + if (!$_SESSION["profile"]) $_SESSION["profile"] = null; } function remprofiles() { - $ids = explode(",", $this->dbh->escape_string(trim($_REQUEST["ids"]))); + $ids = explode(",", trim(clean($_REQUEST["ids"]))); foreach ($ids as $id) { if ($_SESSION["profile"] != $id) { - $this->dbh->query("DELETE FROM ttrss_settings_profiles WHERE id = '$id' AND - owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("DELETE FROM ttrss_settings_profiles WHERE id = ? AND + owner_uid = ?"); + $sth->execute([$id, $_SESSION['uid']]); } } } // Silent function addprofile() { - $title = $this->dbh->escape_string(trim($_REQUEST["title"])); + $title = trim(clean($_REQUEST["title"])); + if ($title) { - $this->dbh->query("BEGIN"); + $this->pdo->beginTransaction(); + + $sth = $this->pdo->prepare("SELECT id FROM ttrss_settings_profiles + WHERE title = ? AND owner_uid = ?"); + $sth->execute([$title, $_SESSION['uid']]); - $result = $this->dbh->query("SELECT id FROM ttrss_settings_profiles - WHERE title = '$title' AND owner_uid = " . $_SESSION["uid"]); + if (!$sth->fetch()) { - if ($this->dbh->num_rows($result) == 0) { + $sth = $this->pdo->prepare("INSERT INTO ttrss_settings_profiles (title, owner_uid) + VALUES (?, ?)"); - $this->dbh->query("INSERT INTO ttrss_settings_profiles (title, owner_uid) - VALUES ('$title', ".$_SESSION["uid"] .")"); + $sth->execute([$title, $_SESSION['uid']]); - $result = $this->dbh->query("SELECT id FROM ttrss_settings_profiles WHERE - title = '$title'"); + $sth = $this->pdo->prepare("SELECT id FROM ttrss_settings_profiles WHERE + title = ? AND owner_uid = ?"); + $sth->execute([$title, $_SESSION['uid']]); - if ($this->dbh->num_rows($result) != 0) { - $profile_id = $this->dbh->fetch_result($result, 0, "id"); + if ($row = $sth->fetch()) { + $profile_id = $row['id']; if ($profile_id) { initialize_user_prefs($_SESSION["uid"], $profile_id); @@ -50,14 +57,13 @@ class RPC extends Handler_Protected { } } - $this->dbh->query("COMMIT"); + $this->pdo->commit(); } } - // Silent function saveprofile() { - $id = $this->dbh->escape_string($_REQUEST["id"]); - $title = $this->dbh->escape_string(trim($_REQUEST["value"])); + $id = clean($_REQUEST["id"]); + $title = trim(clean($_REQUEST["value"])); if ($id == 0) { print __("Default profile"); @@ -65,45 +71,34 @@ class RPC extends Handler_Protected { } if ($title) { - $this->dbh->query("BEGIN"); - - $result = $this->dbh->query("SELECT id FROM ttrss_settings_profiles - WHERE title = '$title' AND owner_uid =" . $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_settings_profiles + SET title = ? WHERE id = ? AND + owner_uid = ?"); - if ($this->dbh->num_rows($result) == 0) { - $this->dbh->query("UPDATE ttrss_settings_profiles - SET title = '$title' WHERE id = '$id' AND - owner_uid = " . $_SESSION["uid"]); - print $title; - } else { - $result = $this->dbh->query("SELECT title FROM ttrss_settings_profiles - WHERE id = '$id' AND owner_uid =" . $_SESSION["uid"]); - print $this->dbh->fetch_result($result, 0, "title"); - } - - $this->dbh->query("COMMIT"); + $sth->execute([$title, $id, $_SESSION['uid']]); + print $title; } } // Silent function remarchive() { - $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); + $ids = explode(",", clean($_REQUEST["ids"])); - foreach ($ids as $id) { - $result = $this->dbh->query("DELETE FROM ttrss_archived_feeds WHERE - (SELECT COUNT(*) FROM ttrss_user_entries - WHERE orig_feed_id = '$id') = 0 AND - id = '$id' AND owner_uid = ".$_SESSION["uid"]); + $sth = $this->pdo->prepare("DELETE FROM ttrss_archived_feeds WHERE + (SELECT COUNT(*) FROM ttrss_user_entries + WHERE orig_feed_id = :id) = 0 AND + id = :id AND owner_uid = :uid"); - $this->dbh->affected_rows($result); + foreach ($ids as $id) { + $sth->execute([":id" => $id, ":uid" => $_SESSION['uid']]); } } function addfeed() { - $feed = $this->dbh->escape_string($_REQUEST['feed']); - $cat = $this->dbh->escape_string($_REQUEST['cat']); - $login = $this->dbh->escape_string($_REQUEST['login']); - $pass = trim($_REQUEST['pass']); // escaped later + $feed = clean($_REQUEST['feed']); + $cat = clean($_REQUEST['cat']); + $login = clean($_REQUEST['login']); + $pass = trim(clean($_REQUEST['pass'])); $rc = Feeds::subscribe_to_feed($feed, $cat, $login, $pass); @@ -111,7 +106,7 @@ class RPC extends Handler_Protected { } function togglepref() { - $key = $this->dbh->escape_string($_REQUEST["key"]); + $key = clean($_REQUEST["key"]); set_pref($key, !get_pref($key)); $value = get_pref($key); @@ -120,8 +115,8 @@ class RPC extends Handler_Protected { function setpref() { // set_pref escapes input, so no need to double escape it here - $key = $_REQUEST['key']; - $value = str_replace("\n", "<br/>", $_REQUEST['value']); + $key = clean($_REQUEST['key']); + $value = $_REQUEST['value']; set_pref($key, $value, false, $key != 'USER_STYLESHEET'); @@ -129,27 +124,25 @@ class RPC extends Handler_Protected { } function mark() { - $mark = $_REQUEST["mark"]; - $id = $this->dbh->escape_string($_REQUEST["id"]); - - if ($mark == "1") { - $mark = "true"; - } else { - $mark = "false"; - } + $mark = clean($_REQUEST["mark"]); + $id = clean($_REQUEST["id"]); - $this->dbh->query("UPDATE ttrss_user_entries SET marked = $mark, + $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET marked = ?, last_marked = NOW() - WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); + WHERE ref_id = ? AND owner_uid = ?"); + + $sth->execute([$mark, $id, $_SESSION['uid']]); print json_encode(array("message" => "UPDATE_COUNTERS")); } function delete() { - $ids = $this->dbh->escape_string($_REQUEST["ids"]); + $ids = explode(",", clean($_REQUEST["ids"])); + $ids_qmarks = arr_qmarks($ids); - $this->dbh->query("DELETE FROM ttrss_user_entries - WHERE ref_id IN ($ids) AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("DELETE FROM ttrss_user_entries + WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?"); + $sth->execute(array_merge($ids, [$_SESSION['uid']])); Article::purge_orphans(); @@ -157,64 +150,60 @@ class RPC extends Handler_Protected { } function unarchive() { - $ids = explode(",", $_REQUEST["ids"]); + $ids = explode(",", clean($_REQUEST["ids"])); foreach ($ids as $id) { - $id = $this->dbh->escape_string(trim($id)); - $this->dbh->query("BEGIN"); - - $result = $this->dbh->query("SELECT feed_url,site_url,title FROM ttrss_archived_feeds - WHERE id = (SELECT orig_feed_id FROM ttrss_user_entries WHERE ref_id = $id - AND owner_uid = ".$_SESSION["uid"].") AND owner_uid = " . $_SESSION["uid"]); + $this->pdo->beginTransaction(); - if ($this->dbh->num_rows($result) != 0) { - $feed_url = $this->dbh->escape_string(db_fetch_result($result, 0, "feed_url")); - $site_url = $this->dbh->escape_string(db_fetch_result($result, 0, "site_url")); - $title = $this->dbh->escape_string(db_fetch_result($result, 0, "title")); + $sth = $this->pdo->prepare("SELECT feed_url,site_url,title FROM ttrss_archived_feeds + WHERE id = (SELECT orig_feed_id FROM ttrss_user_entries WHERE ref_id = :id + AND owner_uid = :uid) AND owner_uid = :uid"); + $sth->execute([":uid" => $_SESSION['uid'], ":id" => $id]); - $result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE feed_url = '$feed_url' - AND owner_uid = " .$_SESSION["uid"]); + if ($row = $sth->fetch()) { + $feed_url = $row['feed_url']; + $site_url = $row['site_url']; + $title = $row['title']; - if ($this->dbh->num_rows($result) == 0) { + $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE feed_url = ? + AND owner_uid = ?"); + $sth->execute([$feed_url, $_SESSION['uid']]); + if ($row = $sth->fetch()) { + $feed_id = $row["id"]; + } else { if (!$title) $title = '[Unknown]'; - $result = $this->dbh->query( - "INSERT INTO ttrss_feeds + $sth = $this->pdo->prepare("INSERT INTO ttrss_feeds (owner_uid,feed_url,site_url,title,cat_id,auth_login,auth_pass,update_method) - VALUES (".$_SESSION["uid"].", - '$feed_url', - '$site_url', - '$title', - NULL, '', '', 0)"); - - $result = $this->dbh->query( - "SELECT id FROM ttrss_feeds WHERE feed_url = '$feed_url' - AND owner_uid = ".$_SESSION["uid"]); - - if ($this->dbh->num_rows($result) != 0) { - $feed_id = $this->dbh->fetch_result($result, 0, "id"); - } + VALUES (?, ?, ?, ?, NULL, '', '', 0)"); + $sth->execute([$_SESSION['uid'], $feed_url, $site_url, $title]); - } else { - $feed_id = $this->dbh->fetch_result($result, 0, "id"); + $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE feed_url = ? + AND owner_uid = ?"); + $sth->execute([$feed_url, $_SESSION['uid']]); + + if ($row = $sth->fetch()) { + $feed_id = $row['id']; + } } if ($feed_id) { - $result = $this->dbh->query("UPDATE ttrss_user_entries - SET feed_id = '$feed_id', orig_feed_id = NULL - WHERE ref_id = $id AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_user_entries + SET feed_id = ?, orig_feed_id = NULL + WHERE ref_id = ? AND owner_uid = ?"); + $sth->execute([$feed_id, $id, $_SESSION['uid']]); } } - $this->dbh->query("COMMIT"); + $this->pdo->commit(); } print json_encode(array("message" => "UPDATE_COUNTERS")); } function archive() { - $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); + $ids = explode(",", clean($_REQUEST["ids"])); foreach ($ids as $id) { $this->archive_article($id, $_SESSION["uid"]); @@ -224,61 +213,64 @@ class RPC extends Handler_Protected { } private function archive_article($id, $owner_uid) { - $this->dbh->query("BEGIN"); + $this->pdo->beginTransaction(); - $result = $this->dbh->query("SELECT feed_id FROM ttrss_user_entries - WHERE ref_id = '$id' AND owner_uid = $owner_uid"); + if (!$owner_uid) $owner_uid = $_SESSION['uid']; - if ($this->dbh->num_rows($result) != 0) { + $sth = $this->pdo->prepare("SELECT feed_id FROM ttrss_user_entries + WHERE ref_id = ? AND owner_uid = ?"); + $sth->execute([$id, $owner_uid]); + + if ($row = $sth->fetch()) { /* prepare the archived table */ - $feed_id = (int) $this->dbh->fetch_result($result, 0, "feed_id"); + $feed_id = (int) $row['feed_id']; if ($feed_id) { - $result = $this->dbh->query("SELECT id FROM ttrss_archived_feeds - WHERE id = '$feed_id' AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("SELECT id FROM ttrss_archived_feeds + WHERE id = ? AND owner_uid = ?"); + $sth->execute([$feed_id, $owner_uid]); - if ($this->dbh->num_rows($result) == 0) { - $result = db_query("SELECT MAX(id) AS id FROM ttrss_archived_feeds"); - $new_feed_id = (int)db_fetch_result($result, 0, "id") + 1; + if ($row = $sth->fetch()) { + $new_feed_id = $row['id']; + } else { + $row = $this->pdo->query("SELECT MAX(id) AS id FROM ttrss_archived_feeds")->fetch(); + $new_feed_id = (int)$row['id'] + 1; - $this->dbh->query("INSERT INTO ttrss_archived_feeds + $sth = $this->pdo->prepare("INSERT INTO ttrss_archived_feeds (id, owner_uid, title, feed_url, site_url) - SELECT $new_feed_id, owner_uid, title, feed_url, site_url from ttrss_feeds - WHERE id = '$feed_id'"); - } else { - $new_feed_id = $this->dbh->fetch_result($result, 0, "id"); + SELECT ?, owner_uid, title, feed_url, site_url from ttrss_feeds + WHERE id = ?"); + + $sth->execute([$new_feed_id, $feed_id]); } - $this->dbh->query("UPDATE ttrss_user_entries - SET orig_feed_id = $new_feed_id, feed_id = NULL - WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_user_entries + SET orig_feed_id = ?, feed_id = NULL + WHERE ref_id = ? AND owner_uid = ?"); + $sth->execute([$new_feed_id, $id, $owner_uid]); } } - $this->dbh->query("COMMIT"); + $this->pdo->commit(); } function publ() { - $pub = $_REQUEST["pub"]; - $id = $this->dbh->escape_string($_REQUEST["id"]); + $pub = clean($_REQUEST["pub"]); + $id = clean($_REQUEST["id"]); - if ($pub == "1") { - $pub = "true"; - } else { - $pub = "false"; - } + $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET + published = ?, last_published = NOW() + WHERE ref_id = ? AND owner_uid = ?"); - $this->dbh->query("UPDATE ttrss_user_entries SET - published = $pub, last_published = NOW() - WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); + $sth->execute([$pub, $id, $_SESSION['uid']]); print json_encode(array("message" => "UPDATE_COUNTERS")); } function getAllCounters() { - $last_article_id = (int) $_REQUEST["last_article_id"]; + $last_article_id = (int) clean($_REQUEST["last_article_id"]); $reply = array(); @@ -295,8 +287,8 @@ class RPC extends Handler_Protected { /* GET["cmode"] = 0 - mark as read, 1 - as unread, 2 - toggle */ function catchupSelected() { - $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); - $cmode = sprintf("%d", $_REQUEST["cmode"]); + $ids = explode(",", clean($_REQUEST["ids"])); + $cmode = sprintf("%d", clean($_REQUEST["cmode"])); Article::catchupArticlesById($ids, $cmode); @@ -304,8 +296,8 @@ class RPC extends Handler_Protected { } function markSelected() { - $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); - $cmode = sprintf("%d", $_REQUEST["cmode"]); + $ids = explode(",", clean($_REQUEST["ids"])); + $cmode = (int)clean($_REQUEST["cmode"]); $this->markArticlesById($ids, $cmode); @@ -313,8 +305,8 @@ class RPC extends Handler_Protected { } function publishSelected() { - $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); - $cmode = sprintf("%d", $_REQUEST["cmode"]); + $ids = explode(",", clean($_REQUEST["ids"])); + $cmode = (int)clean($_REQUEST["cmode"]); $this->publishArticlesById($ids, $cmode); @@ -322,10 +314,10 @@ class RPC extends Handler_Protected { } function sanityCheck() { - $_SESSION["hasAudio"] = $_REQUEST["hasAudio"] === "true"; - $_SESSION["hasSandbox"] = $_REQUEST["hasSandbox"] === "true"; - $_SESSION["hasMp3"] = $_REQUEST["hasMp3"] === "true"; - $_SESSION["clientTzOffset"] = $_REQUEST["clientTzOffset"]; + $_SESSION["hasAudio"] = clean($_REQUEST["hasAudio"]) === "true"; + $_SESSION["hasSandbox"] = clean($_REQUEST["hasSandbox"]) === "true"; + $_SESSION["hasMp3"] = clean($_REQUEST["hasMp3"]) === "true"; + $_SESSION["clientTzOffset"] = clean($_REQUEST["clientTzOffset"]); $reply = array(); @@ -340,42 +332,28 @@ class RPC extends Handler_Protected { } function completeLabels() { - $search = $this->dbh->escape_string($_REQUEST["search"]); + $search = clean($_REQUEST["search"]); - $result = $this->dbh->query("SELECT DISTINCT caption FROM + $sth = $this->pdo->prepare("SELECT DISTINCT caption FROM ttrss_labels2 - WHERE owner_uid = '".$_SESSION["uid"]."' AND - LOWER(caption) LIKE LOWER('$search%') ORDER BY caption + WHERE owner_uid = ? AND + LOWER(caption) LIKE LOWER(?) ORDER BY caption LIMIT 5"); + $sth->execute([$_SESSION['uid'], "%$search%"]); print "<ul>"; - while ($line = $this->dbh->fetch_assoc($result)) { + while ($line = $sth->fetch()) { print "<li>" . $line["caption"] . "</li>"; } print "</ul>"; } - function purge() { - $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); - $days = sprintf("%d", $_REQUEST["days"]); - - foreach ($ids as $id) { - - $result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE - id = '$id' AND owner_uid = ".$_SESSION["uid"]); - - if ($this->dbh->num_rows($result) == 1) { - purge_feed($id, $days); - } - } - } - function updateFeedBrowser() { if (defined('_DISABLE_FEED_BROWSER') && _DISABLE_FEED_BROWSER) return; - $search = $this->dbh->escape_string($_REQUEST["search"]); - $limit = $this->dbh->escape_string($_REQUEST["limit"]); - $mode = (int) $this->dbh->escape_string($_REQUEST["mode"]); + $search = clean($_REQUEST["search"]); + $limit = clean($_REQUEST["limit"]); + $mode = (int) clean($_REQUEST["mode"]); require_once "feedbrowser.php"; @@ -387,46 +365,51 @@ class RPC extends Handler_Protected { // Silent function massSubscribe() { - $payload = json_decode($_REQUEST["payload"], false); - $mode = $_REQUEST["mode"]; + $payload = json_decode(clean($_REQUEST["payload"]), false); + $mode = clean($_REQUEST["mode"]); if (!$payload || !is_array($payload)) return; if ($mode == 1) { foreach ($payload as $feed) { - $title = $this->dbh->escape_string($feed[0]); - $feed_url = $this->dbh->escape_string($feed[1]); + $title = $feed[0]; + $feed_url = $feed[1]; - $result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE - feed_url = '$feed_url' AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE + feed_url = ? AND owner_uid = ?"); + $sth->execute([$feed_url, $_SESSION['uid']]); - if ($this->dbh->num_rows($result) == 0) { - $result = $this->dbh->query("INSERT INTO ttrss_feeds + if (!$sth->fetch()) { + $sth = $this->pdo->prepare("INSERT INTO ttrss_feeds (owner_uid,feed_url,title,cat_id,site_url) - VALUES ('".$_SESSION["uid"]."', - '$feed_url', '$title', NULL, '')"); + VALUES (?, ?, ?, NULL, '')"); + + $sth->execute([$_SESSION['uid'], $feed_url, $title]); } } } else if ($mode == 2) { // feed archive foreach ($payload as $id) { - $result = $this->dbh->query("SELECT * FROM ttrss_archived_feeds - WHERE id = '$id' AND owner_uid = " . $_SESSION["uid"]); - - if ($this->dbh->num_rows($result) != 0) { - $site_url = $this->dbh->escape_string(db_fetch_result($result, 0, "site_url")); - $feed_url = $this->dbh->escape_string(db_fetch_result($result, 0, "feed_url")); - $title = $this->dbh->escape_string(db_fetch_result($result, 0, "title")); - - $result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE - feed_url = '$feed_url' AND owner_uid = " . $_SESSION["uid"]); - - if ($this->dbh->num_rows($result) == 0) { - $result = $this->dbh->query("INSERT INTO ttrss_feeds - (owner_uid,feed_url,title,cat_id,site_url) - VALUES ('".$_SESSION["uid"]."', - '$feed_url', '$title', NULL, '$site_url')"); + $sth = $this->pdo->prepare("SELECT * FROM ttrss_archived_feeds + WHERE id = ? AND owner_uid = ?"); + $sth->execute([$id, $_SESSION['uid']]); + + if ($row = $sth->fetch()) { + $site_url = $row['site_url']; + $feed_url = $row['feed_url']; + $title = $row['title']; + + $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE + feed_url = ? AND owner_uid = ?"); + $sth->execute([$feed_url, $_SESSION['uid']]); + + if (!$sth->fetch()) { + $sth = $this->pdo->prepare("INSERT INTO ttrss_feeds + (owner_uid,feed_url,title,cat_id,site_url) + VALUES (?, ?, ?, NULL, ?)"); + + $sth->execute([$_SESSION['uid'], $feed_url, $title, $site_url]); } } } @@ -434,36 +417,19 @@ class RPC extends Handler_Protected { } function catchupFeed() { - $feed_id = $this->dbh->escape_string($_REQUEST['feed_id']); - $is_cat = $this->dbh->escape_string($_REQUEST['is_cat']) == "true"; - $mode = $this->dbh->escape_string($_REQUEST['mode']); - $search_query = $this->dbh->escape_string($_REQUEST['search_query']); - $search_lang = $this->dbh->escape_string($_REQUEST['search_lang']); + $feed_id = clean($_REQUEST['feed_id']); + $is_cat = clean($_REQUEST['is_cat']) == "true"; + $mode = clean($_REQUEST['mode']); + $search_query = clean($_REQUEST['search_query']); + $search_lang = clean($_REQUEST['search_lang']); Feeds::catchup_feed($feed_id, $is_cat, false, $mode, [$search_query, $search_lang]); print json_encode(array("message" => "UPDATE_COUNTERS")); } - function quickAddCat() { - $cat = $this->dbh->escape_string($_REQUEST["cat"]); - - add_feed_category($cat); - - $result = $this->dbh->query("SELECT id FROM ttrss_feed_categories WHERE - title = '$cat' AND owner_uid = " . $_SESSION["uid"]); - - if ($this->dbh->num_rows($result) == 1) { - $id = $this->dbh->fetch_result($result, 0, "id"); - } else { - $id = 0; - } - - print_feed_cat_select("cat_id", $id, ''); - } - function setpanelmode() { - $wide = (int) $_REQUEST["wide"]; + $wide = (int) clean($_REQUEST["wide"]); setcookie("ttrss_widescreen", $wide, time() + COOKIE_LIFETIME_LONG); @@ -471,7 +437,7 @@ class RPC extends Handler_Protected { print json_encode(array("wide" => $wide)); } - static function updaterandomfeed_real($dbh) { + static function updaterandomfeed_real() { // Test if the feed need a update (update interval exceded). if (DB_TYPE == "pgsql") { @@ -503,15 +469,17 @@ class RPC extends Handler_Protected { $random_qpart = sql_random_function(); + $pdo = Db::pdo(); + // we could be invoked from public.php with no active session if ($_SESSION["uid"]) { - $owner_check_qpart = "AND ttrss_feeds.owner_uid = '".$_SESSION["uid"]."'"; + $owner_check_qpart = "AND ttrss_feeds.owner_uid = ".$pdo->quote($_SESSION["uid"]); } else { $owner_check_qpart = ""; } // We search for feed needing update. - $result = $dbh->query("SELECT ttrss_feeds.feed_url,ttrss_feeds.id + $res = $pdo->query("SELECT ttrss_feeds.feed_url,ttrss_feeds.id FROM ttrss_feeds, ttrss_users, ttrss_user_prefs WHERE @@ -523,13 +491,11 @@ class RPC extends Handler_Protected { $updstart_thresh_qpart ORDER BY $random_qpart LIMIT 30"); - $feed_id = -1; - $num_updated = 0; $tstart = time(); - while ($line = $dbh->fetch_assoc($result)) { + while ($line = $res->fetch()) { $feed_id = $line["id"]; if (time() - $tstart < ini_get("max_execution_time") * 0.7) { @@ -554,68 +520,61 @@ class RPC extends Handler_Protected { } function updaterandomfeed() { - RPC::updaterandomfeed_real($this->dbh); + RPC::updaterandomfeed_real(); } private function markArticlesById($ids, $cmode) { - $tmp_ids = array(); - - foreach ($ids as $id) { - array_push($tmp_ids, "ref_id = '$id'"); - } - - $ids_qpart = join(" OR ", $tmp_ids); + $ids_qmarks = arr_qmarks($ids); if ($cmode == 0) { - $this->dbh->query("UPDATE ttrss_user_entries SET - marked = false, last_marked = NOW() - WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET + marked = false, last_marked = NOW() + WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?"); } else if ($cmode == 1) { - $this->dbh->query("UPDATE ttrss_user_entries SET - marked = true, last_marked = NOW() - WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET + marked = true, last_marked = NOW() + WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?"); } else { - $this->dbh->query("UPDATE ttrss_user_entries SET - marked = NOT marked,last_marked = NOW() - WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET + marked = NOT marked,last_marked = NOW() + WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?"); } + + $sth->execute(array_merge($ids, [$_SESSION['uid']])); } private function publishArticlesById($ids, $cmode) { - $tmp_ids = array(); - - foreach ($ids as $id) { - array_push($tmp_ids, "ref_id = '$id'"); - } - - $ids_qpart = join(" OR ", $tmp_ids); + $ids_qmarks = arr_qmarks($ids); if ($cmode == 0) { - $this->dbh->query("UPDATE ttrss_user_entries SET - published = false,last_published = NOW() - WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET + published = false, last_published = NOW() + WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?"); } else if ($cmode == 1) { - $this->dbh->query("UPDATE ttrss_user_entries SET - published = true,last_published = NOW() - WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET + published = true, last_published = NOW() + WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?"); } else { - $this->dbh->query("UPDATE ttrss_user_entries SET - published = NOT published,last_published = NOW() - WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); + $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET + published = NOT published,last_published = NOW() + WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?"); } + + $sth->execute(array_merge($ids, [$_SESSION['uid']])); } function getlinktitlebyid() { - $id = $this->dbh->escape_string($_REQUEST['id']); + $id = clean($_REQUEST['id']); - $result = $this->dbh->query("SELECT link, title FROM ttrss_entries, ttrss_user_entries - WHERE ref_id = '$id' AND ref_id = id AND owner_uid = ". $_SESSION["uid"]); + $sth = $this->pdo->prepare("SELECT link, title FROM ttrss_entries, ttrss_user_entries + WHERE ref_id = ? AND ref_id = id AND owner_uid = ?"); + $sth->execute([$id, $_SESSION['uid']]); - if ($this->dbh->num_rows($result) != 0) { - $link = $this->dbh->fetch_result($result, 0, "link"); - $title = $this->dbh->fetch_result($result, 0, "title"); + if ($row = $sth->fetch()) { + $link = $row['link']; + $title = $row['title']; echo json_encode(array("link" => $link, "title" => $title)); } else { @@ -624,10 +583,10 @@ class RPC extends Handler_Protected { } function log() { - $msg = $this->dbh->escape_string($_REQUEST['msg']); - $file = $this->dbh->escape_string(basename($_REQUEST['file'])); - $line = (int) $_REQUEST['line']; - $context = $this->dbh->escape_string($_REQUEST['context']); + $msg = clean($_REQUEST['msg']); + $file = basename(clean($_REQUEST['file'])); + $line = (int) clean($_REQUEST['line']); + $context = clean($_REQUEST['context']); if ($msg) { Logger::get()->log_error(E_USER_WARNING, diff --git a/classes/rssutils.php b/classes/rssutils.php index f6326720d..613947afb 100644 --- a/classes/rssutils.php +++ b/classes/rssutils.php @@ -18,31 +18,38 @@ class RSSUtils { static function update_feedbrowser_cache() { - $result = db_query("SELECT feed_url, site_url, title, COUNT(id) AS subscribers + $pdo = Db::pdo(); + + $sth = $pdo->query("SELECT feed_url, site_url, title, COUNT(id) AS subscribers FROM ttrss_feeds WHERE feed_url NOT IN (SELECT feed_url FROM ttrss_feeds WHERE private IS true OR auth_login != '' OR auth_pass != '' OR feed_url LIKE '%:%@%/%') GROUP BY feed_url, site_url, title ORDER BY subscribers DESC LIMIT 1000"); - db_query("BEGIN"); + $pdo->beginTransaction(); - db_query("DELETE FROM ttrss_feedbrowser_cache"); + $pdo->query("DELETE FROM ttrss_feedbrowser_cache"); $count = 0; - while ($line = db_fetch_assoc($result)) { - $subscribers = db_escape_string($line["subscribers"]); - $feed_url = db_escape_string($line["feed_url"]); - $title = db_escape_string($line["title"]); - $site_url = db_escape_string($line["site_url"]); + while ($line = $sth->fetch()) { + + $subscribers = $line["subscribers"]; + $feed_url = $line["feed_url"]; + $title = $line["title"]; + $site_url = $line["site_url"]; - $tmp_result = db_query("SELECT subscribers FROM - ttrss_feedbrowser_cache WHERE feed_url = '$feed_url'"); + $tmph = $pdo->prepare("SELECT subscribers FROM + ttrss_feedbrowser_cache WHERE feed_url = ?"); + $tmph->execute([$feed_url]); - if (db_num_rows($tmp_result) == 0) { + if (!$tmph->fetch()) { - db_query("INSERT INTO ttrss_feedbrowser_cache - (feed_url, site_url, title, subscribers) VALUES ('$feed_url', - '$site_url', '$title', '$subscribers')"); + $tmph = $pdo->prepare("INSERT INTO ttrss_feedbrowser_cache + (feed_url, site_url, title, subscribers) + VALUES + (?, ?, ?, ?)"); + + $tmph->execute([$feed_url, $site_url, $title, $subscribers]); ++$count; @@ -50,7 +57,7 @@ class RSSUtils { } - db_query("COMMIT"); + $pdo->commit(); return $count; @@ -63,6 +70,8 @@ class RSSUtils { die("Schema version is wrong, please upgrade the database.\n"); } + $pdo = Db::pdo(); + if (!SINGLE_USER_MODE && DAEMON_UPDATE_LOGIN_LIMIT > 0) { if (DB_TYPE == "pgsql") { $login_thresh_qpart = "AND ttrss_users.last_login >= NOW() - INTERVAL '".DAEMON_UPDATE_LOGIN_LIMIT." days'"; @@ -124,22 +133,23 @@ class RSSUtils { $updstart_thresh_qpart $query_order $query_limit"; - $result = db_query($query); - - if ($debug) _debug(sprintf("Scheduled %d feeds to update...", db_num_rows($result))); + $res = $pdo->query($query); $feeds_to_update = array(); - while ($line = db_fetch_assoc($result)) { + while ($line = $res->fetch()) { array_push($feeds_to_update, $line['feed_url']); } + if ($debug) _debug(sprintf("Scheduled %d feeds to update...", count($feeds_to_update))); + // Update last_update_started before actually starting the batch // in order to minimize collision risk for parallel daemon tasks if (count($feeds_to_update) > 0) { - $feeds_quoted = array_map(function ($s) { return "'" . db_escape_string($s) . "'"; }, $feeds_to_update); + $feeds_qmarks = arr_qmarks($feeds_to_update); - db_query(sprintf("UPDATE ttrss_feeds SET last_update_started = NOW() - WHERE feed_url IN (%s)", implode(',', $feeds_quoted))); + $tmph = $pdo->prepare("UPDATE ttrss_feeds SET last_update_started = NOW() + WHERE feed_url IN ($feeds_qmarks)"); + $tmph->execute($feeds_to_update); } $nf = 0; @@ -147,38 +157,37 @@ class RSSUtils { $batch_owners = array(); - foreach ($feeds_to_update as $feed) { - if($debug) _debug("Base feed: $feed"); - - //update_rss_feed($line["id"], true); - - // since we have the data cached, we can deal with other feeds with the same url - $tmp_result = db_query("SELECT DISTINCT ttrss_feeds.id,last_updated,ttrss_feeds.owner_uid + // since we have the data cached, we can deal with other feeds with the same url + $usth = $pdo->prepare("SELECT DISTINCT ttrss_feeds.id,last_updated,ttrss_feeds.owner_uid FROM ttrss_feeds, ttrss_users, ttrss_user_prefs WHERE ttrss_user_prefs.owner_uid = ttrss_feeds.owner_uid AND ttrss_users.id = ttrss_user_prefs.owner_uid AND ttrss_user_prefs.pref_name = 'DEFAULT_UPDATE_INTERVAL' AND ttrss_user_prefs.profile IS NULL AND - feed_url = '".db_escape_string($feed)."' + feed_url = ? $update_limit_qpart $login_thresh_qpart ORDER BY ttrss_feeds.id $query_limit"); - if (db_num_rows($tmp_result) > 0) { - while ($tline = db_fetch_assoc($tmp_result)) { - if ($debug) _debug(" => " . $tline["last_updated"] . ", " . $tline["id"] . " " . $tline["owner_uid"]); + foreach ($feeds_to_update as $feed) { + if($debug) _debug("Base feed: $feed"); - if (array_search($tline["owner_uid"], $batch_owners) === FALSE) - array_push($batch_owners, $tline["owner_uid"]); + $usth->execute([$feed]); + //update_rss_feed($line["id"], true); - $fstarted = microtime(true); - RSSUtils::update_rss_feed($tline["id"], true, false); - _debug_suppress(false); + if ($tline = $usth->fetch()) { + if ($debug) _debug(" => " . $tline["last_updated"] . ", " . $tline["id"] . " " . $tline["owner_uid"]); - _debug(sprintf(" %.4f (sec)", microtime(true) - $fstarted)); + if (array_search($tline["owner_uid"], $batch_owners) === FALSE) + array_push($batch_owners, $tline["owner_uid"]); - ++$nf; - } + $fstarted = microtime(true); + RSSUtils::update_rss_feed($tline["id"], true, false); + _debug_suppress(false); + + _debug(sprintf(" %.4f (sec)", microtime(true) - $fstarted)); + + ++$nf; } } @@ -197,85 +206,95 @@ class RSSUtils { Digest::send_headlines_digests($debug); return $nf; - } // this is used when subscribing static function set_basic_feed_info($feed) { - $feed = db_escape_string($feed); + $pdo = Db::pdo(); - $result = db_query("SELECT owner_uid,feed_url,auth_pass,auth_login,auth_pass_encrypted - FROM ttrss_feeds WHERE id = '$feed'"); + $sth = $pdo->prepare("SELECT owner_uid,feed_url,auth_pass,auth_login,auth_pass_encrypted + FROM ttrss_feeds WHERE id = ?"); + $sth->execute([$feed]); - $owner_uid = db_fetch_result($result, 0, "owner_uid"); + if ($row = $sth->fetch()) { - $auth_pass_encrypted = sql_bool_to_bool(db_fetch_result($result, - 0, "auth_pass_encrypted")); + $owner_uid = $row["owner_uid"]; - $auth_login = db_fetch_result($result, 0, "auth_login"); - $auth_pass = db_fetch_result($result, 0, "auth_pass"); + $auth_pass_encrypted = $row["auth_pass_encrypted"]; - if ($auth_pass_encrypted && function_exists("mcrypt_decrypt")) { - require_once "crypt.php"; - $auth_pass = decrypt_string($auth_pass); - } + $auth_login = $row["auth_login"]; + $auth_pass = $row["auth_pass"]; - $fetch_url = db_fetch_result($result, 0, "feed_url"); + if ($auth_pass_encrypted && function_exists("mcrypt_decrypt")) { + require_once "crypt.php"; + $auth_pass = decrypt_string($auth_pass); + } - $pluginhost = new PluginHost(); - $user_plugins = get_pref("_ENABLED_PLUGINS", $owner_uid); + $fetch_url = $row["feed_url"]; - $pluginhost->load(PLUGINS, PluginHost::KIND_ALL); - $pluginhost->load($user_plugins, PluginHost::KIND_USER, $owner_uid); - $pluginhost->load_data(); + $pluginhost = new PluginHost(); + $user_plugins = get_pref("_ENABLED_PLUGINS", $owner_uid); - $basic_info = array(); - foreach ($pluginhost->get_hooks(PluginHost::HOOK_FEED_BASIC_INFO) as $plugin) { - $basic_info = $plugin->hook_feed_basic_info($basic_info, $fetch_url, $owner_uid, $feed, $auth_login, $auth_pass); - } + $pluginhost->load(PLUGINS, PluginHost::KIND_ALL); + $pluginhost->load($user_plugins, PluginHost::KIND_USER, $owner_uid); + $pluginhost->load_data(); - if (!$basic_info) { - $feed_data = fetch_file_contents($fetch_url, false, - $auth_login, $auth_pass, false, - FEED_FETCH_TIMEOUT, - 0); + $basic_info = array(); + foreach ($pluginhost->get_hooks(PluginHost::HOOK_FEED_BASIC_INFO) as $plugin) { + $basic_info = $plugin->hook_feed_basic_info($basic_info, $fetch_url, $owner_uid, $feed, $auth_login, $auth_pass); + } - global $fetch_curl_used; + if (!$basic_info) { + $feed_data = fetch_file_contents($fetch_url, false, + $auth_login, $auth_pass, false, + FEED_FETCH_TIMEOUT, + 0); - if (!$fetch_curl_used) { - $tmp = @gzdecode($feed_data); + global $fetch_curl_used; - if ($tmp) $feed_data = $tmp; - } + if (!$fetch_curl_used) { + $tmp = @gzdecode($feed_data); - $feed_data = trim($feed_data); + if ($tmp) $feed_data = $tmp; + } - $rss = new FeedParser($feed_data); - $rss->init(); + $feed_data = trim($feed_data); - if (!$rss->error()) { - $basic_info = array( - 'title' => db_escape_string(mb_substr($rss->get_title(), 0, 199)), - 'site_url' => db_escape_string(mb_substr(rewrite_relative_url($fetch_url, $rss->get_link()), 0, 245)) - ); + $rss = new FeedParser($feed_data); + $rss->init(); + + if (!$rss->error()) { + $basic_info = array( + 'title' => mb_substr($rss->get_title(), 0, 199), + 'site_url' => mb_substr(rewrite_relative_url($fetch_url, $rss->get_link()), 0, 245) + ); + } } - } - if ($basic_info && is_array($basic_info)) { - $result = db_query("SELECT title, site_url FROM ttrss_feeds WHERE id = '$feed'"); + if ($basic_info && is_array($basic_info)) { + $sth = $pdo->prepare("SELECT title, site_url FROM ttrss_feeds WHERE id = ?"); + $sth->execute([$feed]); - $registered_title = db_fetch_result($result, 0, "title"); - $orig_site_url = db_fetch_result($result, 0, "site_url"); + if ($row = $sth->fetch()) { - if ($basic_info['title'] && (!$registered_title || $registered_title == "[Unknown]")) { - db_query("UPDATE ttrss_feeds SET - title = '${basic_info['title']}' WHERE id = '$feed'"); - } + $registered_title = $row["title"]; + $orig_site_url = $row["site_url"]; + + if ($basic_info['title'] && (!$registered_title || $registered_title == "[Unknown]")) { + + $sth = $pdo->prepare("UPDATE ttrss_feeds SET + title = ? WHERE id = ?"); + $sth->execute([$basic_info['title'], $feed]); + } + + if ($basic_info['site_url'] && $orig_site_url != $basic_info['site_url']) { + $sth = $pdo->prepare("UPDATE ttrss_feeds SET + site_url = ? WHERE id = ?"); + $sth->execute([$basic_info['site_url'], $feed]); + } - if ($basic_info['site_url'] && $orig_site_url != $basic_info['site_url']) { - db_query("UPDATE ttrss_feeds SET - site_url = '${basic_info['site_url']}' WHERE id = '$feed'"); + } } } } @@ -285,21 +304,23 @@ class RSSUtils { */ static function update_rss_feed($feed, $no_cache = false) { - $debug_enabled = defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']; + $debug_enabled = defined('DAEMON_EXTENDED_DEBUG') || clean($_REQUEST['xdebug']); _debug_suppress(!$debug_enabled); _debug("start", $debug_enabled); - $result = db_query("SELECT title FROM ttrss_feeds - WHERE id = '$feed'"); + $pdo = Db::pdo(); + + $sth = $pdo->prepare("SELECT title FROM ttrss_feeds WHERE id = ?"); + $sth->execute([$feed]); - if (db_num_rows($result) == 0) { + if (!$row = $sth->fetch()) { _debug("feed $feed NOT FOUND/SKIPPED", $debug_enabled); user_error("Attempt to update unknown/invalid feed $feed", E_USER_WARNING); return false; } - $title = db_fetch_result($result, 0, "title"); + $title = $row["title"]; // feed was batch-subscribed or something, we need to get basic info // this is not optimal currently as it fetches stuff separately TODO: optimize @@ -308,43 +329,47 @@ class RSSUtils { RSSUtils::set_basic_feed_info($feed); } - $result = db_query("SELECT id,update_interval,auth_login, + $sth = $pdo->prepare("SELECT id,update_interval,auth_login, feed_url,auth_pass,cache_images, mark_unread_on_update, owner_uid, auth_pass_encrypted, feed_language, last_modified, ".SUBSTRING_FOR_DATE."(last_unconditional, 1, 19) AS last_unconditional - FROM ttrss_feeds WHERE id = '$feed'"); + FROM ttrss_feeds WHERE id = ?"); + $sth->execute([$feed]); + + if ($row = $sth->fetch()) { - $owner_uid = db_fetch_result($result, 0, "owner_uid"); - $mark_unread_on_update = sql_bool_to_bool(db_fetch_result($result, - 0, "mark_unread_on_update")); - $auth_pass_encrypted = sql_bool_to_bool(db_fetch_result($result, - 0, "auth_pass_encrypted")); + $owner_uid = $row["owner_uid"]; + $mark_unread_on_update = $row["mark_unread_on_update"]; + $auth_pass_encrypted = $row["auth_pass_encrypted"]; - db_query("UPDATE ttrss_feeds SET last_update_started = NOW() - WHERE id = '$feed'"); + $sth = $pdo->prepare("UPDATE ttrss_feeds SET last_update_started = NOW() + WHERE id = ?"); + $sth->execute([$feed]); - $auth_login = db_fetch_result($result, 0, "auth_login"); - $auth_pass = db_fetch_result($result, 0, "auth_pass"); + $auth_login = $row["auth_login"]; + $auth_pass = $row["auth_pass"]; - if ($auth_pass_encrypted && function_exists("mcrypt_decrypt")) { - require_once "crypt.php"; - $auth_pass = decrypt_string($auth_pass); - } + if ($auth_pass_encrypted && function_exists("mcrypt_decrypt")) { + require_once "crypt.php"; + $auth_pass = decrypt_string($auth_pass); + } - $stored_last_modified = db_fetch_result($result, 0, "last_modified"); - $last_unconditional = db_fetch_result($result, 0, "last_unconditional"); - $cache_images = sql_bool_to_bool(db_fetch_result($result, 0, "cache_images")); - $fetch_url = db_fetch_result($result, 0, "feed_url"); - $feed_language = db_escape_string(mb_strtolower(db_fetch_result($result, 0, "feed_language"))); - if (!$feed_language) $feed_language = 'english'; + $stored_last_modified = $row["last_modified"]; + $last_unconditional = $row["last_unconditional"]; + $cache_images = $row["cache_images"]; + $fetch_url = $row["feed_url"]; + $feed_language = mb_strtolower($row["feed_language"]); + if (!$feed_language) $feed_language = 'english'; - $feed = db_escape_string($feed); + } else { + return false; + } $date_feed_processed = date('Y-m-d H:i'); - $cache_filename = CACHE_DIR . "/simplepie/" . sha1($fetch_url) . ".xml"; + $cache_filename = CACHE_DIR . "/feeds/" . sha1($fetch_url) . ".xml"; $pluginhost = new PluginHost(); $pluginhost->set_debug($debug_enabled); @@ -424,14 +449,12 @@ class RSSUtils { _debug("source last modified: " . $fetch_last_modified, $debug_enabled); if ($feed_data && $fetch_last_modified != $stored_last_modified) { - $last_modified_escaped = db_escape_string(substr($fetch_last_modified, 0, 245)); - - db_query("UPDATE ttrss_feeds SET last_modified = '$last_modified_escaped' WHERE id = '$feed'"); - + $sth = $pdo->prepare("UPDATE ttrss_feeds SET last_modified = ? WHERE id = ?"); + $sth->execute([substr($fetch_last_modified, 0, 245), $feed]); } // cache vanilla feed data for re-use - if ($feed_data && !$auth_pass && !$auth_login && is_writable(CACHE_DIR . "/simplepie")) { + if ($feed_data && !$auth_pass && !$auth_login && is_writable(CACHE_DIR . "/feeds")) { $new_rss_hash = sha1($feed_data); if ($new_rss_hash != $rss_hash) { @@ -447,18 +470,17 @@ class RSSUtils { _debug("unable to fetch: $fetch_last_error [$fetch_last_error_code]", $debug_enabled); - $error_escaped = ''; - // If-Modified-Since if ($fetch_last_error_code != 304) { - $error_escaped = db_escape_string($fetch_last_error); + $error_message = $fetch_last_error; } else { _debug("source claims data not modified, nothing to do.", $debug_enabled); + $error_message = ""; } - db_query( - "UPDATE ttrss_feeds SET last_error = '$error_escaped', - last_updated = NOW() WHERE id = '$feed'"); + $sth = $pdo->prepare("UPDATE ttrss_feeds SET last_error = ?, + last_updated = NOW() WHERE id = ?"); + $sth->execute([$error_message, $feed]); return; } @@ -470,8 +492,6 @@ class RSSUtils { $rss = new FeedParser($feed_data); $rss->init(); - $feed = db_escape_string($feed); - if (!$rss->error()) { // We use local pluginhost here because we need to load different per-user feed plugins @@ -480,26 +500,27 @@ class RSSUtils { _debug("language: $feed_language", $debug_enabled); _debug("processing feed data...", $debug_enabled); -// db_query("BEGIN"); - if (DB_TYPE == "pgsql") { $favicon_interval_qpart = "favicon_last_checked < NOW() - INTERVAL '12 hour'"; } else { $favicon_interval_qpart = "favicon_last_checked < DATE_SUB(NOW(), INTERVAL 12 HOUR)"; } - $result = db_query("SELECT owner_uid,favicon_avg_color, + $sth = $pdo->prepare("SELECT owner_uid,favicon_avg_color, (favicon_last_checked IS NULL OR $favicon_interval_qpart) AS favicon_needs_check - FROM ttrss_feeds WHERE id = '$feed'"); + FROM ttrss_feeds WHERE id = ?"); + $sth->execute([$feed]); - $favicon_needs_check = sql_bool_to_bool(db_fetch_result($result, 0, - "favicon_needs_check")); - $favicon_avg_color = db_fetch_result($result, 0, "favicon_avg_color"); - - $owner_uid = db_fetch_result($result, 0, "owner_uid"); + if ($row = $sth->fetch()) { + $favicon_needs_check = $row["favicon_needs_check"]; + $favicon_avg_color = $row["favicon_avg_color"]; + $owner_uid = $row["owner_uid"]; + } else { + return false; + } - $site_url = db_escape_string(mb_substr(rewrite_relative_url($fetch_url, $rss->get_link()), 0, 245)); + $site_url = mb_substr(rewrite_relative_url($fetch_url, $rss->get_link()), 0, 245); _debug("site_url: $site_url", $debug_enabled); _debug("feed_title: " . $rss->get_title(), $debug_enabled); @@ -520,23 +541,25 @@ class RSSUtils { if ($favicon_modified_new > $favicon_modified) $favicon_avg_color = ''; + $favicon_colorstring = ""; if (file_exists($favicon_file) && function_exists("imagecreatefromstring") && $favicon_avg_color == '') { require_once "colors.php"; - db_query("UPDATE ttrss_feeds SET favicon_avg_color = 'fail' WHERE - id = '$feed'"); + $sth = $pdo->prepare("UPDATE ttrss_feeds SET favicon_avg_color = 'fail' WHERE + id = ?"); + $sth->execute([$feed]); - $favicon_color = db_escape_string( - calculate_avg_color($favicon_file)); + $favicon_color = calculate_avg_color($favicon_file); + + $favicon_colorstring = ",favicon_avg_color = " . $pdo->quote($favicon_color); - $favicon_colorstring = ",favicon_avg_color = '".$favicon_color."'"; } else if ($favicon_avg_color == 'fail') { _debug("floicon failed on this file, not trying to recalculate avg color", $debug_enabled); } - db_query("UPDATE ttrss_feeds SET favicon_last_checked = NOW() - $favicon_colorstring - WHERE id = '$feed'"); + $sth = $pdo->prepare("UPDATE ttrss_feeds SET favicon_last_checked = NOW() + $favicon_colorstring WHERE id = ?"); + $sth->execute([$feed]); } _debug("loading filters & labels...", $debug_enabled); @@ -554,10 +577,11 @@ class RSSUtils { if (!is_array($items)) { _debug("no articles found.", $debug_enabled); - db_query("UPDATE ttrss_feeds - SET last_updated = NOW(), last_unconditional = NOW(), last_error = '' WHERE id = '$feed'"); + $sth = $pdo->prepare("UPDATE ttrss_feeds + SET last_updated = NOW(), last_unconditional = NOW(), last_error = '' WHERE id = ?"); + $sth->execute([$feed]); - return; // no articles + return true; // no articles } _debug("processing articles...", $debug_enabled); @@ -565,29 +589,34 @@ class RSSUtils { $tstart = time(); foreach ($items as $item) { - if ($_REQUEST['xdebug'] == 3) { + $pdo->beginTransaction(); + + if (clean($_REQUEST['xdebug']) == 3) { print_r($item); } if (ini_get("max_execution_time") > 0 && time() - $tstart >= ini_get("max_execution_time") * 0.7) { _debug("looks like there's too many articles to process at once, breaking out", $debug_enabled); + $pdo->commit(); break; } - $entry_guid = $item->get_id(); - if (!$entry_guid) $entry_guid = $item->get_link(); + $entry_guid = strip_tags($item->get_id()); + if (!$entry_guid) $entry_guid = strip_tags($item->get_link()); if (!$entry_guid) $entry_guid = RSSUtils::make_guid_from_title($item->get_title()); - if (!$entry_guid) continue; + + if (!$entry_guid) { + $pdo->commit(); + continue; + } $entry_guid = "$owner_uid,$entry_guid"; - $entry_guid_hashed = db_escape_string('SHA1:' . sha1($entry_guid)); + $entry_guid_hashed = 'SHA1:' . sha1($entry_guid); _debug("guid $entry_guid / $entry_guid_hashed", $debug_enabled); - $entry_timestamp = ""; - - $entry_timestamp = $item->get_date(); + $entry_timestamp = strip_tags($item->get_date()); _debug("orig date: " . $item->get_date(), $debug_enabled); @@ -599,9 +628,7 @@ class RSSUtils { _debug("date $entry_timestamp [$entry_timestamp_fmt]", $debug_enabled); -// $entry_title = html_entity_decode($item->get_title(), ENT_COMPAT, 'UTF-8'); -// $entry_title = decode_numeric_entities($entry_title); - $entry_title = $item->get_title(); + $entry_title = strip_tags($item->get_title()); $entry_link = rewrite_relative_url($site_url, $item->get_link()); @@ -613,17 +640,17 @@ class RSSUtils { $entry_content = $item->get_content(); if (!$entry_content) $entry_content = $item->get_description(); - if ($_REQUEST["xdebug"] == 2) { + if (clean($_REQUEST["xdebug"]) == 2) { print "content: "; print htmlspecialchars($entry_content); print "\n"; } - $entry_comments = db_escape_string(mb_substr($item->get_comments_url(), 0, 245)); + $entry_comments = mb_substr(strip_tags($item->get_comments_url()), 0, 245); $num_comments = (int) $item->get_comments_count(); - $entry_author = $item->get_author(); // escaped later - $entry_guid = db_escape_string(mb_substr($entry_guid, 0, 245)); + $entry_author = strip_tags($item->get_author()); + $entry_guid = mb_substr($entry_guid, 0, 245); _debug("author $entry_author", $debug_enabled); _debug("num_comments: $num_comments", $debug_enabled); @@ -650,18 +677,18 @@ class RSSUtils { _debug("done collecting data.", $debug_enabled); - $result = db_query("SELECT id, content_hash, lang FROM ttrss_entries - WHERE guid = '".db_escape_string($entry_guid)."' OR guid = '$entry_guid_hashed'"); + $sth = $pdo->prepare("SELECT id, content_hash, lang FROM ttrss_entries + WHERE guid = ? OR guid = ?"); + $sth->execute([$entry_guid, $entry_guid_hashed]); - if (db_num_rows($result) != 0) { - $base_entry_id = db_fetch_result($result, 0, "id"); - $entry_stored_hash = db_fetch_result($result, 0, "content_hash"); + if ($row = $sth->fetch()) { + $base_entry_id = $row["id"]; + $entry_stored_hash = $row["content_hash"]; $article_labels = Article::get_article_labels($base_entry_id, $owner_uid); - $entry_language = db_fetch_result($result, 0, "lang"); + $entry_language = $row["lang"]; $existing_tags = Article::get_article_tags($base_entry_id, $owner_uid); $entry_tags = array_unique(array_merge($entry_tags, $existing_tags)); - } else { $base_entry_id = false; $entry_stored_hash = ""; @@ -701,11 +728,11 @@ class RSSUtils { // dupes when the entry gets purged and reinserted again e.g. // in the case of SLOW SLOW OMG SLOW updating feeds - $base_entry_id = db_fetch_result($result, 0, "id"); - - db_query("UPDATE ttrss_entries SET date_updated = NOW() - WHERE id = '$base_entry_id'"); + $sth = $pdo->prepare("UPDATE ttrss_entries SET date_updated = NOW() + WHERE id = ?"); + $sth->execute([$base_entry_id]); + $pdo->commit(); continue; } @@ -722,14 +749,12 @@ class RSSUtils { $entry_plugin_data .= mb_strtolower(get_class($plugin)) . ","; } - if ($_REQUEST["xdebug"] == 2) { + if (clean($_REQUEST["xdebug"]) == 2) { print "processed content: "; print htmlspecialchars($article["content"]); print "\n"; } - $entry_plugin_data = db_escape_string($entry_plugin_data); - _debug("plugin data: $entry_plugin_data", $debug_enabled); // Workaround: 4-byte unicode requires utf8mb4 in MySQL. See https://tt-rss.org/forum/viewtopic.php?f=1&t=3377&p=20077#p20077 @@ -794,15 +819,14 @@ class RSSUtils { } $entry_tags = $article["tags"]; - $entry_guid = db_escape_string($entry_guid); - $entry_title = db_escape_string($article["title"]); - $entry_author = db_escape_string(mb_substr($article["author"], 0, 245)); - $entry_link = db_escape_string($article["link"]); + $entry_title = strip_tags($article["title"]); + $entry_author = mb_substr(strip_tags($article["author"]), 0, 245); + $entry_link = strip_tags($article["link"]); $entry_content = $article["content"]; // escaped below $entry_force_catchup = $article["force_catchup"]; $article_labels = $article["labels"]; $entry_score_modifier = (int) $article["score_modifier"]; - $entry_language = db_escape_string($article["language"]); + $entry_language = $article["language"]; if ($debug_enabled) { _debug("article labels:", $debug_enabled); @@ -817,22 +841,19 @@ class RSSUtils { if ($cache_images && is_writable(CACHE_DIR . '/images')) RSSUtils::cache_media($entry_content, $site_url, $debug_enabled); - $entry_content = db_escape_string($entry_content, false); + $csth = $pdo->prepare("SELECT id FROM ttrss_entries + WHERE guid = ? OR guid = ?"); + $csth->execute([$entry_guid, $entry_guid_hashed]); - //db_query("BEGIN"); - - $result = db_query("SELECT id FROM ttrss_entries - WHERE (guid = '$entry_guid' OR guid = '$entry_guid_hashed')"); - - if (db_num_rows($result) == 0) { + if (!$row = $csth->fetch()) { _debug("base guid [$entry_guid or $entry_guid_hashed] not found, creating...", $debug_enabled); // base post entry does not exist, create it - db_query( + $usth = $pdo->prepare( "INSERT INTO ttrss_entries - (title, + (title, guid, link, updated, @@ -847,48 +868,40 @@ class RSSUtils { lang, author) VALUES - ('$entry_title', - '$entry_guid_hashed', - '$entry_link', - '$entry_timestamp_fmt', - '$entry_content', - '$entry_current_hash', + (?, ?, ?, ?, ?, ?, false, NOW(), - '$date_feed_processed', - '$entry_comments', - '$num_comments', - '$entry_plugin_data', - '$entry_language', - '$entry_author')"); + ?, ?, ?, ?, ?, ?)"); + + $usth->execute([$entry_title, + $entry_guid_hashed, + $entry_link, + $entry_timestamp_fmt, + "$entry_content", + $entry_current_hash, + $date_feed_processed, + $entry_comments, + (int)$num_comments, + $entry_plugin_data, + "$entry_language", + "$entry_author"]); } - // now it should exist, if not - bad luck then - - $result = db_query("SELECT id FROM ttrss_entries - WHERE guid = '$entry_guid' OR guid = '$entry_guid_hashed'"); + $csth->execute([$entry_guid, $entry_guid_hashed]); $entry_ref_id = 0; $entry_int_id = 0; - if (db_num_rows($result) == 1) { + if ($row = $csth->fetch()) { _debug("base guid found, checking for user record", $debug_enabled); - $ref_id = db_fetch_result($result, 0, "id"); + $ref_id = $row['id']; $entry_ref_id = $ref_id; - /* $stored_guid = db_fetch_result($result, 0, "guid"); - if ($stored_guid != $entry_guid_hashed) { - if ($debug_enabled) _debug("upgrading compat guid to hashed one", $debug_enabled); - - db_query("UPDATE ttrss_entries SET guid = '$entry_guid_hashed' WHERE - id = '$ref_id'"); - } */ - if (RSSUtils::find_article_filter($article_filters, "filter")) { - //db_query("COMMIT"); // close transaction in progress + $pdo->commit(); continue; } @@ -898,103 +911,114 @@ class RSSUtils { // check for user post link to main table - $query = "SELECT ref_id, int_id FROM ttrss_user_entries WHERE - ref_id = '$ref_id' AND owner_uid = '$owner_uid'"; - -// if ($_REQUEST["xdebug"]) print "$query\n"; - - $result = db_query($query); + $sth = $pdo->prepare("SELECT ref_id, int_id FROM ttrss_user_entries WHERE + ref_id = ? AND owner_uid = ?"); + $sth->execute([$ref_id, $owner_uid]); // okay it doesn't exist - create user entry - if (db_num_rows($result) == 0) { + if ($row = $sth->fetch()) { + $entry_ref_id = $row["ref_id"]; + $entry_int_id = $row["int_id"]; + _debug("user record FOUND: RID: $entry_ref_id, IID: $entry_int_id", $debug_enabled); + } else { + _debug("user record not found, creating...", $debug_enabled); if ($score >= -500 && !RSSUtils::find_article_filter($article_filters, 'catchup') && !$entry_force_catchup) { - $unread = 'true'; - $last_read_qpart = 'NULL'; + $unread = 1; + $last_read_qpart = null; } else { - $unread = 'false'; + $unread = 0; $last_read_qpart = 'NOW()'; } if (RSSUtils::find_article_filter($article_filters, 'mark') || $score > 1000) { - $marked = 'true'; + $marked = 1; } else { - $marked = 'false'; + $marked = 0; } if (RSSUtils::find_article_filter($article_filters, 'publish')) { - $published = 'true'; + $published = 1; } else { - $published = 'false'; + $published = 0; } - $last_marked = ($marked == 'true') ? 'NOW()' : 'NULL'; - $last_published = ($published == 'true') ? 'NOW()' : 'NULL'; + $last_marked = ($marked == 'true') ? 'NOW()' : null; + $last_published = ($published == 'true') ? 'NOW()' : null; - $result = db_query( + $sth = $pdo->prepare( "INSERT INTO ttrss_user_entries (ref_id, owner_uid, feed_id, unread, last_read, marked, published, score, tag_cache, label_cache, uuid, last_marked, last_published) - VALUES ('$ref_id', '$owner_uid', '$feed', $unread, - $last_read_qpart, $marked, $published, '$score', '', '', - '', $last_marked, $last_published)"); + VALUES (?, ?, ?, ?, ?, ?, ?, ?, '', '', '', ".$last_marked.", ".$last_published.")"); - $result = db_query( - "SELECT int_id FROM ttrss_user_entries WHERE - ref_id = '$ref_id' AND owner_uid = '$owner_uid' AND - feed_id = '$feed' LIMIT 1"); + $sth->execute([$ref_id, $owner_uid, $feed, $unread, $last_read_qpart, $marked, + $published, $score]); - if (db_num_rows($result) == 1) { - $entry_int_id = db_fetch_result($result, 0, "int_id"); - } - } else { - _debug("user record FOUND", $debug_enabled); + $sth = $pdo->prepare("SELECT int_id FROM ttrss_user_entries WHERE + ref_id = ? AND owner_uid = ? AND + feed_id = ? LIMIT 1"); + + $sth->execute([$ref_id, $owner_uid, $feed]); - $entry_ref_id = db_fetch_result($result, 0, "ref_id"); - $entry_int_id = db_fetch_result($result, 0, "int_id"); + if ($row = $sth->fetch()) + $entry_int_id = $row['int_id']; } - _debug("RID: $entry_ref_id, IID: $entry_int_id", $debug_enabled); + _debug("resulting RID: $entry_ref_id, IID: $entry_int_id", $debug_enabled); if (DB_TYPE == "pgsql") { - $tsvector_combined = db_escape_string(mb_substr($entry_title . ' ' . strip_tags(str_replace('<', ' <', $entry_content)), - 0, 1000000)); + $tsvector_combined = mb_substr($entry_title . ' ' . + preg_replace('/[<\?\:]/', ' ', strip_tags($entry_content)), + 0, 1000000); - $tsvector_qpart = "tsvector_combined = to_tsvector('$feed_language', '$tsvector_combined'),"; + $tsvector_qpart = "tsvector_combined = to_tsvector(".$pdo->quote($feed_language).", ".$pdo->quote($tsvector_combined)."),"; } else { $tsvector_qpart = ""; } - db_query("UPDATE ttrss_entries - SET title = '$entry_title', - content = '$entry_content', - content_hash = '$entry_current_hash', - updated = '$entry_timestamp_fmt', + //_debug($tsvector_qpart); + + $sth = $pdo->prepare("UPDATE ttrss_entries + SET title = :title, $tsvector_qpart - num_comments = '$num_comments', - plugin_data = '$entry_plugin_data', - author = '$entry_author', - lang = '$entry_language' - WHERE id = '$ref_id'"); + content = :content, + content_hash = :content_hash, + updated = :updated, + num_comments = :num_comments, + plugin_data = :plugin_data, + author = :author, + lang = :lang + WHERE id = :id"); + + $sth->execute([":title" => $entry_title, + ":content" => "$entry_content", + ":content_hash" => $entry_current_hash, + ":updated" => $entry_timestamp_fmt, + ":num_comments" => (int)$num_comments, + ":plugin_data" => $entry_plugin_data, + ":author" => "$entry_author", + ":lang" => $entry_language, + ":id" => $ref_id]); // update aux data - db_query("UPDATE ttrss_user_entries - SET score = '$score' WHERE ref_id = '$ref_id'"); + $sth = $pdo->prepare("UPDATE ttrss_user_entries + SET score = ? WHERE ref_id = ?"); + $sth->execute([$score, $ref_id]); if ($mark_unread_on_update) { _debug("article updated, marking unread as requested.", $debug_enabled); - db_query("UPDATE ttrss_user_entries - SET last_read = null, unread = true WHERE ref_id = '$ref_id'"); + $sth = $pdo->prepare("UPDATE ttrss_user_entries + SET last_read = null, unread = true WHERE ref_id = ?"); + $sth->execute([$ref_id]); } } - //db_query("COMMIT"); - _debug("assigning labels [other]...", $debug_enabled); foreach ($article_labels as $label) { @@ -1031,31 +1055,28 @@ class RSSUtils { print_r($enclosures); } - //db_query("BEGIN"); + $esth = $pdo->prepare("SELECT id FROM ttrss_enclosures + WHERE content_url = ? AND post_id = ?"); -// debugging -// db_query("DELETE FROM ttrss_enclosures WHERE post_id = '$entry_ref_id'"); + $usth = $pdo->prepare("INSERT INTO ttrss_enclosures + (content_url, content_type, title, duration, post_id, width, height) VALUES + (?, ?, ?, ?, ?, ?, ?)"); foreach ($enclosures as $enc) { - $enc_url = db_escape_string($enc[0]); - $enc_type = db_escape_string($enc[1]); - $enc_dur = db_escape_string($enc[2]); - $enc_title = db_escape_string($enc[3]); + $enc_url = $enc[0]; + $enc_type = $enc[1]; + $enc_dur = (int)$enc[2]; + $enc_title = $enc[3]; $enc_width = intval($enc[4]); $enc_height = intval($enc[5]); - $result = db_query("SELECT id FROM ttrss_enclosures - WHERE content_url = '$enc_url' AND post_id = '$entry_ref_id'"); + $esth->execute([$enc_url, $entry_ref_id]); - if (db_num_rows($result) == 0) { - db_query("INSERT INTO ttrss_enclosures - (content_url, content_type, title, duration, post_id, width, height) VALUES - ('$enc_url', '$enc_type', '$enc_title', '$enc_dur', '$entry_ref_id', $enc_width, $enc_height)"); + if (!$esth->fetch()) { + $usth->execute([$enc_url, $enc_type, (string)$enc_title, $enc_dur, $entry_ref_id, $enc_width, $enc_height]); } } - //db_query("COMMIT"); - // check for manual tags (we have to do it here since they're loaded from filters) foreach ($article_filters as $f) { @@ -1098,24 +1119,24 @@ class RSSUtils { if (count($filtered_tags) > 0) { - //db_query("BEGIN"); + $tsth = $pdo->prepare("SELECT id FROM ttrss_tags + WHERE tag_name = ? AND post_int_id = ? AND + owner_uid = ? LIMIT 1"); + + $usth = $pdo->prepare("INSERT INTO ttrss_tags + (owner_uid,tag_name,post_int_id) + VALUES (?, ?, ?)"); foreach ($filtered_tags as $tag) { $tag = sanitize_tag($tag); - $tag = db_escape_string($tag); if (!tag_is_valid($tag)) continue; - $result = db_query("SELECT id FROM ttrss_tags - WHERE tag_name = '$tag' AND post_int_id = '$entry_int_id' AND - owner_uid = '$owner_uid' LIMIT 1"); + $tsth->execute([$tag, $entry_int_id, $owner_uid]); - if ($result && db_num_rows($result) == 0) { - - db_query("INSERT INTO ttrss_tags - (owner_uid,tag_name,post_int_id) - VALUES ('$owner_uid','$tag', '$entry_int_id')"); + if (!$tsth->fetch()) { + $usth->execute([$owner_uid, $tag, $entry_int_id]); } array_push($tags_to_cache, $tag); @@ -1125,30 +1146,30 @@ class RSSUtils { $tags_to_cache = array_unique($tags_to_cache); - $tags_str = db_escape_string(join(",", $tags_to_cache)); - - db_query("UPDATE ttrss_user_entries - SET tag_cache = '$tags_str' WHERE ref_id = '$entry_ref_id' - AND owner_uid = $owner_uid"); + $tags_str = join(",", $tags_to_cache); - //db_query("COMMIT"); + $tsth = $pdo->prepare("UPDATE ttrss_user_entries + SET tag_cache = ? WHERE ref_id = ? + AND owner_uid = ?"); + $tsth->execute([$tags_str, $entry_ref_id, $owner_uid]); } _debug("article processed", $debug_enabled); + + $pdo->commit(); } _debug("purging feed...", $debug_enabled); purge_feed($feed, 0, $debug_enabled); - db_query("UPDATE ttrss_feeds - SET last_updated = NOW(), last_unconditional = NOW(), last_error = '' WHERE id = '$feed'"); - -// db_query("COMMIT"); + $sth = $pdo->prepare("UPDATE ttrss_feeds + SET last_updated = NOW(), last_unconditional = NOW(), last_error = '' WHERE id = ?"); + $sth->execute([$feed]); } else { - $error_msg = db_escape_string(mb_substr($rss->error(), 0, 245)); + $error_msg = mb_substr($rss->error(), 0, 245); _debug("fetch error: $error_msg", $debug_enabled); @@ -1158,12 +1179,12 @@ class RSSUtils { } } - db_query( - "UPDATE ttrss_feeds SET last_error = '$error_msg', - last_updated = NOW(), last_unconditional = NOW() WHERE id = '$feed'"); + $sth = $pdo->prepare("UPDATE ttrss_feeds SET last_error = ?, + last_updated = NOW(), last_unconditional = NOW() WHERE id = ?"); + $sth->execute([$error_msg, $feed]); unset($rss); - return; + return false; } _debug("done", $debug_enabled); @@ -1232,14 +1253,15 @@ class RSSUtils { static function expire_error_log($debug) { if ($debug) _debug("Removing old error log entries..."); + $pdo = Db::pdo(); + if (DB_TYPE == "pgsql") { - db_query("DELETE FROM ttrss_error_log + $pdo->query("DELETE FROM ttrss_error_log WHERE created_at < NOW() - INTERVAL '7 days'"); } else { - db_query("DELETE FROM ttrss_error_log + $pdo->query("DELETE FROM ttrss_error_log WHERE created_at < DATE_SUB(NOW(), INTERVAL 7 DAY)"); } - } static function expire_lock_files($debug) { @@ -1264,7 +1286,7 @@ class RSSUtils { } static function expire_cached_files($debug) { - foreach (array("simplepie", "images", "export", "upload") as $dir) { + foreach (array("simplepie", "feeds", "images", "export", "upload") as $dir) { $cache_dir = CACHE_DIR . "/$dir"; // if ($debug) _debug("Expiring $cache_dir"); @@ -1446,19 +1468,23 @@ class RSSUtils { } static function cleanup_counters_cache($debug) { - $result = db_query("DELETE FROM ttrss_counters_cache + $pdo = Db::pdo(); + + $res = $pdo->query("DELETE FROM ttrss_counters_cache WHERE feed_id > 0 AND (SELECT COUNT(id) FROM ttrss_feeds WHERE id = feed_id AND ttrss_counters_cache.owner_uid = ttrss_feeds.owner_uid) = 0"); - $frows = db_affected_rows($result); - $result = db_query("DELETE FROM ttrss_cat_counters_cache + $frows = $res->rowCount(); + + $res = $pdo->query("DELETE FROM ttrss_cat_counters_cache WHERE feed_id > 0 AND (SELECT COUNT(id) FROM ttrss_feed_categories WHERE id = feed_id AND ttrss_cat_counters_cache.owner_uid = ttrss_feed_categories.owner_uid) = 0"); - $crows = db_affected_rows($result); + + $crows = $res->rowCount(); if ($debug) _debug("Removed $frows (feeds) $crows (cats) orphaned counter cache entries."); } |