Commit ca4cb7fa authored by Linus Jahn's avatar Linus Jahn

Rewrite MessageController using gloox

Rewrote the MessageController into a MessageSessionHandler and a MessageHandler.
It still supports everything as before (e.g. Message Receipts).
parent a0191b40
......@@ -7,7 +7,8 @@ set(KAIDAN_SOURCES
${CURDIR}/Database.cpp
${CURDIR}/RosterController.cpp
${CURDIR}/RosterModel.cpp
${CURDIR}/MessageController.cpp
${CURDIR}/MessageHandler.cpp
${CURDIR}/MessageSessionHandler.cpp
${CURDIR}/MessageModel.cpp
${CURDIR}/Notifications.cpp
${CURDIR}/PresenceController.cpp
......
......@@ -38,7 +38,7 @@
// Kaidan
#include "RosterController.h"
#include "PresenceController.h"
#include "MessageController.h"
#include "MessageSessionHandler.h"
#include "MessageModel.h"
#include "VCardController.h"
#include "ServiceDiscoveryManager.h"
......@@ -48,17 +48,20 @@ Kaidan::Kaidan(Swift::NetworkFactories *networkFactories, QObject *parent) :
{
connected = false;
netFactories = networkFactories;
// setup database
database = new Database();
database->openDatabase();
if (database->needToConvert()) database->convertDatabase();
// setup components
storages = new Swift::MemoryStorages(Swift::PlatformCryptoProvider::create());
messageController = new MessageController(database->getDatabase());
messageModel = new MessageModel(database->getDatabase());
rosterController = new RosterController(database->getDatabase());
presenceController = new PresenceController();
vCardController = new VCardController();
serviceDiscoveryManager = new ServiceDiscoveryManager();
//
// Load settings data
//
......@@ -78,9 +81,6 @@ Kaidan::Kaidan(Swift::NetworkFactories *networkFactories, QObject *parent) :
jidResource = QString(APPLICATION_NAME);
settings->setValue("auth/resource", jidResource);
}
// create/update the full jid
updateFullJid();
}
Kaidan::~Kaidan()
......@@ -90,10 +90,10 @@ Kaidan::~Kaidan()
softwareVersionResponder->stop();
delete tracer;
delete softwareVersionResponder;
delete messageController;
delete client;
}
delete messageSessionHandler;
delete rosterController;
delete presenceController;
delete vCardController;
......@@ -104,7 +104,17 @@ Kaidan::~Kaidan()
void Kaidan::mainConnect()
{
// Create a new XMPP client
client = new Swift::Client(fullJid.toStdString(), password.toStdString(), netFactories, storages);
client_ = new gloox::Client(gloox::JID(jid.toStdString()), password.toStdString());
// require encryption
client_->setTls(gloox::TLSPolicy::TLSRequired);
// set the JID resource
client_->setResource(jidResource.toStdString());
messageSessionHandler = new MessageSessionHandler(client_, messageModel);
client_->registerMessageSessionHandler((gloox::MessageSessionHandler*) messageSessionHandler);
client = new Swift::Client(jid.toStdString(), password.toStdString(), netFactories, storages);
// trust all certificates
client->setAlwaysTrustCertificates();
......@@ -123,7 +133,6 @@ void Kaidan::mainConnect()
softwareVersionResponder->start();
// set client in message, roster and presence controller
messageController->setClient(client);
rosterController->setClient(client);
presenceController->setClient(client);
vCardController->setClient(client);
......@@ -134,6 +143,8 @@ void Kaidan::mainConnect()
// .. and connect!
client->connect(options);
client_->connect();
}
// we don't want to close client without disconnection
......@@ -163,11 +174,6 @@ bool Kaidan::newLoginNeeded()
return (jid == "") || (password == "");
}
void Kaidan::updateFullJid()
{
fullJid = jid + QString("/") + jidResource;
}
QString Kaidan::getJid()
{
return jid;
......@@ -177,7 +183,6 @@ void Kaidan::setJid(QString jid_)
{
jid = jid_; // set new jid for mainConnect
settings->setValue("auth/jid", jid_); // save to settings
updateFullJid();
}
QString Kaidan::getJidResource()
......@@ -189,7 +194,6 @@ void Kaidan::setJidResource(QString jidResource_)
{
jidResource = jidResource_;
settings->setValue("auth/resource", jidResource); // save jid resource
updateFullJid(); // update the full jid (the resource part has changed)
}
QString Kaidan::getPassword()
......@@ -217,8 +221,8 @@ void Kaidan::setChatPartner(QString chatPartner)
// set the new chat partner
this->chatPartner = chatPartner;
// upsate message controller
messageController->setChatPartner(&chatPartner, &jid);
// update message controller
messageModel->applyRecipientFilter(&chatPartner, &jid);
rosterController->setChatPartner(&chatPartner);
emit chatPartnerChanged();
......@@ -226,7 +230,7 @@ void Kaidan::setChatPartner(QString chatPartner)
void Kaidan::sendMessage(QString jid, QString message)
{
messageController->sendMessage(&(this->jid), &jid, &message);
messageSessionHandler->getMessageHandler()->sendMessage(&(this->jid), &jid, &message);
rosterController->handleNewMessageSent(&jid, &message);
}
......@@ -256,18 +260,22 @@ RosterController* Kaidan::getRosterController()
{
return rosterController;
}
MessageController* Kaidan::getMessageController()
MessageModel* Kaidan::getMessageModel()
{
return messageController;
return messageModel;
}
VCardController* Kaidan::getVCardController()
{
return vCardController;
}
bool Kaidan::getConnectionState() const
{
return connected;
}
QString Kaidan::getVersionString()
{
return QString(VERSION_STRING);
......
......@@ -30,10 +30,12 @@
// Swiften
#include <Swiften/Client/Client.h>
#include <Swiften/Client/ClientXMLTracer.h>
// gloox
#include <gloox/client.h>
// Kaidan
#include "Database.h"
#include "RosterController.h"
#include "MessageController.h"
#include "MessageSessionHandler.h"
#include "PresenceController.h"
#include "VCardController.h"
#include "ServiceDiscoveryManager.h"
......@@ -43,7 +45,7 @@ class Kaidan : public QObject
Q_OBJECT
Q_PROPERTY(RosterController* rosterController READ getRosterController NOTIFY rosterControllerChanged)
Q_PROPERTY(MessageController* messageController READ getMessageController NOTIFY messageControllerChanged)
Q_PROPERTY(MessageModel* messageModel READ getMessageModel NOTIFY messageModelChanged)
Q_PROPERTY(VCardController* vCardController READ getVCardController NOTIFY vCardControllerChanged)
Q_PROPERTY(bool connectionState READ getConnectionState NOTIFY connectionStateConnected NOTIFY connectionStateDisconnected)
Q_PROPERTY(QString jid READ getJid WRITE setJid NOTIFY jidChanged)
......@@ -70,7 +72,7 @@ public:
void setChatPartner(QString);
RosterController* getRosterController();
MessageController* getMessageController();
MessageModel* getMessageModel();
VCardController* getVCardController();
Q_INVOKABLE void sendMessage(QString jid, QString message);
......@@ -79,7 +81,7 @@ public:
signals:
void rosterControllerChanged();
void messageControllerChanged();
void messageModelChanged();
void vCardControllerChanged();
void connectionStateConnected();
void connectionStateDisconnected();
......@@ -91,28 +93,28 @@ signals:
private:
void handleConnected();
void handleDisconnected();
void updateFullJid();
bool connected;
Swift::Client* client;
Swift::ClientXMLTracer* tracer;
Swift::SoftwareVersionResponder* softwareVersionResponder;
Swift::NetworkFactories* netFactories;
Swift::MemoryStorages* storages;
gloox::Client *client_;
Swift::Client *client;
Swift::ClientXMLTracer *tracer;
Swift::SoftwareVersionResponder *softwareVersionResponder;
Swift::NetworkFactories *netFactories;
Swift::MemoryStorages *storages;
Database* database;
RosterController* rosterController;
MessageController* messageController;
PresenceController* presenceController;
VCardController* vCardController;
ServiceDiscoveryManager* serviceDiscoveryManager;
Database *database;
RosterController *rosterController;
MessageModel *messageModel;
MessageSessionHandler *messageSessionHandler;
PresenceController *presenceController;
VCardController *vCardController;
ServiceDiscoveryManager *serviceDiscoveryManager;
QSettings* settings;
QSettings *settings;
QString jid;
QString jidResource;
QString fullJid;
QString password;
QString chatPartner;
};
......
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2017 LNJ <git@lnj.li>
*
* Kaidan is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Kaidan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kaidan. If not, see <http://www.gnu.org/licenses/>.
*/
#include "MessageController.h"
// Qt
#include <QDateTime>
#include <QDebug>
#include <QString>
// Boost
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/optional.hpp>
#include <boost/smart_ptr/make_shared.hpp>
// Swiften
#include <Swiften/Client/Client.h>
#include <Swiften/Elements/Message.h>
#include <Swiften/Elements/DeliveryReceipt.h>
#include <Swiften/Elements/DeliveryReceiptRequest.h>
#include <Swiften/Base/IDGenerator.h>
// Kaidan
#include "MessageModel.h"
#include "Notifications.h"
MessageController::MessageController(QSqlDatabase *database, QObject *parent) : QObject(parent)
{
messageModel = new MessageModel(database);
emit messageModelChanged();
}
MessageController::~MessageController()
{
delete messageModel;
}
void MessageController::setClient(Swift::Client* client_)
{
client = client_;
client->onMessageReceived.connect(boost::bind(&MessageController::handleMessageReceived, this, _1));
}
MessageModel* MessageController::getMessageModel()
{
return messageModel;
}
void MessageController::setChatPartner(QString *recipient, QString* ownJid)
{
// we have to use ownJid here, because this should also be usable when
// we're offline or we haven't connected already.
messageModel->applyRecipientFilter(recipient, ownJid);
}
void MessageController::handleMessageReceived(Swift::Message::ref message_)
{
boost::optional<std::string> bodyOpt = message_->getBody();
if (bodyOpt) {
//
// add the message to the db
//
// author is only the 'bare' JID: e.g. 'albert@einstein.ch'
const QString author = QString::fromStdString(message_->getFrom().toBare().toString());
const QString author_resource = QString::fromStdString(message_->getFrom().getResource());
const QString recipient = QString::fromStdString(message_->getTo().toBare().toString());
const QString recipient_resource = QString::fromStdString(client->getJID().getResource());
QString timestamp = QDateTime::currentDateTime().toString(Qt::ISODate); // fallback timestamp
const QString message = QString::fromStdString(*bodyOpt);
const QString msgId = QString::fromStdString(message_->getID());
// get the timestamp from the message, if exists
boost::optional<boost::posix_time::ptime> timestampOpt = message_->getTimestamp();
if (timestampOpt) {
timestamp = QString::fromStdString(
boost::posix_time::to_iso_extended_string(*timestampOpt)
);
}
messageModel->addMessage(&author, &author_resource, &recipient,
&recipient_resource, &timestamp, &message, &msgId, false);
// send a new notification | TODO: Resolve nickname from JID
Notifications::sendMessageNotification(
message_->getFrom().toBare().toString(),
*bodyOpt
);
}
// XEP-0184: Message Delivery Receipts
// send a reply that the message has arrived
if (message_->getPayload<Swift::DeliveryReceiptRequest>()) {
// create a new reply payload
boost::shared_ptr<Swift::DeliveryReceipt> receiptPayload =
boost::make_shared<Swift::DeliveryReceipt>();
receiptPayload->setReceivedID(message_->getID());
// create a new message
Swift::Message::ref receiptReply = boost::make_shared<Swift::Message>();
receiptReply->setFrom(message_->getTo());
receiptReply->setTo(message_->getFrom());
// add the reply payload to the message
receiptReply->addPayload(receiptPayload);
// send the message
client->sendMessage(receiptReply);
}
// XEP-0184: Message Delivery Receipts
// get a reply of a delivered receipt request
Swift::DeliveryReceipt::ref receipt = message_->getPayload<Swift::DeliveryReceipt>();
if (receipt) {
std::string receivedId = receipt->getReceivedID();
if (receivedId.length() > 0) {
messageModel->setMessageAsDelivered(QString::fromStdString(receivedId));
}
}
}
void MessageController::sendMessage(QString *fromJid, QString *recipient_, QString *message_)
{
// generate a new message id
Swift::IDGenerator idGenerator;
std::string msgId = idGenerator.generateID();
//
// add the message to the db
//
const QString timestamp = QDateTime::currentDateTime().toString(Qt::ISODate);
const QString author_resource = QString::fromStdString(client->getJID().getResource());
const QString recipient_resource = QString("");
const QString qmsgId = QString::fromStdString(msgId);
messageModel->addMessage(fromJid, &author_resource, recipient_,
&recipient_resource, &timestamp, message_, &qmsgId, true);
//
// send the message
//
boost::shared_ptr<Swift::Message> newMessage(new Swift::Message());
newMessage->setTo(Swift::JID(recipient_->toStdString()));
newMessage->setFrom(client->getJID());
newMessage->setBody(message_->toStdString());
newMessage->setID(msgId);
// XEP-0184: Message Delivery Receipts
// add a delivery receipt request
newMessage->addPayload(boost::make_shared<Swift::DeliveryReceiptRequest>());
// send the message
client->sendMessage(newMessage);
}
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2017 LNJ <git@lnj.li>
*
* Kaidan is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Kaidan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kaidan. If not, see <http://www.gnu.org/licenses/>.
*/
#include "MessageHandler.h"
// Std
#include <exception>
#include <iostream>
// Qt
#include <QDateTime>
#include <QDebug>
#include <QString>
// gloox
#include <gloox/receipt.h>
// Kaidan
#include "MessageModel.h"
#include "Notifications.h"
MessageHandler::MessageHandler(gloox::Client *client, MessageModel *model)
{
messageModel = model;
this->client = client;
}
MessageHandler::~MessageHandler()
{
}
void MessageHandler::handleMessage(const gloox::Message &message, gloox::MessageSession *session)
{
QString body = QString::fromStdString(message.body());
if (body.size() > 0) {
//
// add the message to the db
//
// author is only the 'bare' JID: e.g. 'albert@einstein.ch'
const QString author = QString::fromStdString(message.from().bare());
const QString author_resource = QString::fromStdString(message.from().resource());
const QString recipient = QString::fromStdString(message.to().bare());
const QString recipient_resource = QString::fromStdString(message.to().resource());
// TODO: XEP-0203: Delayed Delivery's timestamps
QString timestamp = QDateTime::currentDateTime().toString(Qt::ISODate);
const QString msgId = QString::fromStdString(message.id());
messageModel->addMessage(&author, &author_resource, &recipient,
&recipient_resource, &timestamp, &body, &msgId, false);
// send a new notification | TODO: Resolve nickname from JID
Notifications::sendMessageNotification(message.from().full(), body.toStdString());
}
// XEP-0184: Message Delivery Receipts
// try to get a possible delivery receipt
gloox::Receipt *receipt = (gloox::Receipt*) message.findExtension<gloox::Receipt>(
gloox::ExtReceipt);
if (receipt) {
// get the type of the receipt
gloox::Receipt::ReceiptType receiptType = receipt->rcpt();
if (receiptType == gloox::Receipt::Request) {
// send the asked confirmation, that the message has been arrived
// new message to the author of the request
gloox::Message receiptMessage(gloox::Message::Chat, message.from());
// add the receipt extension containing the request's message id
gloox::Receipt *receiptPayload = new gloox::Receipt(gloox::Receipt::Received,
message.id());
receiptMessage.addExtension(receiptPayload);
// send the receipt message
client->send(receiptMessage);
} else if (receiptType == gloox::Receipt::Received) {
// Delivery Receipt Received -> mark message as read in db
messageModel->setMessageAsDelivered(QString::fromStdString(receipt->id()));
}
}
}
void MessageHandler::sendMessage(QString *fromJid, QString *toJid, QString *body)
{
// create a new message
gloox::Message message(gloox::Message::Chat, gloox::JID(toJid->toStdString()),
body->toStdString());
// add the message to the database
const QString timestamp = QDateTime::currentDateTime().toString(Qt::ISODate);
const QString id = QString::fromStdString(message.id());
messageModel->addMessage(fromJid, nullptr, toJid, nullptr, &timestamp, body,
&id, true);
// XEP-0184: Message Delivery Receipts
// request a delivery receipt from the other client
gloox::Receipt *receiptPayload = new gloox::Receipt(gloox::Receipt::Request);
message.addExtension(receiptPayload);
// send the message
client->send(message);
}
......@@ -17,43 +17,32 @@
* along with Kaidan. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MESSAGECONTROLLER_H
#define MESSAGECONTROLLER_H
#ifndef MESSAGEHANDLER_H
#define MESSAGEHANDLER_H
// Qt
#include <QObject>
#include <QSqlDatabase>
// Swiften
#include <Swiften/Client/Client.h>
#include <Swiften/Elements/Message.h>
// gloox
#include <gloox/client.h>
#include <gloox/message.h>
#include <gloox/messagehandler.h>
#include <gloox/messagesession.h>
// Kaidan
#include "MessageModel.h"
#include "RosterController.h"
class MessageController : public QObject
class MessageHandler : public gloox::MessageHandler
{
Q_OBJECT
Q_PROPERTY(MessageModel *messageModel READ getMessageModel NOTIFY messageModelChanged)
public:
MessageController(QSqlDatabase *database, QObject *parent = 0);
~MessageController();
void setClient(Swift::Client *client_);
MessageModel* getMessageModel();
MessageHandler(gloox::Client *client, MessageModel *model);
~MessageHandler();
void setChatPartner(QString *recipient, QString* ownJid);
void sendMessage(QString *fromJid, QString *recipient_, QString *message_);
signals:
void messageModelChanged();
void recipientChanged();
void sendMessage(QString *fromJid, QString *toJid, QString *body);
virtual void handleMessage(const gloox::Message &message, gloox::MessageSession *session = 0);
private:
void handleMessageReceived(Swift::Message::ref message);
Swift::Client *client;
gloox::Client *client;
MessageModel *messageModel;
};
#endif // MESSAGECONTROLLER_H
#endif // MESSAGEHANDLER_H
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2017 LNJ <git@lnj.li>
*
* Kaidan is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Kaidan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kaidan. If not, see <http://www.gnu.org/licenses/>.
*/
#include "MessageSessionHandler.h"
#include "MessageHandler.h"
MessageSessionHandler::MessageSessionHandler(gloox::Client *client, MessageModel *messageModel)
{
this->client = client;
this->messageModel = messageModel;
messageHandler = new MessageHandler(client, messageModel);
}
MessageSessionHandler::~MessageSessionHandler()