MessageHandler.cpp 8.28 KB
Newer Older
1 2 3
/*
 *  Kaidan - A user-friendly XMPP client for every device!
 *
Linus Jahn's avatar
Linus Jahn committed
4 5
 *  Copyright (C) 2017-2018 Kaidan developers and contributors
 *  (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 34 35 36 37 38 39
 */

#include "MessageHandler.h"
// Std
#include <iostream>
// Qt
#include <QDateTime>
#include <QDebug>
#include <QString>
// gloox
#include <gloox/receipt.h>
Linus Jahn's avatar
Linus Jahn committed
40
#include <gloox/carbons.h>
41 42 43 44
// Kaidan
#include "MessageModel.h"
#include "Notifications.h"

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
QDateTime glooxStampToQDateTime(std::string stamp_)
{
	QString stamp = QString::fromStdString(stamp_);
	QDateTime dateTime;

	if (stamp.contains('Z')) {
		dateTime = QDateTime::fromString(stamp, "yyyy-MM-ddThh:mm:ss.zzzZ");
	} else if (stamp.contains('-') && stamp.contains(':') && stamp.contains('.')) {
		dateTime = QDateTime::fromString(stamp, "yyyy-MM-ddThh:mm:ss.zzz");
	} else if (stamp.contains('-') && stamp.contains(':')) {
		dateTime = QDateTime::fromString(stamp, "yyyy-MM-ddThh:mm:ss");
	} else if (stamp.contains(':') && stamp.contains('T')) {
		dateTime = QDateTime::fromString(stamp, "yyyyMMddThh:mm:ss");
	} else {
		dateTime = QDateTime::fromString(stamp, "hh:mm");
	}

	if (!dateTime.isValid())
		return QDateTime();

	dateTime.setTimeSpec(Qt::UTC);
	return dateTime;
}

69
MessageHandler::MessageHandler(gloox::Client *client, MessageModel *messageModel,
70 71 72 73
                               RosterModel *rosterModel, QObject *parent):
                               QObject(parent), client(client),
                               messageModel(messageModel), rosterModel(rosterModel),
                               chatPartner("")
74 75 76 77 78 79 80
{
}

MessageHandler::~MessageHandler()
{
}

81
void MessageHandler::setCurrentChatPartner(QString *chatPartner)
82
{
83
	this->chatPartner = *chatPartner;
84

85
	resetUnreadMessagesForJid(this->chatPartner);
86 87
}

