Replace deprecated QSqlError::number with ::nativeErrorCode
[quassel.git] / src / core / sqlitestorage.cpp
index a6d7671..893bd77 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2005-2018 by the Quassel Project                        *
+ *   Copyright (C) 2005-2019 by the Quassel Project                        *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
 
 #include <QtSql>
 
-#include "logger.h"
 #include "network.h"
 #include "quassel.h"
 
 int SqliteStorage::_maxRetryCount = 150;
 
-SqliteStorage::SqliteStorage(QObject *parent)
+SqliteStorage::SqliteStorage(QObjectparent)
     : AbstractSqlStorage(parent)
-{
-}
-
-
-SqliteStorage::~SqliteStorage()
-{
-}
-
+{}
 
 bool SqliteStorage::isAvailable() const
 {
-    if (!QSqlDatabase::isDriverAvailable("QSQLITE")) return false;
+    if (!QSqlDatabase::isDriverAvailable("QSQLITE"))
+        return false;
     return true;
 }
 
-
 QString SqliteStorage::backendId() const
 {
     return QString("SQLite");
 }
 
-
 QString SqliteStorage::displayName() const
 {
     // Note: Pre-0.13 clients use the displayName property for backend idenfication
@@ -61,7 +52,6 @@ QString SqliteStorage::displayName() const
     return backendId();
 }
 
-
 QString SqliteStorage::description() const
 {
     return tr("SQLite is a file-based database engine that does not require any setup. It is suitable for small and medium-sized "
@@ -69,7 +59,6 @@ QString SqliteStorage::description() const
               "it is running on, and if you only expect a few users to use your core.");
 }
 
-
 int SqliteStorage::installedSchemaVersion()
 {
     // only used when there is a singlethread (during startup)
@@ -86,24 +75,40 @@ int SqliteStorage::installedSchemaVersion()
     return AbstractSqlStorage::installedSchemaVersion();
 }
 
-
-bool SqliteStorage::updateSchemaVersion(int newVersion)
+bool SqliteStorage::updateSchemaVersion(int newVersion, bool clearUpgradeStep)
 {
     // only used when there is a singlethread (during startup)
     // so we don't need locking here
-    QSqlQuery query(logDb());
+
+    QSqlDatabase db = logDb();
+
+    // Atomically update the schema version and clear the upgrade step, if specified
+    // Note: This will need reworked if "updateSchemaVersion" is ever called within a transaction.
+    db.transaction();
+
+    QSqlQuery query(db);
     query.prepare("UPDATE coreinfo SET value = :version WHERE key = 'schemaversion'");
     query.bindValue(":version", newVersion);
-    query.exec();
+    safeExec(query);
 
-    bool success = true;
-    if (query.lastError().isValid()) {
-        qCritical() << "SqliteStorage::updateSchemaVersion(int): Updating schema version failed!";
-        success = false;
+    if (!watchQuery(query)) {
+        qCritical() << "SqliteStorage::updateSchemaVersion(int, bool): Updating schema version failed!";
+        db.rollback();
+        return false;
+    }
+
+    if (clearUpgradeStep) {
+        // Try clearing the upgrade step if requested
+        if (!setSchemaVersionUpgradeStep("")) {
+            db.rollback();
+            return false;
+        }
     }
-    return success;
-}
 
+    // Successful, commit and return true
+    db.commit();
+    return true;
+}
 
 bool SqliteStorage::setupSchemaVersion(int version)
 {
@@ -122,8 +127,53 @@ bool SqliteStorage::setupSchemaVersion(int version)
     return success;
 }
 
+QString SqliteStorage::schemaVersionUpgradeStep()
+{
+    // Only used when there is a singlethread (during startup), so we don't need locking here
+    QSqlQuery query(logDb());
+    query.prepare("SELECT value FROM coreinfo WHERE key = 'schemaupgradestep'");
+    safeExec(query);
+    watchQuery(query);
+    if (query.first())
+        return query.value(0).toString();
+
+    // Fall back to the default value
+    return AbstractSqlStorage::schemaVersionUpgradeStep();
+}
 
