Hello community,

here is the log from the commit of package leechcraft for openSUSE:Factory 
checked in at 2020-04-02 17:43:19
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/leechcraft (Old)
 and      /work/SRC/openSUSE:Factory/.leechcraft.new.3248 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "leechcraft"

Thu Apr  2 17:43:19 2020 rev:57 rq:790703 version:0.6.70+git.13641.g995b95b3d4

Changes:
--------
--- /work/SRC/openSUSE:Factory/leechcraft/leechcraft-doc.changes        
2020-02-04 19:52:54.897306261 +0100
+++ /work/SRC/openSUSE:Factory/.leechcraft.new.3248/leechcraft-doc.changes      
2020-04-02 17:43:22.785397159 +0200
@@ -1,0 +2,6 @@
+Tue Mar 31 17:33:47 UTC 2020 - Dmitriy Perlow <dap.darkn...@gmail.com>
+
+- Updated to 0.6.70-13641-g995b95b3d4 snapshot:
+  * There will not be source-url till Saturday.
+
+-------------------------------------------------------------------
--- /work/SRC/openSUSE:Factory/leechcraft/leechcraft.changes    2020-03-11 
18:56:12.711698101 +0100
+++ /work/SRC/openSUSE:Factory/.leechcraft.new.3248/leechcraft.changes  
2020-04-02 17:43:22.841397202 +0200
@@ -1,0 +2,7 @@
+Tue Mar 31 17:33:47 UTC 2020 - Dmitriy Perlow <dap.darkn...@gmail.com>
+
+- Updated to 0.6.70-13641-g995b95b3d4 snapshot:
+  * MaxMindDB instead of GeoIP;
+  * There will not be source-url till Saturday.
+
+-------------------------------------------------------------------

Old:
----
  leechcraft-0.6.70-13605-g8cd066ad6a.tar.xz

New:
----
  leechcraft-0.6.70-13641-g995b95b3d4.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ leechcraft-doc.spec ++++++
--- /var/tmp/diff_new_pack.6SX5wU/_old  2020-04-02 17:43:23.561397761 +0200
+++ /var/tmp/diff_new_pack.6SX5wU/_new  2020-04-02 17:43:23.561397761 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package leechcraft-doc
 #
-# Copyright (c) 2020 SUSE LLC
+# Copyright (c) 2020 SUSE LINUX GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -12,20 +12,20 @@
 # license that conforms to the Open Source Definition (Version 1.9)
 # published by the Open Source Initiative.
 
-# Please submit bugfixes or comments via https://bugs.opensuse.org/
+# Please submit bugfixes or comments via http://bugs.opensuse.org/
 #
 
 
-%define LEECHCRAFT_VERSION 0.6.70-13605-g8cd066ad6a
+%define LEECHCRAFT_VERSION 0.6.70-13641-g995b95b3d4
 
 Name:           leechcraft-doc
-Version:        0.6.70+git.13605.g8cd066ad6a
+Version:        0.6.70+git.13641.g995b95b3d4
 Release:        0
 Summary:        Modular Internet Client Documentation
 License:        BSL-1.0
 Group:          Development/Libraries/Other
 URL:            http://leechcraft.org
-Source0:        
https://dist.leechcraft.org/LeechCraft/0.6.75/leechcraft-%{LEECHCRAFT_VERSION}.tar.xz
+Source0:        leechcraft-%{LEECHCRAFT_VERSION}.tar.xz
 
 BuildRequires:  doxygen >= 1.8.3.1
 BuildRequires:  fdupes

++++++ leechcraft.spec ++++++
--- /var/tmp/diff_new_pack.6SX5wU/_old  2020-04-02 17:43:23.581397776 +0200
+++ /var/tmp/diff_new_pack.6SX5wU/_new  2020-04-02 17:43:23.585397779 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package leechcraft
 #
-# Copyright (c) 2020 SUSE LLC
+# Copyright (c) 2020 SUSE LINUX GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -12,7 +12,7 @@
 # license that conforms to the Open Source Definition (Version 1.9)
 # published by the Open Source Initiative.
 
-# Please submit bugfixes or comments via https://bugs.opensuse.org/
+# Please submit bugfixes or comments via http://bugs.opensuse.org/
 #
 
 
@@ -24,7 +24,7 @@
 %define qml_dir %{_datadir}/leechcraft/qml5
 
 %define so_ver -qt5-0_6_75
-%define LEECHCRAFT_VERSION 0.6.70-13605-g8cd066ad6a
+%define LEECHCRAFT_VERSION 0.6.70-13641-g995b95b3d4
 
 %define db_postfix %{so_ver}_1
 %define gui_postfix %{so_ver}_1
@@ -43,14 +43,14 @@
 %define xsd_postfix %{so_ver}
 
 Name:           leechcraft
-Version:        0.6.70+git.13605.g8cd066ad6a
+Version:        0.6.70+git.13641.g995b95b3d4
 Release:        0
 Summary:        Modular Internet Client
 License:        BSL-1.0
 Group:          Productivity/Networking/Other
 URL:            http://leechcraft.org
 
-Source0:        
https://dist.leechcraft.org/LeechCraft/0.6.75/leechcraft-%{LEECHCRAFT_VERSION}.tar.xz
+Source0:        leechcraft-%{LEECHCRAFT_VERSION}.tar.xz
 Source4:        %{name}-rpmlintrc
 Source8:        leechcraft-session.1
 Source9:        lc_plugin_wrapper-qt5.1
@@ -125,6 +125,7 @@
 BuildRequires:  pkgconfig(libcurl)
 BuildRequires:  pkgconfig(libguess)
 BuildRequires:  pkgconfig(libidn)
+BuildRequires:  pkgconfig(libmaxminddb)
 BuildRequires:  pkgconfig(libmtp)
 BuildRequires:  pkgconfig(libnl-3.0)
 BuildRequires:  pkgconfig(libotr)
@@ -2428,6 +2429,7 @@
         -DENABLE_TEXTOGROOSE=True \
 %ifarch %ix86 x86_64 %arm ppc64le
         -DENABLE_TORRENT=True \
+                 -DENABLE_BITTORRENT_GEOIP=True \
 %else
         -DENABLE_TORRENT=False \
 %endif

++++++ _service ++++++
--- /var/tmp/diff_new_pack.6SX5wU/_old  2020-04-02 17:43:23.621397807 +0200
+++ /var/tmp/diff_new_pack.6SX5wU/_new  2020-04-02 17:43:23.621397807 +0200
@@ -1,7 +1,7 @@
 <services>
   <service name="verify_file" mode="buildtime">
-    <param name="file">leechcraft-0.6.70-13605-g8cd066ad6a.tar.xz</param>
+    <param name="file">leechcraft-0.6.70-13641-g995b95b3d4.tar.xz</param>
     <param name="verifier">sha1</param>
-    <param name="checksum">91b4699050fe165378998fc525057d99a4f3ec4e</param>
+    <param name="checksum">6386baadc409762f05bc4b16089bdb96f19c9fcd</param>
   </service>
 </services>

++++++ leechcraft-0.6.70-13605-g8cd066ad6a.tar.xz -> 
leechcraft-0.6.70-13641-g995b95b3d4.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/core/CMakeLists.txt 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/core/CMakeLists.txt
--- old/leechcraft-0.6.70-13605-g8cd066ad6a/src/core/CMakeLists.txt     
2020-01-11 16:25:31.000000000 +0100
+++ new/leechcraft-0.6.70-13641-g995b95b3d4/src/core/CMakeLists.txt     
2020-03-31 20:44:18.000000000 +0200
@@ -94,6 +94,7 @@
        loadprocessbase.cpp
        loadprogressreporter.cpp
        splashscreen.cpp
+       tagsstorage.cpp
        loaders/ipluginloader.cpp
        loaders/sopluginloader.cpp
        )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/core/tagsmanager.cpp 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/core/tagsmanager.cpp
--- old/leechcraft-0.6.70-13605-g8cd066ad6a/src/core/tagsmanager.cpp    
2020-01-11 16:25:31.000000000 +0100
+++ new/leechcraft-0.6.70-13641-g995b95b3d4/src/core/tagsmanager.cpp    
2020-03-31 20:44:18.000000000 +0200
@@ -31,18 +31,24 @@
 #include <stdexcept>
 #include <algorithm>
 #include <QStringList>
-#include <QSettings>
 #include <QCoreApplication>
+#include <QSettings>
 #include <QtDebug>
 #include <util/sll/prelude.h>
+#include <util/sll/qtutil.h>
 #include <util/tags/tagscompleter.h>
 
 using namespace LC;
 
 TagsManager::TagsManager ()
 {
-       ReadSettings ();
+       MigrateToDb ();
+
+       for (const auto& [id, name] : Storage_.GetAllTags ())
+               Tags_ [id] = name;
+
        GetID (tr ("untagged"));
+
        Util::TagsCompleter::SetModel (GetModel ());
 }
 
