MessageHandler.cpp 7.33 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*
 *  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 <iostream>
// Qt
#include <QDateTime>
#include <QDebug>
#include <QString>
// gloox
#include <gloox/receipt.h>
Linus Jahn's avatar
Linus Jahn committed
29
#include <gloox/carbons.h>
30 31 32 33
// Kaidan
#include "MessageModel.h"
#include "Notifications.h"

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
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;
}

58 59
MessageHandler::MessageHandler(gloox::Client *client, MessageModel *messageModel,
	RosterModel *rosterModel)
60
{
61 62
	this->messageModel = messageModel;
	this->rosterModel = rosterModel;
63
	this->client = client;
64
	this->chatPartner = QString("");
65 66 67 68 69 70
}

MessageHandler::~MessageHandler()
{
}

71
void MessageHandler::setCurrentChatPartner(QString *chatPartner)
72
{
73
	this->chatPartner = *chatPartner;
74 75 76 77

	resetUnreadMessagesForJid(chatPartner);
}

Linus Jahn's avatar
Linus Jahn committed
78
void MessageHandler::handleMessage(const gloox::Message &stanza, gloox::MessageSession *session)
79
{
Linus Jahn's avatar
Linus Jahn committed
80 81 82 83 84 85 86 87 88 89 90 91 92
	// 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());
93

94 95
	if (body.size() > 0) {
		//
Linus Jahn's avatar
Linus Jahn committed
96
		// Extract information of the message
97 98 99
		//

		// author is only the 'bare' JID: e.g. 'albert@einstein.ch'
Linus Jahn's avatar
Linus Jahn committed
100 101 102 103 104 105
		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());
106 107
		QString timestamp;

Linus Jahn's avatar
Linus Jahn committed
108
		//
109
		// If it is a delayed delivery (containing a timestamp), use its timestamp
Linus Jahn's avatar
Linus Jahn committed
110 111 112 113
		//

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

Linus Jahn's avatar
Linus Jahn committed
116 117 118
		// fallback: use current time from local clock
		if (timestamp.isEmpty())
			timestamp = QDateTime::currentDateTime().toString(Qt::ISODate);
119

120
		// add the message to the database
Linus Jahn's avatar
Linus Jahn committed
121
		messageModel->addMessage(&fromJid, &toJid, &timestamp, &body, &msgId,
122
					isSentByMe, &fromJidResource, &toJidResource);
123

Linus Jahn's avatar
Linus Jahn committed
124 125 126 127 128 129 130 131 132 133 134 135 136
		//
		// 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
137
		const QString contactJid = isSentByMe ? toJid : fromJid;
138 139

		// update the last message for this contact
140
		rosterModel->setLastMessageForJid(&contactJid, &body);
141 142

		// update the last exchanged for this contact
143
		updateLastExchangedOfJid(&contactJid);
144

Linus Jahn's avatar
Linus Jahn committed
145 146
		// Increase unread message counter
		// don't add new unread message if chat is opened or we wrote the message
147 148
		if (chatPartner != contactJid && !isSentByMe)
			newUnreadMessageForJid(&contactJid);
149 150
	}

151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
	// 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()));
172 173 174 175 176 177 178 179 180 181 182 183 184 185
		}
	}
}

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());

186
	messageModel->addMessage(fromJid, toJid, &timestamp, body, &id, true);
187 188 189 190 191 192 193 194

	// 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);
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220

	// update the last message for this contact
	rosterModel->setLastMessageForJid(toJid, body);
	// update the last exchanged date
	updateLastExchangedOfJid(toJid);
}

void MessageHandler::updateLastExchangedOfJid(const QString *jid)
{
	QString dateTime = QDateTime::currentDateTime().toString(Qt::ISODate);
	rosterModel->setLastExchangedOfJid(jid, &dateTime);
}

void MessageHandler::newUnreadMessageForJid(const QString *jid)
{
	// get the current unread message count
	int msgCount = rosterModel->getUnreadMessageCountOfJid(jid);
	// increase it by one
	msgCount++;
	// set the new increased count
	rosterModel->setUnreadMessageCountOfJid(jid, msgCount);
}

void MessageHandler::resetUnreadMessagesForJid(const QString *jid)
{
	rosterModel->setUnreadMessageCountOfJid(jid, 0);
221
}