-UserId SqliteStorage::addUser(const QString &user, const QString &password, const QString &authenticator)
+bool SqliteStorage::setSchemaVersionUpgradeStep(QString upgradeQuery)
+{
+    // Only used when there is a singlethread (during startup), so we don't need locking here
+
+    // Intentionally do not wrap in a transaction so other functions can include multiple operations
+    QSqlQuery query(logDb());
+    query.prepare("UPDATE coreinfo SET value = :upgradestep WHERE key = 'schemaupgradestep'");
+    query.bindValue(":upgradestep", upgradeQuery);
+    safeExec(query);
+
+    // Don't wrap with watchQuery to avoid an alarming message in the log when the key is missing
+    // Make sure that the query didn't fail, and that some non-zero number of rows were affected
+    bool success = !query.lastError().isValid() && query.numRowsAffected() != 0;
+
+    if (!success) {
+        // The key might not exist (Quassel 0.13.0 and older).  Try inserting it...
+        query = QSqlQuery(logDb());
+        query.prepare("INSERT INTO coreinfo (key, value) VALUES ('schemaupgradestep', :upgradestep)");
+        query.bindValue(":upgradestep", upgradeQuery);
+        safeExec(query);
+
+        if (!watchQuery(query)) {
+            qCritical() << Q_FUNC_INFO << "Setting schema upgrade step failed!";
+            success = false;
+        }
+        else {
+            success = true;
+        }
+    }
+    return success;
+}
+
+UserId SqliteStorage::addUser(const QString& user, const QString& password, const QString& authenticator)
 {
     QSqlDatabase db = logDb();
     UserId uid;
@@ -141,7 +191,8 @@ UserId SqliteStorage::addUser(const QString &user, const QString &password, cons
         query.bindValue(":authenticator", authenticator);
         lockForWrite();
         safeExec(query);
-        if (query.lastError().isValid() && query.lastError().number() == 19) { // user already exists - sadly 19 seems to be the general constraint violation error...
+        if (query.lastError().isValid()
+            && query.lastError().nativeErrorCode() == "19") {  // user already exists - sadly 19 seems to be the general constraint violation error...
             db.rollback();
         }
         else {
@@ -156,8 +207,7 @@ UserId SqliteStorage::addUser(const QString &user, const QString &password, cons
     return uid;
 }
 
-
-bool SqliteStorage::updateUser(UserId user, const QString &password)
+bool SqliteStorage::updateUser(UserId user, const QString& password)
 {
     QSqlDatabase db = logDb();
     bool success = false;
@@ -178,8 +228,7 @@ bool SqliteStorage::updateUser(UserId user, const QString &password)
     return success;
 }
 
-
-void SqliteStorage::renameUser(UserId user, const QString &newName)
+void SqliteStorage::renameUser(UserId user, const QString& newName)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -196,8 +245,7 @@ void SqliteStorage::renameUser(UserId user, const QString &newName)
     emit userRenamed(user, newName);
 }
 
-
-UserId SqliteStorage::validateUser(const QString &user, const QString &password)
+UserId SqliteStorage::validateUser(const QString& user, const QString& password)
 {
     UserId userId;
     QString hashedPassword;
@@ -226,8 +274,7 @@ UserId SqliteStorage::validateUser(const QString &user, const QString &password)
     return returnUserId;
 }
 
-
-UserId SqliteStorage::getUserId(const QString &username)
+UserId SqliteStorage::getUserId(const QString& username)
 {
     UserId userId;
 
@@ -288,7 +335,6 @@ UserId SqliteStorage::internalUser()
     return userId;
 }
 
-
 void SqliteStorage::delUser(UserId user)
 {
     QSqlDatabase db = logDb();
@@ -320,8 +366,7 @@ void SqliteStorage::delUser(UserId user)
     emit userRemoved(user);
 }
 
-
-void SqliteStorage::setUserSetting(UserId userId, const QString &settingName, const QVariant &data)
+void SqliteStorage::setUserSetting(UserId userId, const QString& settingName, const QVariant& data)
 {
     QByteArray rawData;
     QDataStream out(&rawData, QIODevice::WriteOnly);
@@ -352,8 +397,7 @@ void SqliteStorage::setUserSetting(UserId userId, const QString &settingName, co
     unlock();
 }
 
-
-QVariant SqliteStorage::getUserSetting(UserId userId, const QString &settingName, const QVariant &defaultData)
+QVariant SqliteStorage::getUserSetting(UserId userId, const QString& settingName, const QVariant& defaultData)
 {
     QVariant data = defaultData;
     {
@@ -375,8 +419,60 @@ QVariant SqliteStorage::getUserSetting(UserId userId, const QString &settingName
     return data;
 }
 
+void SqliteStorage::setCoreState(const QVariantList& data)
+{
+    QByteArray rawData;
+    QDataStream out(&rawData, QIODevice::WriteOnly);
+    out.setVersion(QDataStream::Qt_4_2);
+    out << data;
+
+    QSqlDatabase db = logDb();
+    db.transaction();
+    {
+        QSqlQuery query(db);
+        query.prepare(queryString("insert_core_state"));
+        query.bindValue(":key", "active_sessions");
+        query.bindValue(":value", rawData);
+        lockForWrite();
+        safeExec(query);
+
+        if (query.lastError().isValid()) {
+            QSqlQuery updateQuery(db);
+            updateQuery.prepare(queryString("update_core_state"));
+            updateQuery.bindValue(":key", "active_sessions");
+            updateQuery.bindValue(":value", rawData);
+            safeExec(updateQuery);
+        }
+        db.commit();
+    }
+    unlock();
+}
+
+QVariantList SqliteStorage::getCoreState(const QVariantList& defaultData)
+{
+    QVariantList data;
+    {
+        QSqlQuery query(logDb());
+        query.prepare(queryString("select_core_state"));
+        query.bindValue(":key", "active_sessions");
+        lockForRead();
+        safeExec(query);
+
+        if (query.first()) {
+            QByteArray rawData = query.value(0).toByteArray();
+            QDataStream in(&rawData, QIODevice::ReadOnly);
+            in.setVersion(QDataStream::Qt_4_2);
+            in >> data;
+        }
+        else {
+            data = defaultData;
+        }
+    }
+    unlock();
+    return data;
+}
 
-IdentityId SqliteStorage::createIdentity(UserId user, CoreIdentity &identity)
+IdentityId SqliteStorage::createIdentity(UserId user, CoreIdentityidentity)
 {
     IdentityId identityId;
 
@@ -427,7 +523,7 @@ IdentityId SqliteStorage::createIdentity(UserId user, CoreIdentity &identity)
 
             QSqlQuery insertNickQuery(db);
             insertNickQuery.prepare(queryString("insert_nick"));
-            foreach(QString nick, identity.nicks()) {
+            foreach (QString nick, identity.nicks()) {
                 insertNickQuery.bindValue(":identityid", identityId.toInt());
                 insertNickQuery.bindValue(":nick", nick);
                 safeExec(insertNickQuery);
@@ -440,8 +536,7 @@ IdentityId SqliteStorage::createIdentity(UserId user, CoreIdentity &identity)
     return identityId;
 }
 
-
-bool SqliteStorage::updateIdentity(UserId user, const CoreIdentity &identity)
+bool SqliteStorage::updateIdentity(UserId user, const CoreIdentity& identity)
 {
     QSqlDatabase db = logDb();
     bool error = false;
@@ -502,7 +597,7 @@ bool SqliteStorage::updateIdentity(UserId user, const CoreIdentity &identity)
 
         QSqlQuery insertNickQuery(db);
         insertNickQuery.prepare(queryString("insert_nick"));
-        foreach(QString nick, identity.nicks()) {
+        foreach (QString nick, identity.nicks()) {
             insertNickQuery.bindValue(":identityid", identity.id().toInt());
             insertNickQuery.bindValue(":nick", nick);
             safeExec(insertNickQuery);
@@ -514,7 +609,6 @@ bool SqliteStorage::updateIdentity(UserId user, const CoreIdentity &identity)
     return true;
 }
 
-
 void SqliteStorage::removeIdentity(UserId user, IdentityId identityId)
 {
     QSqlDatabase db = logDb();
@@ -553,7 +647,6 @@ void SqliteStorage::removeIdentity(UserId user, IdentityId identityId)
     unlock();
 }
 
-
 QList<CoreIdentity> SqliteStorage::identities(UserId user)
 {
     QList<CoreIdentity> identities;
@@ -612,8 +705,7 @@ QList<CoreIdentity> SqliteStorage::identities(UserId user)
     return identities;
 }
 
-
-NetworkId SqliteStorage::createNetwork(UserId user, const NetworkInfo &info)
+NetworkId SqliteStorage::createNetwork(UserId user, const NetworkInfo& info)
 {
     NetworkId networkId;
 
@@ -638,13 +730,13 @@ NetworkId SqliteStorage::createNetwork(UserId user, const NetworkInfo &info)
     }
     if (error) {
         unlock();
-        return NetworkId();
+        return {};
     }
 
     {
         QSqlQuery insertServersQuery(db);
         insertServersQuery.prepare(queryString("insert_server"));
-        foreach(Network::Server server, info.serverList) {
+        foreach (Network::Server server, info.serverList) {
             insertServersQuery.bindValue(":userid", user.toInt());
             insertServersQuery.bindValue(":networkid", networkId.toInt());
             bindServerInfo(insertServersQuery, server);
@@ -660,13 +752,12 @@ NetworkId SqliteStorage::createNetwork(UserId user, const NetworkInfo &info)
     }
     unlock();
     if (error)
-        return NetworkId();
+        return {};
     else
         return networkId;
 }
 
-
-void SqliteStorage::bindNetworkInfo(QSqlQuery &query, const NetworkInfo &info)
+void SqliteStorage::bindNetworkInfo(QSqlQuery& query, const NetworkInfo& info)
 {
     query.bindValue(":networkname", info.networkName);
     query.bindValue(":identityid", info.identity.toInt());
@@ -695,8 +786,7 @@ void SqliteStorage::bindNetworkInfo(QSqlQuery &query, const NetworkInfo &info)
         query.bindValue(":networkid", info.networkId.toInt());
 }
 
-
-void SqliteStorage::bindServerInfo(QSqlQuery &query, const Network::Server &server)
+void SqliteStorage::bindServerInfo(QSqlQuery& query, const Network::Server& server)
 {
     query.bindValue(":hostname", server.host);
     query.bindValue(":port", server.port);
@@ -712,8 +802,7 @@ void SqliteStorage::bindServerInfo(QSqlQuery &query, const Network::Server &serv
     query.bindValue(":sslverify", server.sslVerify ? 1 : 0);
 }
 
-
-bool SqliteStorage::updateNetwork(UserId user, const NetworkInfo &info)
+bool SqliteStorage::updateNetwork(UserId user, const NetworkInfo& info)
 {
     QSqlDatabase db = logDb();
     bool error = false;
@@ -755,7 +844,7 @@ bool SqliteStorage::updateNetwork(UserId user, const NetworkInfo &info)
     {
         QSqlQuery insertServersQuery(db);
         insertServersQuery.prepare(queryString("insert_server"));
-        foreach(Network::Server server, info.serverList) {
+        foreach (Network::Server server, info.serverList) {
             insertServersQuery.bindValue(":userid", user.toInt());
             insertServersQuery.bindValue(":networkid", info.networkId.toInt());
             bindServerInfo(insertServersQuery, server);
@@ -773,8 +862,7 @@ bool SqliteStorage::updateNetwork(UserId user, const NetworkInfo &info)
     return !error;
 }
 
-
-bool SqliteStorage::removeNetwork(UserId user, const NetworkId &networkId)
+bool SqliteStorage::removeNetwork(UserId user, const NetworkId& networkId)
 {
     QSqlDatabase db = logDb();
     bool error = false;
@@ -847,7 +935,6 @@ bool SqliteStorage::removeNetwork(UserId user, const NetworkId &networkId)
     return true;
 }
 
-
 QList<NetworkInfo> SqliteStorage::networks(UserId user)
 {
     QList<NetworkInfo> nets;
@@ -928,7 +1015,6 @@ QList<NetworkInfo> SqliteStorage::networks(UserId user)
     return nets;
 }
 
-
 QList<NetworkId> SqliteStorage::connectedNetworks(UserId user)
 {
     QList<NetworkId> connectedNets;
@@ -953,8 +1039,7 @@ QList<NetworkId> SqliteStorage::connectedNetworks(UserId user)
     return connectedNets;
 }
 
-
-void SqliteStorage::setNetworkConnected(UserId user, const NetworkId &networkId, bool isConnected)
+void SqliteStorage::setNetworkConnected(UserId user, const NetworkId& networkId, bool isConnected)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -974,8 +1059,7 @@ void SqliteStorage::setNetworkConnected(UserId user, const NetworkId &networkId,
     unlock();
 }
 
-
-QHash<QString, QString> SqliteStorage::persistentChannels(UserId user, const NetworkId &networkId)
+QHash<QString, QString> SqliteStorage::persistentChannels(UserId user, const NetworkId& networkId)
 {
     QHash<QString, QString> persistentChans;
 
@@ -998,8 +1082,7 @@ QHash<QString, QString> SqliteStorage::persistentChannels(UserId user, const Net
     return persistentChans;
 }
 
-
-void SqliteStorage::setChannelPersistent(UserId user, const NetworkId &networkId, const QString &channel, bool isJoined)
+void SqliteStorage::setChannelPersistent(UserId user, const NetworkId& networkId, const QString& channel, bool isJoined)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1020,8 +1103,7 @@ void SqliteStorage::setChannelPersistent(UserId user, const NetworkId &networkId
     unlock();
 }
 
-
-void SqliteStorage::setPersistentChannelKey(UserId user, const NetworkId &networkId, const QString &channel, const QString &key)
+void SqliteStorage::setPersistentChannelKey(UserId user, const NetworkId& networkId, const QString& channel, const QString& key)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1042,7 +1124,6 @@ void SqliteStorage::setPersistentChannelKey(UserId user, const NetworkId &networ
     unlock();
 }
 
-
 QString SqliteStorage::awayMessage(UserId user, NetworkId networkId)
 {
     QSqlDatabase db = logDb();
@@ -1067,8 +1148,7 @@ QString SqliteStorage::awayMessage(UserId user, NetworkId networkId)
     return awayMsg;
 }
 
-
-void SqliteStorage::setAwayMessage(UserId user, NetworkId networkId, const QString &awayMsg)
+void SqliteStorage::setAwayMessage(UserId user, NetworkId networkId, const QString& awayMsg)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1088,7 +1168,6 @@ void SqliteStorage::setAwayMessage(UserId user, NetworkId networkId, const QStri
     unlock();
 }
 
-
 QString SqliteStorage::userModes(UserId user, NetworkId networkId)
 {
     QSqlDatabase db = logDb();
@@ -1113,8 +1192,7 @@ QString SqliteStorage::userModes(UserId user, NetworkId networkId)
     return modes;
 }
 
-
-void SqliteStorage::setUserModes(UserId user, NetworkId networkId, const QString &userModes)
+void SqliteStorage::setUserModes(UserId user, NetworkId networkId, const QString& userModes)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1134,8 +1212,7 @@ void SqliteStorage::setUserModes(UserId user, NetworkId networkId, const QString
     unlock();
 }
 
-
-BufferInfo SqliteStorage::bufferInfo(UserId user, const NetworkId &networkId, BufferInfo::Type type, const QString &buffer, bool create)
+BufferInfo SqliteStorage::bufferInfo(UserId user, const NetworkId& networkId, BufferInfo::Type type, const QString& buffer, bool create)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1186,8 +1263,7 @@ BufferInfo SqliteStorage::bufferInfo(UserId user, const NetworkId &networkId, Bu
     return bufferInfo;
 }
 
-
-BufferInfo SqliteStorage::getBufferInfo(UserId user, const BufferId &bufferId)
+BufferInfo SqliteStorage::getBufferInfo(UserId user, const BufferId& bufferId)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1203,7 +1279,11 @@ BufferInfo SqliteStorage::getBufferInfo(UserId user, const BufferId &bufferId)
         safeExec(query);
 
         if (watchQuery(query) && query.first()) {
-            bufferInfo = BufferInfo(query.value(0).toInt(), query.value(1).toInt(), (BufferInfo::Type)query.value(2).toInt(), 0, query.value(4).toString());
+            bufferInfo = BufferInfo(query.value(0).toInt(),
+                                    query.value(1).toInt(),
+                                    (BufferInfo::Type)query.value(2).toInt(),
+                                    0,
+                                    query.value(4).toString());
             Q_ASSERT(!query.next());
         }
         db.commit();
@@ -1212,7 +1292,6 @@ BufferInfo SqliteStorage::getBufferInfo(UserId user, const BufferId &bufferId)
     return bufferInfo;
 }
 
-
 QList<BufferInfo> SqliteStorage::requestBuffers(UserId user)
 {
     QList<BufferInfo> bufferlist;
@@ -1229,7 +1308,11 @@ QList<BufferInfo> SqliteStorage::requestBuffers(UserId user)
         safeExec(query);
         watchQuery(query);
         while (query.next()) {
-            bufferlist << BufferInfo(query.value(0).toInt(), query.value(1).toInt(), (BufferInfo::Type)query.value(2).toInt(), query.value(3).toInt(), query.value(4).toString());
+            bufferlist << BufferInfo(query.value(0).toInt(),
+                                     query.value(1).toInt(),
+                                     (BufferInfo::Type)query.value(2).toInt(),
+                                     query.value(3).toInt(),
+                                     query.value(4).toString());
         }
         db.commit();
     }
@@ -1238,7 +1321,6 @@ QList<BufferInfo> SqliteStorage::requestBuffers(UserId user)
     return bufferlist;
 }
 
-
 QList<BufferId> SqliteStorage::requestBufferIdsForNetwork(UserId user, NetworkId networkId)
 {
     QList<BufferId> bufferList;
@@ -1265,8 +1347,7 @@ QList<BufferId> SqliteStorage::requestBufferIdsForNetwork(UserId user, NetworkId
     return bufferList;
 }
 
-
-bool SqliteStorage::removeBuffer(const UserId &user, const BufferId &bufferId)
+bool SqliteStorage::removeBuffer(const UserId& user, const BufferId& bufferId)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1309,8 +1390,7 @@ bool SqliteStorage::removeBuffer(const UserId &user, const BufferId &bufferId)
     return !error;
 }
 
-
-bool SqliteStorage::renameBuffer(const UserId &user, const BufferId &bufferId, const QString &newName)
+bool SqliteStorage::renameBuffer(const UserId& user, const BufferId& bufferId, const QString& newName)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1329,7 +1409,7 @@ bool SqliteStorage::renameBuffer(const UserId &user, const BufferId &bufferId, c
 
         error = query.lastError().isValid();
         // unexepcted error occured (19 == constraint violation)
-        if (error && query.lastError().number() != 19) {
+        if (error && query.lastError().nativeErrorCode() != "19") {
             watchQuery(query);
         }
         else {
@@ -1346,8 +1426,7 @@ bool SqliteStorage::renameBuffer(const UserId &user, const BufferId &bufferId, c
     return !error;
 }
 
-
-bool SqliteStorage::mergeBuffersPermanently(const UserId &user, const BufferId &bufferId1, const BufferId &bufferId2)
+bool SqliteStorage::mergeBuffersPermanently(const UserId& user, const BufferId& bufferId1, const BufferId& bufferId2)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1403,8 +1482,7 @@ bool SqliteStorage::mergeBuffersPermanently(const UserId &user, const BufferId &
     return !error;
 }
 
-
-void SqliteStorage::setBufferLastSeenMsg(UserId user, const BufferId &bufferId, const MsgId &msgId)
+void SqliteStorage::setBufferLastSeenMsg(UserId user, const BufferId& bufferId, const MsgId& msgId)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1414,7 +1492,7 @@ void SqliteStorage::setBufferLastSeenMsg(UserId user, const BufferId &bufferId,
         query.prepare(queryString("update_buffer_lastseen"));
         query.bindValue(":userid", user.toInt());
         query.bindValue(":bufferid", bufferId.toInt());
-        query.bindValue(":lastseenmsgid", msgId.toInt());
+        query.bindValue(":lastseenmsgid", msgId.toQint64());
 
         lockForWrite();
         safeExec(query);
@@ -1424,7 +1502,6 @@ void SqliteStorage::setBufferLastSeenMsg(UserId user, const BufferId &bufferId,
     unlock();
 }
 
-
 QHash<BufferId, MsgId> SqliteStorage::bufferLastSeenMsgIds(UserId user)
 {
     QHash<BufferId, MsgId> lastSeenHash;
@@ -1443,7 +1520,7 @@ QHash<BufferId, MsgId> SqliteStorage::bufferLastSeenMsgIds(UserId user)
         error = !watchQuery(query);
         if (!error) {
             while (query.next()) {
-                lastSeenHash[query.value(0).toInt()] = query.value(1).toInt();
+                lastSeenHash[query.value(0).toInt()] = query.value(1).toLongLong();
             }
         }
     }
@@ -1453,8 +1530,7 @@ QHash<BufferId, MsgId> SqliteStorage::bufferLastSeenMsgIds(UserId user)
     return lastSeenHash;
 }
 
-
-void SqliteStorage::setBufferMarkerLineMsg(UserId user, const BufferId &bufferId, const MsgId &msgId)
+void SqliteStorage::setBufferMarkerLineMsg(UserId user, const BufferId& bufferId, const MsgId& msgId)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1464,7 +1540,7 @@ void SqliteStorage::setBufferMarkerLineMsg(UserId user, const BufferId &bufferId
         query.prepare(queryString("update_buffer_markerlinemsgid"));
         query.bindValue(":userid", user.toInt());
         query.bindValue(":bufferid", bufferId.toInt());
-        query.bindValue(":markerlinemsgid", msgId.toInt());
+        query.bindValue(":markerlinemsgid", msgId.toQint64());
 
         lockForWrite();
         safeExec(query);
@@ -1474,7 +1550,6 @@ void SqliteStorage::setBufferMarkerLineMsg(UserId user, const BufferId &bufferId
     unlock();
 }
 
-
 QHash<BufferId, MsgId> SqliteStorage::bufferMarkerLineMsgIds(UserId user)
 {
     QHash<BufferId, MsgId> markerLineHash;
@@ -1493,7 +1568,7 @@ QHash<BufferId, MsgId> SqliteStorage::bufferMarkerLineMsgIds(UserId user)
         error = !watchQuery(query);
         if (!error) {
             while (query.next()) {
-                markerLineHash[query.value(0).toInt()] = query.value(1).toInt();
+                markerLineHash[query.value(0).toInt()] = query.value(1).toLongLong();
             }
         }
     }
@@ -1513,7 +1588,7 @@ void SqliteStorage::setBufferActivity(UserId user, BufferId bufferId, Message::T
         query.prepare(queryString("update_buffer_bufferactivity"));
         query.bindValue(":userid", user.toInt());
         query.bindValue(":bufferid", bufferId.toInt());
-        query.bindValue(":bufferactivity", (int) bufferActivity);
+        query.bindValue(":bufferactivity", (int)bufferActivity);
 
         lockForWrite();
         safeExec(query);
@@ -1523,7 +1598,6 @@ void SqliteStorage::setBufferActivity(UserId user, BufferId bufferId, Message::T
     unlock();
 }
 
-
 QHash<BufferId, Message::Types> SqliteStorage::bufferActivities(UserId user)
 {
     QHash<BufferId, Message::Types> bufferActivityHash;
@@ -1552,18 +1626,17 @@ QHash<BufferId, Message::Types> SqliteStorage::bufferActivities(UserId user)
     return bufferActivityHash;
 }
 
-
 Message::Types SqliteStorage::bufferActivity(BufferId bufferId, MsgId lastSeenMsgId)
 {
     QSqlDatabase db = logDb();
     db.transaction();
 
-    Message::Types result = Message::Types(0);
+    Message::Types result = Message::Types(nullptr);
     {
         QSqlQuery query(db);
         query.prepare(queryString("select_buffer_bufferactivity"));
         query.bindValue(":bufferid", bufferId.toInt());
-        query.bindValue(":lastseenmsgid", lastSeenMsgId.toInt());
+        query.bindValue(":lastseenmsgid", lastSeenMsgId.toQint64());
 
         lockForRead();
         safeExec(query);
@@ -1576,7 +1649,122 @@ Message::Types SqliteStorage::bufferActivity(BufferId bufferId, MsgId lastSeenMs
     return result;
 }
 
-bool SqliteStorage::logMessage(Message &msg)
+QHash<QString, QByteArray> SqliteStorage::bufferCiphers(UserId user, const NetworkId& networkId)
+{
+    QHash<QString, QByteArray> bufferCiphers;
+
+    QSqlDatabase db = logDb();
+    db.transaction();
+    {
+        QSqlQuery query(db);
+        query.prepare(queryString("select_buffer_ciphers"));
+        query.bindValue(":userid", user.toInt());
+        query.bindValue(":networkid", networkId.toInt());
+
+        lockForRead();
+        safeExec(query);
+        watchQuery(query);
+        while (query.next()) {
+            bufferCiphers[query.value(0).toString()] = QByteArray::fromHex(query.value(1).toString().toUtf8());
+        }
+    }
+    unlock();
+    return bufferCiphers;
+}
+
+void SqliteStorage::setBufferCipher(UserId user, const NetworkId& networkId, const QString& bufferName, const QByteArray& cipher)
+{
+    QSqlDatabase db = logDb();
+    db.transaction();
+
+    {
+        QSqlQuery query(db);
+        query.prepare(queryString("update_buffer_cipher"));
+        query.bindValue(":userid", user.toInt());
+        query.bindValue(":networkid", networkId.toInt());
+        query.bindValue(":buffercname", bufferName.toLower());
+        query.bindValue(":cipher", QString(cipher.toHex()));
+
+        lockForWrite();
+        safeExec(query);
+        watchQuery(query);
+        db.commit();
+    }
+    unlock();
+}
+
+void SqliteStorage::setHighlightCount(UserId user, BufferId bufferId, int count)
+{
+    QSqlDatabase db = logDb();
+    db.transaction();
+
+    {
+        QSqlQuery query(db);
+        query.prepare(queryString("update_buffer_highlightcount"));
+        query.bindValue(":userid", user.toInt());
+        query.bindValue(":bufferid", bufferId.toInt());
+        query.bindValue(":highlightcount", count);
+
+        lockForWrite();
+        safeExec(query);
+        watchQuery(query);
+    }
+    db.commit();
+    unlock();
+}
+
+QHash<BufferId, int> SqliteStorage::highlightCounts(UserId user)
+{
+    QHash<BufferId, int> highlightCountHash;
+
+    QSqlDatabase db = logDb();
+    db.transaction();
+
+    bool error = false;
+    {
+        QSqlQuery query(db);
+        query.prepare(queryString("select_buffer_highlightcounts"));
+        query.bindValue(":userid", user.toInt());
+
+        lockForRead();
+        safeExec(query);
+        error = !watchQuery(query);
+        if (!error) {
+            while (query.next()) {
+                highlightCountHash[query.value(0).toInt()] = query.value(1).toInt();
+            }
+        }
+    }
+
+    db.commit();
+    unlock();
+    return highlightCountHash;
+}
+
+int SqliteStorage::highlightCount(BufferId bufferId, MsgId lastSeenMsgId)
+{
+    QSqlDatabase db = logDb();
+    db.transaction();
+
+    int result = 0;
+    {
+        QSqlQuery query(db);
+        query.prepare(queryString("select_buffer_highlightcount"));
+        query.bindValue(":bufferid", bufferId.toInt());
+        query.bindValue(":lastseenmsgid", lastSeenMsgId.toQint64());
+
+        lockForRead();
+        safeExec(query);
+        if (query.first())
+            result = query.value(0).toInt();
+    }
+
+    db.commit();
+    unlock();
+    return result;
+}
+
+bool SqliteStorage::logMessage(Message& msg)
 {
     QSqlDatabase db = logDb();
     db.transaction();
@@ -1585,12 +1773,15 @@ bool SqliteStorage::logMessage(Message &msg)
     {
         QSqlQuery logMessageQuery(db);
         logMessageQuery.prepare(queryString("insert_message"));
-
-        logMessageQuery.bindValue(":time", msg.timestamp().toTime_t());
+        // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
+        // seconds.  This nets us more precision as well as simplifying 64-bit time.
+        logMessageQuery.bindValue(":time", msg.timestamp().toMSecsSinceEpoch());
         logMessageQuery.bindValue(":bufferid", msg.bufferInfo().bufferId().toInt());
         logMessageQuery.bindValue(":type", msg.type());
         logMessageQuery.bindValue(":flags", (int)msg.flags());
         logMessageQuery.bindValue(":sender", msg.sender());
+        logMessageQuery.bindValue(":realname", msg.realName());
+        logMessageQuery.bindValue(":avatarurl", msg.avatarUrl());
         logMessageQuery.bindValue(":senderprefixes", msg.senderPrefixes());
         logMessageQuery.bindValue(":message", msg.contents());
 
@@ -1599,10 +1790,12 @@ bool SqliteStorage::logMessage(Message &msg)
 
         if (logMessageQuery.lastError().isValid()) {
             // constraint violation - must be NOT NULL constraint - probably the sender is missing...
-            if (logMessageQuery.lastError().number() == 19) {
+            if (logMessageQuery.lastError().nativeErrorCode() == "19") {
                 QSqlQuery addSenderQuery(db);
                 addSenderQuery.prepare(queryString("insert_sender"));
                 addSenderQuery.bindValue(":sender", msg.sender());
+                addSenderQuery.bindValue(":realname", msg.realName());
+                addSenderQuery.bindValue(":avatarurl", msg.avatarUrl());
                 safeExec(addSenderQuery);
                 safeExec(logMessageQuery);
                 error = !watchQuery(logMessageQuery);
@@ -1612,7 +1805,7 @@ bool SqliteStorage::logMessage(Message &msg)
             }
         }
         if (!error) {
-            MsgId msgId = logMessageQuery.lastInsertId().toInt();
+            MsgId msgId = logMessageQuery.lastInsertId().toLongLong();
             if (msgId.isValid()) {
                 msg.setMsgId(msgId);
             }
@@ -1633,24 +1826,26 @@ bool SqliteStorage::logMessage(Message &msg)
     return !error;
 }
 
-
-bool SqliteStorage::logMessages(MessageList &msgs)
+bool SqliteStorage::logMessages(MessageList& msgs)
 {
     QSqlDatabase db = logDb();
     db.transaction();
 
     {
-        QSet<QString> senders;
+        QSet<SenderData> senders;
         QSqlQuery addSenderQuery(db);
         addSenderQuery.prepare(queryString("insert_sender"));
         lockForWrite();
         for (int i = 0; i < msgs.count(); i++) {
-            const QString &sender = msgs.at(i).sender();
+            auto& msg = msgs.at(i);
+            SenderData sender = {msg.sender(), msg.realName(), msg.avatarUrl()};
             if (senders.contains(sender))
                 continue;
             senders << sender;
 
-            addSenderQuery.bindValue(":sender", sender);
+            addSenderQuery.bindValue(":sender", sender.sender);
+            addSenderQuery.bindValue(":realname", sender.realname);
+            addSenderQuery.bindValue(":avatarurl", sender.avatarurl);
             safeExec(addSenderQuery);
         }
     }
@@ -1660,13 +1855,16 @@ bool SqliteStorage::logMessages(MessageList &msgs)
         QSqlQuery logMessageQuery(db);
         logMessageQuery.prepare(queryString("insert_message"));
         for (int i = 0; i < msgs.count(); i++) {
-            Message &msg = msgs[i];
-
-            logMessageQuery.bindValue(":time", msg.timestamp().toTime_t());
+            Message& msg = msgs[i];
+            // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
+            // seconds.  This nets us more precision as well as simplifying 64-bit time.
+            logMessageQuery.bindValue(":time", msg.timestamp().toMSecsSinceEpoch());
             logMessageQuery.bindValue(":bufferid", msg.bufferInfo().bufferId().toInt());
             logMessageQuery.bindValue(":type", msg.type());
             logMessageQuery.bindValue(":flags", (int)msg.flags());
             logMessageQuery.bindValue(":sender", msg.sender());
+            logMessageQuery.bindValue(":realname", msg.realName());
+            logMessageQuery.bindValue(":avatarurl", msg.avatarUrl());
             logMessageQuery.bindValue(":senderprefixes", msg.senderPrefixes());
             logMessageQuery.bindValue(":message", msg.contents());
 
@@ -1676,7 +1874,7 @@ bool SqliteStorage::logMessages(MessageList &msgs)
                 break;
             }
             else {
-                msg.setMsgId(logMessageQuery.lastInsertId().toInt());
+                msg.setMsgId(logMessageQuery.lastInsertId().toLongLong());
             }
         }
     }
@@ -1696,7 +1894,6 @@ bool SqliteStorage::logMessages(MessageList &msgs)
     return !error;
 }
 
-
 QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, MsgId first, MsgId last, int limit)
 {
     QList<Message> messagelist;
@@ -1707,7 +1904,7 @@ QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, MsgId
     bool error = false;
     BufferInfo bufferInfo;
     {
-        // code dupication from getBufferInfo:
+        // code duplication from getBufferInfo:
         // this is due to the impossibility of nesting transactions and recursive locking
         QSqlQuery bufferInfoQuery(db);
         bufferInfoQuery.prepare(queryString("select_buffer_by_id"));
@@ -1718,7 +1915,11 @@ QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, MsgId
         safeExec(bufferInfoQuery);
         error = !watchQuery(bufferInfoQuery) || !bufferInfoQuery.first();
         if (!error) {
-            bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(), bufferInfoQuery.value(1).toInt(), (BufferInfo::Type)bufferInfoQuery.value(2).toInt(), 0, bufferInfoQuery.value(4).toString());
+            bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
+                                    bufferInfoQuery.value(1).toInt(),
+                                    (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
+                                    0,
+                                    bufferInfoQuery.value(4).toString());
             error = !bufferInfo.isValid();
         }
     }
@@ -1735,12 +1936,12 @@ QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, MsgId
         }
         else if (last == -1) {
             query.prepare(queryString("select_messagesNewerThan"));
-            query.bindValue(":firstmsg", first.toInt());
+            query.bindValue(":firstmsg", first.toQint64());
         }
         else {
             query.prepare(queryString("select_messagesRange"));
-            query.bindValue(":lastmsg", last.toInt());
-            query.bindValue(":firstmsg", first.toInt());
+            query.bindValue(":lastmsg", last.toQint64());
+            query.bindValue(":firstmsg", first.toQint64());
         }
         query.bindValue(":bufferid", bufferId.toInt());
         query.bindValue(":limit", limit);
@@ -1749,14 +1950,19 @@ QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, MsgId
         watchQuery(query);
 
         while (query.next()) {
-            Message msg(QDateTime::fromTime_t(query.value(1).toInt()),
+            Message msg(
+                // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
+                // seconds.  This nets us more precision as well as simplifying 64-bit time.
+                QDateTime::fromMSecsSinceEpoch(query.value(1).toLongLong()),
                 bufferInfo,
-                (Message::Type)query.value(2).toUInt(),
-                query.value(6).toString(),
+                (Message::Type)query.value(2).toInt(),
+                query.value(8).toString(),
                 query.value(4).toString(),
                 query.value(5).toString(),
-                (Message::Flags)query.value(3).toUInt());
-            msg.setMsgId(query.value(0).toInt());
+                query.value(6).toString(),
+                query.value(7).toString(),
+                (Message::Flags)query.value(3).toInt());
+            msg.setMsgId(query.value(0).toLongLong());
             messagelist << msg;
         }
     }
@@ -1766,6 +1972,89 @@ QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, MsgId
     return messagelist;
 }
 
+QList<Message> SqliteStorage::requestMsgsFiltered(
+    UserId user, BufferId bufferId, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags)
+{
+    QList<Message> messagelist;
+
+    QSqlDatabase db = logDb();
+    db.transaction();
+
+    bool error = false;
+    BufferInfo bufferInfo;
+    {
+        // code dupication from getBufferInfo:
+        // this is due to the impossibility of nesting transactions and recursive locking
+        QSqlQuery bufferInfoQuery(db);
+        bufferInfoQuery.prepare(queryString("select_buffer_by_id"));
+        bufferInfoQuery.bindValue(":userid", user.toInt());
+        bufferInfoQuery.bindValue(":bufferid", bufferId.toInt());
+
+        lockForRead();
+        safeExec(bufferInfoQuery);
+        error = !watchQuery(bufferInfoQuery) || !bufferInfoQuery.first();
+        if (!error) {
+            bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
+                                    bufferInfoQuery.value(1).toInt(),
+                                    (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
+                                    0,
+                                    bufferInfoQuery.value(4).toString());
+            error = !bufferInfo.isValid();
+        }
+    }
+    if (error) {
+        db.rollback();
+        unlock();
+        return messagelist;
+    }
+
+    {
+        QSqlQuery query(db);
+        if (last == -1 && first == -1) {
+            query.prepare(queryString("select_messagesNewestK_filtered"));
+        }
+        else if (last == -1) {
+            query.prepare(queryString("select_messagesNewerThan_filtered"));
+            query.bindValue(":firstmsg", first.toQint64());
+        }
+        else {
+            query.prepare(queryString("select_messagesRange_filtered"));
+            query.bindValue(":lastmsg", last.toQint64());
+            query.bindValue(":firstmsg", first.toQint64());
+        }
+        query.bindValue(":bufferid", bufferId.toInt());
+        query.bindValue(":limit", limit);
+        int typeRaw = type;
+        query.bindValue(":type", typeRaw);
+        int flagsRaw = flags;
+        query.bindValue(":flags", flagsRaw);
+
+        safeExec(query);
+        watchQuery(query);
+
+        while (query.next()) {
+            Message msg(
+                // As of SQLite schema version 31, timestamps are stored in milliseconds
+                // instead of seconds.  This nets us more precision as well as simplifying
+                // 64-bit time.
+                QDateTime::fromMSecsSinceEpoch(query.value(1).toLongLong()),
+                bufferInfo,
+                (Message::Type)query.value(2).toInt(),
+                query.value(8).toString(),
+                query.value(4).toString(),
+                query.value(5).toString(),
+                query.value(6).toString(),
+                query.value(7).toString(),
+                Message::Flags{query.value(3).toInt()});
+            msg.setMsgId(query.value(0).toLongLong());
+            messagelist << msg;
+        }
+    }
+    db.commit();
+    unlock();
+
+    return messagelist;
+}
 
 QList<Message> SqliteStorage::requestAllMsgs(UserId user, MsgId first, MsgId last, int limit)
 {
@@ -1784,7 +2073,11 @@ QList<Message> SqliteStorage::requestAllMsgs(UserId user, MsgId first, MsgId las
         safeExec(bufferInfoQuery);
         watchQuery(bufferInfoQuery);
         while (bufferInfoQuery.next()) {
-            BufferInfo bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(), bufferInfoQuery.value(1).toInt(), (BufferInfo::Type)bufferInfoQuery.value(2).toInt(), bufferInfoQuery.value(3).toInt(), bufferInfoQuery.value(4).toString());
+            BufferInfo bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
+                                               bufferInfoQuery.value(1).toInt(),
+                                               (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
+                                               bufferInfoQuery.value(3).toInt(),
+                                               bufferInfoQuery.value(4).toString());
             bufferInfoHash[bufferInfo.bufferId()] = bufferInfo;
         }
 
@@ -1794,24 +2087,29 @@ QList<Message> SqliteStorage::requestAllMsgs(UserId user, MsgId first, MsgId las
         }
         else {
             query.prepare(queryString("select_messagesAll"));
-            query.bindValue(":lastmsg", last.toInt());
+            query.bindValue(":lastmsg", last.toQint64());
         }
         query.bindValue(":userid", user.toInt());
-        query.bindValue(":firstmsg", first.toInt());
+        query.bindValue(":firstmsg", first.toQint64());
         query.bindValue(":limit", limit);
         safeExec(query);
 
         watchQuery(query);
 
         while (query.next()) {
-            Message msg(QDateTime::fromTime_t(query.value(2).toInt()),
+            Message msg(
+                // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
+                // seconds.  This nets us more precision as well as simplifying 64-bit time.
+                QDateTime::fromMSecsSinceEpoch(query.value(2).toLongLong()),
                 bufferInfoHash[query.value(1).toInt()],
-                (Message::Type)query.value(3).toUInt(),
-                query.value(7).toString(),
+                (Message::Type)query.value(3).toInt(),
+                query.value(9).toString(),
                 query.value(5).toString(),
                 query.value(6).toString(),
-                (Message::Flags)query.value(4).toUInt());
-            msg.setMsgId(query.value(0).toInt());
+                query.value(7).toString(),
+                query.value(8).toString(),
+                (Message::Flags)query.value(4).toInt());
+            msg.setMsgId(query.value(0).toLongLong());
             messagelist << msg;
         }
     }
@@ -1820,6 +2118,72 @@ QList<Message> SqliteStorage::requestAllMsgs(UserId user, MsgId first, MsgId las
     return messagelist;
 }
 
+QList<Message> SqliteStorage::requestAllMsgsFiltered(UserId user, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags)
+{
+    QList<Message> messagelist;
+
+    QSqlDatabase db = logDb();
+    db.transaction();
+
+    QHash<BufferId, BufferInfo> bufferInfoHash;
+    {
+        QSqlQuery bufferInfoQuery(db);
+        bufferInfoQuery.prepare(queryString("select_buffers"));
+        bufferInfoQuery.bindValue(":userid", user.toInt());
+
+        lockForRead();
+        safeExec(bufferInfoQuery);
+        watchQuery(bufferInfoQuery);
+        while (bufferInfoQuery.next()) {
+            BufferInfo bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(),
+                                               bufferInfoQuery.value(1).toInt(),
+                                               (BufferInfo::Type)bufferInfoQuery.value(2).toInt(),
+                                               bufferInfoQuery.value(3).toInt(),
+                                               bufferInfoQuery.value(4).toString());
+            bufferInfoHash[bufferInfo.bufferId()] = bufferInfo;
+        }
+
+        QSqlQuery query(db);
+        if (last == -1) {
+            query.prepare(queryString("select_messagesAllNew_filtered"));
+        }
+        else {
+            query.prepare(queryString("select_messagesAll_filtered"));
+            query.bindValue(":lastmsg", last.toQint64());
+        }
+        query.bindValue(":userid", user.toInt());
+        query.bindValue(":firstmsg", first.toQint64());
+        query.bindValue(":limit", limit);
+        int typeRaw = type;
+        query.bindValue(":type", typeRaw);
+        int flagsRaw = flags;
+        query.bindValue(":flags", flagsRaw);
+        safeExec(query);
+
+        watchQuery(query);
+
+        while (query.next()) {
+            Message msg(
+                // As of SQLite schema version 31, timestamps are stored in milliseconds
+                // instead of seconds.  This nets us more precision as well as simplifying
+                // 64-bit time.
+                QDateTime::fromMSecsSinceEpoch(query.value(2).toLongLong()),
+                bufferInfoHash[query.value(1).toInt()],
+                (Message::Type)query.value(3).toInt(),
+                query.value(9).toString(),
+                query.value(5).toString(),
+                query.value(6).toString(),
+                query.value(7).toString(),
+                query.value(8).toString(),
+                Message::Flags{query.value(4).toInt()});
+            msg.setMsgId(query.value(0).toLongLong());
+            messagelist << msg;
+        }
+    }
+    db.commit();
+    unlock();
+    return messagelist;
+}
 
 QMap<UserId, QString> SqliteStorage::getAllAuthUserNames()
 {
@@ -1843,62 +2207,35 @@ QMap<UserId, QString> SqliteStorage::getAllAuthUserNames()
     return authusernames;
 }
 
-
-QString SqliteStorage::getAuthUserName(UserId user) {
-    QString authusername;
-    QSqlQuery query(logDb());
-    query.prepare(queryString("select_authusername"));
-    query.bindValue(":userid", user.toInt());
-
-    lockForRead();
-    safeExec(query);
-    watchQuery(query);
-    unlock();
-
-    if (query.first()) {
-        authusername = query.value(0).toString();
-    }
-
-    return authusername;
-}
-
-
 QString SqliteStorage::backlogFile()
 {
     return Quassel::configDirPath() + "quassel-storage.sqlite";
 }
 
-
-bool SqliteStorage::safeExec(QSqlQuery &query, int retryCount)
+bool SqliteStorage::safeExec(QSqlQuery& query, int retryCount)
 {
     query.exec();
 
     if (!query.lastError().isValid())
         return true;
 
-    switch (query.lastError().number()) {
-    case 5: // SQLITE_BUSY         5   /* The database file is locked */
-        [[clang::fallthrough]];
-    case 6: // SQLITE_LOCKED       6   /* A table in the database is locked */
+    QString nativeErrorCode = query.lastError().nativeErrorCode();
+
+    // SQLITE_BUSY         5   /* The database file is locked */
+    // SQLITE_LOCKED       6   /* A table in the database is locked */
+    if (nativeErrorCode == "5" || nativeErrorCode == "6") {
         if (retryCount < _maxRetryCount)
             return safeExec(query, retryCount + 1);
-        break;
-    default:
-        ;
     }
     return false;
 }
 
-
 // ========================================
 //  SqliteMigration
 // ========================================
 SqliteMigrationReader::SqliteMigrationReader()
-    : SqliteStorage(),
-    _maxId(0)
-{
-}
-
+    : SqliteStorage()
+{}
 
 void SqliteMigrationReader::setMaxId(MigrationObject mo)
 {
@@ -1916,10 +2253,9 @@ void SqliteMigrationReader::setMaxId(MigrationObject mo)
     }
     QSqlQuery query = logDb().exec(queryString);
     query.first();
-    _maxId = query.value(0).toInt();
+    _maxId = query.value(0).toLongLong();
 }
 
-
 bool SqliteMigrationReader::prepareQuery(MigrationObject mo)
 {
     setMaxId(mo);
@@ -1956,12 +2292,14 @@ bool SqliteMigrationReader::prepareQuery(MigrationObject mo)
     case UserSetting:
         newQuery(queryString("migrate_read_usersetting"), logDb());
         break;
+    case CoreState:
+        newQuery(queryString("migrate_read_corestate"), logDb());
+        break;
     }
     return exec();
 }
 
-
-bool SqliteMigrationReader::readMo(QuasselUserMO &user)
+bool SqliteMigrationReader::readMo(QuasselUserMO& user)
 {
     if (!next())
         return false;
@@ -1974,8 +2312,7 @@ bool SqliteMigrationReader::readMo(QuasselUserMO &user)
     return true;
 }
 
-
-bool SqliteMigrationReader::readMo(IdentityMO &identity)
+bool SqliteMigrationReader::readMo(IdentityMO& identity)
 {
     if (!next())
         return false;
@@ -1994,7 +2331,7 @@ bool SqliteMigrationReader::readMo(IdentityMO &identity)
     identity.autoAwayReasonEnabled = value(11).toInt() == 1 ? true : false;
     identity.detachAwayEnabled = value(12).toInt() == 1 ? true : false;
     identity.detachAwayReason = value(13).toString();
-    identity.detchAwayReasonEnabled = value(14).toInt() == 1 ? true : false;
+    identity.detachAwayReasonEnabled = value(14).toInt() == 1 ? true : false;
     identity.ident = value(15).toString();
     identity.kickReason = value(16).toString();
     identity.partReason = value(17).toString();
@@ -2004,8 +2341,7 @@ bool SqliteMigrationReader::readMo(IdentityMO &identity)
     return true;
 }
 
-
-bool SqliteMigrationReader::readMo(IdentityNickMO &identityNick)
+bool SqliteMigrationReader::readMo(IdentityNickMO& identityNick)
 {
     if (!next())
         return false;
@@ -2016,8 +2352,7 @@ bool SqliteMigrationReader::readMo(IdentityNickMO &identityNick)
     return true;
 }
 
-
-bool SqliteMigrationReader::readMo(NetworkMO &network)
+bool SqliteMigrationReader::readMo(NetworkMO& network)
 {
     if (!next())
         return false;
@@ -2055,8 +2390,7 @@ bool SqliteMigrationReader::readMo(NetworkMO &network)
     return true;
 }
 
-
-bool SqliteMigrationReader::readMo(BufferMO &buffer)
+bool SqliteMigrationReader::readMo(BufferMO& buffer)
 {
     if (!next())
         return false;
@@ -2068,17 +2402,18 @@ bool SqliteMigrationReader::readMo(BufferMO &buffer)
     buffer.buffername = value(4).toString();
     buffer.buffercname = value(5).toString();
     buffer.buffertype = value(6).toInt();
-    buffer.lastmsgid = value(7).toInt();
-    buffer.lastseenmsgid = value(8).toInt();
-    buffer.markerlinemsgid = value(9).toInt();
+    buffer.lastmsgid = value(7).toLongLong();
+    buffer.lastseenmsgid = value(8).toLongLong();
+    buffer.markerlinemsgid = value(9).toLongLong();
     buffer.bufferactivity = value(10).toInt();
-    buffer.key = value(11).toString();
-    buffer.joined = value(12).toInt() == 1 ? true : false;
+    buffer.highlightcount = value(11).toInt();
+    buffer.key = value(12).toString();
+    buffer.joined = value(13).toInt() == 1 ? true : false;
+    buffer.cipher = value(14).toString();
     return true;
 }
 
-
-bool SqliteMigrationReader::readMo(SenderMO &sender)
+bool SqliteMigrationReader::readMo(SenderMO& sender)
 {
     int skipSteps = 0;
     while (!next()) {
@@ -2094,19 +2429,20 @@ bool SqliteMigrationReader::readMo(SenderMO &sender)
         }
     }
 
-    sender.senderId = value(0).toInt();
+    sender.senderId = value(0).toLongLong();
     sender.sender = value(1).toString();
+    sender.realname = value(2).toString();
+    sender.avatarurl = value(3).toString();
     return true;
 }
 
-
-bool SqliteMigrationReader::readMo(BacklogMO &backlog)
+bool SqliteMigrationReader::readMo(BacklogMO& backlog)
 {
-    int skipSteps = 0;
+    qint64 skipSteps = 0;
     while (!next()) {
         if (backlog.messageid < _maxId) {
-            bindValue(0, backlog.messageid.toInt() + (skipSteps * stepSize()));
-            bindValue(1, backlog.messageid.toInt() + ((skipSteps + 1) * stepSize()));
+            bindValue(0, backlog.messageid.toQint64() + (skipSteps * stepSize()));
+            bindValue(1, backlog.messageid.toQint64() + ((skipSteps + 1) * stepSize()));
             skipSteps++;
             if (!exec())
                 return false;
@@ -2116,19 +2452,20 @@ bool SqliteMigrationReader::readMo(BacklogMO &backlog)
         }
     }
 
-    backlog.messageid = value(0).toInt();
-    backlog.time = QDateTime::fromTime_t(value(1).toInt()).toUTC();
+    backlog.messageid = value(0).toLongLong();
+    // As of SQLite schema version 31, timestamps are stored in milliseconds instead of
+    // seconds.  This nets us more precision as well as simplifying 64-bit time.
+    backlog.time = QDateTime::fromMSecsSinceEpoch(value(1).toLongLong()).toUTC();
     backlog.bufferid = value(2).toInt();
     backlog.type = value(3).toInt();
     backlog.flags = value(4).toInt();
-    backlog.senderid = value(5).toInt();
+    backlog.senderid = value(5).toLongLong();
     backlog.senderprefixes = value(6).toString();
     backlog.message = value(7).toString();
     return true;
 }
 
-
-bool SqliteMigrationReader::readMo(IrcServerMO &ircserver)
+bool SqliteMigrationReader::readMo(IrcServerMO& ircserver)
 {
     if (!next())
         return false;
@@ -2151,8 +2488,7 @@ bool SqliteMigrationReader::readMo(IrcServerMO &ircserver)
     return true;
 }
 
-
-bool SqliteMigrationReader::readMo(UserSettingMO &userSetting)
+bool SqliteMigrationReader::readMo(UserSettingMO& userSetting)
 {
     if (!next())
         return false;
@@ -2163,3 +2499,14 @@ bool SqliteMigrationReader::readMo(UserSettingMO &userSetting)
 
     return true;
 }
+
+bool SqliteMigrationReader::readMo(CoreStateMO& coreState)
+{
+    if (!next())
+        return false;
+
+    coreState.key = value(0).toString();
+    coreState.value = value(1).toByteArray();
+
+    return true;
+}