X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fcore%2Fpostgresqlstorage.cpp;h=40ff80cb1659a07c7dd65e86606d17afec48aebf;hp=3965704608ada44b5c5ef08847d7dfbdffb98b81;hb=f91f3faa1432894e6d6ecaaf0a1f60a483dd0129;hpb=bae8c28f27159ec803daff083da125b313c128d1 diff --git a/src/core/postgresqlstorage.cpp b/src/core/postgresqlstorage.cpp index 39657046..40ff80cb 100644 --- a/src/core/postgresqlstorage.cpp +++ b/src/core/postgresqlstorage.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005-2013 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 * @@ -22,49 +22,45 @@ #include -#include "logger.h" #include "network.h" #include "quassel.h" -PostgreSqlStorage::PostgreSqlStorage(QObject *parent) - : AbstractSqlStorage(parent), - _port(-1) -{ -} - - -PostgreSqlStorage::~PostgreSqlStorage() -{ -} - +PostgreSqlStorage::PostgreSqlStorage(QObject* parent) + : AbstractSqlStorage(parent) +{} -AbstractSqlMigrationWriter *PostgreSqlStorage::createMigrationWriter() +std::unique_ptr PostgreSqlStorage::createMigrationWriter() { - PostgreSqlMigrationWriter *writer = new PostgreSqlMigrationWriter(); + auto writer = new PostgreSqlMigrationWriter(); QVariantMap properties; properties["Username"] = _userName; properties["Password"] = _password; properties["Hostname"] = _hostName; properties["Port"] = _port; properties["Database"] = _databaseName; - writer->setConnectionProperties(properties); - return writer; + writer->setConnectionProperties(properties, {}, false); + return std::unique_ptr{writer}; } - bool PostgreSqlStorage::isAvailable() const { - qDebug() << QSqlDatabase::drivers(); - if (!QSqlDatabase::isDriverAvailable("QPSQL")) return false; + if (!QSqlDatabase::isDriverAvailable("QPSQL")) { + qWarning() << qPrintable(tr("PostgreSQL driver plugin not available for Qt. Installed drivers:")) + << qPrintable(QSqlDatabase::drivers().join(", ")); + return false; + } return true; } - -QString PostgreSqlStorage::displayName() const +QString PostgreSqlStorage::backendId() const { return QString("PostgreSQL"); } +QString PostgreSqlStorage::displayName() const +{ + return backendId(); // Note: Pre-0.13 clients use the displayName property for backend idenfication +} QString PostgreSqlStorage::description() const { @@ -72,101 +68,204 @@ QString PostgreSqlStorage::description() const return tr("PostgreSQL Turbo Bomber HD!"); } - -QStringList PostgreSqlStorage::setupKeys() const +QVariantList PostgreSqlStorage::setupData() const { - QStringList keys; - keys << "Username" - << "Password" - << "Hostname" - << "Port" - << "Database"; - return keys; + QVariantList data; + data << "Username" << tr("Username") << QString("quassel") << "Password" << tr("Password") << QString() << "Hostname" << tr("Hostname") + << QString("localhost") << "Port" << tr("Port") << 5432 << "Database" << tr("Database") << QString("quassel"); + return data; } - -QVariantMap PostgreSqlStorage::setupDefaults() const +bool PostgreSqlStorage::initDbSession(QSqlDatabase& db) { - QVariantMap map; - map["Username"] = QVariant(QString("quassel")); - map["Hostname"] = QVariant(QString("localhost")); - map["Port"] = QVariant(5432); - map["Database"] = QVariant(QString("quassel")); - return map; -} + // check whether the Qt driver performs string escaping or not. + // i.e. test if it doubles slashes. + QSqlField testField; + testField.setType(QVariant::String); + testField.setValue("\\"); + QString formattedString = db.driver()->formatValue(testField); + switch (formattedString.count('\\')) { + case 2: + // yes it does... and we cannot do anything to change the behavior of Qt. + // If this is a legacy DB (Postgres < 8.2), then everything is already ok, + // as this is the expected behavior. + // If it is a newer version, switch to legacy mode. + qWarning() << "Switching Postgres to legacy mode. (set standard conforming strings to off)"; + // If the following calls fail, it is a legacy DB anyways, so it doesn't matter + // and no need to check the outcome. + db.exec("set standard_conforming_strings = off"); + db.exec("set escape_string_warning = off"); + break; + case 1: + // ok, so Qt does not escape... + // That means we have to ensure that postgres uses standard conforming strings... + { + QSqlQuery query = db.exec("set standard_conforming_strings = on"); + if (query.lastError().isValid()) { + // We cannot enable standard conforming strings... + // since Quassel does no escaping by itself, this would yield a major vulnerability. + qCritical() << "Failed to enable standard_conforming_strings for the Postgres db!"; + return false; + } + } + break; + default: + // The slash got replaced with 0 or more than 2 slashes! o_O + qCritical() << "Your version of Qt does something _VERY_ strange to slashes in QSqlQueries! You should consult your trusted doctor!"; + return false; + break; + } -void PostgreSqlStorage::initDbSession(QSqlDatabase &db) -{ - // this blows... but unfortunately Qt's PG driver forces us to this... - db.exec("set standard_conforming_strings = off"); - db.exec("set escape_string_warning = off"); -} + // Set the PostgreSQL session timezone to UTC, since we want timestamps stored in UTC + QSqlQuery tzQuery = db.exec("SET timezone = 'UTC'"); + if (tzQuery.lastError().isValid()) { + qCritical() << "Failed to set timezone to UTC!"; + return false; + } + return true; +} -void PostgreSqlStorage::setConnectionProperties(const QVariantMap &properties) +void PostgreSqlStorage::setConnectionProperties(const QVariantMap& properties, const QProcessEnvironment& environment, bool loadFromEnvironment) { - _userName = properties["Username"].toString(); - _password = properties["Password"].toString(); - _hostName = properties["Hostname"].toString(); - _port = properties["Port"].toInt(); - _databaseName = properties["Database"].toString(); + if (loadFromEnvironment) { + _userName = environment.value("DB_PGSQL_USERNAME"); + _password = environment.value("DB_PGSQL_PASSWORD"); + _hostName = environment.value("DB_PGSQL_HOSTNAME"); + _port = environment.value("DB_PGSQL_PORT").toInt(); + _databaseName = environment.value("DB_PGSQL_DATABASE"); + } + else { + _userName = properties["Username"].toString(); + _password = properties["Password"].toString(); + _hostName = properties["Hostname"].toString(); + _port = properties["Port"].toInt(); + _databaseName = properties["Database"].toString(); + } } - int PostgreSqlStorage::installedSchemaVersion() { - QSqlQuery query = logDb().exec("SELECT value FROM coreinfo WHERE key = 'schemaversion'"); + QSqlQuery query(logDb()); + query.prepare("SELECT value FROM coreinfo WHERE key = 'schemaversion'"); + safeExec(query); + watchQuery(query); if (query.first()) return query.value(0).toInt(); // maybe it's really old... (schema version 0) - query = logDb().exec("SELECT MAX(version) FROM coreinfo"); + query.prepare("SELECT MAX(version) FROM coreinfo"); + safeExec(query); + watchQuery(query); if (query.first()) return query.value(0).toInt(); return AbstractSqlStorage::installedSchemaVersion(); } - -bool PostgreSqlStorage::updateSchemaVersion(int newVersion) +bool PostgreSqlStorage::updateSchemaVersion(int newVersion, bool clearUpgradeStep) { - QSqlQuery query(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. + QSqlDatabase db = logDb(); + if (!beginTransaction(db)) { + qWarning() << "PostgreSqlStorage::updateSchemaVersion(int, bool): cannot start transaction!"; + qWarning() << " -" << qPrintable(db.lastError().text()); + return false; + } + + 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() << "PostgreSqlStorage::updateSchemaVersion(int): Updating schema version failed!"; - success = false; + if (!watchQuery(query)) { + qCritical() << "PostgreSqlStorage::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 PostgreSqlStorage::setupSchemaVersion(int version) { QSqlQuery query(logDb()); query.prepare("INSERT INTO coreinfo (key, value) VALUES ('schemaversion', :version)"); query.bindValue(":version", version); - query.exec(); + safeExec(query); bool success = true; - if (query.lastError().isValid()) { + if (!watchQuery(query)) { qCritical() << "PostgreSqlStorage::setupSchemaVersion(int): Updating schema version failed!"; success = false; } return success; } +QString PostgreSqlStorage::schemaVersionUpgradeStep() +{ + QSqlQuery query(logDb()); + query.prepare("SELECT value FROM coreinfo WHERE key = 'schemaupgradestep'"); + safeExec(query); + watchQuery(query); + if (query.first()) + return query.value(0).toString(); -UserId PostgreSqlStorage::addUser(const QString &user, const QString &password) + // Fall back to the default value + return AbstractSqlStorage::schemaVersionUpgradeStep(); +} + +bool PostgreSqlStorage::setSchemaVersionUpgradeStep(QString upgradeQuery) +{ + // 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); + + // Make sure that the query didn't fail (shouldn't ever happen), and that some non-zero number + // of rows were affected + bool success = watchQuery(query) && 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 PostgreSqlStorage::addUser(const QString& user, const QString& password, const QString& authenticator) { QSqlQuery query(logDb()); query.prepare(queryString("insert_quasseluser")); query.bindValue(":username", user); - query.bindValue(":password", cryptedPassword(password)); + query.bindValue(":password", hashPassword(password)); + query.bindValue(":hashversion", Storage::HashVersion::Latest); + query.bindValue(":authenticator", authenticator); safeExec(query); if (!watchQuery(query)) return 0; @@ -177,38 +276,42 @@ UserId PostgreSqlStorage::addUser(const QString &user, const QString &password) return uid; } - -bool PostgreSqlStorage::updateUser(UserId user, const QString &password) +bool PostgreSqlStorage::updateUser(UserId user, const QString& password) { QSqlQuery query(logDb()); query.prepare(queryString("update_userpassword")); query.bindValue(":userid", user.toInt()); - query.bindValue(":password", cryptedPassword(password)); + query.bindValue(":password", hashPassword(password)); + query.bindValue(":hashversion", Storage::HashVersion::Latest); safeExec(query); + watchQuery(query); return query.numRowsAffected() != 0; } - -void PostgreSqlStorage::renameUser(UserId user, const QString &newName) +void PostgreSqlStorage::renameUser(UserId user, const QString& newName) { QSqlQuery query(logDb()); query.prepare(queryString("update_username")); query.bindValue(":userid", user.toInt()); query.bindValue(":username", newName); safeExec(query); + watchQuery(query); emit userRenamed(user, newName); } - -UserId PostgreSqlStorage::validateUser(const QString &user, const QString &password) +UserId PostgreSqlStorage::validateUser(const QString& user, const QString& password) { QSqlQuery query(logDb()); query.prepare(queryString("select_authuser")); query.bindValue(":username", user); - query.bindValue(":password", cryptedPassword(password)); safeExec(query); + watchQuery(query); - if (query.first()) { + if (query.first() + && checkHashedPassword(query.value(0).toInt(), + password, + query.value(1).toString(), + static_cast(query.value(2).toInt()))) { return query.value(0).toInt(); } else { @@ -216,13 +319,13 @@ UserId PostgreSqlStorage::validateUser(const QString &user, const QString &passw } } - -UserId PostgreSqlStorage::getUserId(const QString &user) +UserId PostgreSqlStorage::getUserId(const QString& user) { QSqlQuery query(logDb()); query.prepare(queryString("select_userid")); query.bindValue(":username", user); safeExec(query); + watchQuery(query); if (query.first()) { return query.value(0).toInt(); @@ -232,12 +335,28 @@ UserId PostgreSqlStorage::getUserId(const QString &user) } } +QString PostgreSqlStorage::getUserAuthenticator(const UserId userid) +{ + QSqlQuery query(logDb()); + query.prepare(queryString("select_authenticator")); + query.bindValue(":userid", userid.toInt()); + safeExec(query); + watchQuery(query); + + if (query.first()) { + return query.value(0).toString(); + } + else { + return QString(""); + } +} UserId PostgreSqlStorage::internalUser() { QSqlQuery query(logDb()); query.prepare(queryString("select_internaluser")); safeExec(query); + watchQuery(query); if (query.first()) { return query.value(0).toInt(); @@ -247,11 +366,10 @@ UserId PostgreSqlStorage::internalUser() } } - void PostgreSqlStorage::delUser(UserId user) { QSqlDatabase db = logDb(); - if (!db.transaction()) { + if (!beginTransaction(db)) { qWarning() << "PostgreSqlStorage::delUser(): cannot start transaction!"; return; } @@ -270,8 +388,7 @@ void PostgreSqlStorage::delUser(UserId user) } } - -void PostgreSqlStorage::setUserSetting(UserId userId, const QString &settingName, const QVariant &data) +void PostgreSqlStorage::setUserSetting(UserId userId, const QString& settingName, const QVariant& data) { QByteArray rawData; QDataStream out(&rawData, QIODevice::WriteOnly); @@ -284,6 +401,7 @@ void PostgreSqlStorage::setUserSetting(UserId userId, const QString &settingName selectQuery.bindValue(":userid", userId.toInt()); selectQuery.bindValue(":settingname", settingName); safeExec(selectQuery); + watchQuery(selectQuery); QString setQueryString; if (!selectQuery.first()) { @@ -299,16 +417,17 @@ void PostgreSqlStorage::setUserSetting(UserId userId, const QString &settingName setQuery.bindValue(":settingname", settingName); setQuery.bindValue(":settingvalue", rawData); safeExec(setQuery); + watchQuery(setQuery); } - -QVariant PostgreSqlStorage::getUserSetting(UserId userId, const QString &settingName, const QVariant &defaultData) +QVariant PostgreSqlStorage::getUserSetting(UserId userId, const QString& settingName, const QVariant& defaultData) { QSqlQuery query(logDb()); query.prepare(queryString("select_user_setting")); query.bindValue(":userid", userId.toInt()); query.bindValue(":settingname", settingName); safeExec(query); + watchQuery(query); if (query.first()) { QVariant data; @@ -323,13 +442,63 @@ QVariant PostgreSqlStorage::getUserSetting(UserId userId, const QString &setting } } +void PostgreSqlStorage::setCoreState(const QVariantList& data) +{ + QByteArray rawData; + QDataStream out(&rawData, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_2); + out << data; + + QSqlDatabase db = logDb(); + QSqlQuery selectQuery(db); + selectQuery.prepare(queryString("select_core_state")); + selectQuery.bindValue(":key", "active_sessions"); + safeExec(selectQuery); + watchQuery(selectQuery); + + QString setQueryString; + if (!selectQuery.first()) { + setQueryString = queryString("insert_core_state"); + } + else { + setQueryString = queryString("update_core_state"); + } + + QSqlQuery setQuery(db); + setQuery.prepare(setQueryString); + setQuery.bindValue(":key", "active_sessions"); + setQuery.bindValue(":value", rawData); + safeExec(setQuery); + watchQuery(setQuery); +} -IdentityId PostgreSqlStorage::createIdentity(UserId user, CoreIdentity &identity) +QVariantList PostgreSqlStorage::getCoreState(const QVariantList& defaultData) +{ + QSqlQuery query(logDb()); + query.prepare(queryString("select_core_state")); + query.bindValue(":key", "active_sessions"); + safeExec(query); + watchQuery(query); + + if (query.first()) { + QVariantList data; + QByteArray rawData = query.value(0).toByteArray(); + QDataStream in(&rawData, QIODevice::ReadOnly); + in.setVersion(QDataStream::Qt_4_2); + in >> data; + return data; + } + else { + return defaultData; + } +} + +IdentityId PostgreSqlStorage::createIdentity(UserId user, CoreIdentity& identity) { IdentityId identityId; QSqlDatabase db = logDb(); - if (!db.transaction()) { + if (!beginTransaction(db)) { qWarning() << "PostgreSqlStorage::createIdentity(): Unable to start Transaction!"; qWarning() << " -" << qPrintable(db.lastError().text()); return identityId; @@ -363,10 +532,9 @@ IdentityId PostgreSqlStorage::createIdentity(UserId user, CoreIdentity &identity query.bindValue(":sslkey", QByteArray()); #endif safeExec(query); - if (query.lastError().isValid()) { - watchQuery(query); + if (!watchQuery(query)) { db.rollback(); - return IdentityId(); + return {}; } query.first(); @@ -374,36 +542,34 @@ IdentityId PostgreSqlStorage::createIdentity(UserId user, CoreIdentity &identity identity.setId(identityId); if (!identityId.isValid()) { - watchQuery(query); db.rollback(); - return IdentityId(); + return {}; } 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); if (!watchQuery(insertNickQuery)) { db.rollback(); - return IdentityId(); + return {}; } } if (!db.commit()) { qWarning() << "PostgreSqlStorage::createIdentity(): committing data failed!"; qWarning() << " -" << qPrintable(db.lastError().text()); - return IdentityId(); + return {}; } return identityId; } - -bool PostgreSqlStorage::updateIdentity(UserId user, const CoreIdentity &identity) +bool PostgreSqlStorage::updateIdentity(UserId user, const CoreIdentity& identity) { QSqlDatabase db = logDb(); - if (!db.transaction()) { + if (!beginTransaction(db)) { qWarning() << "PostgreSqlStorage::updateIdentity(): Unable to start Transaction!"; qWarning() << " -" << qPrintable(db.lastError().text()); return false; @@ -414,6 +580,7 @@ bool PostgreSqlStorage::updateIdentity(UserId user, const CoreIdentity &identity checkQuery.bindValue(":identityid", identity.id().toInt()); checkQuery.bindValue(":userid", user.toInt()); safeExec(checkQuery); + watchQuery(checkQuery); // there should be exactly one identity for the given id and user if (!checkQuery.first() || checkQuery.value(0).toInt() != 1) { @@ -466,7 +633,7 @@ bool PostgreSqlStorage::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); @@ -484,11 +651,10 @@ bool PostgreSqlStorage::updateIdentity(UserId user, const CoreIdentity &identity return true; } - void PostgreSqlStorage::removeIdentity(UserId user, IdentityId identityId) { QSqlDatabase db = logDb(); - if (!db.transaction()) { + if (!beginTransaction(db)) { qWarning() << "PostgreSqlStorage::removeIdentity(): Unable to start Transaction!"; qWarning() << " -" << qPrintable(db.lastError().text()); return; @@ -507,7 +673,6 @@ void PostgreSqlStorage::removeIdentity(UserId user, IdentityId identityId) } } - QList PostgreSqlStorage::identities(UserId user) { QList identities; @@ -527,6 +692,7 @@ QList PostgreSqlStorage::identities(UserId user) nickQuery.prepare(queryString("select_nicks")); safeExec(query); + watchQuery(query); while (query.next()) { CoreIdentity identity(IdentityId(query.value(0).toInt())); @@ -567,13 +733,12 @@ QList PostgreSqlStorage::identities(UserId user) return identities; } - -NetworkId PostgreSqlStorage::createNetwork(UserId user, const NetworkInfo &info) +NetworkId PostgreSqlStorage::createNetwork(UserId user, const NetworkInfo& info) { NetworkId networkId; QSqlDatabase db = logDb(); - if (!db.transaction()) { + if (!beginTransaction(db)) { qWarning() << "PostgreSqlStorage::createNetwork(): failed to begin transaction!"; qWarning() << " -" << qPrintable(db.lastError().text()); return false; @@ -584,44 +749,41 @@ NetworkId PostgreSqlStorage::createNetwork(UserId user, const NetworkInfo &info) query.bindValue(":userid", user.toInt()); bindNetworkInfo(query, info); safeExec(query); - if (query.lastError().isValid()) { - watchQuery(query); + if (!watchQuery(query)) { db.rollback(); - return NetworkId(); + return {}; } query.first(); networkId = query.value(0).toInt(); if (!networkId.isValid()) { - watchQuery(query); db.rollback(); - 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); safeExec(insertServersQuery); if (!watchQuery(insertServersQuery)) { db.rollback(); - return NetworkId(); + return {}; } } if (!db.commit()) { qWarning() << "PostgreSqlStorage::createNetwork(): committing data failed!"; qWarning() << " -" << qPrintable(db.lastError().text()); - return NetworkId(); + return {}; } return networkId; } - -void PostgreSqlStorage::bindNetworkInfo(QSqlQuery &query, const NetworkInfo &info) +void PostgreSqlStorage::bindNetworkInfo(QSqlQuery& query, const NetworkInfo& info) { query.bindValue(":networkname", info.networkName); query.bindValue(":identityid", info.identity.isValid() ? info.identity.toInt() : QVariant()); @@ -641,12 +803,16 @@ void PostgreSqlStorage::bindNetworkInfo(QSqlQuery &query, const NetworkInfo &inf query.bindValue(":autoreconnectretries", info.autoReconnectRetries); query.bindValue(":unlimitedconnectretries", info.unlimitedReconnectRetries); query.bindValue(":rejoinchannels", info.rejoinChannels); + // Custom rate limiting + query.bindValue(":usecustomessagerate", info.useCustomMessageRate); + query.bindValue(":messagerateburstsize", info.messageRateBurstSize); + query.bindValue(":messageratedelay", info.messageRateDelay); + query.bindValue(":unlimitedmessagerate", info.unlimitedMessageRate); if (info.networkId.isValid()) query.bindValue(":networkid", info.networkId.toInt()); } - -void PostgreSqlStorage::bindServerInfo(QSqlQuery &query, const Network::Server &server) +void PostgreSqlStorage::bindServerInfo(QSqlQuery& query, const Network::Server& server) { query.bindValue(":hostname", server.host); query.bindValue(":port", server.port); @@ -659,13 +825,13 @@ void PostgreSqlStorage::bindServerInfo(QSqlQuery &query, const Network::Server & query.bindValue(":proxyport", server.proxyPort); query.bindValue(":proxyuser", server.proxyUser); query.bindValue(":proxypass", server.proxyPass); + query.bindValue(":sslverify", server.sslVerify); } - -bool PostgreSqlStorage::updateNetwork(UserId user, const NetworkInfo &info) +bool PostgreSqlStorage::updateNetwork(UserId user, const NetworkInfo& info) { QSqlDatabase db = logDb(); - if (!db.transaction()) { + if (!beginTransaction(db)) { qWarning() << "PostgreSqlStorage::updateNetwork(): failed to begin transaction!"; qWarning() << " -" << qPrintable(db.lastError().text()); return false; @@ -697,7 +863,7 @@ bool PostgreSqlStorage::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); @@ -716,11 +882,10 @@ bool PostgreSqlStorage::updateNetwork(UserId user, const NetworkInfo &info) return true; } - -bool PostgreSqlStorage::removeNetwork(UserId user, const NetworkId &networkId) +bool PostgreSqlStorage::removeNetwork(UserId user, const NetworkId& networkId) { QSqlDatabase db = logDb(); - if (!db.transaction()) { + if (!beginTransaction(db)) { qWarning() << "PostgreSqlStorage::removeNetwork(): cannot start transaction!"; qWarning() << " -" << qPrintable(db.lastError().text()); return false; @@ -740,7 +905,6 @@ bool PostgreSqlStorage::removeNetwork(UserId user, const NetworkId &networkId) return true; } - QList PostgreSqlStorage::networks(UserId user) { QList nets; @@ -770,9 +934,9 @@ QList PostgreSqlStorage::networks(UserId user) net.networkId = networksQuery.value(0).toInt(); net.networkName = networksQuery.value(1).toString(); net.identity = networksQuery.value(2).toInt(); - net.codecForServer = networksQuery.value(3).toString().toAscii(); - net.codecForEncoding = networksQuery.value(4).toString().toAscii(); - net.codecForDecoding = networksQuery.value(5).toString().toAscii(); + net.codecForServer = networksQuery.value(3).toString().toLatin1(); + net.codecForEncoding = networksQuery.value(4).toString().toLatin1(); + net.codecForDecoding = networksQuery.value(5).toString().toLatin1(); net.useRandomServer = networksQuery.value(6).toBool(); net.perform = networksQuery.value(7).toString().split("\n"); net.useAutoIdentify = networksQuery.value(8).toBool(); @@ -786,6 +950,11 @@ QList PostgreSqlStorage::networks(UserId user) net.useSasl = networksQuery.value(16).toBool(); net.saslAccount = networksQuery.value(17).toString(); net.saslPassword = networksQuery.value(18).toString(); + // Custom rate limiting + net.useCustomMessageRate = networksQuery.value(19).toBool(); + net.messageRateBurstSize = networksQuery.value(20).toUInt(); + net.messageRateDelay = networksQuery.value(21).toUInt(); + net.unlimitedMessageRate = networksQuery.value(22).toBool(); serversQuery.bindValue(":networkid", net.networkId.toInt()); safeExec(serversQuery); @@ -808,6 +977,7 @@ QList PostgreSqlStorage::networks(UserId user) server.proxyPort = serversQuery.value(8).toUInt(); server.proxyUser = serversQuery.value(9).toString(); server.proxyPass = serversQuery.value(10).toString(); + server.sslVerify = serversQuery.value(11).toBool(); servers << server; } net.serverList = servers; @@ -817,7 +987,6 @@ QList PostgreSqlStorage::networks(UserId user) return nets; } - QList PostgreSqlStorage::connectedNetworks(UserId user) { QList connectedNets; @@ -843,8 +1012,7 @@ QList PostgreSqlStorage::connectedNetworks(UserId user) return connectedNets; } - -void PostgreSqlStorage::setNetworkConnected(UserId user, const NetworkId &networkId, bool isConnected) +void PostgreSqlStorage::setNetworkConnected(UserId user, const NetworkId& networkId, bool isConnected) { QSqlQuery query(logDb()); query.prepare(queryString("update_network_connected")); @@ -855,8 +1023,7 @@ void PostgreSqlStorage::setNetworkConnected(UserId user, const NetworkId &networ watchQuery(query); } - -QHash PostgreSqlStorage::persistentChannels(UserId user, const NetworkId &networkId) +QHash PostgreSqlStorage::persistentChannels(UserId user, const NetworkId& networkId) { QHash persistentChans; @@ -882,33 +1049,30 @@ QHash PostgreSqlStorage::persistentChannels(UserId user, const return persistentChans; } - -void PostgreSqlStorage::setChannelPersistent(UserId user, const NetworkId &networkId, const QString &channel, bool isJoined) +void PostgreSqlStorage::setChannelPersistent(UserId user, const NetworkId& networkId, const QString& channel, bool isJoined) { QSqlQuery query(logDb()); query.prepare(queryString("update_buffer_persistent_channel")); query.bindValue(":userid", user.toInt()); - query.bindValue(":networkId", networkId.toInt()); + query.bindValue(":networkid", networkId.toInt()); query.bindValue(":buffercname", channel.toLower()); query.bindValue(":joined", isJoined); safeExec(query); watchQuery(query); } - -void PostgreSqlStorage::setPersistentChannelKey(UserId user, const NetworkId &networkId, const QString &channel, const QString &key) +void PostgreSqlStorage::setPersistentChannelKey(UserId user, const NetworkId& networkId, const QString& channel, const QString& key) { QSqlQuery query(logDb()); query.prepare(queryString("update_buffer_set_channel_key")); query.bindValue(":userid", user.toInt()); - query.bindValue(":networkId", networkId.toInt()); + query.bindValue(":networkid", networkId.toInt()); query.bindValue(":buffercname", channel.toLower()); query.bindValue(":key", key); safeExec(query); watchQuery(query); } - QString PostgreSqlStorage::awayMessage(UserId user, NetworkId networkId) { QSqlQuery query(logDb()); @@ -923,8 +1087,7 @@ QString PostgreSqlStorage::awayMessage(UserId user, NetworkId networkId) return awayMsg; } - -void PostgreSqlStorage::setAwayMessage(UserId user, NetworkId networkId, const QString &awayMsg) +void PostgreSqlStorage::setAwayMessage(UserId user, NetworkId networkId, const QString& awayMsg) { QSqlQuery query(logDb()); query.prepare(queryString("update_network_set_awaymsg")); @@ -935,7 +1098,6 @@ void PostgreSqlStorage::setAwayMessage(UserId user, NetworkId networkId, const Q watchQuery(query); } - QString PostgreSqlStorage::userModes(UserId user, NetworkId networkId) { QSqlQuery query(logDb()); @@ -950,8 +1112,7 @@ QString PostgreSqlStorage::userModes(UserId user, NetworkId networkId) return modes; } - -void PostgreSqlStorage::setUserModes(UserId user, NetworkId networkId, const QString &userModes) +void PostgreSqlStorage::setUserModes(UserId user, NetworkId networkId, const QString& userModes) { QSqlQuery query(logDb()); query.prepare(queryString("update_network_set_usermode")); @@ -962,14 +1123,13 @@ void PostgreSqlStorage::setUserModes(UserId user, NetworkId networkId, const QSt watchQuery(query); } - -BufferInfo PostgreSqlStorage::bufferInfo(UserId user, const NetworkId &networkId, BufferInfo::Type type, const QString &buffer, bool create) +BufferInfo PostgreSqlStorage::bufferInfo(UserId user, const NetworkId& networkId, BufferInfo::Type type, const QString& buffer, bool create) { QSqlDatabase db = logDb(); - if (!db.transaction()) { + if (!beginTransaction(db)) { qWarning() << "PostgreSqlStorage::bufferInfo(): cannot start read only transaction!"; qWarning() << " -" << qPrintable(db.lastError().text()); - return BufferInfo(); + return {}; } QSqlQuery query(db); @@ -978,6 +1138,7 @@ BufferInfo PostgreSqlStorage::bufferInfo(UserId user, const NetworkId &networkId query.bindValue(":userid", user.toInt()); query.bindValue(":buffercname", buffer.toLower()); safeExec(query); + watchQuery(query); if (query.first()) { BufferInfo bufferInfo = BufferInfo(query.value(0).toInt(), networkId, (BufferInfo::Type)query.value(1).toInt(), 0, buffer); @@ -987,7 +1148,7 @@ BufferInfo PostgreSqlStorage::bufferInfo(UserId user, const NetworkId &networkId qCritical() << " bound Values:"; QList list = query.boundValues().values(); for (int i = 0; i < list.size(); ++i) - qCritical() << i << ":" << list.at(i).toString().toAscii().data(); + qCritical() << i << ":" << list.at(i).toString().toLatin1().data(); Q_ASSERT(false); } db.commit(); @@ -996,7 +1157,7 @@ BufferInfo PostgreSqlStorage::bufferInfo(UserId user, const NetworkId &networkId if (!create) { db.rollback(); - return BufferInfo(); + return {}; } QSqlQuery createQuery(db); @@ -1010,9 +1171,8 @@ BufferInfo PostgreSqlStorage::bufferInfo(UserId user, const NetworkId &networkId safeExec(createQuery); - if (createQuery.lastError().isValid()) { + if (!watchQuery(createQuery)) { qWarning() << "PostgreSqlStorage::bufferInfo(): unable to create buffer"; - watchQuery(createQuery); db.rollback(); return BufferInfo(); } @@ -1024,8 +1184,7 @@ BufferInfo PostgreSqlStorage::bufferInfo(UserId user, const NetworkId &networkId return bufferInfo; } - -BufferInfo PostgreSqlStorage::getBufferInfo(UserId user, const BufferId &bufferId) +BufferInfo PostgreSqlStorage::getBufferInfo(UserId user, const BufferId& bufferId) { QSqlQuery query(logDb()); query.prepare(queryString("select_buffer_by_id")); @@ -1033,18 +1192,21 @@ BufferInfo PostgreSqlStorage::getBufferInfo(UserId user, const BufferId &bufferI query.bindValue(":bufferid", bufferId.toInt()); safeExec(query); if (!watchQuery(query)) - return BufferInfo(); + return {}; if (!query.first()) - return BufferInfo(); + return {}; - 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()); return bufferInfo; } - QList PostgreSqlStorage::requestBuffers(UserId user) { QList bufferlist; @@ -1063,13 +1225,16 @@ QList PostgreSqlStorage::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(); return bufferlist; } - QList PostgreSqlStorage::requestBufferIdsForNetwork(UserId user, NetworkId networkId) { QList bufferList; @@ -1095,11 +1260,10 @@ QList PostgreSqlStorage::requestBufferIdsForNetwork(UserId user, Netwo return bufferList; } - -bool PostgreSqlStorage::removeBuffer(const UserId &user, const BufferId &bufferId) +bool PostgreSqlStorage::removeBuffer(const UserId& user, const BufferId& bufferId) { QSqlDatabase db = logDb(); - if (!db.transaction()) { + if (!beginTransaction(db)) { qWarning() << "PostgreSqlStorage::removeBuffer(): cannot start transaction!"; return false; } @@ -1124,17 +1288,17 @@ bool PostgreSqlStorage::removeBuffer(const UserId &user, const BufferId &bufferI return true; default: // there was more then one buffer deleted... - qWarning() << "PostgreSqlStorage::removeBuffer(): Userid" << user << "BufferId" << "caused deletion of" << numRows << "Buffers! Rolling back transaction..."; + qWarning() << "PostgreSqlStorage::removeBuffer(): Userid" << user << "BufferId" + << "caused deletion of" << numRows << "Buffers! Rolling back transaction..."; db.rollback(); return false; } } - -bool PostgreSqlStorage::renameBuffer(const UserId &user, const BufferId &bufferId, const QString &newName) +bool PostgreSqlStorage::renameBuffer(const UserId& user, const BufferId& bufferId, const QString& newName) { QSqlDatabase db = logDb(); - if (!db.transaction()) { + if (!beginTransaction(db)) { qWarning() << "PostgreSqlStorage::renameBuffer(): cannot start transaction!"; return false; } @@ -1146,8 +1310,7 @@ bool PostgreSqlStorage::renameBuffer(const UserId &user, const BufferId &bufferI query.bindValue(":userid", user.toInt()); query.bindValue(":bufferid", bufferId.toInt()); safeExec(query); - if (query.lastError().isValid()) { - watchQuery(query); + if (!watchQuery(query)) { db.rollback(); return false; } @@ -1162,17 +1325,17 @@ bool PostgreSqlStorage::renameBuffer(const UserId &user, const BufferId &bufferI return true; default: // there was more then one buffer deleted... - qWarning() << "PostgreSqlStorage::renameBuffer(): Userid" << user << "BufferId" << "affected" << numRows << "Buffers! Rolling back transaction..."; + qWarning() << "PostgreSqlStorage::renameBuffer(): Userid" << user << "BufferId" + << "affected" << numRows << "Buffers! Rolling back transaction..."; db.rollback(); return false; } } - -bool PostgreSqlStorage::mergeBuffersPermanently(const UserId &user, const BufferId &bufferId1, const BufferId &bufferId2) +bool PostgreSqlStorage::mergeBuffersPermanently(const UserId& user, const BufferId& bufferId1, const BufferId& bufferId2) { QSqlDatabase db = logDb(); - if (!db.transaction()) { + if (!beginTransaction(db)) { qWarning() << "PostgreSqlStorage::mergeBuffersPermanently(): cannot start transaction!"; qWarning() << " -" << qPrintable(db.lastError().text()); return false; @@ -1219,20 +1382,18 @@ bool PostgreSqlStorage::mergeBuffersPermanently(const UserId &user, const Buffer return true; } - -void PostgreSqlStorage::setBufferLastSeenMsg(UserId user, const BufferId &bufferId, const MsgId &msgId) +void PostgreSqlStorage::setBufferLastSeenMsg(UserId user, const BufferId& bufferId, const MsgId& msgId) { QSqlQuery query(logDb()); 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()); safeExec(query); watchQuery(query); } - QHash PostgreSqlStorage::bufferLastSeenMsgIds(UserId user) { QHash lastSeenHash; @@ -1254,27 +1415,25 @@ QHash PostgreSqlStorage::bufferLastSeenMsgIds(UserId user) } while (query.next()) { - lastSeenHash[query.value(0).toInt()] = query.value(1).toInt(); + lastSeenHash[query.value(0).toInt()] = query.value(1).toLongLong(); } db.commit(); return lastSeenHash; } - -void PostgreSqlStorage::setBufferMarkerLineMsg(UserId user, const BufferId &bufferId, const MsgId &msgId) +void PostgreSqlStorage::setBufferMarkerLineMsg(UserId user, const BufferId& bufferId, const MsgId& msgId) { QSqlQuery query(logDb()); 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()); safeExec(query); watchQuery(query); } - QHash PostgreSqlStorage::bufferMarkerLineMsgIds(UserId user) { QHash markerLineHash; @@ -1296,53 +1455,199 @@ QHash PostgreSqlStorage::bufferMarkerLineMsgIds(UserId user) } while (query.next()) { - markerLineHash[query.value(0).toInt()] = query.value(1).toInt(); + markerLineHash[query.value(0).toInt()] = query.value(1).toLongLong(); } db.commit(); return markerLineHash; } +void PostgreSqlStorage::setBufferActivity(UserId user, BufferId bufferId, Message::Types bufferActivity) +{ + QSqlQuery query(logDb()); + query.prepare(queryString("update_buffer_bufferactivity")); + + query.bindValue(":userid", user.toInt()); + query.bindValue(":bufferid", bufferId.toInt()); + query.bindValue(":bufferactivity", (int)bufferActivity); + safeExec(query); + watchQuery(query); +} + +QHash PostgreSqlStorage::bufferActivities(UserId user) +{ + QHash bufferActivityHash; + + QSqlDatabase db = logDb(); + if (!beginReadOnlyTransaction(db)) { + qWarning() << "PostgreSqlStorage::bufferActivities(): cannot start read only transaction!"; + qWarning() << " -" << qPrintable(db.lastError().text()); + return bufferActivityHash; + } + + QSqlQuery query(db); + query.prepare(queryString("select_buffer_bufferactivities")); + query.bindValue(":userid", user.toInt()); + safeExec(query); + if (!watchQuery(query)) { + db.rollback(); + return bufferActivityHash; + } + + while (query.next()) { + bufferActivityHash[query.value(0).toInt()] = Message::Types(query.value(1).toInt()); + } + + db.commit(); + return bufferActivityHash; +} + +Message::Types PostgreSqlStorage::bufferActivity(BufferId bufferId, MsgId lastSeenMsgId) +{ + QSqlQuery query(logDb()); + query.prepare(queryString("select_buffer_bufferactivity")); + query.bindValue(":bufferid", bufferId.toInt()); + query.bindValue(":lastseenmsgid", lastSeenMsgId.toQint64()); + safeExec(query); + watchQuery(query); + Message::Types result = Message::Types(nullptr); + if (query.first()) + result = Message::Types(query.value(0).toInt()); + return result; +} + +QHash PostgreSqlStorage::bufferCiphers(UserId user, const NetworkId& networkId) +{ + QHash bufferCiphers; + + QSqlDatabase db = logDb(); + if (!beginReadOnlyTransaction(db)) { + qWarning() << "PostgreSqlStorage::persistentChannels(): cannot start read only transaction!"; + qWarning() << " -" << qPrintable(db.lastError().text()); + return bufferCiphers; + } + + QSqlQuery query(db); + query.prepare(queryString("select_buffer_ciphers")); + query.bindValue(":userid", user.toInt()); + query.bindValue(":networkid", networkId.toInt()); + safeExec(query); + watchQuery(query); + + while (query.next()) { + bufferCiphers[query.value(0).toString()] = QByteArray::fromHex(query.value(1).toString().toUtf8()); + } + + db.commit(); + return bufferCiphers; +} + +void PostgreSqlStorage::setBufferCipher(UserId user, const NetworkId& networkId, const QString& bufferName, const QByteArray& cipher) +{ + QSqlQuery query(logDb()); + 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())); + safeExec(query); + watchQuery(query); +} + +void PostgreSqlStorage::setHighlightCount(UserId user, BufferId bufferId, int highlightcount) +{ + QSqlQuery query(logDb()); + query.prepare(queryString("update_buffer_highlightcount")); + + query.bindValue(":userid", user.toInt()); + query.bindValue(":bufferid", bufferId.toInt()); + query.bindValue(":highlightcount", highlightcount); + safeExec(query); + watchQuery(query); +} + +QHash PostgreSqlStorage::highlightCounts(UserId user) +{ + QHash highlightCountHash; + + QSqlDatabase db = logDb(); + if (!beginReadOnlyTransaction(db)) { + qWarning() << "PostgreSqlStorage::highlightCounts(): cannot start read only transaction!"; + qWarning() << " -" << qPrintable(db.lastError().text()); + return highlightCountHash; + } + + QSqlQuery query(db); + query.prepare(queryString("select_buffer_highlightcounts")); + query.bindValue(":userid", user.toInt()); + safeExec(query); + if (!watchQuery(query)) { + db.rollback(); + return highlightCountHash; + } + + while (query.next()) { + highlightCountHash[query.value(0).toInt()] = query.value(1).toInt(); + } + + db.commit(); + return highlightCountHash; +} + +int PostgreSqlStorage::highlightCount(BufferId bufferId, MsgId lastSeenMsgId) +{ + QSqlQuery query(logDb()); + query.prepare(queryString("select_buffer_highlightcount")); + query.bindValue(":bufferid", bufferId.toInt()); + query.bindValue(":lastseenmsgid", lastSeenMsgId.toQint64()); + safeExec(query); + watchQuery(query); + auto result = int(0); + if (query.first()) + result = query.value(0).toInt(); + return result; +} -bool PostgreSqlStorage::logMessage(Message &msg) +bool PostgreSqlStorage::logMessage(Message& msg) { QSqlDatabase db = logDb(); - if (!db.transaction()) { + if (!beginTransaction(db)) { qWarning() << "PostgreSqlStorage::logMessage(): cannot start transaction!"; qWarning() << " -" << qPrintable(db.lastError().text()); return false; } - QSqlQuery getSenderIdQuery = executePreparedQuery("select_senderid", msg.sender(), db); - int senderId; + QVariantList senderParams; + senderParams << msg.sender() << msg.realName() << msg.avatarUrl(); + QSqlQuery getSenderIdQuery = executePreparedQuery("select_senderid", senderParams, db); + qint64 senderId; if (getSenderIdQuery.first()) { - senderId = getSenderIdQuery.value(0).toInt(); + senderId = getSenderIdQuery.value(0).toLongLong(); } else { // it's possible that the sender was already added by another thread // since the insert might fail we're setting a savepoint savePoint("sender_sp1", db); - QSqlQuery addSenderQuery = executePreparedQuery("insert_sender", msg.sender(), db); + QSqlQuery addSenderQuery = executePreparedQuery("insert_sender", senderParams, db); if (addSenderQuery.lastError().isValid()) { rollbackSavePoint("sender_sp1", db); - getSenderIdQuery = db.exec(getSenderIdQuery.lastQuery()); + getSenderIdQuery = executePreparedQuery("select_senderid", senderParams, db); + watchQuery(getSenderIdQuery); getSenderIdQuery.first(); - senderId = getSenderIdQuery.value(0).toInt(); + senderId = getSenderIdQuery.value(0).toLongLong(); } else { releaseSavePoint("sender_sp1", db); addSenderQuery.first(); - senderId = addSenderQuery.value(0).toInt(); + senderId = addSenderQuery.value(0).toLongLong(); } } QVariantList params; - params << msg.timestamp() - << msg.bufferInfo().bufferId().toInt() - << msg.type() - << (int)msg.flags() - << senderId + // PostgreSQL handles QDateTime()'s serialized format by default, and QDateTime() serializes + // to a 64-bit time compatible format by default. + params << msg.timestamp() << msg.bufferInfo().bufferId().toInt() << msg.type() << (int)msg.flags() << senderId << msg.senderPrefixes() << msg.contents(); QSqlQuery logMessageQuery = executePreparedQuery("insert_message", params, db); @@ -1352,7 +1657,7 @@ bool PostgreSqlStorage::logMessage(Message &msg) } logMessageQuery.first(); - MsgId msgId = logMessageQuery.value(0).toInt(); + MsgId msgId = logMessageQuery.value(0).toLongLong(); db.commit(); if (msgId.isValid()) { msg.setMsgId(msgId); @@ -1363,48 +1668,53 @@ bool PostgreSqlStorage::logMessage(Message &msg) } } - -bool PostgreSqlStorage::logMessages(MessageList &msgs) +bool PostgreSqlStorage::logMessages(MessageList& msgs) { QSqlDatabase db = logDb(); - if (!db.transaction()) { + if (!beginTransaction(db)) { qWarning() << "PostgreSqlStorage::logMessage(): cannot start transaction!"; qWarning() << " -" << qPrintable(db.lastError().text()); return false; } QList senderIdList; - QHash senderIds; + QHash senderIds; QSqlQuery addSenderQuery; - QSqlQuery selectSenderQuery;; + QSqlQuery selectSenderQuery; + ; 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 (senderIds.contains(sender)) { senderIdList << senderIds[sender]; continue; } - selectSenderQuery = executePreparedQuery("select_senderid", sender, db); + QVariantList senderParams; + senderParams << sender.sender << sender.realname << sender.avatarurl; + + selectSenderQuery = executePreparedQuery("select_senderid", senderParams, db); if (selectSenderQuery.first()) { - senderIdList << selectSenderQuery.value(0).toInt(); - senderIds[sender] = selectSenderQuery.value(0).toInt(); + senderIdList << selectSenderQuery.value(0).toLongLong(); + senderIds[sender] = selectSenderQuery.value(0).toLongLong(); } else { savePoint("sender_sp", db); - addSenderQuery = executePreparedQuery("insert_sender", sender, db); + addSenderQuery = executePreparedQuery("insert_sender", senderParams, db); if (addSenderQuery.lastError().isValid()) { // seems it was inserted meanwhile... by a different thread rollbackSavePoint("sender_sp", db); - selectSenderQuery = db.exec(selectSenderQuery.lastQuery()); + selectSenderQuery = executePreparedQuery("select_senderid", senderParams, db); + watchQuery(selectSenderQuery); selectSenderQuery.first(); - senderIdList << selectSenderQuery.value(0).toInt(); - senderIds[sender] = selectSenderQuery.value(0).toInt(); + senderIdList << selectSenderQuery.value(0).toLongLong(); + senderIds[sender] = selectSenderQuery.value(0).toLongLong(); } else { releaseSavePoint("sender_sp", db); addSenderQuery.first(); - senderIdList << addSenderQuery.value(0).toInt(); - senderIds[sender] = addSenderQuery.value(0).toInt(); + senderIdList << addSenderQuery.value(0).toLongLong(); + senderIds[sender] = addSenderQuery.value(0).toLongLong(); } } } @@ -1412,14 +1722,12 @@ bool PostgreSqlStorage::logMessages(MessageList &msgs) // yes we loop twice over the same list. This avoids alternating queries. bool error = false; for (int i = 0; i < msgs.count(); i++) { - Message &msg = msgs[i]; + Message& msg = msgs[i]; QVariantList params; - params << msg.timestamp() - << msg.bufferInfo().bufferId().toInt() - << msg.type() - << (int)msg.flags() - << senderIdList.at(i) - << msg.contents(); + // PostgreSQL handles QDateTime()'s serialized format by default, and QDateTime() serializes + // to a 64-bit time compatible format by default. + params << msg.timestamp() << msg.bufferInfo().bufferId().toInt() << msg.type() << (int)msg.flags() << senderIdList.at(i) + << msg.senderPrefixes() << msg.contents(); QSqlQuery logMessageQuery = executePreparedQuery("insert_message", params, db); if (!watchQuery(logMessageQuery)) { db.rollback(); @@ -1428,7 +1736,7 @@ bool PostgreSqlStorage::logMessages(MessageList &msgs) } else { logMessageQuery.first(); - msg.setMsgId(logMessageQuery.value(0).toInt()); + msg.setMsgId(logMessageQuery.value(0).toLongLong()); } } @@ -1444,7 +1752,6 @@ bool PostgreSqlStorage::logMessages(MessageList &msgs) return true; } - QList PostgreSqlStorage::requestMsgs(UserId user, BufferId bufferId, MsgId first, MsgId last, int limit) { QList messagelist; @@ -1465,22 +1772,22 @@ QList PostgreSqlStorage::requestMsgs(UserId user, BufferId bufferId, Ms QString queryName; QVariantList params; if (last == -1 && first == -1) { - queryName = "select_messages"; + queryName = "select_messagesNewestK"; } else if (last == -1) { queryName = "select_messagesNewerThan"; - params << first.toInt(); + params << first.toQint64(); } else { queryName = "select_messagesRange"; - params << first.toInt(); - params << last.toInt(); + params << first.toQint64(); + params << last.toQint64(); } params << bufferId.toInt(); if (limit != -1) params << limit; else - params << "ALL"; + params << QVariant(QVariant::Int); QSqlQuery query = executePreparedQuery(queryName, params, db); @@ -1492,15 +1799,20 @@ QList PostgreSqlStorage::requestMsgs(UserId user, BufferId bufferId, Ms QDateTime timestamp; while (query.next()) { + // PostgreSQL returns date/time in ISO 8601 format, no 64-bit handling needed + // See https://www.postgresql.org/docs/current/static/datatype-datetime.html#DATATYPE-DATETIME-OUTPUT timestamp = query.value(1).toDateTime(); timestamp.setTimeSpec(Qt::UTC); Message msg(timestamp, - bufferInfo, - (Message::Type)query.value(2).toUInt(), - query.value(5).toString(), - query.value(4).toString(), - (Message::Flags)query.value(3).toUInt()); - msg.setMsgId(query.value(0).toInt()); + 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; } @@ -1508,6 +1820,73 @@ QList PostgreSqlStorage::requestMsgs(UserId user, BufferId bufferId, Ms return messagelist; } +QList PostgreSqlStorage::requestMsgsFiltered( + UserId user, BufferId bufferId, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags) +{ + QList messagelist; + + QSqlDatabase db = logDb(); + if (!beginReadOnlyTransaction(db)) { + qWarning() << "PostgreSqlStorage::requestMsgs(): cannot start read only transaction!"; + qWarning() << " -" << qPrintable(db.lastError().text()); + return messagelist; + } + + BufferInfo bufferInfo = getBufferInfo(user, bufferId); + if (!bufferInfo.isValid()) { + db.rollback(); + 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(":first", first.toQint64()); + } + else { + query.prepare(queryString("select_messagesRange_filtered")); + query.bindValue(":last", last.toQint64()); + query.bindValue(":first", first.toQint64()); + } + query.bindValue(":buffer", bufferId.toInt()); + query.bindValue(":limit", limit); + int typeRaw = type; + query.bindValue(":type", typeRaw); + int flagsRaw = flags; + query.bindValue(":flags", flagsRaw); + + safeExec(query); + if (!watchQuery(query)) { + qDebug() << "select_messages failed"; + db.rollback(); + return messagelist; + } + + QDateTime timestamp; + while (query.next()) { + // PostgreSQL returns date/time in ISO 8601 format, no 64-bit handling needed + // See https://www.postgresql.org/docs/current/static/datatype-datetime.html#DATATYPE-DATETIME-OUTPUT + timestamp = query.value(1).toDateTime(); + timestamp.setTimeSpec(Qt::UTC); + Message msg(timestamp, + 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(); + return messagelist; +} QList PostgreSqlStorage::requestAllMsgs(UserId user, MsgId first, MsgId last, int limit) { @@ -1515,7 +1894,7 @@ QList PostgreSqlStorage::requestAllMsgs(UserId user, MsgId first, MsgId // requestBuffers uses it's own transaction. QHash bufferInfoHash; - foreach(BufferInfo bufferInfo, requestBuffers(user)) { + foreach (BufferInfo bufferInfo, requestBuffers(user)) { bufferInfoHash[bufferInfo.bufferId()] = bufferInfo; } @@ -1532,10 +1911,10 @@ QList PostgreSqlStorage::requestAllMsgs(UserId user, MsgId first, MsgId } 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()); safeExec(query); if (!watchQuery(query)) { db.rollback(); @@ -1544,15 +1923,20 @@ QList PostgreSqlStorage::requestAllMsgs(UserId user, MsgId first, MsgId QDateTime timestamp; for (int i = 0; i < limit && query.next(); i++) { - timestamp = query.value(1).toDateTime(); + // PostgreSQL returns date/time in ISO 8601 format, no 64-bit handling needed + // See https://www.postgresql.org/docs/current/static/datatype-datetime.html#DATATYPE-DATETIME-OUTPUT + timestamp = query.value(2).toDateTime(); timestamp.setTimeSpec(Qt::UTC); Message msg(timestamp, - bufferInfoHash[query.value(1).toInt()], - (Message::Type)query.value(3).toUInt(), - query.value(6).toString(), - query.value(5).toString(), - (Message::Flags)query.value(4).toUInt()); - msg.setMsgId(query.value(0).toInt()); + 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; } @@ -1560,6 +1944,83 @@ QList PostgreSqlStorage::requestAllMsgs(UserId user, MsgId first, MsgId return messagelist; } +QList PostgreSqlStorage::requestAllMsgsFiltered( + UserId user, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags) +{ + QList messagelist; + + // requestBuffers uses it's own transaction. + QHash bufferInfoHash; + foreach (BufferInfo bufferInfo, requestBuffers(user)) { + bufferInfoHash[bufferInfo.bufferId()] = bufferInfo; + } + + QSqlDatabase db = logDb(); + if (!beginReadOnlyTransaction(db)) { + qWarning() << "PostgreSqlStorage::requestAllMsgs(): cannot start read only transaction!"; + qWarning() << " -" << qPrintable(db.lastError().text()); + return messagelist; + } + + 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()); + + int typeRaw = type; + query.bindValue(":type", typeRaw); + + int flagsRaw = flags; + query.bindValue(":flags", flagsRaw); + + safeExec(query); + if (!watchQuery(query)) { + db.rollback(); + return messagelist; + } + + QDateTime timestamp; + for (int i = 0; i < limit && query.next(); i++) { + // PostgreSQL returns date/time in ISO 8601 format, no 64-bit handling needed + // See https://www.postgresql.org/docs/current/static/datatype-datetime.html#DATATYPE-DATETIME-OUTPUT + timestamp = query.value(2).toDateTime(); + timestamp.setTimeSpec(Qt::UTC); + Message msg(timestamp, + 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(); + return messagelist; +} + +QMap PostgreSqlStorage::getAllAuthUserNames() +{ + QMap authusernames; + QSqlQuery query(logDb()); + query.prepare(queryString("select_all_authusernames")); + safeExec(query); + watchQuery(query); + + while (query.next()) { + authusernames[query.value(0).toInt()] = query.value(1).toString(); + } + return authusernames; +} // void PostgreSqlStorage::safeExec(QSqlQuery &query) { // qDebug() << "PostgreSqlStorage::safeExec"; @@ -1567,7 +2028,7 @@ QList PostgreSqlStorage::requestAllMsgs(UserId user, MsgId first, MsgId // qDebug() << " bound Values:"; // QList list = query.boundValues().values(); // for (int i = 0; i < list.size(); ++i) -// qCritical() << i << ": " << list.at(i).toString().toAscii().data(); +// qCritical() << i << ": " << list.at(i).toString().toLatin1().data(); // query.exec(); @@ -1584,14 +2045,27 @@ QList PostgreSqlStorage::requestAllMsgs(UserId user, MsgId first, MsgId // return; // } -bool PostgreSqlStorage::beginReadOnlyTransaction(QSqlDatabase &db) +bool PostgreSqlStorage::beginTransaction(QSqlDatabase& db) +{ + bool result = db.transaction(); + if (!db.isOpen()) { + db = logDb(); + result = db.transaction(); + } + return result; +} + +bool PostgreSqlStorage::beginReadOnlyTransaction(QSqlDatabase& db) { QSqlQuery query = db.exec("BEGIN TRANSACTION READ ONLY"); + if (!db.isOpen()) { + db = logDb(); + query = db.exec("BEGIN TRANSACTION READ ONLY"); + } return !query.lastError().isValid(); } - -QSqlQuery PostgreSqlStorage::prepareAndExecuteQuery(const QString &queryname, const QString ¶mstring, const QSqlDatabase &db) +QSqlQuery PostgreSqlStorage::prepareAndExecuteQuery(const QString& queryname, const QString& paramstring, QSqlDatabase& db) { // Query preparing is done lazily. That means that instead of always checking if the query is already prepared // we just EXECUTE and catch the error @@ -1605,11 +2079,26 @@ QSqlQuery PostgreSqlStorage::prepareAndExecuteQuery(const QString &queryname, co query = db.exec(QString("EXECUTE quassel_%1 (%2)").arg(queryname).arg(paramstring)); } - if (db.lastError().isValid()) { - // and once again: Qt leaves us without error codes so we either parse (language dependant(!)) strings + if (!db.isOpen() || db.lastError().isValid()) { + // If the query failed because the DB connection was down, reopen the connection and start a new transaction. + if (!db.isOpen()) { + db = logDb(); + if (!beginTransaction(db)) { + qWarning() + << "PostgreSqlStorage::prepareAndExecuteQuery(): cannot start transaction while recovering from connection loss!"; + qWarning() << " -" << qPrintable(db.lastError().text()); + return query; + } + db.exec("SAVEPOINT quassel_prepare_query"); + } + else { + db.exec("ROLLBACK TO SAVEPOINT quassel_prepare_query"); + } + + // and once again: Qt leaves us without error codes so we either parse (language dependent(!)) strings // or we just guess the error. As we're only interested in unprepared queries, this will be our guess. :) - db.exec("ROLLBACK TO SAVEPOINT quassel_prepare_query"); - QSqlQuery checkQuery = db.exec(QString("SELECT count(name) FROM pg_prepared_statements WHERE name = 'quassel_%1' AND from_sql = TRUE").arg(queryname.toLower())); + QSqlQuery checkQuery = db.exec( + QString("SELECT count(name) FROM pg_prepared_statements WHERE name = 'quassel_%1' AND from_sql = TRUE").arg(queryname.toLower())); checkQuery.first(); if (checkQuery.value(0).toInt() == 0) { db.exec(QString("PREPARE quassel_%1 AS %2").arg(queryname).arg(queryString(queryname))); @@ -1619,7 +2108,7 @@ QSqlQuery PostgreSqlStorage::prepareAndExecuteQuery(const QString &queryname, co return QSqlQuery(db); } } - // we alwas execute the query again, even if the query was already prepared. + // we always execute the query again, even if the query was already prepared. // this ensures, that the error is properly propagated to the calling function // (otherwise the last call would be the testing select to pg_prepared_statements // which always gives a proper result and the error would be lost) @@ -1637,15 +2126,14 @@ QSqlQuery PostgreSqlStorage::prepareAndExecuteQuery(const QString &queryname, co return query; } - -QSqlQuery PostgreSqlStorage::executePreparedQuery(const QString &queryname, const QVariantList ¶ms, const QSqlDatabase &db) +QSqlQuery PostgreSqlStorage::executePreparedQuery(const QString& queryname, const QVariantList& params, QSqlDatabase& db) { - QSqlDriver *driver = db.driver(); + QSqlDriver* driver = db.driver(); QStringList paramStrings; QSqlField field; for (int i = 0; i < params.count(); i++) { - const QVariant &value = params.at(i); + const QVariant& value = params.at(i); field.setType(value.type()); if (value.isNull()) field.clear(); @@ -1663,8 +2151,7 @@ QSqlQuery PostgreSqlStorage::executePreparedQuery(const QString &queryname, cons } } - -QSqlQuery PostgreSqlStorage::executePreparedQuery(const QString &queryname, const QVariant ¶m, const QSqlDatabase &db) +QSqlQuery PostgreSqlStorage::executePreparedQuery(const QString& queryname, const QVariant& param, QSqlDatabase& db) { QSqlField field; field.setType(param.type()); @@ -1677,21 +2164,35 @@ QSqlQuery PostgreSqlStorage::executePreparedQuery(const QString &queryname, cons return prepareAndExecuteQuery(queryname, paramString, db); } - -void PostgreSqlStorage::deallocateQuery(const QString &queryname, const QSqlDatabase &db) +void PostgreSqlStorage::deallocateQuery(const QString& queryname, const QSqlDatabase& db) { db.exec(QString("DEALLOCATE quassel_%1").arg(queryname)); } +void PostgreSqlStorage::safeExec(QSqlQuery& query) +{ + // If the query fails due to the connection being gone, it seems to cause + // exec() to return false but no lastError to be set + if (!query.exec() && !query.lastError().isValid()) { + QSqlDatabase db = logDb(); + QSqlQuery retryQuery(db); + retryQuery.prepare(query.lastQuery()); + QMapIterator i(query.boundValues()); + while (i.hasNext()) { + i.next(); + retryQuery.bindValue(i.key(), i.value()); + } + query = retryQuery; + query.exec(); + } +} // ======================================== // PostgreSqlMigrationWriter // ======================================== PostgreSqlMigrationWriter::PostgreSqlMigrationWriter() : PostgreSqlStorage() -{ -} - +{} bool PostgreSqlMigrationWriter::prepareQuery(MigrationObject mo) { @@ -1725,33 +2226,37 @@ bool PostgreSqlMigrationWriter::prepareQuery(MigrationObject mo) case UserSetting: query = queryString("migrate_write_usersetting"); break; + case CoreState: + query = queryString("migrate_write_corestate"); + break; } newQuery(query, logDb()); return true; } - -//bool PostgreSqlMigrationWriter::writeUser(const QuasselUserMO &user) { -bool PostgreSqlMigrationWriter::writeMo(const QuasselUserMO &user) +// bool PostgreSqlMigrationWriter::writeUser(const QuasselUserMO &user) { +bool PostgreSqlMigrationWriter::writeMo(const QuasselUserMO& user) { bindValue(0, user.id.toInt()); bindValue(1, user.username); bindValue(2, user.password); + bindValue(3, user.hashversion); + bindValue(4, user.authenticator); return exec(); } - -//bool PostgreSqlMigrationWriter::writeSender(const SenderMO &sender) { -bool PostgreSqlMigrationWriter::writeMo(const SenderMO &sender) +// bool PostgreSqlMigrationWriter::writeSender(const SenderMO &sender) { +bool PostgreSqlMigrationWriter::writeMo(const SenderMO& sender) { bindValue(0, sender.senderId); bindValue(1, sender.sender); + bindValue(2, sender.realname); + bindValue(3, sender.avatarurl); return exec(); } - -//bool PostgreSqlMigrationWriter::writeIdentity(const IdentityMO &identity) { -bool PostgreSqlMigrationWriter::writeMo(const IdentityMO &identity) +// bool PostgreSqlMigrationWriter::writeIdentity(const IdentityMO &identity) { +bool PostgreSqlMigrationWriter::writeMo(const IdentityMO& identity) { _validIdentities << identity.id.toInt(); bindValue(0, identity.id.toInt()); @@ -1768,7 +2273,7 @@ bool PostgreSqlMigrationWriter::writeMo(const IdentityMO &identity) bindValue(11, identity.autoAwayReasonEnabled); bindValue(12, identity.detachAwayEnabled); bindValue(13, identity.detachAwayReason); - bindValue(14, identity.detchAwayReasonEnabled); + bindValue(14, identity.detachAwayReasonEnabled); bindValue(15, identity.ident); bindValue(16, identity.kickReason); bindValue(17, identity.partReason); @@ -1778,9 +2283,8 @@ bool PostgreSqlMigrationWriter::writeMo(const IdentityMO &identity) return exec(); } - -//bool PostgreSqlMigrationWriter::writeIdentityNick(const IdentityNickMO &identityNick) { -bool PostgreSqlMigrationWriter::writeMo(const IdentityNickMO &identityNick) +// bool PostgreSqlMigrationWriter::writeIdentityNick(const IdentityNickMO &identityNick) { +bool PostgreSqlMigrationWriter::writeMo(const IdentityNickMO& identityNick) { bindValue(0, identityNick.nickid); bindValue(1, identityNick.identityId.toInt()); @@ -1788,9 +2292,8 @@ bool PostgreSqlMigrationWriter::writeMo(const IdentityNickMO &identityNick) return exec(); } - -//bool PostgreSqlMigrationWriter::writeNetwork(const NetworkMO &network) { -bool PostgreSqlMigrationWriter::writeMo(const NetworkMO &network) +// bool PostgreSqlMigrationWriter::writeNetwork(const NetworkMO &network) { +bool PostgreSqlMigrationWriter::writeMo(const NetworkMO& network) { bindValue(0, network.networkid.toInt()); bindValue(1, network.userid.toInt()); @@ -1820,12 +2323,16 @@ bool PostgreSqlMigrationWriter::writeMo(const NetworkMO &network) bindValue(22, network.usesasl); bindValue(23, network.saslaccount); bindValue(24, network.saslpassword); + // Custom rate limiting + bindValue(25, network.usecustommessagerate); + bindValue(26, network.messagerateburstsize); + bindValue(27, network.messageratedelay); + bindValue(28, network.unlimitedmessagerate); return exec(); } - -//bool PostgreSqlMigrationWriter::writeBuffer(const BufferMO &buffer) { -bool PostgreSqlMigrationWriter::writeMo(const BufferMO &buffer) +// bool PostgreSqlMigrationWriter::writeBuffer(const BufferMO &buffer) { +bool PostgreSqlMigrationWriter::writeMo(const BufferMO& buffer) { bindValue(0, buffer.bufferid.toInt()); bindValue(1, buffer.userid.toInt()); @@ -1834,30 +2341,33 @@ bool PostgreSqlMigrationWriter::writeMo(const BufferMO &buffer) bindValue(4, buffer.buffername); bindValue(5, buffer.buffercname); bindValue(6, (int)buffer.buffertype); - bindValue(7, buffer.lastseenmsgid); - bindValue(8, buffer.markerlinemsgid); - bindValue(9, buffer.key); - bindValue(10, buffer.joined); + bindValue(7, buffer.lastmsgid); + bindValue(8, buffer.lastseenmsgid); + bindValue(9, buffer.markerlinemsgid); + bindValue(10, buffer.bufferactivity); + bindValue(11, buffer.highlightcount); + bindValue(12, buffer.key); + bindValue(13, buffer.joined); + bindValue(14, buffer.cipher); return exec(); } - -//bool PostgreSqlMigrationWriter::writeBacklog(const BacklogMO &backlog) { -bool PostgreSqlMigrationWriter::writeMo(const BacklogMO &backlog) +// bool PostgreSqlMigrationWriter::writeBacklog(const BacklogMO &backlog) { +bool PostgreSqlMigrationWriter::writeMo(const BacklogMO& backlog) { - bindValue(0, backlog.messageid.toInt()); + bindValue(0, backlog.messageid.toQint64()); bindValue(1, backlog.time); bindValue(2, backlog.bufferid.toInt()); bindValue(3, backlog.type); bindValue(4, (int)backlog.flags); bindValue(5, backlog.senderid); - bindValue(6, backlog.message); + bindValue(6, backlog.senderprefixes); + bindValue(7, backlog.message); return exec(); } - -//bool PostgreSqlMigrationWriter::writeIrcServer(const IrcServerMO &ircserver) { -bool PostgreSqlMigrationWriter::writeMo(const IrcServerMO &ircserver) +// bool PostgreSqlMigrationWriter::writeIrcServer(const IrcServerMO &ircserver) { +bool PostgreSqlMigrationWriter::writeMo(const IrcServerMO& ircserver) { bindValue(0, ircserver.serverid); bindValue(1, ircserver.userid.toInt()); @@ -1873,12 +2383,12 @@ bool PostgreSqlMigrationWriter::writeMo(const IrcServerMO &ircserver) bindValue(11, ircserver.proxyport); bindValue(12, ircserver.proxyuser); bindValue(13, ircserver.proxypass); + bindValue(14, ircserver.sslverify); return exec(); } - -//bool PostgreSqlMigrationWriter::writeUserSetting(const UserSettingMO &userSetting) { -bool PostgreSqlMigrationWriter::writeMo(const UserSettingMO &userSetting) +// bool PostgreSqlMigrationWriter::writeUserSetting(const UserSettingMO &userSetting) { +bool PostgreSqlMigrationWriter::writeMo(const UserSettingMO& userSetting) { bindValue(0, userSetting.userid.toInt()); bindValue(1, userSetting.settingname); @@ -1886,25 +2396,32 @@ bool PostgreSqlMigrationWriter::writeMo(const UserSettingMO &userSetting) return exec(); } +bool PostgreSqlMigrationWriter::writeMo(const CoreStateMO& coreState) +{ + bindValue(0, coreState.key); + bindValue(1, coreState.value); + return exec(); +} bool PostgreSqlMigrationWriter::postProcess() { QSqlDatabase db = logDb(); QList sequences; - sequences << Sequence("backlog", "messageid") - << Sequence("buffer", "bufferid") - << Sequence("identity", "identityid") - << Sequence("identity_nick", "nickid") - << Sequence("ircserver", "serverid") - << Sequence("network", "networkid") - << Sequence("quasseluser", "userid") - << Sequence("sender", "senderid"); + sequences << Sequence("backlog", "messageid") << Sequence("buffer", "bufferid") << Sequence("identity", "identityid") + << Sequence("identity_nick", "nickid") << Sequence("ircserver", "serverid") << Sequence("network", "networkid") + << Sequence("quasseluser", "userid") << Sequence("sender", "senderid"); QList::const_iterator iter; - for (iter = sequences.constBegin(); iter != sequences.constEnd(); iter++) { + for (iter = sequences.constBegin(); iter != sequences.constEnd(); ++iter) { resetQuery(); newQuery(QString("SELECT setval('%1_%2_seq', max(%2)) FROM %1").arg(iter->table, iter->field), db); if (!exec()) return false; } + + // Update the lastmsgid for all existing buffers. + resetQuery(); + newQuery(QString("SELECT populate_lastmsgid()"), db); + if (!exec()) + return false; return true; }