diff --git a/src/Message.cpp b/src/Message.cpp
index db8473b..e987cbc 100644
--- a/src/Message.cpp
+++ b/src/Message.cpp
@@ -1,198 +1,257 @@
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2019 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* 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.
*
* 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.
*
* 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 .
*/
#include "Message.h"
+
+#include
#include
+static bool operator==(const QXmppStanza::Error &left, const QXmppStanza::Error &right) {
+ return left.code() == right.code()
+ && left.text() == right.text()
+ && left.condition() == right.condition()
+ && left.type() == right.type();
+}
+
+static bool operator==(const QXmppElement &left, const QXmppElement &right) {
+ return left.sourceDomElement() == right.sourceDomElement()
+ && left.attributeNames() == right.attributeNames()
+ && left.tagName() == right.tagName()
+ && left.value() == right.value();
+}
+
+static bool operator==(const QXmppExtendedAddress &left, const QXmppExtendedAddress &right) {
+ return left.description() == right.description()
+ && left.jid() == right.jid()
+ && left.type() == right.type()
+ && left.isDelivered() == right.isDelivered();
+}
+
+static bool operator==(const QXmppStanza &left, const QXmppStanza &right) {
+ return left.to() == right.to()
+ && left.from() == right.from()
+ && left.id() == right.id()
+ && left.lang() == right.lang()
+ && left.error() == right.error()
+ && left.extensions() == right.extensions()
+ && left.extendedAddresses() == right.extendedAddresses()
+ && left.isXmppStanza() == right.isXmppStanza();
+}
+
+static bool operator==(const QXmppMessage &left, const QXmppMessage &right) {
+ return operator==(static_cast(left), static_cast(right))
+ && left.body() == right.body()
+ && left.isAttentionRequested() == right.isAttentionRequested()
+ && left.isReceiptRequested() == right.isReceiptRequested()
+ && left.mucInvitationJid() == right.mucInvitationJid()
+ && left.mucInvitationPassword() == right.mucInvitationPassword()
+ && left.mucInvitationReason() == right.mucInvitationReason()
+ && left.receiptId() == right.receiptId()
+ && left.stamp() == right.stamp()
+ && left.state() == right.state()
+ && left.subject() == right.subject()
+ && left.thread() == right.thread()
+ && left.type() == right.type()
+ && left.xhtml() == right.xhtml()
+ && left.isMarkable() == right.isMarkable()
+ && left.markedId() == right.markedId()
+ && left.markedThread() == right.markedThread()
+ && left.marker() == right.marker()
+ && left.isPrivate() == right.isPrivate()
+ && left.isXmppStanza() == right.isXmppStanza()
+#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 0, 0)
+ && left.outOfBandUrl() == right.outOfBandUrl()
+#endif
+ && left.replaceId() == right.replaceId();
+}
+
MessageType Message::mediaTypeFromMimeType(const QMimeType &type)
{
if (type.inherits("image/jpeg") || type.inherits("image/png") ||
type.inherits("image/gif"))
return MessageType::MessageImage;
if (type.inherits("audio/flac") || type.inherits("audio/mp4") ||
type.inherits("audio/ogg") || type.inherits("audio/wav") ||
type.inherits("audio/mpeg") || type.inherits("audio/webm"))
return MessageType::MessageAudio;
if (type.inherits("video/mpeg") || type.inherits("video/x-msvideo") ||
type.inherits("video/quicktime") || type.inherits("video/mp4") ||
type.inherits("video/x-matroska"))
return MessageType::MessageVideo;
if (type.inherits("text/plain"))
return MessageType::MessageDocument;
return MessageType::MessageFile;
}
bool Message::operator==(const Message &m) const
{
- return m.id() == id() &&
- m.body() == body() &&
- m.from() == from() &&
- m.to() == to() &&
- m.type() == type() &&
- m.stamp() == stamp() &&
-#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 0, 0)
- m.outOfBandUrl() == outOfBandUrl() &&
+ return ::operator==(static_cast(m), static_cast(*this))
+ && m.mediaType() == mediaType()
+ && m.sentByMe() == sentByMe()
+ && m.isEdited() == isEdited()
+ && m.isSent() == isSent()
+ && m.isDelivered() == isDelivered()
+#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 0, 0)
+ && m.outOfBandUrl() == outOfBandUrl()
#endif
- m.isSent() == isSent() &&
- m.isDelivered() == isDelivered() &&
- m.mediaType() == mediaType() &&
- m.mediaContentType() == mediaContentType() &&
- m.mediaLocation() == mediaLocation() &&
- m.isEdited() == isEdited() &&
- m.spoilerHint() == spoilerHint() &&
- m.isSpoiler() == isSpoiler();
+ && m.mediaLocation() == mediaLocation()
+ && m.mediaContentType() == mediaContentType()
+ && m.mediaLastModified() == mediaLastModified()
+ && m.mediaSize() == mediaSize()
+ && m.isSpoiler() == isSpoiler()
+ && m.spoilerHint() == spoilerHint();
}
bool Message::operator!=(const Message &m) const
{
return !operator==(m);
}
MessageType Message::mediaType() const
{
return m_mediaType;
}
void Message::setMediaType(MessageType mediaType)
{
m_mediaType = mediaType;
}
bool Message::sentByMe() const
{
return m_sentByMe;
}
void Message::setSentByMe(bool sentByMe)
{
m_sentByMe = sentByMe;
}
bool Message::isEdited() const
{
return m_isEdited;
}
void Message::setIsEdited(bool isEdited)
{
m_isEdited = isEdited;
}
bool Message::isSent() const
{
return m_isSent;
}
void Message::setIsSent(bool isSent)
{
m_isSent = isSent;
}
bool Message::isDelivered() const
{
return m_isDelivered;
}
void Message::setIsDelivered(bool isDelivered)
{
m_isDelivered = isDelivered;
}
QString Message::mediaLocation() const
{
return m_mediaLocation;
}
void Message::setMediaLocation(const QString &mediaLocation)
{
m_mediaLocation = mediaLocation;
}
QString Message::mediaContentType() const
{
return m_mediaContentType;
}
void Message::setMediaContentType(const QString &mediaContentType)
{
m_mediaContentType = mediaContentType;
}
QDateTime Message::mediaLastModified() const
{
return m_mediaLastModified;
}
void Message::setMediaLastModified(const QDateTime &mediaLastModified)
{
m_mediaLastModified = mediaLastModified;
}
qint64 Message::mediaSize() const
{
return m_mediaSize;
}
void Message::setMediaSize(const qint64 &mediaSize)
{
m_mediaSize = mediaSize;
}
bool Message::isSpoiler() const
{
return m_isSpoiler;
}
void Message::setIsSpoiler(bool isSpoiler)
{
m_isSpoiler = isSpoiler;
}
QString Message::spoilerHint() const
{
return m_spoilerHint;
}
void Message::setSpoilerHint(const QString &spoilerHint)
{
m_spoilerHint = spoilerHint;
}
#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 0, 0)
QString Message::outOfBandUrl() const
{
return m_outOfBandUrl;
}
void Message::setOutOfBandUrl(const QString &outOfBandUrl)
{
m_outOfBandUrl = outOfBandUrl;
}
#endif
diff --git a/src/Message.h b/src/Message.h
index e2ddc68..46736ba 100644
--- a/src/Message.h
+++ b/src/Message.h
@@ -1,156 +1,156 @@
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2019 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* 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.
*
* 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.
*
* 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 .
*/
#ifndef MESSAGE_H
#define MESSAGE_H
#include
#include "Enums.h"
class QMimeType;
using namespace Enums;
/**
* @brief This class is used to load messages from the database and use them in
* the @c MessageModel. The class inherits from @c QXmppMessage and most
* properties are shared.
*/
class Message : public QXmppMessage
{
public:
static MessageType mediaTypeFromMimeType(const QMimeType&);
/**
* Compares another @c Message with this. Only attributes that are saved in the
* database are checked.
*/
bool operator==(const Message &m) const;
bool operator!=(const Message &m) const;
MessageType mediaType() const;
void setMediaType(MessageType mediaType);
bool sentByMe() const;
void setSentByMe(bool sentByMe);
bool isEdited() const;
void setIsEdited(bool isEdited);
bool isSent() const;
void setIsSent(bool isSent);
bool isDelivered() const;
void setIsDelivered(bool isDelivered);
QString mediaLocation() const;
void setMediaLocation(const QString &mediaLocation);
QString mediaContentType() const;
void setMediaContentType(const QString &mediaContentType);
QDateTime mediaLastModified() const;
void setMediaLastModified(const QDateTime &mediaLastModified);
qint64 mediaSize() const;
void setMediaSize(const qint64 &mediaSize);
bool isSpoiler() const;
void setIsSpoiler(bool isSpoiler);
QString spoilerHint() const;
void setSpoilerHint(const QString &spoilerHint);
#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 0, 0)
QString outOfBandUrl() const;
void setOutOfBandUrl(const QString &outOfBandUrl);
#endif
private:
/**
* Media type of the message, e.g. a text or image.
*/
MessageType m_mediaType = MessageType::MessageText;
/**
* True if the message was sent by the user.
*/
bool m_sentByMe = true;
/**
* True if the orginal message was edited.
*/
bool m_isEdited = false;
/**
* True if the message was sent.
*/
bool m_isSent = false;
/**
* True if a sent message was delivered to the contact.
*/
bool m_isDelivered = false;
/**
* Location of the media on the local storage.
*/
QString m_mediaLocation;
/**
* Media content type, e.g. "image/jpeg".
*/
QString m_mediaContentType;
/**
* Size of the file in bytes.
*/
- qint64 m_mediaSize;
+ qint64 m_mediaSize = 0;
/**
* Timestamp of the last modification date of the file locally on disk.
*/
QDateTime m_mediaLastModified;
/**
* True if the message is a spoiler message.
*/
bool m_isSpoiler = false;
/**
* Hint of the spoiler message.
*/
QString m_spoilerHint;
#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 0, 0)
QString m_outOfBandUrl;
#endif
};
#endif // MESSAGE_H
diff --git a/src/MessageDb.cpp b/src/MessageDb.cpp
index 8bbab9c..f5a46ee 100644
--- a/src/MessageDb.cpp
+++ b/src/MessageDb.cpp
@@ -1,289 +1,290 @@
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2019 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* 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.
*
* 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.
*
* 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 .
*/
#include "MessageDb.h"
// Kaidan
#include "Globals.h"
#include "Message.h"
#include "Utils.h"
// Qt
#include
#include
#include
#include
#include
MessageDb::MessageDb(QObject *parent)
: QObject(parent)
{
connect(this, &MessageDb::fetchMessagesRequested,
this, &MessageDb::fetchMessages);
}
void MessageDb::parseMessagesFromQuery(QSqlQuery &query, QVector &msgs)
{
// get indexes of attributes
QSqlRecord rec = query.record();
int idxFrom = rec.indexOf("author");
int idxTo = rec.indexOf("recipient");
int idxStamp = rec.indexOf("timestamp");
int idxId = rec.indexOf("id");
int idxBody = rec.indexOf("message");
int idxIsSent = rec.indexOf("isSent");
int idxIsDelivered = rec.indexOf("isDelivered");
int idxMediaType = rec.indexOf("type");
int idxOutOfBandUrl = rec.indexOf("mediaUrl");
int idxMediaContentType = rec.indexOf("mediaContentType");
int idxMediaLocation = rec.indexOf("mediaLocation");
int idxMediaSize = rec.indexOf("mediaSize");
int idxMediaLastModified = rec.indexOf("mediaLastModified");
int idxIsEdited = rec.indexOf("edited");
int idxSpoilerHint = rec.indexOf("spoilerHint");
int idxIsSpoiler = rec.indexOf("isSpoiler");
while (query.next()) {
Message msg;
msg.setFrom(query.value(idxFrom).toString());
msg.setTo(query.value(idxTo).toString());
msg.setStamp(QDateTime::fromString(
query.value(idxStamp).toString(),
Qt::ISODate
));
msg.setId(query.value(idxId).toString());
msg.setBody(query.value(idxBody).toString());
msg.setIsSent(query.value(idxIsSent).toBool());
msg.setIsDelivered(query.value(idxIsDelivered).toBool());
msg.setMediaType(static_cast(query.value(idxMediaType).toInt()));
msg.setOutOfBandUrl(query.value(idxOutOfBandUrl).toString());
msg.setMediaContentType(query.value(idxMediaContentType).toString());
msg.setMediaLocation(query.value(idxMediaLocation).toString());
msg.setMediaSize(query.value(idxMediaSize).toLongLong());
msg.setMediaLastModified(QDateTime::fromMSecsSinceEpoch(
query.value(idxMediaLastModified).toLongLong()
));
msg.setIsEdited(query.value(idxIsEdited).toBool());
msg.setSpoilerHint(query.value(idxSpoilerHint).toString());
msg.setIsSpoiler(query.value(idxIsSpoiler).toBool());
msgs << msg;
}
}
QSqlRecord MessageDb::createUpdateRecord(const Message &oldMsg, const Message &newMsg)
{
QSqlRecord rec;
if (oldMsg.from() != newMsg.from())
rec.append(Utils::createSqlField("author", newMsg.from()));
if (oldMsg.to() != newMsg.to())
rec.append(Utils::createSqlField("recipient", newMsg.to()));
if (oldMsg.stamp() != newMsg.stamp())
rec.append(Utils::createSqlField(
"timestamp",
newMsg.stamp().toString(Qt::ISODate)
));
if (oldMsg.id() != newMsg.id())
rec.append(Utils::createSqlField("id", newMsg.id()));
if (oldMsg.body() != newMsg.body())
rec.append(Utils::createSqlField("message", newMsg.body()));
if (oldMsg.isSent() != newMsg.isSent())
rec.append(Utils::createSqlField("isSent", newMsg.isSent()));
if (oldMsg.isDelivered() != newMsg.isDelivered())
rec.append(Utils::createSqlField("isDelivered", newMsg.isDelivered()));
if (oldMsg.mediaType() != newMsg.mediaType())
rec.append(Utils::createSqlField("type", int(newMsg.mediaType())));
if (oldMsg.outOfBandUrl() != newMsg.outOfBandUrl())
rec.append(Utils::createSqlField("mediaUrl", newMsg.outOfBandUrl()));
if (oldMsg.mediaContentType() != newMsg.mediaContentType())
rec.append(Utils::createSqlField(
"mediaContentType",
newMsg.mediaContentType()
));
if (oldMsg.mediaLocation() != newMsg.mediaLocation())
rec.append(Utils::createSqlField(
"mediaLocation",
newMsg.mediaLocation()
));
if (oldMsg.mediaSize() != newMsg.mediaSize())
rec.append(Utils::createSqlField("mediaSize", newMsg.mediaSize()));
if (oldMsg.mediaLastModified() != newMsg.mediaLastModified())
rec.append(Utils::createSqlField(
"mediaLastModified",
newMsg.mediaLastModified().toMSecsSinceEpoch()
));
if (oldMsg.isEdited() != newMsg.isEdited())
rec.append(Utils::createSqlField("edited", newMsg.isEdited()));
if (oldMsg.spoilerHint() != newMsg.spoilerHint())
rec.append(Utils::createSqlField("spoilerHint", newMsg.spoilerHint()));
if (oldMsg.isSpoiler() != newMsg.isSpoiler())
rec.append(Utils::createSqlField("isSpoiler", newMsg.isSpoiler()));
return rec;
}
void MessageDb::fetchMessages(const QString &user1, const QString &user2, int index)
{
QSqlQuery query(QSqlDatabase::database(DB_CONNECTION));
query.setForwardOnly(true);
QMap bindValues;
bindValues[":user1"] = user1;
bindValues[":user2"] = user2;
bindValues[":index"] = index;
bindValues[":limit"] = DB_MSG_QUERY_LIMIT;
Utils::execQuery(
query,
"SELECT * FROM Messages "
"WHERE (author = :user1 AND recipient = :user2) OR "
"(author = :user2 AND recipient = :user1) "
"ORDER BY timestamp DESC "
"LIMIT :index, :limit",
bindValues
);
QVector messages;
parseMessagesFromQuery(query, messages);
emit messagesFetched(messages);
}
void MessageDb::addMessage(const Message &msg)
{
QSqlDatabase db = QSqlDatabase::database(DB_CONNECTION);
QSqlRecord record = db.record(DB_TABLE_MESSAGES);
record.setValue("author", msg.from());
record.setValue("recipient", msg.to());
record.setValue("timestamp", msg.stamp().toString(Qt::ISODate));
record.setValue("message", msg.body());
record.setValue("id", msg.id().isEmpty() ? " " : msg.id());
record.setValue("isSent", msg.isSent());
record.setValue("isDelivered", msg.isDelivered());
record.setValue("type", int(msg.mediaType()));
record.setValue("edited", msg.isEdited());
record.setValue("isSpoiler", msg.isSpoiler());
record.setValue("spoilerHint", msg.spoilerHint());
+ record.setValue("mediaUrl", msg.outOfBandUrl());
record.setValue("mediaContentType", msg.mediaContentType());
record.setValue("mediaLocation", msg.mediaLocation());
record.setValue("mediaSize", msg.mediaSize());
record.setValue("mediaLastModified", msg.mediaLastModified().toMSecsSinceEpoch());
QSqlQuery query(db);
Utils::execQuery(query, db.driver()->sqlStatement(
QSqlDriver::InsertStatement,
DB_TABLE_MESSAGES,
record,
false
));
}
void MessageDb::removeMessage(const QString &id)
{
QSqlQuery query(QSqlDatabase::database(DB_CONNECTION));
Utils::execQuery(
query,
"DELETE FROM Messages WHERE id = ?",
QVector() << id
);
}
void MessageDb::updateMessage(const QString &id,
const std::function &updateMsg)
{
// load current message item from db
QSqlDatabase db = QSqlDatabase::database(DB_CONNECTION);
QSqlQuery query(db);
query.setForwardOnly(true);
Utils::execQuery(
query,
"SELECT * FROM Messages WHERE id = ? LIMIT 1",
QVector() << id
);
QVector msgs;
parseMessagesFromQuery(query, msgs);
// update loaded item
if (!msgs.isEmpty()) {
Message msg = msgs.first();
updateMsg(msg);
// replace old message with updated one, if message has changed
if (msgs.first() != msg) {
// create an SQL record with only the differences
QSqlRecord rec = createUpdateRecord(msgs.first(), msg);
Utils::execQuery(
query,
db.driver()->sqlStatement(
QSqlDriver::UpdateStatement,
DB_TABLE_MESSAGES,
rec,
false
) +
Utils::simpleWhereStatement(db.driver(), "id", id)
);
}
}
}
void MessageDb::updateMessageRecord(const QString &id,
const QSqlRecord &updateRecord)
{
QSqlDatabase db = QSqlDatabase::database(DB_CONNECTION);
QSqlQuery query(db);
Utils::execQuery(
query,
db.driver()->sqlStatement(
QSqlDriver::UpdateStatement,
DB_TABLE_MESSAGES,
updateRecord,
false
) +
Utils::simpleWhereStatement(db.driver(), "id", id)
);
}
void MessageDb::setMessageAsSent(const QString &msgId)
{
QSqlRecord rec;
rec.append(Utils::createSqlField("isSent", true));
updateMessageRecord(msgId, rec);
}
void MessageDb::setMessageAsDelivered(const QString &msgId)
{
QSqlRecord rec;
rec.append(Utils::createSqlField("isDelivered", true));
updateMessageRecord(msgId, rec);
}