Verified Commit 555deda8 authored by Linus Jahn's avatar Linus Jahn

add mam support

parent 88109567
Pipeline #14818 passed with stages
in 21 minutes and 19 seconds
......@@ -50,24 +50,28 @@
#include "UploadManager.h"
#include "VCardManager.h"
ClientWorker::ClientWorker(Caches *caches, Kaidan *kaidan, bool enableLogging, QGuiApplication *app,
QObject* parent)
: QObject(parent), caches(caches), kaidan(kaidan), enableLogging(enableLogging), app(app)
ClientWorker::ClientWorker(
Caches *caches,
Kaidan *kaidan,
bool enableLogging,
QGuiApplication *app,
QObject* parent
) : QObject(parent),
caches(caches),
kaidan(kaidan),
client(new QXmppClient(this)),
logger(new LogHandler(client, this)),
enableLogging(enableLogging),
app(app),
registrationManager(new RegistrationManager(kaidan, caches->settings)),
vCardManager(new VCardManager(client, caches->avatarStorage, this)),
rosterManager(new RosterManager(kaidan, client, caches->rosterModel, caches->avatarStorage, vCardManager, this)),
msgHandler(new MessageHandler(client, caches->msgModel, caches->settings, this)),
discoManager(new DiscoveryManager(client, this)),
uploadManager(new UploadManager(kaidan, client, caches->msgModel, rosterManager, caches->transferCache, this)),
downloadManager(new DownloadManager(kaidan, caches->transferCache, caches->msgModel, this))
{
client = new QXmppClient(this);
logger = new LogHandler(client, this);
logger->enableLogging(enableLogging);
vCardManager = new VCardManager(client, caches->avatarStorage, this);
registrationManager = new RegistrationManager(kaidan, caches->settings);
rosterManager = new RosterManager(kaidan, client, caches->rosterModel,
caches->avatarStorage, vCardManager, this);
msgHandler = new MessageHandler(kaidan, client, caches->msgModel, this);
discoManager = new DiscoveryManager(client, this);
uploadManager = new UploadManager(kaidan, client, caches->msgModel, rosterManager,
caches->transferCache, this);
downloadManager = new DownloadManager(kaidan, caches->transferCache,
caches->msgModel, this);
client->addExtension(registrationManager);
connect(client, &QXmppClient::presenceReceived,
......
......@@ -203,10 +203,10 @@ private:
QGuiApplication *app;
RegistrationManager *registrationManager;
VCardManager *vCardManager;
RosterManager *rosterManager;
MessageHandler *msgHandler;
DiscoveryManager *discoManager;
VCardManager *vCardManager;
UploadManager *uploadManager;
DownloadManager *downloadManager;
};
......
......@@ -48,8 +48,10 @@
static const int DATABASE_LATEST_VERSION = 10;
Database::Database(QObject *parent)
: QObject(parent)
: QObject(parent)
{
connect(this, &Database::transactionRequested, this, &Database::transaction);
connect(this, &Database::commitRequested, this, &Database::commit);
}
Database::~Database()
......
......@@ -64,6 +64,13 @@ public:
*/
void commit();
signals:
/// Emit, to begin a transaction if none has been started already.
void transactionRequested();
/// Emit, to commit the transaction if every transaction has been finished.
void commitRequested();
private:
/**
* @return true if the database has to be converted using @c convertDatabase()
......
......@@ -268,11 +268,21 @@ void Kaidan::notifyLoginUriNotFound()
emit passiveNotificationRequested(tr("No valid login QR code found."));
}
MessageDb *Kaidan::getMsgDb() const
{
return m_msgDb;
}
ClientWorker *Kaidan::getClient() const
{
return m_client;
}
Database *Kaidan::getDatabase() const
{
return m_database;
}
Kaidan *Kaidan::instance()
{
return s_instance;
......
......@@ -191,12 +191,12 @@ public:
*/
void addOpenUri(const QString &uri);
/**
* Connects to the server by the parsed credentials (bare JID and password)
* from a given XMPP URI (e.g. from scanning a QR code)
* like "xmpp:user@example.org?login;password=abc"
*/
Q_INVOKABLE void loginByUri(const QString &uri);
/**
* Connects to the server by the parsed credentials (bare JID and password)
* from a given XMPP URI (e.g. from scanning a QR code)
* like "xmpp:user@example.org?login;password=abc"
*/
Q_INVOKABLE void loginByUri(const QString &uri);
/**
* Returns whether an HTTP File Upload service has been found
......@@ -206,6 +206,10 @@ public:
return uploadServiceFound;
}
Database *getDatabase() const;
MessageDb *getMsgDb() const;
signals:
void avatarStorageChanged();
......
......@@ -45,6 +45,9 @@ MessageDb::MessageDb(QObject *parent)
{
connect(this, &MessageDb::fetchMessagesRequested,
this, &MessageDb::fetchMessages);
connect(this, &MessageDb::fetchLastMessageStampRequested,
this, &MessageDb::fetchLastMessageStamp);
}
void MessageDb::parseMessagesFromQuery(QSqlQuery &query, QVector<Message> &msgs)
......@@ -179,6 +182,24 @@ void MessageDb::fetchMessages(const QString &user1, const QString &user2, int in
emit messagesFetched(messages);
}
void MessageDb::fetchLastMessageStamp()
{
QSqlQuery query(QSqlDatabase::database(DB_CONNECTION));
query.setForwardOnly(true);
Utils::execQuery(query, "SELECT timestamp FROM Messages ORDER BY timestamp DESC LIMIT 1");
QDateTime stamp;
while (query.next()) {
stamp = QDateTime::fromString(
query.value(query.record().indexOf("timestamp")).toString(),
Qt::ISODate
);
}
emit lastMessageStampFetched(stamp);
}
void MessageDb::addMessage(const Message &msg)
{
QSqlDatabase db = QSqlDatabase::database(DB_CONNECTION);
......
......@@ -79,6 +79,13 @@ signals:
*/
void messagesFetched(const QVector<Message> &messages);
/**
*
*/
void fetchLastMessageStampRequested();
void lastMessageStampFetched(const QDateTime &stamp);
public slots:
/**
* @brief Fetches more entries from the database and emits messagesFetched() with
......@@ -92,6 +99,11 @@ public slots:
const QString &user2,
int index);
/**
*
*/
void fetchLastMessageStamp();
/**
* Adds a message to the database.
*/
......
......@@ -42,50 +42,112 @@
// Kaidan
#include "Kaidan.h"
#include "Message.h"
#include "MessageDb.h"
#include "MessageModel.h"
#include "Notifications.h"
#include "MediaUtils.h"
MessageHandler::MessageHandler(Kaidan *kaidan, QXmppClient *client, MessageModel *model,
QObject *parent)
: QObject(parent), kaidan(kaidan), client(client), model(model)
MessageHandler::MessageHandler(
QXmppClient *client,
MessageModel *model,
QSettings *settings,
QObject *parent
) : QObject(parent),
client(client),
model(model),
settings(settings),
carbonManager(new QXmppCarbonManager),
mamManager(new QXmppMamManager)
{
connect(client, &QXmppClient::connected, this, &MessageHandler::handleConnected);
connect(client, &QXmppClient::messageReceived, this, &MessageHandler::handleMessage);
connect(kaidan, &Kaidan::sendMessage, this, &MessageHandler::sendMessage);
connect(kaidan, &Kaidan::correctMessage, this, &MessageHandler::correctMessage);
client->addExtension(&receiptManager);
connect(&client->rosterManager(), &QXmppRosterManager::rosterReceived,
this, &MessageHandler::handleRosterReceived);
connect(Kaidan::instance(), &Kaidan::sendMessage, this, &MessageHandler::sendMessage);
connect(Kaidan::instance(), &Kaidan::correctMessage,
this, &MessageHandler::correctMessage);
connect(Kaidan::instance()->getMsgDb(), &MessageDb::lastMessageStampFetched,
this, &MessageHandler::handleLastMessageStampFetched);
connect(&receiptManager, &QXmppMessageReceiptManager::messageDelivered,
this, [=] (const QString&, const QString &id) {
emit model->setMessageAsDeliveredRequested(id);
});
carbonManager = new QXmppCarbonManager();
client->addExtension(carbonManager);
// messages sent to our account (forwarded from another client)
connect(carbonManager, &QXmppCarbonManager::messageReceived,
client, &QXmppClient::messageReceived);
client, &QXmppClient::messageReceived);
// messages sent from our account (but another client)
connect(carbonManager, &QXmppCarbonManager::messageSent,
client, &QXmppClient::messageReceived);
client, &QXmppClient::messageReceived);
connect(mamManager, &QXmppMamManager::archivedMessageReceived,
this, &MessageHandler::handleArchiveMessage);
connect(mamManager, &QXmppMamManager::resultsRecieved,
this, &MessageHandler::handleArchiveResults);
client->addExtension(&receiptManager);
client->addExtension(carbonManager);
client->addExtension(mamManager);
// carbons discovery
auto *discoManager = client->findExtension<QXmppDiscoveryManager>();
if (!discoManager)
return;
if (discoManager != nullptr) {
connect(discoManager, &QXmppDiscoveryManager::infoReceived,
this, &MessageHandler::handleDiscoInfo);
}
connect(discoManager, &QXmppDiscoveryManager::infoReceived,
this, &MessageHandler::handleDiscoInfo);
// get last message stamp to retrieve all new messages from the server since then
emit Kaidan::instance()->getMsgDb()->fetchLastMessageStampRequested();
}
MessageHandler::~MessageHandler()
{
delete carbonManager;
delete mamManager;
}
void MessageHandler::handleConnected()
{
// retrieve missed messages, if the last saved message has been loaded and exists
if (m_lastMessageLoaded && !m_lastMessageStamp.isNull())
retrieveMissedMessages(m_lastMessageStamp);
}
void MessageHandler::handleRosterReceived()
{
// retrieve initial messages for each contact, if there is no last message locally
if (m_lastMessageLoaded && m_lastMessageStamp.isNull())
retrieveInitialMessageHistory();
}
void MessageHandler::handleLastMessageStampFetched(const QDateTime &stamp)
{
m_lastMessageStamp = stamp;
m_lastMessageLoaded = true;
// this is for the case that loading the last message took longer than connecting to
// the server:
// if already connected directly retrieve messages
if (client->isConnected()) {
// if there are no messages at all, load initial history,
// otherwise load all missed messages since last online.
if (stamp.isNull()) {
// only start if roster was received already
if (client->rosterManager().isRosterReceived())
retrieveInitialMessageHistory();
} else {
retrieveMissedMessages(stamp);
}
}
}
void MessageHandler::handleMessage(const QXmppMessage &msg)
{
// TODO: check if messge is archived by archive id and don't send notification
if (msg.body().isEmpty() || msg.type() == QXmppMessage::Error)
return;
......@@ -153,6 +215,10 @@ void MessageHandler::handleMessage(const QXmppMessage &msg)
? QDateTime::currentDateTimeUtc()
: msg.stamp().toUTC());
// if the received message is newer than the currently latest message, update
// if (settings->value("cache/latest-message").toDateTime() < message.stamp())
// settings->setValue("cache/latest-message", message.stamp());
// save the message to the database
// in case of message correction, replace old message
if (msg.replaceId().isEmpty()) {
......@@ -185,7 +251,7 @@ void MessageHandler::handleMessage(const QXmppMessage &msg)
message.isSpoiler() ? message.spoilerHint().isEmpty() ? tr("Spoiler")
: message.spoilerHint()
: msg.body();
emit kaidan->getRosterModel()->updateItemRequested(
emit Kaidan::instance()->getRosterModel()->updateItemRequested(
contactJid,
[=] (RosterItem &item) {
item.setLastMessage(lastMessage);
......@@ -200,7 +266,7 @@ void MessageHandler::sendMessage(const QString& toJid,
{
// TODO: Add offline message cache and send when connnected again
if (client->state() != QXmppClient::ConnectedState) {
emit kaidan->passiveNotificationRequested(
emit Kaidan::instance()->passiveNotificationRequested(
tr("Could not send message, as a result of not being connected.")
);
qWarning() << "[client] [MessageHandler] Could not send message, as a result of "
......@@ -241,11 +307,13 @@ void MessageHandler::sendMessage(const QString& toJid,
emit model->addMessageRequested(msg);
if (client->sendPacket(static_cast<QXmppMessage>(msg)))
if (client->sendPacket(static_cast<QXmppMessage>(msg))) {
emit model->setMessageAsSentRequested(msg.id());
else
emit kaidan->passiveNotificationRequested(tr("Message could not be sent."));
// TODO: handle error
// settings->setValue("cache/latest-message", msg.stamp());
} else {
emit Kaidan::instance()->passiveNotificationRequested(tr("Message could not be sent."));
// TODO: handle error
}
}
void MessageHandler::correctMessage(const QString& toJid,
......@@ -257,7 +325,7 @@ void MessageHandler::correctMessage(const QString& toJid,
// TODO: Add offline message cache and send when connnected again
if (client->state() != QXmppClient::ConnectedState) {
emit kaidan->passiveNotificationRequested(
emit Kaidan::instance()->passiveNotificationRequested(
tr("Could not correct message, as a result of not being connected.")
);
qWarning() << "[client] [MessageHandler] Could not correct message, as a result of "
......@@ -282,7 +350,7 @@ void MessageHandler::correctMessage(const QString& toJid,
if (client->sendPacket(msg))
emit model->setMessageAsSentRequested(msg.id());
else
emit kaidan->passiveNotificationRequested(
emit Kaidan::instance()->passiveNotificationRequested(
tr("Message correction was not successful."));
}
......@@ -294,3 +362,81 @@ void MessageHandler::handleDiscoInfo(const QXmppDiscoveryIq &info)
if (info.features().contains(NS_CARBONS))
carbonManager->setCarbonsEnabled(true);
}
void MessageHandler::handleArchiveMessage(const QString &queryId,
const QXmppMessage &message)
{
Q_UNUSED(queryId);
// TODO: check if chat marker and fetch another message
emit client->messageReceived(message);
// handleMessage(message);
}
void MessageHandler::handleArchiveResults(const QString &queryId,
const QXmppResultSetReply &resultSetReply,
bool complete)
{
Q_UNUSED(resultSetReply);
Q_UNUSED(complete);
if (queryId == m_runnningMissedMessagesQueryId) {
m_runnningMissedMessagesQueryId.clear();
emit Kaidan::instance()->getDatabase()->commitRequested();
}
if (m_runningInitialMessageQueryIds.contains(queryId)) {
m_runningInitialMessageQueryIds.removeOne(queryId);
if (m_runningInitialMessageQueryIds.isEmpty())
emit Kaidan::instance()->getDatabase()->commitRequested();
}
}
void MessageHandler::retrieveInitialMessageHistory()
{
qDebug() << "RETRIEVE INITIAL MESSAGES HISTORY()";
Q_ASSERT(m_runningInitialMessageQueryIds.isEmpty());
QXmppResultSetQuery queryLimit;
// load only one message per user (the rest can be loaded when required)
queryLimit.setMax(1);
// query last (newest) first
queryLimit.setBefore("");
const QStringList bareJids = client->rosterManager().getRosterBareJids();
if (bareJids.isEmpty())
return;
m_runningInitialMessageQueryIds.resize(bareJids.size());
int i = 0;
for (const auto &jid : bareJids) {
m_runningInitialMessageQueryIds[i] = mamManager->retrieveArchivedMessages(
QString(),
QString(),
jid,
QDateTime(),
QDateTime(),
queryLimit
);
i++;
}
emit Kaidan::instance()->getDatabase()->transactionRequested();
}
void MessageHandler::retrieveMissedMessages(const QDateTime &stamp)
{
qDebug() << "RETRIEVE MISSED MESSAGES (" << stamp.toString() << ")";
Q_ASSERT(m_runnningMissedMessagesQueryId.isEmpty());
QXmppResultSetQuery queryLimit;
// no limit
queryLimit.setMax(-1);
m_runnningMissedMessagesQueryId = mamManager->retrieveArchivedMessages({}, {}, {}, stamp, {}, queryLimit);
emit Kaidan::instance()->getDatabase()->transactionRequested();
}
......@@ -35,12 +35,19 @@
#include <QObject>
// QXmpp
#include <QXmppGlobal.h>
#include <QXmppMamManager.h>
#include <QXmppMessageReceiptManager.h>
// Kaidan
#include "Enums.h"
class Kaidan;
class MessageModel;
class QMimeType;
class QSettings;
class QXmppMessage;
class QXmppDiscoveryIq;
class QXmppDiscoveryIq;
class QXmppCarbonManager;
/**
......@@ -51,12 +58,20 @@ class MessageHandler : public QObject
Q_OBJECT
public:
MessageHandler(Kaidan *kaidan, QXmppClient *client, MessageModel *model,
QObject *parent = nullptr);
MessageHandler(
QXmppClient *client,
MessageModel *model,
QSettings *settings,
QObject *parent = nullptr
);
~MessageHandler();
public slots:
void handleConnected();
void handleRosterReceived();
void handleLastMessageStampFetched(const QDateTime &stamp);
/**
* Handles incoming messages from the server.
*/
......@@ -77,13 +92,38 @@ public slots:
*/
void handleDiscoInfo(const QXmppDiscoveryIq &);
/**
* Handles incoming archived messages
*/
void handleArchiveMessage(const QString &queryId, const QXmppMessage &message);
/**
* Called when an archive request was handled
*/
void handleArchiveResults(const QString &queryId,
const QXmppResultSetReply &resultSetReply,
bool complete);
/**
* @brief Requests all missed messages
*/
void retrieveInitialMessageHistory();
void retrieveMissedMessages(const QDateTime &stamp);
private:
Kaidan *kaidan;
QXmppClient *client;
QXmppMessageReceiptManager receiptManager;
MessageModel *model;
QSettings *settings;
QXmppCarbonManager *carbonManager;
QString chatPartner;
QXmppMamManager *mamManager;
QDateTime m_lastMessageStamp;
bool m_lastMessageLoaded = false;
QVector<QString> m_runningInitialMessageQueryIds;
QString m_runnningMissedMessagesQueryId;
};
#endif // MESSAGEHANDLER_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment