// This file Copyright © Mnemosyne LLC. // It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only), // or any future license endorsed by Mnemosyne LLC. // License text can be found in the licenses/ folder. #include "TorrentFilter.h" #include "FilterBase.hh" #include "Utils.h" #include #include #include TorrentFilter::TorrentFilter() : Glib::ObjectBase(typeid(TorrentFilter)) { } void TorrentFilter::set_activity(Activity type) { if (activity_type_ == type) { return; } auto change = Change::DIFFERENT; if (activity_type_ == Activity::ALL) { change = Change::MORE_STRICT; } else if (type == Activity::ALL) { change = Change::LESS_STRICT; } activity_type_ = type; changed(change); } void TorrentFilter::set_tracker(Tracker type, Glib::ustring const& host) { if (tracker_type_ == type && tracker_host_ == host) { return; } auto change = Change::DIFFERENT; if (tracker_type_ != type) { if (tracker_type_ == Tracker::ALL) { change = Change::MORE_STRICT; } else if (type == Tracker::ALL) { change = Change::LESS_STRICT; } } else // tracker_host_ != host { if (tracker_host_.empty() || host.find(tracker_host_) != Glib::ustring::npos) { change = Change::MORE_STRICT; } else if (host.empty() || tracker_host_.find(host) != Glib::ustring::npos) { change = Change::LESS_STRICT; } } tracker_type_ = type; tracker_host_ = host; changed(change); } void TorrentFilter::set_text(Glib::ustring const& text) { auto const normalized_text = gtr_str_strip(text.casefold()); if (text_ == normalized_text) { return; } auto change = Change::DIFFERENT; if (text_.empty() || normalized_text.find(text_) != Glib::ustring::npos) { change = Change::MORE_STRICT; } else if (normalized_text.empty() || text_.find(normalized_text) != Glib::ustring::npos) { change = Change::LESS_STRICT; } text_ = normalized_text; changed(change); } bool TorrentFilter::match_activity(Torrent const& torrent) const { return match_activity(torrent, activity_type_); } bool TorrentFilter::match_tracker(Torrent const& torrent) const { return match_tracker(torrent, tracker_type_, tracker_host_); } bool TorrentFilter::match_text(Torrent const& torrent) const { return match_text(torrent, text_); } bool TorrentFilter::match(Torrent const& torrent) const { return match_activity(torrent) && match_tracker(torrent) && match_text(torrent); } bool TorrentFilter::matches_all() const { return activity_type_ == Activity::ALL && tracker_type_ == Tracker::ALL && text_.empty(); } void TorrentFilter::update(Torrent::ChangeFlags changes) { using Flag = Torrent::ChangeFlag; bool refilter_needed = false; if (activity_type_ != Activity::ALL) { static constexpr auto ActivityFlags = std::array, 7U>{ { { Activity::DOWNLOADING, Flag::ACTIVITY }, { Activity::SEEDING, Flag::ACTIVITY }, { Activity::ACTIVE, Flag::ACTIVE_PEER_COUNT | Flag::ACTIVITY }, { Activity::PAUSED, Flag::ACTIVITY }, { Activity::FINISHED, Flag::FINISHED }, { Activity::VERIFYING, Flag::ACTIVITY }, { Activity::ERROR, Flag::ERROR_CODE }, } }; auto const iter = std::find_if( std::begin(ActivityFlags), std::end(ActivityFlags), [key = activity_type_](auto const& row) { return row.first == key; }); refilter_needed = iter != std::end(ActivityFlags) && changes.test(iter->second); } if (!refilter_needed) { refilter_needed = tracker_type_ != Tracker::ALL && changes.test(Flag::TRACKERS); } if (!refilter_needed) { refilter_needed = !text_.empty() && changes.test(Flag::NAME); } if (refilter_needed) { changed(Change::DIFFERENT); } } Glib::RefPtr TorrentFilter::create() { // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) return Glib::make_refptr_for_instance(new TorrentFilter()); } bool TorrentFilter::match_activity(Torrent const& torrent, Activity type) { auto activity = tr_torrent_activity(); switch (type) { case Activity::ALL: return true; case Activity::DOWNLOADING: activity = torrent.get_activity(); return activity == TR_STATUS_DOWNLOAD || activity == TR_STATUS_DOWNLOAD_WAIT; case Activity::SEEDING: activity = torrent.get_activity(); return activity == TR_STATUS_SEED || activity == TR_STATUS_SEED_WAIT; case Activity::ACTIVE: return torrent.get_active_peer_count() > 0 || torrent.get_activity() == TR_STATUS_CHECK; case Activity::PAUSED: return torrent.get_activity() == TR_STATUS_STOPPED; case Activity::FINISHED: return torrent.get_finished(); case Activity::VERIFYING: activity = torrent.get_activity(); return activity == TR_STATUS_CHECK || activity == TR_STATUS_CHECK_WAIT; case Activity::ERROR: return torrent.get_error_code() != 0; default: g_assert_not_reached(); return true; } } bool TorrentFilter::match_tracker(Torrent const& torrent, Tracker type, Glib::ustring const& host) { if (type == Tracker::ALL) { return true; } g_assert(type == Tracker::HOST); auto const& raw_torrent = torrent.get_underlying(); for (auto i = size_t{ 0 }, n = tr_torrentTrackerCount(&raw_torrent); i < n; ++i) { if (auto const tracker = tr_torrentTracker(&raw_torrent, i); std::data(tracker.sitename) == host) { return true; } } return false; } bool TorrentFilter::match_text(Torrent const& torrent, Glib::ustring const& text) { bool ret = false; if (text.empty()) { ret = true; } else { auto const& raw_torrent = torrent.get_underlying(); /* test the torrent name... */ ret = torrent.get_name().casefold().find(text) != Glib::ustring::npos; /* test the files... */ for (auto i = size_t{ 0 }, n = tr_torrentFileCount(&raw_torrent); i < n && !ret; ++i) { ret = Glib::ustring(tr_torrentFile(&raw_torrent, i).name).casefold().find(text) != Glib::ustring::npos; } } return ret; }