MessageHandler.cpp 6.76 KB
Newer Older
1 2 3
/*
 *  Kaidan - A user-friendly XMPP client for every device!
 *
Linus Jahn's avatar
Linus Jahn committed
4
 *  Copyright (C) 2016-2018 Kaidan developers and contributors
Linus Jahn's avatar
Linus Jahn committed
5
 *  (see the LICENSE file for a full list of copyright authors)
6 7 8 9 10 11
 *
 *  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.
 *
Linus Jahn's avatar
Linus Jahn committed
12 13 14 15 16 17 18 19 20 21
 *  In addition, as a special exception, the author of Kaidan gives
 *  permission to link the code of its release with the OpenSSL
 *  project's "OpenSSL" library (or with modified versions of it that
 *  use the same license as the "OpenSSL" library), and distribute the
 *  linked executables. You must obey the GNU General Public License in
 *  all respects for all of the code used other than "OpenSSL". If you
 *  modify this file, you may extend this exception to your version of
 *  the file, but you are not obligated to do so.  If you do not wish to
 *  do so, delete this exception statement from your version.
 *
22 23 24 25 26 27
 *  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
Linus Jahn's avatar
Linus Jahn committed
28
 *  along with Kaidan.  If not, see <http://www.gnu.org/licenses/>.
29 30 31 32 33
 */

#include "MessageHandler.h"
// Qt
#include <QDateTime>
34
#include <QMimeDatabase>
35
#include <QString>
36
#include <QUrl>
Linus Jahn's avatar
Linus Jahn committed
37 38 39 40
// QXmpp
#include <QXmppClient.h>
#include <QXmppUtils.h>
#include <QXmppRosterManager.h>
41 42 43 44
#include <QXmppDiscoveryManager.h>
#if QXMPP_VERSION >= 0x000904
#include <QXmppCarbonManager.h>
#endif
45
// Kaidan
Linus Jahn's avatar
Linus Jahn committed
46
#include "Kaidan.h"
47 48 49
#include "MessageModel.h"
#include "Notifications.h"

Linus Jahn's avatar
Linus Jahn committed
50 51 52
MessageHandler::MessageHandler(Kaidan *kaidan, QXmppClient *client, MessageModel *model,
                               QObject *parent)
	: QObject(parent), kaidan(kaidan), client(client), model(model)
53
{
Linus Jahn's avatar
Linus Jahn committed
54 55 56 57 58 59 60 61
	connect(client, &QXmppClient::messageReceived, this, &MessageHandler::handleMessage);
	connect(kaidan, &Kaidan::sendMessage, this, &MessageHandler::sendMessage);

	client->addExtension(&receiptManager);
	connect(&receiptManager, &QXmppMessageReceiptManager::messageDelivered,
		[=] (const QString&, const QString &id) {
		emit model->setMessageAsDeliveredRequested(id);
	});
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88

#if QXMPP_VERSION >= 0x000904 // QXmppCarbonManager was added in v0.9.4
	carbonManager = new QXmppCarbonManager();
	client->addExtension(carbonManager);

	// messages sent to our account (forwarded from another client)
	connect(carbonManager, &QXmppCarbonManager::messageReceived,
	        client, &QXmppClient::messageReceived);
	// messages sent from our account (but another client)
	connect(carbonManager, &QXmppCarbonManager::messageSent,
	        client, &QXmppClient::messageReceived);

	// carbons discovery
	QXmppDiscoveryManager *discoManager = client->findExtension<QXmppDiscoveryManager>();
	if (!discoManager)
		return;

	connect(discoManager, &QXmppDiscoveryManager::infoReceived,
	        this, &MessageHandler::handleDiscoInfo);
#endif
}

MessageHandler::~MessageHandler()
{
#if QXMPP_VERSION >= 0x000904
	delete carbonManager;
#endif
89 90
}

