X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fcore%2Fpostgresqlstorage.cpp;h=6b2642bdb7c489db0ce05c33b2398fad9fd9a450;hp=acb7f37c922cb6917e36c57fe34b47226bd05968;hb=158443f71d48215eea8b47b836b61afd77654b78;hpb=26068a99397ea9bdd9fd07af1354fc5065fd7d00 diff --git a/src/core/postgresqlstorage.cpp b/src/core/postgresqlstorage.cpp index acb7f37c..6b2642bd 100644 --- a/src/core/postgresqlstorage.cpp +++ b/src/core/postgresqlstorage.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005-2015 by the Quassel Project * + * Copyright (C) 2005-2018 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@ -22,13 +22,12 @@ #include -#include "logger.h" +#include "logmessage.h" #include "network.h" #include "quassel.h" PostgreSqlStorage::PostgreSqlStorage(QObject *parent) - : AbstractSqlStorage(parent), - _port(-1) + : AbstractSqlStorage(parent) { } @@ -38,61 +37,60 @@ PostgreSqlStorage::~PostgreSqlStorage() } -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")) { + quWarning() << 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::description() const +QString PostgreSqlStorage::displayName() const { - // FIXME: proper description - return tr("PostgreSQL Turbo Bomber HD!"); + return backendId(); // Note: Pre-0.13 clients use the displayName property for backend idenfication } -QStringList PostgreSqlStorage::setupKeys() const +QString PostgreSqlStorage::description() const { - QStringList keys; - keys << "Username" - << "Password" - << "Hostname" - << "Port" - << "Database"; - return keys; + // FIXME: proper description + return tr("PostgreSQL Turbo Bomber HD!"); } -QVariantMap PostgreSqlStorage::setupDefaults() const +QVariantList PostgreSqlStorage::setupData() const { - QVariantMap map; - map["Username"] = QVariant(QString("quassel")); - map["Hostname"] = QVariant(QString("localhost")); - map["Port"] = QVariant(5432); - map["Database"] = QVariant(QString("quassel")); - return map; + 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; } @@ -148,13 +146,23 @@ bool PostgreSqlStorage::initDbSession(QSqlDatabase &db) } -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(); + } } @@ -210,13 +218,14 @@ bool PostgreSqlStorage::setupSchemaVersion(int version) } -UserId PostgreSqlStorage::addUser(const QString &user, const QString &password) +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", hashPassword(password)); query.bindValue(":hashversion", Storage::HashVersion::Latest); + query.bindValue(":authenticator", authenticator); safeExec(query); if (!watchQuery(query)) return 0; @@ -286,6 +295,21 @@ 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() { @@ -382,6 +406,58 @@ 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); +} + + +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; @@ -697,6 +773,11 @@ 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()); } @@ -715,6 +796,7 @@ 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); } @@ -842,6 +924,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); @@ -864,6 +951,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; @@ -1282,7 +1370,7 @@ void PostgreSqlStorage::setBufferLastSeenMsg(UserId user, const BufferId &buffer query.bindValue(":userid", user.toInt()); query.bindValue(":bufferid", bufferId.toInt()); - query.bindValue(":lastseenmsgid", msgId.toInt()); + query.bindValue(":lastseenmsgid", msgId.toQint64()); safeExec(query); watchQuery(query); } @@ -1309,7 +1397,7 @@ 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(); @@ -1324,7 +1412,7 @@ void PostgreSqlStorage::setBufferMarkerLineMsg(UserId user, const BufferId &buff query.bindValue(":userid", user.toInt()); query.bindValue(":bufferid", bufferId.toInt()); - query.bindValue(":markerlinemsgid", msgId.toInt()); + query.bindValue(":markerlinemsgid", msgId.toQint64()); safeExec(query); watchQuery(query); } @@ -1351,7 +1439,7 @@ 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(); @@ -1359,6 +1447,153 @@ QHash PostgreSqlStorage::bufferMarkerLineMsgIds(UserId user) } +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); + int result = int(0); + if (query.first()) + result = query.value(0).toInt(); + return result; +} + bool PostgreSqlStorage::logMessage(Message &msg) { QSqlDatabase db = logDb(); @@ -1368,37 +1603,44 @@ bool PostgreSqlStorage::logMessage(Message &msg) 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 = executePreparedQuery("select_senderid", msg.sender(), db); + 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; + // 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); @@ -1408,7 +1650,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); @@ -1430,38 +1672,44 @@ bool PostgreSqlStorage::logMessages(MessageList &msgs) } QList senderIdList; - QHash senderIds; + QHash senderIds; QSqlQuery addSenderQuery; 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 = executePreparedQuery("select_senderid", sender, db); + 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(); } } } @@ -1471,11 +1719,14 @@ bool PostgreSqlStorage::logMessages(MessageList &msgs) for (int i = 0; i < msgs.count(); i++) { Message &msg = msgs[i]; QVariantList params; + // 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)) { @@ -1485,7 +1736,7 @@ bool PostgreSqlStorage::logMessages(MessageList &msgs) } else { logMessageQuery.first(); - msg.setMsgId(logMessageQuery.value(0).toInt()); + msg.setMsgId(logMessageQuery.value(0).toLongLong()); } } @@ -1522,16 +1773,16 @@ 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) @@ -1549,15 +1800,86 @@ 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(), + (Message::Type)query.value(2).toInt(), + query.value(8).toString(), query.value(4).toString(), - (Message::Flags)query.value(3).toUInt()); - msg.setMsgId(query.value(0).toInt()); + 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::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; } @@ -1589,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(); @@ -1601,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(), + (Message::Type)query.value(3).toInt(), + query.value(9).toString(), query.value(5).toString(), - (Message::Flags)query.value(4).toUInt()); - msg.setMsgId(query.value(0).toInt()); + 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; } @@ -1618,6 +1945,84 @@ QList PostgreSqlStorage::requestAllMsgs(UserId user, MsgId first, MsgId } +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"; // qDebug() << " executing:\n" << query.executedQuery(); @@ -1829,6 +2234,9 @@ 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; @@ -1842,6 +2250,7 @@ bool PostgreSqlMigrationWriter::writeMo(const QuasselUserMO &user) bindValue(1, user.username); bindValue(2, user.password); bindValue(3, user.hashversion); + bindValue(4, user.authenticator); return exec(); } @@ -1851,6 +2260,8 @@ bool PostgreSqlMigrationWriter::writeMo(const SenderMO &sender) { bindValue(0, sender.senderId); bindValue(1, sender.sender); + bindValue(2, sender.realname); + bindValue(3, sender.avatarurl); return exec(); } @@ -1873,7 +2284,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); @@ -1925,6 +2336,11 @@ 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(); } @@ -1939,10 +2355,14 @@ 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(); } @@ -1950,13 +2370,14 @@ bool PostgreSqlMigrationWriter::writeMo(const BufferMO &buffer) //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(); } @@ -1978,6 +2399,7 @@ bool PostgreSqlMigrationWriter::writeMo(const IrcServerMO &ircserver) bindValue(11, ircserver.proxyport); bindValue(12, ircserver.proxyuser); bindValue(13, ircserver.proxypass); + bindValue(14, ircserver.sslverify); return exec(); } @@ -1991,6 +2413,13 @@ 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() { @@ -2011,5 +2440,11 @@ bool PostgreSqlMigrationWriter::postProcess() if (!exec()) return false; } + + // Update the lastmsgid for all existing buffers. + resetQuery(); + newQuery(QString("SELECT populate_lastmsgid()"), db); + if (!exec()) + return false; return true; }