Linus Jahn's avatar
Linus Jahn committed
88
void MessageHandler::handleMessage(const gloox::Message &stanza, gloox::MessageSession *session)
89
{
Linus Jahn's avatar
Linus Jahn committed
90 91 92 93 94 95 96 97 98 99 100 101 102
	// this should contain the real message (e.g. containing the body)
	gloox::Message *message = const_cast<gloox::Message*>(&stanza);
	// if the real message is in a message carbon extract it from there
	if (stanza.hasEmbeddedStanza()) {
		// get the possible carbon extension
		const gloox::Carbons *carbon = stanza.findExtension<const gloox::Carbons>(gloox::ExtCarbons);

		// if the extension exists and contains a message, use it as the real message
		if(carbon && carbon->embeddedStanza())
			message = static_cast<gloox::Message*>(carbon->embeddedStanza());
	}

	QString body = QString::fromStdString(message->body());
103

104 105
	if (body.size() > 0) {
		//
Linus Jahn's avatar
Linus Jahn committed
106
		// Extract information of the message
107 108 109
		//

		// author is only the 'bare' JID: e.g. 'albert@einstein.ch'
Linus Jahn's avatar
Linus Jahn committed
110 111 112 113 114 115
		const QString fromJid = QString::fromStdString(message->from().bare());
		const QString fromJidResource = QString::fromStdString(message->from().resource());
		const QString toJid = QString::fromStdString(message->to().bare());
		const QString toJidResource = QString::fromStdString(message->to().resource());
		const QString msgId = QString::fromStdString(message->id());
		const bool isSentByMe = fromJid == QString::fromStdString(client->jid().bare());
116 117
		QString timestamp;

Linus Jahn's avatar
Linus Jahn committed
118
		//
119
		// If it is a delayed delivery (containing a timestamp), use its timestamp
Linus Jahn's avatar
Linus Jahn committed
120 121 122 123
		//

		const gloox::DelayedDelivery *delayedDelivery = message->when();
		if (delayedDelivery)
124 125
			timestamp = glooxStampToQDateTime(delayedDelivery->stamp())
			            .toString(Qt::ISODate);
126

Linus Jahn's avatar
Linus Jahn committed
127 128 129
		// fallback: use current time from local clock
		if (timestamp.isEmpty())
			timestamp = QDateTime::currentDateTime().toString(Qt::ISODate);
130

131
		// add the message to the database
132 133 134
		emit messageModel->addMessageRequested(fromJid, toJid, timestamp,
		                                       body, msgId, isSentByMe,
		                                       fromJidResource, toJidResource);
135

Linus Jahn's avatar
Linus Jahn committed
136 137 138 139 140 141 142 143 144 145 146 147 148
		//
		// Send a new notification | TODO: Resolve nickname from JID
		//

		if (!isSentByMe)
			Notifications::sendMessageNotification(message->from().full(), body.toStdString());

		//
		// Update contact sort (lastExchanged), last message and unread message count
		//

		// the contact can differ if the message is really from a contact or just
		// a forward of another of the user's clients
149
		const QString contactJid = isSentByMe ? toJid : fromJid;
150 151

		// update the last message for this contact
152
		emit rosterModel->setLastMessageRequested(contactJid, body);
153 154

		// update the last exchanged for this contact
155
		updateLastExchangedOfJid(contactJid);
156

Linus Jahn's avatar
Linus Jahn committed
157 158
		// Increase unread message counter
		// don't add new unread message if chat is opened or we wrote the message
159
		if (chatPartner != contactJid && !isSentByMe)
160
			newUnreadMessageForJid(contactJid);
161 162
	}

163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
	// 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
183 184
			emit messageModel->setMessageAsDeliveredRequested(
				QString::fromStdString(receipt->id()));
185 186 187 188
		}
	}
}

189
void MessageHandler::sendMessage(QString toJid, QString body)
190 191
{
	// create a new message
192 193
	gloox::Message message(gloox::Message::Chat, gloox::JID(toJid.toStdString()),
	                       body.toStdString());
194 195 196 197

	// add the message to the database
	const QString timestamp = QDateTime::currentDateTime().toString(Qt::ISODate);
	const QString id = QString::fromStdString(message.id());
198
	const QString fromJid = QString::fromStdString(client->jid().bare());
199

200
	emit messageModel->addMessageRequested(fromJid, toJid, timestamp, body, id, true); 
201 202 203 204 205 206 207 208

	// 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);
209 210

	// update the last message for this contact
211
	emit rosterModel->setLastMessageRequested(toJid, body);
212 213 214 215
	// update the last exchanged date
	updateLastExchangedOfJid(toJid);
}

216
void MessageHandler::updateLastExchangedOfJid(const QString &jid)
217 218
{
	QString dateTime = QDateTime::currentDateTime().toString(Qt::ISODate);
219
	emit rosterModel->setLastExchangedRequested(jid, dateTime);
220 221
}

222
void MessageHandler::newUnreadMessageForJid(const QString &jid)
223
{
224 225
	// add a new unread message to the contact
	emit rosterModel->newUnreadMessageRequested(jid);
226 227
}

228
void MessageHandler::resetUnreadMessagesForJid(const QString &jid)
229
{
230 231
	// reset the unread message count to 0
	emit rosterModel->setUnreadMessageCountRequested(jid, 0);
232
}