@@ -110,13 +116,12 @@
 QStringList TagsManager::Split (const QString& string) const
 {
        return Util::Map (string.split (";", QString::SkipEmptyParts),
-                       [] (const QString& s) { return s.trimmed (); }); // 
TODO gcc 6
+                       [] (QString& s) { return std::move (s).trimmed (); });
 }
 
-QStringList TagsManager::SplitToIDs (const QString& string)
+QList<ITagsManager::tag_id> TagsManager::SplitToIDs (const QString& string)
 {
-       return Util::Map (Split (string),
-                       [this] (const QString& tag) { return GetID 
(tag.simplified ()); }); // TODO gcc 6
+       return GetIDs (Split (string));
 }
 
 QString TagsManager::Join (const QStringList& tags) const
@@ -135,6 +140,8 @@
 {
        const auto& uuid = QUuid::createUuid ();
 
+       Storage_.AddTag (uuid, tag);
+
        auto updated = Tags_;
        auto pos = updated.insert (uuid, tag);
        const auto dist = std::distance (updated.begin (), pos);
@@ -143,8 +150,6 @@
        Tags_ = std::move (updated);
        endInsertRows ();
 
-       WriteSettings ();
-
        emit tagsUpdated (GetAllTags ());
 
        return uuid.toString ();
@@ -155,10 +160,14 @@
        if (!index.isValid ())
                return;
 
+       const auto pos = Tags_.begin () + index.row ();
+
+       Storage_.DeleteTag (pos.key ());
+
        beginRemoveRows (QModelIndex (), index.row (), index.row ());
-       Tags_.erase (Tags_.begin () + index.row ());
+       Tags_.erase (pos);
        endRemoveRows ();
-       WriteSettings ();
+
        emit tagsUpdated (GetAllTags ());
 }
 
@@ -170,9 +179,9 @@
        auto pos = Tags_.begin () + index.row ();
        *pos = newTag;
 
-       emit dataChanged (index, index);
+       Storage_.SetTagName (pos.key (), newTag);
 
-       WriteSettings ();
+       emit dataChanged (index, index);
 
        emit tagsUpdated (GetAllTags ());
 }
@@ -187,26 +196,21 @@
        return this;
 }
 
-void TagsManager::ReadSettings ()
+// TODO post 0.6.75 drop this
+void TagsManager::MigrateToDb ()
 {
        QSettings settings (QCoreApplication::organizationName (),
                        QCoreApplication::applicationName ());
        settings.beginGroup ("Tags");
-       Tags_ = settings.value ("Dict").value<TagsDictionary_t> ();
-       if (!Tags_.isEmpty ())
+       if (!settings.value ("Migrated").toBool ())
        {
-               beginInsertRows (QModelIndex (), 0, Tags_.size () - 1);
-               endInsertRows ();
-       }
-       settings.endGroup ();
-}
+               const auto& tags = settings.value 
("Dict").value<TagsDictionary_t> ();
 
-void TagsManager::WriteSettings () const
-{
-       QSettings settings (QCoreApplication::organizationName (),
-                       QCoreApplication::applicationName ());
-       settings.beginGroup ("Tags");
-       settings.setValue ("Dict", QVariant::fromValue<TagsDictionary_t> 
(Tags_));
+               qDebug () << Q_FUNC_INFO << "migrating" << tags.size () << 
"tags to the DB";
+
+               for (const auto& [id, name] : Util::Stlize (tags))
+                       Storage_.AddTag (id, name);
+               settings.setValue ("Migrated", true);
+       }
        settings.endGroup ();
 }
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/core/tagsmanager.h 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/core/tagsmanager.h
--- old/leechcraft-0.6.70-13605-g8cd066ad6a/src/core/tagsmanager.h      
2020-01-11 16:25:31.000000000 +0100
+++ new/leechcraft-0.6.70-13641-g995b95b3d4/src/core/tagsmanager.h      
2020-03-31 20:44:18.000000000 +0200
@@ -35,6 +35,7 @@
 #include <QString>
 #include <QMetaType>
 #include "interfaces/core/itagsmanager.h"