Linus Jahn's avatar
Linus Jahn committed
91
void MessageHandler::handleMessage(const QXmppMessage &msg)
92
{
Linus Jahn's avatar
Linus Jahn committed
93
	// TODO: Enable carbons (currently needs QXmpp master)
94 95
	bool isCarbonMessage = false;

Linus Jahn's avatar
Linus Jahn committed
96
	if (msg.body().isEmpty())
97
		return;
98

Linus Jahn's avatar
Linus Jahn committed
99 100 101 102 103 104
	MessageModel::Message entry;
	entry.author = QXmppUtils::jidToBareJid(msg.from());
	entry.recipient = QXmppUtils::jidToBareJid(msg.to());
	entry.id = msg.id();
	entry.sentByMe = (entry.author == client->configuration().jidBare());
	entry.message = msg.body();
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
	entry.type = MessageType::MessageText; // default to text message without media

	// check if message contains a link
	QList<QString> bodyWords = msg.body().split(" ");
	for (const QString &word : bodyWords) {
		bool isLink = word.startsWith("https://") || word.startsWith("http://");
		if (!isLink)
			continue;

		// check message type by file name in link
		// This is hacky, but needed without SIMS or an additional HTTP request.
		// Also, this can be useful when a user manually posts an HTTP url.
		QUrl url(word);
		QList<QMimeType> mediaTypes = QMimeDatabase().mimeTypesForFileName(url.fileName());
		for (const QMimeType &type : mediaTypes) {
			MessageType mType = MessageModel::messageTypeFromMimeType(type);
			if (mType == MessageType::MessageImage ||
			    mType == MessageType::MessageAudio ||
			    mType == MessageType::MessageVideo) {
				entry.type = mType;
				entry.mediaContentType = type.name();
				entry.mediaUrl = url.toEncoded();
				break;
			}
		}
		break; // we can only handle one link
	}
132

Linus Jahn's avatar
Linus Jahn committed
133 134 135 136 137
	// get possible delay (timestamp)
	QDateTime stamp = msg.stamp();
	if (!stamp.isValid() || stamp.isNull())
		stamp = QDateTime::currentDateTime();
	entry.timestamp = stamp.toUTC().toString(Qt::ISODate);
138

Linus Jahn's avatar
Linus Jahn committed
139 140
	// save the message to the database
	emit model->addMessageRequested(entry);
141

Linus Jahn's avatar
Linus Jahn committed
142 143 144 145 146 147 148 149 150 151 152 153 154
	// Send a message notification
	//
	// The contact can differ if the message is really from a contact or just
	// a forward of another of the user's clients.
	QString contactJid = entry.sentByMe ? entry.recipient : entry.author;
	// resolve user-defined name of this JID
	QString contactName = client->rosterManager().getRosterEntry(contactJid).name();
	if (contactName.isEmpty())
		contactName = contactJid;

	if (!entry.sentByMe && !isCarbonMessage)
		Notifications::sendMessageNotification(contactName.toStdString(),
		                                       msg.body().toStdString());
155 156
}

157
void MessageHandler::sendMessage(QString toJid, QString body)
158
{
Linus Jahn's avatar
Linus Jahn committed
159 160 161 162 163 164 165 166
	// TODO: Add offline message cache and send when connnected again
	if (client->state() != QXmppClient::ConnectedState) {
		emit kaidan->passiveNotificationRequested(
			tr("Could not send message, as a result of not being connected.")
		);
		qWarning() << "[client] [MessageHandler] Could not send message, as a result of "
		              "not being connected.";
		return;
Linus Jahn's avatar
Linus Jahn committed
167
	}
168

169
	MessageModel::Message msg;
Linus Jahn's avatar
Linus Jahn committed
170
	msg.author = client->configuration().jidBare();
171
	msg.recipient = toJid;
Linus Jahn's avatar
Linus Jahn committed
172
	msg.id = QXmppUtils::generateStanzaHash(48);
173
	msg.sentByMe = true;
Linus Jahn's avatar
Linus Jahn committed
174 175 176
	msg.message = body;
	msg.type = MessageType::MessageText; // text message without media
	msg.timestamp = QDateTime::currentDateTime().toUTC().toString(Qt::ISODate);
177

Linus Jahn's avatar
Linus Jahn committed
178
	emit model->addMessageRequested(msg);
179

Linus Jahn's avatar
Linus Jahn committed
180 181 182
	QXmppMessage m(msg.author, msg.recipient, body);
	m.setId(msg.id);
	m.setReceiptRequested(true);
183

Linus Jahn's avatar
Linus Jahn committed
184 185
	// TODO: check return code
	client->sendPacket(m);
186
}
Linus Jahn's avatar
Linus Jahn committed
187

188 189 190 191 192 193 194 195 196 197
void MessageHandler::handleDiscoInfo(const QXmppDiscoveryIq &info)
{
#if QXMPP_VERSION >= 0x000904
	if (info.from() != client->configuration().domain())
		return;
	// enable carbons, if feature found
	if (info.features().contains(QString("urn:xmpp:carbons:2")))
		carbonManager->setCarbonsEnabled(true);
#endif
}