X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fcore%2Fsqlitestorage.cpp;h=db174cb451cec9c841eddff445f18d9167bea402;hp=7dc5fea798aa654f627a6d492402d5ad3da7765e;hb=56f686f70bbb212d83803be88adec6fdd225ea8e;hpb=76db8cdfbeffaaba359c8e80cf2146da9e9e7f8a diff --git a/src/core/sqlitestorage.cpp b/src/core/sqlitestorage.cpp index 7dc5fea7..db174cb4 100644 --- a/src/core/sqlitestorage.cpp +++ b/src/core/sqlitestorage.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005-2013 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 * @@ -46,12 +46,19 @@ bool SqliteStorage::isAvailable() const } +QString SqliteStorage::backendId() const +{ + return QString("SQLite"); +} + + QString SqliteStorage::displayName() const { + // Note: Pre-0.13 clients use the displayName property for backend idenfication // We identify the backend to use for the monolithic core by its displayname. // so only change this string if you _really_ have to and make sure the core // setup for the mono client still works ;) - return QString("SQLite"); + return backendId(); } @@ -116,7 +123,7 @@ bool SqliteStorage::setupSchemaVersion(int version) } -UserId SqliteStorage::addUser(const QString &user, const QString &password) +UserId SqliteStorage::addUser(const QString &user, const QString &password, const QString &authenticator) { QSqlDatabase db = logDb(); UserId uid; @@ -129,7 +136,9 @@ UserId SqliteStorage::addUser(const QString &user, const QString &password) QSqlQuery query(db); 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); lockForWrite(); safeExec(query); if (query.lastError().isValid() && query.lastError().number() == 19) { // user already exists - sadly 19 seems to be the general constraint violation error... @@ -158,7 +167,8 @@ bool SqliteStorage::updateUser(UserId user, const QString &password) QSqlQuery query(db); 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); lockForWrite(); safeExec(query); success = query.numRowsAffected() != 0; @@ -190,23 +200,30 @@ void SqliteStorage::renameUser(UserId user, const QString &newName) UserId SqliteStorage::validateUser(const QString &user, const QString &password) { UserId userId; + QString hashedPassword; + Storage::HashVersion hashVersion = Storage::HashVersion::Latest; { QSqlQuery query(logDb()); query.prepare(queryString("select_authuser")); query.bindValue(":username", user); - query.bindValue(":password", cryptedPassword(password)); lockForRead(); safeExec(query); if (query.first()) { userId = query.value(0).toInt(); + hashedPassword = query.value(1).toString(); + hashVersion = static_cast(query.value(2).toInt()); } } unlock(); - return userId; + UserId returnUserId; + if (userId != 0 && checkHashedPassword(userId, password, hashedPassword, hashVersion)) { + returnUserId = userId; + } + return returnUserId; } @@ -231,6 +248,26 @@ UserId SqliteStorage::getUserId(const QString &username) return userId; } +QString SqliteStorage::getUserAuthenticator(const UserId userid) +{ + QString authenticator = QString(""); + + { + QSqlQuery query(logDb()); + query.prepare(queryString("select_authenticator")); + query.bindValue(":userid", userid.toInt()); + + lockForRead(); + safeExec(query); + + if (query.first()) { + authenticator = query.value(0).toString(); + } + } + unlock(); + + return authenticator; +} UserId SqliteStorage::internalUser() { @@ -339,6 +376,60 @@ QVariant SqliteStorage::getUserSetting(UserId userId, const QString &settingName } +void SqliteStorage::setCoreState(const QVariantList &data) +{ + QByteArray rawData; + QDataStream out(&rawData, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_2); + out << data; + + QSqlDatabase db = logDb(); + db.transaction(); + { + QSqlQuery query(db); + query.prepare(queryString("insert_core_state")); + query.bindValue(":key", "active_sessions"); + query.bindValue(":value", rawData); + lockForWrite(); + safeExec(query); + + if (query.lastError().isValid()) { + QSqlQuery updateQuery(db); + updateQuery.prepare(queryString("update_core_state")); + updateQuery.bindValue(":key", "active_sessions"); + updateQuery.bindValue(":value", rawData); + safeExec(updateQuery); + } + db.commit(); + } + unlock(); +} + + +QVariantList SqliteStorage::getCoreState(const QVariantList &defaultData) +{ + QVariantList data; + { + QSqlQuery query(logDb()); + query.prepare(queryString("select_core_state")); + query.bindValue(":key", "active_sessions"); + lockForRead(); + safeExec(query); + + if (query.first()) { + QByteArray rawData = query.value(0).toByteArray(); + QDataStream in(&rawData, QIODevice::ReadOnly); + in.setVersion(QDataStream::Qt_4_2); + in >> data; + } else { + data = defaultData; + } + } + unlock(); + return data; +} + + IdentityId SqliteStorage::createIdentity(UserId user, CoreIdentity &identity) { IdentityId identityId; @@ -649,6 +740,11 @@ void SqliteStorage::bindNetworkInfo(QSqlQuery &query, const NetworkInfo &info) query.bindValue(":autoreconnectretries", info.autoReconnectRetries); query.bindValue(":unlimitedconnectretries", info.unlimitedReconnectRetries ? 1 : 0); query.bindValue(":rejoinchannels", info.rejoinChannels ? 1 : 0); + // Custom rate limiting + query.bindValue(":usecustomessagerate", info.useCustomMessageRate ? 1 : 0); + query.bindValue(":messagerateburstsize", info.messageRateBurstSize); + query.bindValue(":messageratedelay", info.messageRateDelay); + query.bindValue(":unlimitedmessagerate", info.unlimitedMessageRate ? 1 : 0); if (info.networkId.isValid()) query.bindValue(":networkid", info.networkId.toInt()); } @@ -667,6 +763,7 @@ void SqliteStorage::bindServerInfo(QSqlQuery &query, const Network::Server &serv query.bindValue(":proxyport", server.proxyPort); query.bindValue(":proxyuser", server.proxyUser); query.bindValue(":proxypass", server.proxyPass); + query.bindValue(":sslverify", server.sslVerify ? 1 : 0); } @@ -828,9 +925,9 @@ QList SqliteStorage::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).toInt() == 1 ? true : false; net.perform = networksQuery.value(7).toString().split("\n"); net.useAutoIdentify = networksQuery.value(8).toInt() == 1 ? true : false; @@ -844,6 +941,11 @@ QList SqliteStorage::networks(UserId user) net.useSasl = networksQuery.value(16).toInt() == 1 ? true : false; net.saslAccount = networksQuery.value(17).toString(); net.saslPassword = networksQuery.value(18).toString(); + // Custom rate limiting + net.useCustomMessageRate = networksQuery.value(19).toInt() == 1 ? true : false; + net.messageRateBurstSize = networksQuery.value(20).toUInt(); + net.messageRateDelay = networksQuery.value(21).toUInt(); + net.unlimitedMessageRate = networksQuery.value(22).toInt() == 1 ? true : false; serversQuery.bindValue(":networkid", net.networkId.toInt()); safeExec(serversQuery); @@ -866,6 +968,7 @@ QList SqliteStorage::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).toInt() == 1 ? true : false; servers << server; } net.serverList = servers; @@ -959,7 +1062,7 @@ void SqliteStorage::setChannelPersistent(UserId user, const NetworkId &networkId QSqlQuery query(db); 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 ? 1 : 0); @@ -981,7 +1084,7 @@ void SqliteStorage::setPersistentChannelKey(UserId user, const NetworkId &networ QSqlQuery query(db); 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); @@ -1110,7 +1213,7 @@ BufferInfo SqliteStorage::bufferInfo(UserId user, const NetworkId &networkId, Bu 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); } } @@ -1365,7 +1468,7 @@ void SqliteStorage::setBufferLastSeenMsg(UserId user, const BufferId &bufferId, query.prepare(queryString("update_buffer_lastseen")); query.bindValue(":userid", user.toInt()); query.bindValue(":bufferid", bufferId.toInt()); - query.bindValue(":lastseenmsgid", msgId.toInt()); + query.bindValue(":lastseenmsgid", msgId.toQint64()); lockForWrite(); safeExec(query); @@ -1394,7 +1497,7 @@ QHash SqliteStorage::bufferLastSeenMsgIds(UserId user) error = !watchQuery(query); if (!error) { while (query.next()) { - lastSeenHash[query.value(0).toInt()] = query.value(1).toInt(); + lastSeenHash[query.value(0).toInt()] = query.value(1).toLongLong(); } } } @@ -1415,7 +1518,7 @@ void SqliteStorage::setBufferMarkerLineMsg(UserId user, const BufferId &bufferId query.prepare(queryString("update_buffer_markerlinemsgid")); query.bindValue(":userid", user.toInt()); query.bindValue(":bufferid", bufferId.toInt()); - query.bindValue(":markerlinemsgid", msgId.toInt()); + query.bindValue(":markerlinemsgid", msgId.toQint64()); lockForWrite(); safeExec(query); @@ -1444,7 +1547,7 @@ QHash SqliteStorage::bufferMarkerLineMsgIds(UserId user) error = !watchQuery(query); if (!error) { while (query.next()) { - markerLineHash[query.value(0).toInt()] = query.value(1).toInt(); + markerLineHash[query.value(0).toInt()] = query.value(1).toLongLong(); } } } @@ -1454,6 +1557,195 @@ QHash SqliteStorage::bufferMarkerLineMsgIds(UserId user) return markerLineHash; } +void SqliteStorage::setBufferActivity(UserId user, BufferId bufferId, Message::Types bufferActivity) +{ + QSqlDatabase db = logDb(); + db.transaction(); + + { + QSqlQuery query(db); + query.prepare(queryString("update_buffer_bufferactivity")); + query.bindValue(":userid", user.toInt()); + query.bindValue(":bufferid", bufferId.toInt()); + query.bindValue(":bufferactivity", (int) bufferActivity); + + lockForWrite(); + safeExec(query); + watchQuery(query); + } + db.commit(); + unlock(); +} + + +QHash SqliteStorage::bufferActivities(UserId user) +{ + QHash bufferActivityHash; + + QSqlDatabase db = logDb(); + db.transaction(); + + bool error = false; + { + QSqlQuery query(db); + query.prepare(queryString("select_buffer_bufferactivities")); + query.bindValue(":userid", user.toInt()); + + lockForRead(); + safeExec(query); + error = !watchQuery(query); + if (!error) { + while (query.next()) { + bufferActivityHash[query.value(0).toInt()] = Message::Types(query.value(1).toInt()); + } + } + } + + db.commit(); + unlock(); + return bufferActivityHash; +} + + +Message::Types SqliteStorage::bufferActivity(BufferId bufferId, MsgId lastSeenMsgId) +{ + QSqlDatabase db = logDb(); + db.transaction(); + + Message::Types result = Message::Types(0); + { + QSqlQuery query(db); + query.prepare(queryString("select_buffer_bufferactivity")); + query.bindValue(":bufferid", bufferId.toInt()); + query.bindValue(":lastseenmsgid", lastSeenMsgId.toQint64()); + + lockForRead(); + safeExec(query); + if (query.first()) + result = Message::Types(query.value(0).toInt()); + } + + db.commit(); + unlock(); + return result; +} + +QHash SqliteStorage::bufferCiphers(UserId user, const NetworkId &networkId) +{ + QHash bufferCiphers; + + QSqlDatabase db = logDb(); + db.transaction(); + { + QSqlQuery query(db); + query.prepare(queryString("select_buffer_ciphers")); + query.bindValue(":userid", user.toInt()); + query.bindValue(":networkid", networkId.toInt()); + + lockForRead(); + safeExec(query); + watchQuery(query); + while (query.next()) { + bufferCiphers[query.value(0).toString()] = QByteArray::fromHex(query.value(1).toString().toUtf8()); + } + } + unlock(); + return bufferCiphers; +} + +void SqliteStorage::setBufferCipher(UserId user, const NetworkId &networkId, const QString &bufferName, const QByteArray &cipher) +{ + QSqlDatabase db = logDb(); + db.transaction(); + + { + QSqlQuery query(db); + query.prepare(queryString("update_buffer_cipher")); + query.bindValue(":userid", user.toInt()); + query.bindValue(":networkid", networkId.toInt()); + query.bindValue(":buffercname", bufferName.toLower()); + query.bindValue(":cipher", QString(cipher.toHex())); + + lockForWrite(); + safeExec(query); + watchQuery(query); + db.commit(); + } + unlock(); +} + +void SqliteStorage::setHighlightCount(UserId user, BufferId bufferId, int count) +{ + QSqlDatabase db = logDb(); + db.transaction(); + + { + QSqlQuery query(db); + query.prepare(queryString("update_buffer_highlightcount")); + query.bindValue(":userid", user.toInt()); + query.bindValue(":bufferid", bufferId.toInt()); + query.bindValue(":highlightcount", count); + + lockForWrite(); + safeExec(query); + watchQuery(query); + } + db.commit(); + unlock(); +} + + +QHash SqliteStorage::highlightCounts(UserId user) +{ + QHash highlightCountHash; + + QSqlDatabase db = logDb(); + db.transaction(); + + bool error = false; + { + QSqlQuery query(db); + query.prepare(queryString("select_buffer_highlightcounts")); + query.bindValue(":userid", user.toInt()); + + lockForRead(); + safeExec(query); + error = !watchQuery(query); + if (!error) { + while (query.next()) { + highlightCountHash[query.value(0).toInt()] = query.value(1).toInt(); + } + } + } + + db.commit(); + unlock(); + return highlightCountHash; +} + + +int SqliteStorage::highlightCount(BufferId bufferId, MsgId lastSeenMsgId) +{ + QSqlDatabase db = logDb(); + db.transaction(); + + int result = 0; + { + QSqlQuery query(db); + query.prepare(queryString("select_buffer_highlightcount")); + query.bindValue(":bufferid", bufferId.toInt()); + query.bindValue(":lastseenmsgid", lastSeenMsgId.toQint64()); + + lockForRead(); + safeExec(query); + if (query.first()) + result = query.value(0).toInt(); + } + + db.commit(); + unlock(); + return result; +} bool SqliteStorage::logMessage(Message &msg) { @@ -1464,12 +1756,16 @@ bool SqliteStorage::logMessage(Message &msg) { QSqlQuery logMessageQuery(db); logMessageQuery.prepare(queryString("insert_message")); - - logMessageQuery.bindValue(":time", msg.timestamp().toTime_t()); + // As of SQLite schema version 31, timestamps are stored in milliseconds instead of + // seconds. This nets us more precision as well as simplifying 64-bit time. + logMessageQuery.bindValue(":time", msg.timestamp().toMSecsSinceEpoch()); logMessageQuery.bindValue(":bufferid", msg.bufferInfo().bufferId().toInt()); logMessageQuery.bindValue(":type", msg.type()); logMessageQuery.bindValue(":flags", (int)msg.flags()); logMessageQuery.bindValue(":sender", msg.sender()); + logMessageQuery.bindValue(":realname", msg.realName()); + logMessageQuery.bindValue(":avatarurl", msg.avatarUrl()); + logMessageQuery.bindValue(":senderprefixes", msg.senderPrefixes()); logMessageQuery.bindValue(":message", msg.contents()); lockForWrite(); @@ -1481,6 +1777,8 @@ bool SqliteStorage::logMessage(Message &msg) QSqlQuery addSenderQuery(db); addSenderQuery.prepare(queryString("insert_sender")); addSenderQuery.bindValue(":sender", msg.sender()); + addSenderQuery.bindValue(":realname", msg.realName()); + addSenderQuery.bindValue(":avatarurl", msg.avatarUrl()); safeExec(addSenderQuery); safeExec(logMessageQuery); error = !watchQuery(logMessageQuery); @@ -1490,7 +1788,7 @@ bool SqliteStorage::logMessage(Message &msg) } } if (!error) { - MsgId msgId = logMessageQuery.lastInsertId().toInt(); + MsgId msgId = logMessageQuery.lastInsertId().toLongLong(); if (msgId.isValid()) { msg.setMsgId(msgId); } @@ -1518,17 +1816,20 @@ bool SqliteStorage::logMessages(MessageList &msgs) db.transaction(); { - QSet senders; + QSet senders; QSqlQuery addSenderQuery(db); addSenderQuery.prepare(queryString("insert_sender")); lockForWrite(); for (int i = 0; i < msgs.count(); i++) { - const QString &sender = msgs.at(i).sender(); + auto &msg = msgs.at(i); + SenderData sender = { msg.sender(), msg.realName(), msg.avatarUrl() }; if (senders.contains(sender)) continue; senders << sender; - addSenderQuery.bindValue(":sender", sender); + addSenderQuery.bindValue(":sender", sender.sender); + addSenderQuery.bindValue(":realname", sender.realname); + addSenderQuery.bindValue(":avatarurl", sender.avatarurl); safeExec(addSenderQuery); } } @@ -1539,12 +1840,16 @@ bool SqliteStorage::logMessages(MessageList &msgs) logMessageQuery.prepare(queryString("insert_message")); for (int i = 0; i < msgs.count(); i++) { Message &msg = msgs[i]; - - logMessageQuery.bindValue(":time", msg.timestamp().toTime_t()); + // As of SQLite schema version 31, timestamps are stored in milliseconds instead of + // seconds. This nets us more precision as well as simplifying 64-bit time. + logMessageQuery.bindValue(":time", msg.timestamp().toMSecsSinceEpoch()); logMessageQuery.bindValue(":bufferid", msg.bufferInfo().bufferId().toInt()); logMessageQuery.bindValue(":type", msg.type()); logMessageQuery.bindValue(":flags", (int)msg.flags()); logMessageQuery.bindValue(":sender", msg.sender()); + logMessageQuery.bindValue(":realname", msg.realName()); + logMessageQuery.bindValue(":avatarurl", msg.avatarUrl()); + logMessageQuery.bindValue(":senderprefixes", msg.senderPrefixes()); logMessageQuery.bindValue(":message", msg.contents()); safeExec(logMessageQuery); @@ -1553,7 +1858,7 @@ bool SqliteStorage::logMessages(MessageList &msgs) break; } else { - msg.setMsgId(logMessageQuery.lastInsertId().toInt()); + msg.setMsgId(logMessageQuery.lastInsertId().toLongLong()); } } } @@ -1584,7 +1889,7 @@ QList SqliteStorage::requestMsgs(UserId user, BufferId bufferId, MsgId bool error = false; BufferInfo bufferInfo; { - // code dupication from getBufferInfo: + // code duplication from getBufferInfo: // this is due to the impossibility of nesting transactions and recursive locking QSqlQuery bufferInfoQuery(db); bufferInfoQuery.prepare(queryString("select_buffer_by_id")); @@ -1612,12 +1917,12 @@ QList SqliteStorage::requestMsgs(UserId user, BufferId bufferId, MsgId } else if (last == -1) { query.prepare(queryString("select_messagesNewerThan")); - query.bindValue(":firstmsg", first.toInt()); + query.bindValue(":firstmsg", first.toQint64()); } else { - query.prepare(queryString("select_messages")); - query.bindValue(":lastmsg", last.toInt()); - query.bindValue(":firstmsg", first.toInt()); + query.prepare(queryString("select_messagesRange")); + query.bindValue(":lastmsg", last.toQint64()); + query.bindValue(":firstmsg", first.toQint64()); } query.bindValue(":bufferid", bufferId.toInt()); query.bindValue(":limit", limit); @@ -1626,13 +1931,99 @@ QList SqliteStorage::requestMsgs(UserId user, BufferId bufferId, MsgId watchQuery(query); while (query.next()) { - Message msg(QDateTime::fromTime_t(query.value(1).toInt()), + Message msg( + // As of SQLite schema version 31, timestamps are stored in milliseconds instead of + // seconds. This nets us more precision as well as simplifying 64-bit time. + QDateTime::fromMSecsSinceEpoch(query.value(1).toLongLong()), bufferInfo, - (Message::Type)query.value(2).toUInt(), - query.value(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(); + unlock(); + + return messagelist; +} + + +QList SqliteStorage::requestMsgsFiltered(UserId user, BufferId bufferId, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags) +{ + QList messagelist; + + QSqlDatabase db = logDb(); + db.transaction(); + + bool error = false; + BufferInfo bufferInfo; + { + // code dupication from getBufferInfo: + // this is due to the impossibility of nesting transactions and recursive locking + QSqlQuery bufferInfoQuery(db); + bufferInfoQuery.prepare(queryString("select_buffer_by_id")); + bufferInfoQuery.bindValue(":userid", user.toInt()); + bufferInfoQuery.bindValue(":bufferid", bufferId.toInt()); + + lockForRead(); + safeExec(bufferInfoQuery); + error = !watchQuery(bufferInfoQuery) || !bufferInfoQuery.first(); + if (!error) { + bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(), bufferInfoQuery.value(1).toInt(), (BufferInfo::Type)bufferInfoQuery.value(2).toInt(), 0, bufferInfoQuery.value(4).toString()); + error = !bufferInfo.isValid(); + } + } + if (error) { + db.rollback(); + unlock(); + return messagelist; + } + + { + QSqlQuery query(db); + if (last == -1 && first == -1) { + query.prepare(queryString("select_messagesNewestK_filtered")); + } + else if (last == -1) { + query.prepare(queryString("select_messagesNewerThan_filtered")); + query.bindValue(":firstmsg", first.toQint64()); + } + else { + query.prepare(queryString("select_messagesRange_filtered")); + query.bindValue(":lastmsg", last.toQint64()); + query.bindValue(":firstmsg", first.toQint64()); + } + query.bindValue(":bufferid", bufferId.toInt()); + query.bindValue(":limit", limit); + int typeRaw = type; + query.bindValue(":type", typeRaw); + int flagsRaw = flags; + query.bindValue(":flags", flagsRaw); + + safeExec(query); + watchQuery(query); + + while (query.next()) { + Message msg( + // As of SQLite schema version 31, timestamps are stored in milliseconds + // instead of seconds. This nets us more precision as well as simplifying + // 64-bit time. + QDateTime::fromMSecsSinceEpoch(query.value(1).toLongLong()), + bufferInfo, + (Message::Type)query.value(2).toInt(), + query.value(8).toString(), + query.value(4).toString(), + query.value(5).toString(), + query.value(6).toString(), + query.value(7).toString(), + Message::Flags{query.value(3).toInt()}); + msg.setMsgId(query.value(0).toLongLong()); messagelist << msg; } } @@ -1670,23 +2061,29 @@ QList SqliteStorage::requestAllMsgs(UserId user, MsgId first, MsgId las } else { query.prepare(queryString("select_messagesAll")); - query.bindValue(":lastmsg", last.toInt()); + query.bindValue(":lastmsg", last.toQint64()); } query.bindValue(":userid", user.toInt()); - query.bindValue(":firstmsg", first.toInt()); + query.bindValue(":firstmsg", first.toQint64()); query.bindValue(":limit", limit); safeExec(query); watchQuery(query); while (query.next()) { - Message msg(QDateTime::fromTime_t(query.value(2).toInt()), + Message msg( + // As of SQLite schema version 31, timestamps are stored in milliseconds instead of + // seconds. This nets us more precision as well as simplifying 64-bit time. + QDateTime::fromMSecsSinceEpoch(query.value(2).toLongLong()), bufferInfoHash[query.value(1).toInt()], - (Message::Type)query.value(3).toUInt(), - query.value(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; } } @@ -1695,6 +2092,110 @@ QList SqliteStorage::requestAllMsgs(UserId user, MsgId first, MsgId las return messagelist; } +QList SqliteStorage::requestAllMsgsFiltered(UserId user, MsgId first, MsgId last, int limit, Message::Types type, Message::Flags flags) +{ + QList messagelist; + + QSqlDatabase db = logDb(); + db.transaction(); + + QHash bufferInfoHash; + { + QSqlQuery bufferInfoQuery(db); + bufferInfoQuery.prepare(queryString("select_buffers")); + bufferInfoQuery.bindValue(":userid", user.toInt()); + + lockForRead(); + safeExec(bufferInfoQuery); + watchQuery(bufferInfoQuery); + while (bufferInfoQuery.next()) { + BufferInfo bufferInfo = BufferInfo(bufferInfoQuery.value(0).toInt(), bufferInfoQuery.value(1).toInt(), (BufferInfo::Type)bufferInfoQuery.value(2).toInt(), bufferInfoQuery.value(3).toInt(), bufferInfoQuery.value(4).toString()); + bufferInfoHash[bufferInfo.bufferId()] = bufferInfo; + } + + QSqlQuery query(db); + if (last == -1) { + query.prepare(queryString("select_messagesAllNew_filtered")); + } + else { + query.prepare(queryString("select_messagesAll_filtered")); + query.bindValue(":lastmsg", last.toQint64()); + } + query.bindValue(":userid", user.toInt()); + query.bindValue(":firstmsg", first.toQint64()); + query.bindValue(":limit", limit); + int typeRaw = type; + query.bindValue(":type", typeRaw); + int flagsRaw = flags; + query.bindValue(":flags", flagsRaw); + safeExec(query); + + watchQuery(query); + + while (query.next()) { + Message msg( + // As of SQLite schema version 31, timestamps are stored in milliseconds + // instead of seconds. This nets us more precision as well as simplifying + // 64-bit time. + QDateTime::fromMSecsSinceEpoch(query.value(2).toLongLong()), + bufferInfoHash[query.value(1).toInt()], + (Message::Type)query.value(3).toInt(), + query.value(9).toString(), + query.value(5).toString(), + query.value(6).toString(), + query.value(7).toString(), + query.value(8).toString(), + Message::Flags{query.value(4).toInt()}); + msg.setMsgId(query.value(0).toLongLong()); + messagelist << msg; + } + } + db.commit(); + unlock(); + return messagelist; +} + +QMap SqliteStorage::getAllAuthUserNames() +{ + QMap authusernames; + + QSqlDatabase db = logDb(); + db.transaction(); + { + QSqlQuery query(db); + query.prepare(queryString("select_all_authusernames")); + + lockForRead(); + safeExec(query); + watchQuery(query); + while (query.next()) { + authusernames[query.value(0).toInt()] = query.value(1).toString(); + } + } + db.commit(); + unlock(); + return authusernames; +} + + +QString SqliteStorage::getAuthUserName(UserId user) { + QString authusername; + QSqlQuery query(logDb()); + query.prepare(queryString("select_authusername")); + query.bindValue(":userid", user.toInt()); + + lockForRead(); + safeExec(query); + watchQuery(query); + unlock(); + + if (query.first()) { + authusername = query.value(0).toString(); + } + + return authusername; +} + QString SqliteStorage::backlogFile() { @@ -1711,12 +2212,15 @@ bool SqliteStorage::safeExec(QSqlQuery &query, int retryCount) switch (query.lastError().number()) { case 5: // SQLITE_BUSY 5 /* The database file is locked */ + [[clang::fallthrough]]; case 6: // SQLITE_LOCKED 6 /* A table in the database is locked */ if (retryCount < _maxRetryCount) return safeExec(query, retryCount + 1); + break; default: - return false; + ; } + return false; } @@ -1746,7 +2250,7 @@ void SqliteMigrationReader::setMaxId(MigrationObject mo) } QSqlQuery query = logDb().exec(queryString); query.first(); - _maxId = query.value(0).toInt(); + _maxId = query.value(0).toLongLong(); } @@ -1786,6 +2290,9 @@ bool SqliteMigrationReader::prepareQuery(MigrationObject mo) case UserSetting: newQuery(queryString("migrate_read_usersetting"), logDb()); break; + case CoreState: + newQuery(queryString("migrate_read_corestate"), logDb()); + break; } return exec(); } @@ -1799,6 +2306,8 @@ bool SqliteMigrationReader::readMo(QuasselUserMO &user) user.id = value(0).toInt(); user.username = value(1).toString(); user.password = value(2).toString(); + user.hashversion = value(3).toInt(); + user.authenticator = value(4).toString(); return true; } @@ -1822,7 +2331,7 @@ bool SqliteMigrationReader::readMo(IdentityMO &identity) identity.autoAwayReasonEnabled = value(11).toInt() == 1 ? true : false; identity.detachAwayEnabled = value(12).toInt() == 1 ? true : false; identity.detachAwayReason = value(13).toString(); - identity.detchAwayReasonEnabled = value(14).toInt() == 1 ? true : false; + identity.detachAwayReasonEnabled = value(14).toInt() == 1 ? true : false; identity.ident = value(15).toString(); identity.kickReason = value(16).toString(); identity.partReason = value(17).toString(); @@ -1875,6 +2384,11 @@ bool SqliteMigrationReader::readMo(NetworkMO &network) network.usesasl = value(22).toInt() == 1 ? true : false; network.saslaccount = value(23).toString(); network.saslpassword = value(24).toString(); + // Custom rate limiting + network.usecustommessagerate = value(25).toInt() == 1 ? true : false; + network.messagerateburstsize = value(26).toInt(); + network.messageratedelay = value(27).toUInt(); + network.unlimitedmessagerate = value(28).toInt() == 1 ? true : false; return true; } @@ -1891,10 +2405,14 @@ bool SqliteMigrationReader::readMo(BufferMO &buffer) buffer.buffername = value(4).toString(); buffer.buffercname = value(5).toString(); buffer.buffertype = value(6).toInt(); - buffer.lastseenmsgid = value(7).toInt(); - buffer.markerlinemsgid = value(8).toInt(); - buffer.key = value(9).toString(); - buffer.joined = value(10).toInt() == 1 ? true : false; + buffer.lastmsgid = value(7).toLongLong(); + buffer.lastseenmsgid = value(8).toLongLong(); + buffer.markerlinemsgid = value(9).toLongLong(); + buffer.bufferactivity = value(10).toInt(); + buffer.highlightcount = value(11).toInt(); + buffer.key = value(12).toString(); + buffer.joined = value(13).toInt() == 1 ? true : false; + buffer.cipher = value(14).toString(); return true; } @@ -1915,19 +2433,21 @@ bool SqliteMigrationReader::readMo(SenderMO &sender) } } - sender.senderId = value(0).toInt(); + sender.senderId = value(0).toLongLong(); sender.sender = value(1).toString(); + sender.realname = value(2).toString(); + sender.avatarurl = value(3).toString(); return true; } bool SqliteMigrationReader::readMo(BacklogMO &backlog) { - int skipSteps = 0; + qint64 skipSteps = 0; while (!next()) { if (backlog.messageid < _maxId) { - bindValue(0, backlog.messageid.toInt() + (skipSteps * stepSize())); - bindValue(1, backlog.messageid.toInt() + ((skipSteps + 1) * stepSize())); + bindValue(0, backlog.messageid.toQint64() + (skipSteps * stepSize())); + bindValue(1, backlog.messageid.toQint64() + ((skipSteps + 1) * stepSize())); skipSteps++; if (!exec()) return false; @@ -1937,13 +2457,16 @@ bool SqliteMigrationReader::readMo(BacklogMO &backlog) } } - backlog.messageid = value(0).toInt(); - backlog.time = QDateTime::fromTime_t(value(1).toInt()).toUTC(); + backlog.messageid = value(0).toLongLong(); + // As of SQLite schema version 31, timestamps are stored in milliseconds instead of + // seconds. This nets us more precision as well as simplifying 64-bit time. + backlog.time = QDateTime::fromMSecsSinceEpoch(value(1).toLongLong()).toUTC(); backlog.bufferid = value(2).toInt(); backlog.type = value(3).toInt(); backlog.flags = value(4).toInt(); - backlog.senderid = value(5).toInt(); - backlog.message = value(6).toString(); + backlog.senderid = value(5).toLongLong(); + backlog.senderprefixes = value(6).toString(); + backlog.message = value(7).toString(); return true; } @@ -1967,6 +2490,7 @@ bool SqliteMigrationReader::readMo(IrcServerMO &ircserver) ircserver.proxyport = value(11).toInt(); ircserver.proxyuser = value(12).toString(); ircserver.proxypass = value(13).toString(); + ircserver.sslverify = value(14).toInt() == 1 ? true : false; return true; } @@ -1982,3 +2506,15 @@ bool SqliteMigrationReader::readMo(UserSettingMO &userSetting) return true; } + + +bool SqliteMigrationReader::readMo(CoreStateMO &coreState) +{ + if (!next()) + return false; + + coreState.key = value(0).toString(); + coreState.value = value(1).toByteArray(); + + return true; +}