+#include "tagsstorage.h"
 
 namespace LC
 {
@@ -49,31 +50,31 @@
                typedef QMap<QUuid, QString> TagsDictionary_t;
        private:
                TagsDictionary_t Tags_;
+               TagsStorage Storage_;
        public:
                static TagsManager& Instance ();
 
-               int columnCount (const QModelIndex&) const;
-               QVariant data (const QModelIndex&, int) const;
-               QModelIndex index (int, int, const QModelIndex&) const;
-               QModelIndex parent (const QModelIndex&) const;
-               int rowCount (const QModelIndex&) const;
+               int columnCount (const QModelIndex&) const override;
+               QVariant data (const QModelIndex&, int) const override;
+               QModelIndex index (int, int, const QModelIndex&) const override;
+               QModelIndex parent (const QModelIndex&) const override;
+               int rowCount (const QModelIndex&) const override;
 
-               tag_id GetID (const QString&);
-               QString GetTag (tag_id) const;
-               QStringList GetAllTags () const;
-               QStringList Split (const QString&) const;
-               QStringList SplitToIDs (const QString&);
-               QString Join (const QStringList&) const;
-               QString JoinIDs (const QStringList&) const;
-               QAbstractItemModel* GetModel ();
-               QObject* GetQObject ();
+               tag_id GetID (const QString&) override;
+               QString GetTag (tag_id) const override;
+               QStringList GetAllTags () const override;
+               QStringList Split (const QString&) const override;
+               QList<tag_id> SplitToIDs (const QString&) override;
+               QString Join (const QStringList&) const override;
+               QString JoinIDs (const QStringList&) const override;
+               QAbstractItemModel* GetModel () override;
+               QObject* GetQObject () override;
 
                void RemoveTag (const QModelIndex&);
                void SetTag (const QModelIndex&, const QString&);
        private:
                tag_id InsertTag (const QString&);
-               void ReadSettings ();
-               void WriteSettings () const;
+               void MigrateToDb ();
        signals:
                void tagsUpdated (const QStringList&);
        };
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/core/tagsstorage.cpp 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/core/tagsstorage.cpp
--- old/leechcraft-0.6.70-13605-g8cd066ad6a/src/core/tagsstorage.cpp    
1970-01-01 01:00:00.000000000 +0100
+++ new/leechcraft-0.6.70-13641-g995b95b3d4/src/core/tagsstorage.cpp    
2020-03-31 20:44:18.000000000 +0200
@@ -0,0 +1,104 @@
+/**********************************************************************
+ * LeechCraft - modular cross-platform feature rich internet client.
+ * Copyright (C) 2006-2014  Georg Rudoy
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ **********************************************************************/
+
+#include "tagsstorage.h"
+#include <QDir>
+#include <QSqlError>
+#include <util/db/oral/oral.h>
+#include <util/db/dblock.h>
+#include <util/db/util.h>
+#include <util/sll/prelude.h>
+#include <util/sys/paths.h>
+
+namespace LC
+{
+       struct TagsStorage::Record
+       {
+               Util::oral::Unique<Util::oral::NotNull<QByteArray>> Id_;
+               Util::oral::NotNull<QString> Name_;
+
+               static QString ClassName ()
+               {
+                       return "Tags";
+               }
+       };
+}
+
+BOOST_FUSION_ADAPT_STRUCT (LC::TagsStorage::Record,
+               Id_,
+               Name_)
+
+namespace LC
+{
+       namespace sph = Util::oral::sph;
+
+       TagsStorage::TagsStorage (QObject *parent)
+       : QObject { parent }
+       , DB_ { QSqlDatabase::addDatabase ("QSQLITE",
+                       Util::GenConnectionName 
("org.LeechCraft.Core.TagsStorage")) }
+       {
+               const auto& coreDir = Util::GetUserDir (Util::UserDir::LC, 
"core");
+               DB_.setDatabaseName (coreDir.filePath ("core.db"));
+
+               if (!DB_.open ())
+               {
+                       qWarning () << Q_FUNC_INFO
+                                       << "cannot open the database";
+                       Util::DBLock::DumpError (DB_.lastError ());
+                       throw std::runtime_error { "Cannot create database" };
+               }
+
+               Util::RunTextQuery (DB_, "PRAGMA synchronous = NORMAL;");
+               Util::RunTextQuery (DB_, "PRAGMA journal_mode = WAL;");
+
+               Record_ = Util::oral::AdaptPtr<Record> (DB_);
+       }
+
+       void TagsStorage::AddTag (const Id& id, const QString& name)
+       {
+               Record_->Insert ({ id.toByteArray (), name });
+       }
+
+       void TagsStorage::DeleteTag (const Id& id)
+       {
+               Record_->DeleteBy (sph::f<&Record::Id_> == id.toByteArray ());
+       }
+
+       void TagsStorage::SetTagName (const Id& id, const QString& newName)
+       {
+               Record_->Update (sph::f<&Record::Name_> = newName,
+                               sph::f<&Record::Id_> == id.toByteArray ());
+       }
+
+       QList<QPair<TagsStorage::Id, QString>> TagsStorage::GetAllTags () const
+       {
+               return Util::Map (Record_->Select (),
+                               [] (const Record& rec) { return QPair { QUuid { 
QString::fromLatin1 (**rec.Id_) }, *rec.Name_ }; });
+       }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/core/tagsstorage.h 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/core/tagsstorage.h
--- old/leechcraft-0.6.70-13605-g8cd066ad6a/src/core/tagsstorage.h      
1970-01-01 01:00:00.000000000 +0100
+++ new/leechcraft-0.6.70-13641-g995b95b3d4/src/core/tagsstorage.h      
2020-03-31 20:44:18.000000000 +0200
@@ -0,0 +1,60 @@
+/**********************************************************************
+ * LeechCraft - modular cross-platform feature rich internet client.
+ * Copyright (C) 2006-2014  Georg Rudoy
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ **********************************************************************/
+
+#pragma once
+
+#include <optional>
+#include <QObject>
+#include <QUuid>
+#include <QString>
+#include <QList>
+#include <QSqlDatabase>
+#include <util/db/oral/oralfwd.h>
+
+namespace LC
+{
+       class TagsStorage : public QObject
+       {
+       public:
+               struct Record;
+       private:
+               QSqlDatabase DB_;
+
+               Util::oral::ObjectInfo_ptr<Record> Record_;
+       public:
+               explicit TagsStorage (QObject* = nullptr);
+
+               using Id = QUuid;
+
+               void AddTag (const Id&, const QString&);
+               void DeleteTag (const Id&);
+               void SetTagName (const Id&, const QString&);
+               QList<QPair<Id, QString>> GetAllTags () const;
+       };
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/interfaces/core/itagsmanager.h 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/interfaces/core/itagsmanager.h
--- old/leechcraft-0.6.70-13605-g8cd066ad6a/src/interfaces/core/itagsmanager.h  
2020-01-11 16:25:31.000000000 +0100
+++ new/leechcraft-0.6.70-13641-g995b95b3d4/src/interfaces/core/itagsmanager.h  
2020-03-31 20:44:18.000000000 +0200
@@ -132,7 +132,7 @@
         * @param[in] string String with tags.
         * @return The list of the tags IDs.
         */
-       virtual QStringList SplitToIDs (const QString& string) = 0;
+       virtual QList<tag_id> SplitToIDs (const QString& string) = 0;
 
        /** @brief Joins the given tags into one string that's suitable to
         * display to the user.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/aggregator/channelsmodel.cpp
 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/aggregator/channelsmodel.cpp
--- 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/aggregator/channelsmodel.cpp
    2020-01-11 16:25:31.000000000 +0100
+++ 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/aggregator/channelsmodel.cpp
    2020-03-31 20:44:18.000000000 +0200
@@ -73,6 +73,11 @@
                                this,
                                [this] (const Channel& channel) { AddChannel 
(channel.ToShort ()); });
                connect (&StorageBackendManager::Instance (),
+                               
&StorageBackendManager::channelUnreadCountUpdated,
+                               this,
+                               &ChannelsModel::UpdateChannelUnreadCount,
+                               Qt::QueuedConnection);
+               connect (&StorageBackendManager::Instance (),
                                &StorageBackendManager::channelDataUpdated,
                                this,
                                &ChannelsModel::UpdateChannelData,
@@ -327,17 +332,29 @@
                                emit dataChanged (index (i, 0), index (i, 
columnCount () - 1));
        }
 
-       void ChannelsModel::UpdateChannelData (const Channel& channel)
+       void ChannelsModel::UpdateChannelUnreadCount (IDType_t cid, int count)
        {
-               const auto cid = channel.ChannelID_;
-
                const auto pos = std::find_if (Channels_.begin (), 
Channels_.end (),
                                [cid] (const ChannelShort& cs) { return 
cs.ChannelID_ == cid; });
                if (pos == Channels_.end ())
                        return;
 
+               pos->Unread_ = count;
+
+               const auto idx = pos - Channels_.begin ();
+               emit dataChanged (index (idx, 0), index (idx, 2));
+       }
+
+       void ChannelsModel::UpdateChannelData (const Channel& channel)
+       {
+               const auto pos = std::find_if (Channels_.begin (), 
Channels_.end (),
+                               [&channel] (const ChannelShort& cs) { return 
cs.ChannelID_ == channel.ChannelID_; });
+               if (pos == Channels_.end ())
+                       return;
+
+               auto unreadCount = pos->Unread_;
                *pos = channel.ToShort ();
-               pos->Unread_ = StorageBackendManager::Instance 
().MakeStorageBackendForThread ()->GetUnreadItemsCount (cid);
+               pos->Unread_ = unreadCount;
 
                const auto idx = pos - Channels_.begin ();
                emit dataChanged (index (idx, 0), index (idx, 2));
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/aggregator/channelsmodel.h 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/aggregator/channelsmodel.h
--- 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/aggregator/channelsmodel.h  
    2020-01-11 16:25:31.000000000 +0100
+++ 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/aggregator/channelsmodel.h  
    2020-03-31 20:44:18.000000000 +0200
@@ -79,6 +79,7 @@
 
                void HandleFeedErrorsChanged (IDType_t);
 
+               void UpdateChannelUnreadCount (IDType_t, int);
                void UpdateChannelData (const Channel&);
 
                void AddChannel (const ChannelShort&);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/aggregator/dumbstorage.h 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/aggregator/dumbstorage.h
--- 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/aggregator/dumbstorage.h    
    2020-01-11 16:25:31.000000000 +0100
+++ 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/aggregator/dumbstorage.h    
    2020-03-31 20:44:18.000000000 +0200
@@ -74,9 +74,9 @@
                void RemoveItems (const QSet<IDType_t>&) override {}
                void RemoveChannel (IDType_t) override {}
                void RemoveFeed (IDType_t) override {}
-               bool UpdateFeedsStorage (int, int) override { return {}; }
-               bool UpdateChannelsStorage (int, int) override { return {}; }
-               bool UpdateItemsStorage (int, int) override { return {}; }
+               bool UpdateFeedsStorage (int) override { return {}; }
+               bool UpdateChannelsStorage (int) override { return {}; }
+               bool UpdateItemsStorage (int) override { return {}; }
                void ToggleChannelUnread (IDType_t, bool) override {}
                QList<ITagsManager::tag_id> GetItemTags (IDType_t) override { 
return {}; }
                void SetItemTags (IDType_t, const QList<ITagsManager::tag_id>&) 
override {}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/aggregator/feedsettings.cpp 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/aggregator/feedsettings.cpp
--- 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/aggregator/feedsettings.cpp 
    2020-01-11 16:25:31.000000000 +0100
+++ 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/aggregator/feedsettings.cpp 
    2020-03-31 20:44:18.000000000 +0200
@@ -142,10 +142,14 @@
                const auto& storage = StorageBackendManager::Instance 
().MakeStorageBackendForThread ();
 
                const auto itm = Proxy_->GetTagsManager ();
-               storage->SetChannelTags (Index_.data 
(ChannelRoles::ChannelID).value<IDType_t> (),
-                               itm->GetIDs (itm->Split (Ui_.ChannelTags_->text 
())));
-               storage->SetFeedTags (Index_.data 
(ChannelRoles::FeedID).value<IDType_t> (),
-                               itm->GetIDs (itm->Split (Ui_.FeedTags_->text 
())));
+
+               const auto& channelTags = Ui_.ChannelTags_->text ();
+               storage->SetChannelTags (Index_.data 
(ChannelRoles::ChannelID).value<IDType_t> (), itm->SplitToIDs (channelTags));
+
+               auto feedTags = Ui_.FeedTags_->text ();
+               if (feedTags.isEmpty ())
+                       feedTags = channelTags;
+               storage->SetFeedTags (Index_.data 
(ChannelRoles::FeedID).value<IDType_t> (), itm->SplitToIDs (feedTags));
 
                storage->SetFeedSettings ({
                                Index_.data 
(ChannelRoles::FeedID).value<IDType_t> (),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/aggregator/itemslistmodel.cpp
 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/aggregator/itemslistmodel.cpp
--- 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/aggregator/itemslistmodel.cpp
   2020-01-11 16:25:31.000000000 +0100
+++ 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/aggregator/itemslistmodel.cpp
   2020-03-31 20:44:18.000000000 +0200
@@ -71,11 +71,15 @@
                connect (&StorageBackendManager::Instance (),
                                &StorageBackendManager::itemsRemoved,
                                this,
-                               &ItemsListModel::handleItemsRemoved);
+                               &ItemsListModel::HandleItemsRemoved);
                connect (&StorageBackendManager::Instance (),
                                &StorageBackendManager::itemDataUpdated,
                                this,
-                               &ItemsListModel::handleItemDataUpdated);
+                               &ItemsListModel::HandleItemDataUpdated);
+               connect (&StorageBackendManager::Instance (),
+                               &StorageBackendManager::itemReadStatusUpdated,
+                               this,
+                               &ItemsListModel::HandleItemReadStatusUpdated);
        }
 
        int ItemsListModel::GetSelectedRow () const
@@ -437,27 +441,49 @@
                return SB_.localData ();
        }
 
-       void ItemsListModel::reset (IDType_t channelId)
+       void ItemsListModel::HandleItemsRemoved (const QSet<IDType_t>& items)
        {
-               Reset (channelId);
+               RemoveItems (items);
        }
 
-       void ItemsListModel::selected (const QModelIndex& index)
+       void ItemsListModel::HandleItemDataUpdated (const Item& item)
        {
-               Selected (index);
+               if (item.ChannelID_ != CurrentChannel_)
+                       return;
+
+               ItemDataUpdated (item);
        }
 
-       void ItemsListModel::handleItemsRemoved (const QSet<IDType_t>& items)
+       void ItemsListModel::HandleItemReadStatusUpdated (IDType_t channelId, 
IDType_t itemId, bool unread)
        {
-               RemoveItems (items);
+               if (channelId != CurrentChannel_)
+                       return;
+
+               const auto pos = std::find_if (CurrentItems_.begin (), 
CurrentItems_.end (),
+                               [&itemId] (const ItemShort& itemShort) { return 
itemShort.ItemID_ == itemId; });
+               if (pos == CurrentItems_.end ())
+               {
+                       qWarning () << Q_FUNC_INFO
+                                       << "unable to find"
+                                       << channelId
+                                       << itemId;
+                       return;
+               }
+
+               pos->Unread_ = unread;
+
+               int distance = std::distance (CurrentItems_.begin (), pos);
+               emit dataChanged (index (distance, 0), index (distance, 1));
        }
 
-       void ItemsListModel::handleItemDataUpdated (const Item& item, const 
Channel& channel)
+       void ItemsListModel::reset (IDType_t channelId)
        {
-               if (channel.ChannelID_ != CurrentChannel_)
-                       return;
+               Reset (channelId);
+       }
 
-               ItemDataUpdated (item);
+       void ItemsListModel::selected (const QModelIndex& index)
+       {
+               Selected (index);
        }
 }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/aggregator/itemslistmodel.h 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/aggregator/itemslistmodel.h
--- 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/aggregator/itemslistmodel.h 
    2020-01-11 16:25:31.000000000 +0100
+++ 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/aggregator/itemslistmodel.h 
    2020-03-31 20:44:18.000000000 +0200
@@ -86,13 +86,12 @@
                int rowCount (const QModelIndex& = QModelIndex ()) const 
override;
        private:
                StorageBackend_ptr GetSB () const;
+               void HandleItemsRemoved (const QSet<IDType_t>&);
+               void HandleItemDataUpdated (const Item&);
+               void HandleItemReadStatusUpdated (IDType_t, IDType_t, bool);
        public slots:
                void reset (IDType_t) override;
                void selected (const QModelIndex&) override;
-       private slots:
-               void handleItemsRemoved (const QSet<IDType_t>&);
-
-               void handleItemDataUpdated (const Item&, const Channel&);
        };
 }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/aggregator/sqlstoragebackend.cpp
 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/aggregator/sqlstoragebackend.cpp
--- 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/aggregator/sqlstoragebackend.cpp
        2020-01-11 16:25:31.000000000 +0100
+++ 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/aggregator/sqlstoragebackend.cpp
        2020-03-31 20:44:18.000000000 +0200
@@ -78,30 +78,60 @@
        {
                QStringList TagsList_;
 
+               Tags () = default;
+
+               Tags (QStringList tags)
+               : TagsList_ { std::move (tags) }
+               {
+               }
+
+               operator QStringList () const
+               {
+                       return TagsList_;
+               }
+
                using BaseType = QString;
 
+               // need to migrate off this in the next schema migration
+               static const BaseType EmptyMarker_;
+
                BaseType ToBaseType () const
                {
+                       if (TagsList_.isEmpty ())
+                               return EmptyMarker_;
+
                        static const auto itm = Util::CoreProxyHolder::Get 
()->GetTagsManager ();
                        return itm->Join (TagsList_);
                }
 
                static Tags FromBaseType (const BaseType& var)
                {
+                       if (var == EmptyMarker_)
+                               return {};
+
                        static const auto itm = Util::CoreProxyHolder::Get 
()->GetTagsManager ();
                        return { itm->Split (var) };
                }
-
-               operator QStringList () const
-               {
-                       return TagsList_;
-               }
        };
 
+       const QString Tags::EmptyMarker_ = "<<<null>>>";
+
        struct Image
        {
                QImage Image_;
 
+               Image () = default;
+
+               Image (QImage image)
+               : Image_ { std::move (image) }
+               {
+               }
+
+               operator QImage () const
+               {
+                       return Image_;
+               }
+
                using BaseType = QByteArray;
 
                BaseType ToBaseType () const
@@ -123,17 +153,24 @@
                                result.loadFromData (var, "PNG");
                        return { result };
                }
-
-               operator QImage () const
-               {
-                       return Image_;
-               }
        };
 
        struct ItemCategories
        {
                QStringList Categories_;
 
+               ItemCategories () = default;
+
+               ItemCategories (QStringList cats)
+               : Categories_ { std::move (cats) }
+               {
+               }
+
+               operator QStringList () const
+               {
+                       return Categories_;
+               }
+
                using BaseType = QString;
 
                BaseType ToBaseType () const
@@ -145,16 +182,23 @@
                {
                        return { var.split ("<<<", QString::SkipEmptyParts) };
                }
-
-               operator QStringList () const
-               {
-                       return Categories_;
-               }
        };
 
        struct GeoCoord
        {
-               double Coord_;
+               double Coord_ = 0;
+
+               GeoCoord () = default;
+
+               GeoCoord (double c)
+               : Coord_ { c }
+               {
+               }
+
+               operator double () const
+               {
+                       return Coord_;
+               }
 
                using BaseType = QString;
 
@@ -167,11 +211,6 @@
                {
                        return { var.toDouble () };
                }
-
-               operator double () const
-               {
-                       return Coord_;
-               }
        };
 
        template<typename OralType, typename Src>
@@ -371,7 +410,7 @@
 
        struct SQLStorageBackend::Feed2TagsR
        {
-               oral::References<&FeedR::FeedID_> FeedID_;
+               oral::Unique<oral::References<&FeedR::FeedID_>> FeedID_;
                oral::NotNull<Tags> Tags_;
 
                static QString ClassName ()
@@ -486,7 +525,7 @@
                lock.Good ();
 
                if (const auto& item = GetItem (id))
-                       emit itemDataUpdated (*item, GetChannel 
(item->ChannelID_));
+                       emit itemDataUpdated (*item);
        }
 
        QList<IDType_t> SQLStorageBackend::GetItemsForTag (const 
ITagsManager::tag_id& tag)
@@ -725,10 +764,11 @@
 
                auto removeByDate = Items_->Select 
(sph::fields<&ItemR::ItemID_>,
                                sph::f<&ItemR::ChannelID_> == channelId &&
+                               sph::f<&ItemR::Unread_> == false &&
                                sph::f<&ItemR::PubDate_> < cutoff);
                auto removeByCount = Items_->Select.Build ()
                                .Select (sph::fields<&ItemR::ItemID_>)
-                               .Where (sph::f<&ItemR::ChannelID_> == channelId)
+                               .Where (sph::f<&ItemR::ChannelID_> == channelId 
&& sph::f<&ItemR::Unread_> == false)
                                .Order 
(oral::OrderBy<sph::desc<&ItemR::PubDate_>>)
                                .Offset (number)
                                ();
@@ -742,8 +782,6 @@
                for (auto id : removedIds)
                        Items_->DeleteBy (sph::f<&ItemR::ItemID_> == id);
                lock.Good ();
-
-               emit channelDataUpdated (GetChannel (channelId));
        }
 
        std::optional<QImage> SQLStorageBackend::GetChannelPixmap (IDType_t 
channelId) const
@@ -755,31 +793,37 @@
        {
                // TODO no need for value_or when oral will support setting NULL
                Channels_->Update (sph::f<&ChannelR::Pixmap_> = img.value_or 
(QImage {}), sph::f<&ChannelR::ChannelID_> == id);
+               emit channelDataUpdated (GetChannel (id));
        }
 
        void SQLStorageBackend::SetChannelFavicon (IDType_t id, const 
std::optional<QImage>& img)
        {
                Channels_->Update (sph::f<&ChannelR::Favicon_> = img.value_or 
(QImage {}), sph::f<&ChannelR::ChannelID_> == id);
+               emit channelDataUpdated (GetChannel (id));
        }
 
        void SQLStorageBackend::SetChannelTags (IDType_t id, const QStringList& 
tagIds)
        {
                Channels_->Update (sph::f<&ChannelR::Tags_> = tagIds, 
sph::f<&ChannelR::ChannelID_> == id);
+               emit channelDataUpdated (GetChannel (id));
        }
 
        void SQLStorageBackend::SetChannelDisplayTitle (IDType_t id, const 
QString& displayTitle)
        {
                Channels_->Update (sph::f<&ChannelR::DisplayTitle_> = 
displayTitle, sph::f<&ChannelR::ChannelID_> == id);
+               emit channelDataUpdated (GetChannel (id));
        }
 
        void SQLStorageBackend::SetChannelTitle (IDType_t id, const QString& 
title)
        {
                Channels_->Update (sph::f<&ChannelR::Title_> = title, 
sph::f<&ChannelR::ChannelID_> == id);
+               emit channelDataUpdated (GetChannel (id));
        }
 
        void SQLStorageBackend::SetChannelLink (IDType_t id, const QString& 
link)
        {
                Channels_->Update (sph::f<&ChannelR::URL_> = link, 
sph::f<&ChannelR::ChannelID_> == id);
+               emit channelDataUpdated (GetChannel (id));
        }
 
        items_shorts_t SQLStorageBackend::GetItems (IDType_t channelId) const
@@ -862,9 +906,7 @@
                WriteEnclosures (item.Enclosures_);
                WriteMRSSEntries (item.MRSSEntries_);
 
-               const auto& channel = GetChannel (item.ChannelID_);
-               emit itemDataUpdated (item, channel);
-               emit channelDataUpdated (channel);
+               emit itemDataUpdated (item);
        }
 
        void SQLStorageBackend::SetItemUnread (IDType_t itemId, bool unread)
@@ -873,10 +915,9 @@
 
                if (const auto& fullItem = GetItem (itemId))
                {
-                       const auto& channel = GetChannel (fullItem->ChannelID_);
-
-                       emit itemDataUpdated (*fullItem, channel);
-                       emit channelDataUpdated (channel);
+                       const auto channelId = fullItem->ChannelID_;
+                       emit itemReadStatusUpdated (channelId, itemId, unread);
+                       emit channelUnreadCountUpdated (channelId, 
GetUnreadItemsCount (channelId));
                }
        }
 
@@ -897,9 +938,8 @@
 
                emit hookItemAdded (std::make_shared<Util::DefaultHookProxy> 
(), item);
 
-               const auto& channel = GetChannel (item.ChannelID_);
-               emit itemDataUpdated (item, channel);
-               emit channelDataUpdated (channel);
+               emit itemDataUpdated (item);
+               emit channelUnreadCountUpdated (item.ChannelID_, 
GetUnreadItemsCount (item.ChannelID_));
        }
 
        void SQLStorageBackend::RemoveItems (const QSet<IDType_t>& items)
@@ -925,7 +965,7 @@
                emit itemsRemoved ({ items });
 
                for (const auto& cid : modifiedChannels)
-                       emit channelDataUpdated (GetChannel (cid));
+                       emit channelUnreadCountUpdated (cid, 
GetUnreadItemsCount (cid));
        }
 
        void SQLStorageBackend::RemoveChannel (IDType_t channelId)
@@ -948,40 +988,63 @@
 
        void SQLStorageBackend::ToggleChannelUnread (IDType_t channelId, bool 
state)
        {
-               const auto& oldItems = GetFullItems (channelId);
+               const auto oldItems = Items_->Select 
(sph::fields<&ItemR::ItemID_, &ItemR::Unread_>,
+                               sph::f<&ItemR::ChannelID_> == channelId);
 
                Items_->Update (sph::f<&ItemR::Unread_> = state,
                                sph::f<&ItemR::ChannelID_> == channelId);
 
-               const auto& channel = GetChannel (channelId);
-               emit channelDataUpdated (channel);
-               for (auto& oldItem : oldItems)
-                       if (oldItem->Unread_ != state)
-                       {
-                               oldItem->Unread_ = state;
-                               emit itemDataUpdated (*oldItem, channel);
-                       }
+               emit channelUnreadCountUpdated (channelId, state ? 
oldItems.size () : 0);
+
+               for (const auto& [itemId, oldState] : oldItems)
+                       if (oldState != state)
+                               emit itemReadStatusUpdated (channelId, itemId, 
state);
        }
 
-       bool SQLStorageBackend::UpdateFeedsStorage (int, int)
+       bool SQLStorageBackend::UpdateFeedsStorage (int from)
        {
+               Util::DBLock lock { DB_ };
+               lock.Init ();
+               try
+               {
+                       if (from <= 1)
+                       {
+                               qDebug () << Q_FUNC_INFO << "migrating tags";
+
+                               const auto& feedsTags = Feeds2Tags_->Select ();
+
+                               Util::RunTextQuery (DB_, "DROP TABLE " + 
Feed2TagsR::ClassName ());
+
+                               Feeds2Tags_ = Type_ == SBSQLite ?
+                                               oral::AdaptPtr<Feed2TagsR, 
oral::SQLiteImplFactory> (DB_) :
+                                               oral::AdaptPtr<Feed2TagsR, 
oral::PostgreSQLImplFactory> (DB_);
+
+                               for (const auto& [feedId, tags] : feedsTags)
+                                       SetFeedTags (*feedId, tags->TagsList_);
+                       }
+               }
+               catch (const std::exception& e)
+               {
+                       qWarning () << Q_FUNC_INFO
+                                       << e.what ();
+                       return false;
+               }
+               lock.Good ();
                return true;
        }
 
-       bool SQLStorageBackend::UpdateChannelsStorage (int oldV, int newV)
+       bool SQLStorageBackend::UpdateChannelsStorage (int)
        {
-               if (oldV != newV)
-                       qCritical () << Q_FUNC_INFO
-                                       << "support for old channel storage 
tables dropped";
-               return oldV == newV;
+               qCritical () << Q_FUNC_INFO
+                               << "support for old channel storage tables 
dropped";
+               return false;
        }
 
-       bool SQLStorageBackend::UpdateItemsStorage (int oldV, int newV)
+       bool SQLStorageBackend::UpdateItemsStorage (int)
        {
-               if (oldV != newV)
-                       qCritical () << Q_FUNC_INFO
-                                       << "support for old items storage 
tables dropped";
-               return oldV == newV;
+               qCritical () << Q_FUNC_INFO
+                               << "support for old items storage tables 
dropped";
+               return false;
        }
 
        void SQLStorageBackend::WriteEnclosures (const QList<Enclosure>& 
enclosures)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/aggregator/sqlstoragebackend.h
 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/aggregator/sqlstoragebackend.h
--- 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/aggregator/sqlstoragebackend.h
  2020-01-11 16:25:31.000000000 +0100
+++ 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/aggregator/sqlstoragebackend.h
  2020-03-31 20:44:18.000000000 +0200
@@ -122,9 +122,9 @@
                void RemoveItems (const QSet<IDType_t>&) override;
                void RemoveChannel (IDType_t) override;
                void RemoveFeed (IDType_t) override;
-               bool UpdateFeedsStorage (int, int) override;
-               bool UpdateChannelsStorage (int, int) override;
-               bool UpdateItemsStorage (int, int) override;
+               bool UpdateFeedsStorage (int) override;
+               bool UpdateChannelsStorage (int) override;
+               bool UpdateItemsStorage (int) override;
                void ToggleChannelUnread (IDType_t, bool) override;
 
                QList<ITagsManager::tag_id> GetItemTags (IDType_t) override;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/aggregator/storagebackend.h 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/aggregator/storagebackend.h
--- 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/aggregator/storagebackend.h 
    2020-01-11 16:25:31.000000000 +0100
+++ 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/aggregator/storagebackend.h 
    2020-03-31 20:44:18.000000000 +0200
@@ -374,11 +374,10 @@
                 * stored in application settings is lower than newer one.
                 *
                 * @param[in] oldV Old storage version.
-                * @param[in] newV New storage version.
                 *
                 * @return true if update successful, else false.
                 */
-               virtual bool UpdateFeedsStorage (int oldV, int newV) = 0;
+               virtual bool UpdateFeedsStorage (int oldV) = 0;
 
                /** @brief Update channels storage section.
                 *
@@ -386,11 +385,10 @@
                 * stored in application settings is lower than newer one.
                 *
                 * @param[in] oldV Old storage version.
-                * @param[in] newV New storage version.
                 *
                 * @return true if update successful, else false.
                 */
-               virtual bool UpdateChannelsStorage (int oldV, int newV) = 0;
+               virtual bool UpdateChannelsStorage (int oldV) = 0;
 
                /** @brief Update items storage section.
                 *
@@ -398,11 +396,10 @@
                 * stored in application settings is lower than newer one.
                 *
                 * @param[in] oldV Old storage version.
-                * @param[in] newV New storage version.
                 *
                 * @return true if update successful, else false.
                 */
-               virtual bool UpdateItemsStorage (int oldV, int newV) = 0;
+               virtual bool UpdateItemsStorage (int oldV) = 0;
 
                /** @brief Toggle channel state.
                 *
@@ -430,7 +427,7 @@
                 *
                 * This signal is emitted when a channel is updated.
                 *
-                * @warning StorageBackendManager::channelDataUpdated() should
+                * @warning StorageBackendManager::channelUnreadCountUpdated() 
should
                 * be used instead as it collects the signal from all
                 * instantiated storage managers.
                 *
@@ -438,7 +435,11 @@
                 *
                 * @sa StorageBackendManager
                 */
-               void channelDataUpdated (const Channel& channel) const;
+               void channelUnreadCountUpdated (IDType_t channelId, int 
unreadCount) const;
+
+               void channelDataUpdated (const Channel&) const;
+
+               void itemReadStatusUpdated (IDType_t channelId, IDType_t 
itemId, bool unread) const;
 
                /** @brief Notifies about updated item information.
                 *
@@ -449,12 +450,10 @@
                 * instantiated storage managers.
                 *
                 * @param[out] item Pointer to the updated item.
-                * @param[out] channel Pointer to the channel containing updated
-                * item.
                 *
                 * @sa StorageBackendManager
                 */
-               void itemDataUpdated (const Item& item, const Channel& channel) 
const;
+               void itemDataUpdated (const Item& item) const;
 
                /** @brief Notifies that a number of items was removed.
                 *
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/aggregator/storagebackendmanager.cpp
 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/aggregator/storagebackendmanager.cpp
--- 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/aggregator/storagebackendmanager.cpp
    2020-01-11 16:25:31.000000000 +0100
+++ 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/aggregator/storagebackendmanager.cpp
    2020-03-31 20:44:18.000000000 +0200
@@ -67,14 +67,17 @@
                        return StorageCreationResult_t::Left ({ s.what () });
                }
 
-               const int feedsTable = 1;
+               const int feedsTable = 2;
                const int channelsTable = 2;
                const int itemsTable = 6;
 
                auto runUpdate = [this, &strType] (auto updater, const char 
*suffix, int targetVersion)
                {
                        const auto curVersion = XmlSettingsManager::Instance 
()->Property (strType + suffix, targetVersion).toInt ();
-                       if (!std::invoke (updater, PrimaryStorageBackend_.get 
(), curVersion, targetVersion))
+                       if (curVersion == targetVersion)
+                               return true;
+
+                       if (!std::invoke (updater, PrimaryStorageBackend_.get 
(), curVersion))
                                return false;
 
                        XmlSettingsManager::Instance ()->setProperty (strType + 
suffix, targetVersion);
@@ -126,10 +129,18 @@
                                this,
                                &StorageBackendManager::channelAdded);
                connect (backendPtr,
+                               &StorageBackend::channelUnreadCountUpdated,
+                               this,
+                               
&StorageBackendManager::channelUnreadCountUpdated);
+               connect (backendPtr,
                                &StorageBackend::channelDataUpdated,
                                this,
                                &StorageBackendManager::channelDataUpdated);
                connect (backendPtr,
+                               &StorageBackend::itemReadStatusUpdated,
+                               this,
+                               &StorageBackendManager::itemReadStatusUpdated);
+               connect (backendPtr,
                                &StorageBackend::itemDataUpdated,
                                this,
                                &StorageBackendManager::itemDataUpdated);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/aggregator/storagebackendmanager.h
 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/aggregator/storagebackendmanager.h
--- 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/aggregator/storagebackendmanager.h
      2020-01-11 16:25:31.000000000 +0100
+++ 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/aggregator/storagebackendmanager.h
      2020-03-31 20:44:18.000000000 +0200
@@ -76,7 +76,11 @@
                 *
                 * @param[out] channel Pointer to the updated channel.
                 */
-               void channelDataUpdated (const Channel& channel) const;
+               void channelUnreadCountUpdated (IDType_t channelId, int 
unreadCount) const;
+
+               void channelDataUpdated (const Channel&) const;
+
+               void itemReadStatusUpdated (IDType_t channelId, IDType_t 
itemId, bool unread) const;
 
                /** @brief Notifies about updated item information.
                 *
@@ -84,10 +88,8 @@
                 * the instantiated StorageBackend objects.
                 *
                 * @param[out] item Pointer to the updated item.
-                * @param[out] channel Pointer to the channel containing updated
-                * item.
                 */
-               void itemDataUpdated (const Item& item, const Channel& channel) 
const;
+               void itemDataUpdated (const Item& item) const;
 
                /** @brief Notifies that a number of items was removed.
                 *
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/bittorrent/CMakeLists.txt 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/bittorrent/CMakeLists.txt
--- 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/bittorrent/CMakeLists.txt   
    2020-01-11 16:25:31.000000000 +0100
+++ 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/bittorrent/CMakeLists.txt   
    2020-03-31 20:44:18.000000000 +0200
@@ -12,10 +12,10 @@
 
 if (ENABLE_BITTORRENT_GEOIP)
        add_definitions (-DENABLE_GEOIP)
-       find_package (GeoIP REQUIRED)
+       find_package (MaxMindDB REQUIRED)
 else ()
-       set (GEOIP_INCLUDE_DIRS)
-       set (GEOIP_LIBRARIES)
+       set (MMDB_INCLUDE_DIRS)
+       set (MMDB_LIBRARIES)
 endif ()
 
 include_directories (${CMAKE_CURRENT_BINARY_DIR}
@@ -23,7 +23,7 @@
        ${Boost_INCLUDE_DIR}
        ${LibtorrentRasterbar_INCLUDE_DIRS}
        ${LEECHCRAFT_INCLUDE_DIR}
-       ${GEOIP_INCLUDE_DIRS}
+       ${MMDB_INCLUDE_DIRS}
        )
 
 set (SRCS
@@ -149,7 +149,7 @@
        ${QT_LIBRARIES}
        ${LibtorrentRasterbar_LIBRARIES}
        ${LEECHCRAFT_LIBRARIES}
-       ${GEOIP_LIBRARIES}
+       ${MMDB_LIBRARIES}
 )
 install (TARGETS leechcraft_bittorrent DESTINATION ${LC_PLUGINS_DEST})
 install (FILES torrentsettings.xml DESTINATION ${LC_SETTINGS_DEST})
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/bittorrent/cmake/FindGeoIP.cmake
 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/bittorrent/cmake/FindGeoIP.cmake
--- 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/bittorrent/cmake/FindGeoIP.cmake
        2020-01-11 16:25:31.000000000 +0100
+++ 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/bittorrent/cmake/FindGeoIP.cmake
        1970-01-01 01:00:00.000000000 +0100
@@ -1,67 +0,0 @@
-#
-# - Find GeoIP
-# Find the native GEOIP includes and library
-#
-#  GEOIP_INCLUDE_DIRS - where to find GeoIP.h, etc.
-#  GEOIP_LIBRARIES    - List of libraries when using GeoIP.
-#  GEOIP_FOUND        - True if GeoIP found.
-#  GEOIP_DLL_DIR      - (Windows) Path to the GeoIP DLL.
-#  GEOIP_DLL          - (Windows) Name of the GeoIP DLL.
-
-
-IF (GEOIP_INCLUDE_DIRS)
-  # Already in cache, be silent
-  SET(GEOIP_FIND_QUIETLY TRUE)
-ENDIF (GEOIP_INCLUDE_DIRS)
-
-find_package(PkgConfig)
-pkg_search_module(GEOIP geoip)
-
-FIND_PATH(GEOIP_INCLUDE_DIR GeoIP.h
-  HINTS
-    "${GEOIP_INCLUDEDIR}"
-    "${GEOIP_HINTS}/include"
-)
-
-SET(GEOIP_NAMES GeoIP libGeoIP-1)
-FIND_LIBRARY(GEOIP_LIBRARY NAMES ${GEOIP_NAMES}
-  HINTS
-    "${GEOIP_LIBDIR}"
-    "${GEOIP_HINTS}/lib"
-  )
-
-# handle the QUIETLY and REQUIRED arguments and set GEOIP_FOUND to TRUE if
-# all listed variables are TRUE
-INCLUDE(FindPackageHandleStandardArgs)
-FIND_PACKAGE_HANDLE_STANDARD_ARGS(GEOIP DEFAULT_MSG GEOIP_LIBRARY 
GEOIP_INCLUDE_DIR)
-
-IF(GEOIP_FOUND)
-  SET(GEOIP_LIBRARIES ${GEOIP_LIBRARY} )
-  SET(GEOIP_INCLUDE_DIRS ${GEOIP_INCLUDE_DIR} )
-  INCLUDE(CheckFunctionExists)
-  SET(CMAKE_REQUIRED_INCLUDES ${GEOIP_INCLUDE_DIRS})
-  SET(CMAKE_REQUIRED_LIBRARIES ${GEOIP_LIBRARIES})
-  CHECK_FUNCTION_EXISTS("GeoIP_country_name_by_ipnum_v6" HAVE_GEOIP_V6)
-  SET(CMAKE_REQUIRED_INCLUDES "")
-  SET(CMAKE_REQUIRED_LIBRARIES "")
-  if (WIN32)
-    set ( GEOIP_DLL_DIR "${GEOIP_HINTS}/bin"
-      CACHE PATH "Path to the GeoIP DLL"
-    )
-    file( GLOB _geoip_dll RELATIVE "${GEOIP_DLL_DIR}"
-      "${GEOIP_DLL_DIR}/libGeoIP-*.dll"
-    )
-    set ( GEOIP_DLL ${_geoip_dll}
-      # We're storing filenames only. Should we use STRING instead?
-      CACHE FILEPATH "GeoIP DLL file name"
-    )
-    mark_as_advanced( GEOIP_DLL_DIR GEOIP_DLL )
-  endif()
-ELSE(GEOIP_FOUND)
-  SET(GEOIP_LIBRARIES )
-  SET(GEOIP_INCLUDE_DIRS )
-  SET(GEOIP_DLL_DIR )
-  SET(GEOIP_DLL )
-ENDIF(GEOIP_FOUND)
-
-MARK_AS_ADVANCED( GEOIP_LIBRARIES GEOIP_INCLUDE_DIRS )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/bittorrent/cmake/FindMaxMindDB.cmake
 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/bittorrent/cmake/FindMaxMindDB.cmake
--- 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/bittorrent/cmake/FindMaxMindDB.cmake
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/bittorrent/cmake/FindMaxMindDB.cmake
    2020-03-31 20:44:18.000000000 +0200
@@ -0,0 +1,13 @@
+# - Find libmaxminddb
+# Find the native maxminddb includes and library
+#
+#  MMDB_INCLUDE_DIRS
+#  MMDB_LIBRARIES
+#  MMDB_FOUND
+
+find_package(PkgConfig QUIET)
+pkg_search_module(MMDB libmaxminddb)
+
+if (NOT MMDB_FOUND)
+       message (STATUS "Unable to find libmaxminddb via pkgconfig")
+endif ()
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/bittorrent/geoip.cpp 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/bittorrent/geoip.cpp
--- old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/bittorrent/geoip.cpp    
2020-01-11 16:25:31.000000000 +0100
+++ new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/bittorrent/geoip.cpp    
2020-03-31 20:44:18.000000000 +0200
@@ -34,17 +34,15 @@
 #include <util/sll/monad.h>
 
 #ifdef ENABLE_GEOIP
-#include <GeoIP.h>
+#include <maxminddb.h>
 #endif
 
-namespace LC
-{
-namespace BitTorrent
+namespace LC::BitTorrent
 {
 #ifdef ENABLE_GEOIP
        namespace
        {
-               boost::optional<QString> FindDB ()
+               std::optional<QString> FindDB ()
                {
                        const QStringList geoipCands
                        {
@@ -55,7 +53,7 @@
 
                        for (const auto& cand : geoipCands)
                        {
-                               const auto& name = cand + "/GeoIP.dat";
+                               const auto& name = cand + 
"/GeoLite2-Country.mmdb";
                                if (QFile::exists (name))
                                        return { name };
                        }
@@ -69,58 +67,58 @@
                using Util::operator>>;
 
                const auto maybeImpl = FindDB () >>
-                               [] (const QString& path) -> 
boost::optional<ImplPtr_t>
+                               [] (const QString& path) -> 
std::optional<ImplPtr_t>
                                {
-                                       const auto geoip = GeoIP_open 
(path.toStdString ().c_str (), GEOIP_STANDARD);
-
-                                       qDebug () << Q_FUNC_INFO << "loading 
GeoIP from" << path << geoip;
+                                       qDebug () << Q_FUNC_INFO << "loading 
GeoIP from" << path;
 
-                                       if (!geoip)
+                                       MMDB_s mmdb;
+                                       if (int status = MMDB_open 
(path.toStdString ().c_str (), MMDB_MODE_MMAP, &mmdb);
+                                               status != MMDB_SUCCESS)
+                                       {
+                                               qWarning () << Q_FUNC_INFO
+                                                               << "unable to 
load MaxMind DB from:"
+                                                               << status;
                                                return {};
-                                       return { { geoip, &GeoIP_delete } };
+                                       }
+
+                                       auto ptr = ImplPtr_t { new MMDB_s, 
&MMDB_close };
+                                       *ptr = mmdb;
+                                       return { ptr };
                                };
                Impl_ = maybeImpl.value_or (ImplPtr_t {});
        }
 
-       namespace
-       {
-               boost::optional<QString> Code2Str (const char *code)
-               {
-                       if (!code)
-                               return {};
-
-                       return QString::fromLatin1 (code, 2).toLower ();
-               }
-       }
-
-       boost::optional<QString> GeoIP::GetCountry (const libtorrent::address& 
addr) const
+       std::optional<QString> GeoIP::GetCountry (const libtorrent::address& 
addr) const
        {
                if (!Impl_)
                        return {};
 
-               if (addr.is_v4 ())
-                       return Code2Str (GeoIP_country_code_by_ipnum (Impl_.get 
(), addr.to_v4 ().to_ulong ()));
-
-               if (addr.is_v6 ())
+               int gai_error;
+               int mmdb_error;
+               auto entry = MMDB_lookup_string (Impl_.get (), addr.to_string 
().c_str (), &gai_error, &mmdb_error);
+               if (gai_error != 0 || mmdb_error != MMDB_SUCCESS)
                {
-                       const auto& bytes = addr.to_v6 ().to_bytes ();
-
-                       in6_addr in6addr;
-
-                       constexpr auto bytesSize = 
std::tuple_size<std::decay_t<decltype (bytes)>>::value;
-                       static_assert (sizeof (in6addr.__in6_u.__u6_addr8) == 
bytesSize,
-                                       "Unexpected IPv6 address size");
+                       qWarning () << Q_FUNC_INFO
+                                       << "unable to query MMDB for"
+                                       << addr.to_string ().c_str ();
+                       return {};
+               }
 
-                       std::copy (bytes.begin (), bytes.end (), 
&in6addr.__in6_u.__u6_addr8 [0]);
+               if (!entry.found_entry)
+                       return {};
 
-                       return Code2Str (GeoIP_country_code_by_ipnum_v6 
(Impl_.get (), in6addr));
+               MMDB_entry_data_s entryData;
+               if (int result = MMDB_get_value (&entry.entry, &entryData, 
"country", "iso_code", NULL);
+                       result != MMDB_SUCCESS || !entryData.has_data || 
!entryData.utf8_string)
+               {
+                       qWarning () << Q_FUNC_INFO
+                                       << "unable to query MMDB entry for the 
country iso code:"
+                                       << result
+                                       << entryData.has_data;
+                       return {};
                }
 
-               qWarning () << Q_FUNC_INFO
-                               << "the address is neither IPv4 nor IPv6"
-                               << addr.to_string ().c_str ();
-
-               return {};
+               return QString::fromLatin1 (entryData.utf8_string, 2).toLower 
();
        }
 #else
        GeoIP::GeoIP ()
@@ -133,4 +131,3 @@
        }
 #endif
 }
-}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/bittorrent/geoip.h 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/bittorrent/geoip.h
--- old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/bittorrent/geoip.h      
2020-01-11 16:25:31.000000000 +0100
+++ new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/bittorrent/geoip.h      
2020-03-31 20:44:18.000000000 +0200
@@ -30,24 +30,21 @@
 #pragma once
 
 #include <memory>
-#include <boost/optional.hpp>
+#include <optional>
 #include <QString>
 #include <libtorrent/address.hpp>
 
-struct GeoIPTag;
+struct MMDB_s;
 
-namespace LC
-{
-namespace BitTorrent
+namespace LC::BitTorrent
 {
        class GeoIP
        {
-               using ImplPtr_t = std::shared_ptr<GeoIPTag>;
+               using ImplPtr_t = std::shared_ptr<MMDB_s>;
                ImplPtr_t Impl_;
        public:
                GeoIP ();
 
-               boost::optional<QString> GetCountry (const 
libtorrent::address&) const;
+               std::optional<QString> GetCountry (const libtorrent::address&) 
const;
        };
 }
-}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/poshuku/plugins/webengineview/cookiessyncer.cpp
 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/poshuku/plugins/webengineview/cookiessyncer.cpp
--- 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/plugins/poshuku/plugins/webengineview/cookiessyncer.cpp
 2020-01-11 16:25:31.000000000 +0100
+++ 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/plugins/poshuku/plugins/webengineview/cookiessyncer.cpp
 2020-03-31 20:44:18.000000000 +0200
@@ -40,7 +40,7 @@
        : LCJar_ { lcJar }
        , WebEngineStore_ { weStore }
        {
-               WebEngineStore_->loadAllCookies ();
+               WebEngineStore_->deleteAllCookies ();
 
                HandleLCCookiesAdded (LCJar_->allCookies ());
 
@@ -93,7 +93,7 @@
 
        void CookiesSyncer::HandleWebEngineCookieRemoved (const QNetworkCookie& 
cookie)
        {
-               WebEngine2LCQueue_.removeOne (cookie);
+               WebEngine2LCQueue_.removeAll (cookie);
                LCJar_->deleteCookie (cookie);
        }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/util/db/consistencychecker.cpp 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/util/db/consistencychecker.cpp
--- old/leechcraft-0.6.70-13605-g8cd066ad6a/src/util/db/consistencychecker.cpp  
2020-01-11 16:25:31.000000000 +0100
+++ new/leechcraft-0.6.70-13641-g995b95b3d4/src/util/db/consistencychecker.cpp  
2020-03-31 20:44:18.000000000 +0200
@@ -105,7 +105,10 @@
                }
 
                QSqlQuery pragma { *db };
-               const auto isGood = pragma.exec ("PRAGMA integrity_check;") &&
+               const QString checkQuery = qgetenv ("LC_THOROUGH_SQLITE_CHECK") 
== "1" ?
+                               "PRAGMA integrity_check;" :
+                               "PRAGMA quick_check;";
+               const auto isGood = pragma.exec (checkQuery) &&
                                pragma.next () &&
                                pragma.value (0) == "ok";
                qDebug () << Q_FUNC_INFO
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/util/db/oral/oral.h 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/util/db/oral/oral.h
--- old/leechcraft-0.6.70-13605-g8cd066ad6a/src/util/db/oral/oral.h     
2020-01-11 16:25:31.000000000 +0100
+++ new/leechcraft-0.6.70-13641-g995b95b3d4/src/util/db/oral/oral.h     
2020-03-31 20:44:18.000000000 +0200
@@ -683,47 +683,6 @@
                        }
                };
 
-               template<auto... Ptr>
-               struct MemberPtrs {};
-
-               template<auto Ptr>
-               class ExprTree<ExprType::LeafStaticPlaceholder, 
MemberPtrs<Ptr>, void>
-               {
-               public:
-                       template<typename>
-                       using ValueType_t = MemberPtrType_t<Ptr>;
-
-                       template<typename T>
-                       QString ToSql (ToSqlState<T>&) const noexcept
-                       {
-                               return MemberPtrStruct_t<Ptr>::ClassName () + 
"." + GetFieldName ();
-                       }
-
-                       QString GetFieldName () const noexcept
-                       {
-                               return detail::GetFieldNamePtr<Ptr> ();
-                       }
-
-                       template<typename T>
-                       QSet<QString> AdditionalTables () const noexcept
-                       {
-                               using Seq = MemberPtrStruct_t<Ptr>;
-                               if constexpr (std::is_same_v<Seq, T>)
-                                       return {};
-                               else
-                                       return { Seq::ClassName () };
-                       }
-
-                       template<typename T>
-                       constexpr static bool HasAdditionalTables () noexcept
-                       {
-                               return !std::is_same_v<MemberPtrStruct_t<Ptr>, 
T>;
-                       }
-
-                       template<typename R>
-                       auto operator= (const R&) const noexcept;
-               };
-
                template<typename T>
                class ExprTree<ExprType::LeafData, T, void>
                {
@@ -733,7 +692,7 @@
                        using ValueType_t = T;
 
                        ExprTree (const T& t) noexcept
-                       : Data_ (t)
+                                       : Data_ (t)
                        {
                        }
 
@@ -758,11 +717,6 @@
                        }
                };
 
-               template<>
-               class ExprTree<ExprType::ConstTrue, void, void> {};
-
-               constexpr auto ConstTrueTree_v = ExprTree<ExprType::ConstTrue> 
{};
-
                template<typename T>
                constexpr auto AsLeafData (const T& node) noexcept
                {
@@ -772,12 +726,54 @@
                                return ExprTree<ExprType::LeafData, T> { node };
                }
 
+               template<auto... Ptr>
+               struct MemberPtrs {};
+
                template<auto Ptr>
-               template<typename R>
-               auto ExprTree<ExprType::LeafStaticPlaceholder, MemberPtrs<Ptr>, 
void>::operator= (const R& r) const noexcept
+               class ExprTree<ExprType::LeafStaticPlaceholder, 
MemberPtrs<Ptr>, void>
                {
-                       return AssignList { *this, AsLeafData (r) };
-               }
+                       using ExpectedType_t = MemberPtrType_t<Ptr>;
+               public:
+                       template<typename>
+                       using ValueType_t = ExpectedType_t;
+
+                       template<typename T>
+                       QString ToSql (ToSqlState<T>&) const noexcept
+                       {
+                               return MemberPtrStruct_t<Ptr>::ClassName () + 
"." + GetFieldName ();
+                       }
+
+                       QString GetFieldName () const noexcept
+                       {
+                               return detail::GetFieldNamePtr<Ptr> ();
+                       }
+
+                       template<typename T>
+                       QSet<QString> AdditionalTables () const noexcept
+                       {
+                               using Seq = MemberPtrStruct_t<Ptr>;
+                               if constexpr (std::is_same_v<Seq, T>)
+                                       return {};
+                               else
+                                       return { Seq::ClassName () };
+                       }
+
+                       template<typename T>
+                       constexpr static bool HasAdditionalTables () noexcept
+                       {
+                               return !std::is_same_v<MemberPtrStruct_t<Ptr>, 
T>;
+                       }
+
+                       auto operator= (const ExpectedType_t& r) const noexcept
+                       {
+                               return AssignList { *this, AsLeafData (r) };
+                       }
+               };
+
+               template<>
+               class ExprTree<ExprType::ConstTrue, void, void> {};
+
+               constexpr auto ConstTrueTree_v = ExprTree<ExprType::ConstTrue> 
{};
 
                template<ExprType Type, typename L, typename R>
                auto MakeExprTree (const L& left, const R& right) noexcept
@@ -883,7 +879,7 @@
                template<typename>
                auto HandleExprTree (const ExprTree<ExprType::ConstTrue>&, int 
lastId = 0) noexcept
                {
-                       return ExprTreeHandler { QString {}, Void {}, lastId };
+                       return ExprTreeHandler { "1 = 1", [] (auto&&) {}, 
lastId };
                }
 
                template<typename Seq, typename Tree,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/leechcraft-0.6.70-13605-g8cd066ad6a/src/util/network/customcookiejar.h 
new/leechcraft-0.6.70-13641-g995b95b3d4/src/util/network/customcookiejar.h
--- old/leechcraft-0.6.70-13605-g8cd066ad6a/src/util/network/customcookiejar.h  
2020-01-11 16:25:31.000000000 +0100
+++ new/leechcraft-0.6.70-13641-g995b95b3d4/src/util/network/customcookiejar.h  
2020-03-31 20:44:18.000000000 +0200
@@ -63,7 +63,7 @@
                 *
                 * @param[in] parent The parent object.
                 */
-               CustomCookieJar (QObject *parent = 0);
+               explicit CustomCookieJar (QObject *parent = nullptr);
 
                /** Enables or disables filtering tracking cookies.
                 *
@@ -147,7 +147,7 @@
                 * @param[in] url The url to return cookies for.
                 * @return The list of cookies, dup-free.
                 */
-               QList<QNetworkCookie> cookiesForUrl (const QUrl& url) const;
+               QList<QNetworkCookie> cookiesForUrl (const QUrl& url) const 
override;
 
                /** @brief Adds the cookieList for the given url to the jar.
                 *
@@ -157,7 +157,7 @@
                 * @param[in] url The url to set cookies for.
                 * @return Whether the jar has been modified as the result.
                 */
-               bool setCookiesFromUrl (const QList<QNetworkCookie>& 
cookieList, const QUrl& url);
+               bool setCookiesFromUrl (const QList<QNetworkCookie>& 
cookieList, const QUrl& url) override;
 
                using QNetworkCookieJar::allCookies;
                using QNetworkCookieJar::setAllCookies;


Reply via email to