Verified Commit 975f4c31 authored by Linus Jahn's avatar Linus Jahn Committed by Linus Jahn

UploadHandler: Save media information in database

With this local file paths, content types, sizes of file uploads are saved in
the database.

The database has some new columns for the media information and the version has
been increased to 6.

This also contains some clean ups and minor improvements of the code.
parent b16e9d5e
......@@ -121,7 +121,9 @@ void ClientThread::run()
vCardManager = new VCardManager(client, avatarStorage, rosterModel);
rosterManager = new RosterManager(kaidan, client, rosterModel, vCardManager);
presenceHandler = new PresenceHandler(client, presenceCache);
uploadHandler = new UploadHandler(client, messageSessionHandler->getMessageHandler());
uploadHandler = new UploadHandler(
client, messageSessionHandler->getMessageHandler(), messageModel
);
serviceDiscoveryManager = new ServiceDiscoveryManager(
client, client->disco(), uploadHandler->getUploadManager()
);
......
......@@ -41,7 +41,7 @@
#include <QSqlQuery>
#include <QSqlRecord>
static const int DATABASE_LATEST_VERSION = 5;
static const int DATABASE_LATEST_VERSION = 6;
static const char *DATABASE_TABLE_INFO = "dbinfo";
static const char *DATABASE_TABLE_MESSAGES = "Messages";
static const char *DATABASE_TABLE_ROSTER = "Roster";
......@@ -137,6 +137,8 @@ void Database::convertDatabase()
convertDatabaseToV4(); version = 4; break;
case 4:
convertDatabaseToV5(); version = 5; break;
case 5:
convertDatabaseToV6(); version = 6; break;
default:
break;
}
......@@ -167,16 +169,17 @@ void Database::createNewDatabase()
//
if (!query.exec("CREATE TABLE IF NOT EXISTS 'Roster' ("
"'jid' TEXT NOT NULL,"
"'name' TEXT NOT NULL,"
"'lastExchanged' TEXT NOT NULL,"
"'unreadMessages' INTEGER,"
"'lastMessage' TEXT,"
"'lastOnline' TEXT," // < UNUSED v
"'activity' TEXT,"
"'status' TEXT,"
"'mood' TEXT" // < UNUSED ^
")"))
"'jid' TEXT NOT NULL,"
"'name' TEXT NOT NULL,"
"'lastExchanged' TEXT NOT NULL,"
"'unreadMessages' INTEGER,"
"'lastMessage' TEXT,"
"'lastOnline' TEXT," // < UNUSED v
"'activity' TEXT,"
"'status' TEXT,"
"'mood' TEXT" // < UNUSED ^
")"
))
{
qFatal("Error creating roster table: Failed to query database: %s", qPrintable(query.lastError().text()));
}
......@@ -185,22 +188,26 @@ void Database::createNewDatabase()
// Messages
//
if (!query.exec(
"CREATE TABLE IF NOT EXISTS 'Messages' ("
"'author' TEXT NOT NULL,"
"'author_resource' TEXT,"
"'recipient' TEXT NOT NULL,"
"'recipient_resource' TEXT,"
"'timestamp' TEXT NOT NULL,"
"'message' TEXT NOT NULL,"
"'id' TEXT NOT NULL,"
"'isSent' BOOL," // is sent to server
"'isDelivered' BOOL," // message has arrived at other client
"'type' INTEGER," // type of message (text/image/video/...)
"'mediaUrl' TEXT,"
"FOREIGN KEY('author') REFERENCES Roster ('jid'),"
"FOREIGN KEY('recipient') REFERENCES Roster ('jid')"
")"))
if (!query.exec("CREATE TABLE IF NOT EXISTS 'Messages' ("
"'author' TEXT NOT NULL,"
"'author_resource' TEXT,"
"'recipient' TEXT NOT NULL,"
"'recipient_resource' TEXT,"
"'timestamp' TEXT NOT NULL,"
"'message' TEXT NOT NULL,"
"'id' TEXT NOT NULL,"
"'isSent' BOOL," // is sent to server
"'isDelivered' BOOL," // message has arrived at other client
"'type' INTEGER," // type of message (text/image/video/...)
"'mediaUrl' TEXT,"
"'mediaSize' INTEGER,"
"'mediaContentType' TEXT,"
"'mediaLastModified' INTEGER,"
"'mediaLocation' TEXT,"
"FOREIGN KEY('author') REFERENCES Roster ('jid'),"
"FOREIGN KEY('recipient') REFERENCES Roster ('jid')"
")"
))
{
qFatal("Error creating messages table: Failed to query database: %s", qPrintable(query.lastError().text()));
}
......@@ -273,6 +280,16 @@ void Database::convertDatabaseToV5()
execQuery(query);
}
void Database::convertDatabaseToV6()
{
QSqlQuery query(database);
for (QString column : {"'mediaSize' INTEGER", "'mediaContentType' TEXT",
"'mediaLastModified' INTEGER", "'mediaLocation' TEXT"}) {
query.prepare(QString("ALTER TABLE 'Messages' ADD ").append(column));
execQuery(query);
}
}
void Database::execQuery(QSqlQuery &query)
{
if (!query.exec()) {
......
......@@ -57,6 +57,7 @@ private:
void convertDatabaseToV3();
void convertDatabaseToV4();
void convertDatabaseToV5();
void convertDatabaseToV6();
void execQuery(QSqlQuery &query);
QSqlDatabase database;
......
......@@ -108,13 +108,13 @@ void MessageHandler::handleMessage(const gloox::Message &stanza, gloox::MessageS
//
// author is only the 'bare' JID: e.g. 'albert@einstein.ch'
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());
QString timestamp;
MessageModel::Message msg;
msg.author = QString::fromStdString(message->from().bare());
msg.authorResource = QString::fromStdString(message->from().resource());
msg.recipient = QString::fromStdString(message->to().bare());
msg.recipientResource = QString::fromStdString(message->to().resource());
msg.id = QString::fromStdString(message->id());
msg.sentByMe = (msg.author == QString::fromStdString(client->jid().bare()));
//
// If it is a delayed delivery (containing a timestamp), use its timestamp
......@@ -122,25 +122,22 @@ void MessageHandler::handleMessage(const gloox::Message &stanza, gloox::MessageS
const gloox::DelayedDelivery *delayedDelivery = message->when();
if (delayedDelivery)
timestamp = stringToQDateTime(delayedDelivery->stamp())
msg.timestamp = stringToQDateTime(delayedDelivery->stamp())
.toString(Qt::ISODate);
// fallback: use current time from local clock
if (timestamp.isEmpty())
timestamp = QDateTime::currentDateTime().toUTC()
if (msg.timestamp.isEmpty())
msg.timestamp = QDateTime::currentDateTime().toUTC()
.toString(Qt::ISODate);
// add the message to the database
emit messageModel->addMessageRequested(
fromJid, toJid, timestamp, body, msgId, isSentByMe,
MessageType::MessageText, fromJidResource, toJidResource
);
emit messageModel->addMessageRequested(msg);
//
// Send a new notification | TODO: Resolve nickname from JID
//
if (!isSentByMe && !isCarbonMessage)
if (!msg.sentByMe && !isCarbonMessage)
Notifications::sendMessageNotification(message->from().full(), body.toStdString());
//
......@@ -149,7 +146,7 @@ void MessageHandler::handleMessage(const gloox::Message &stanza, gloox::MessageS
// the contact can differ if the message is really from a contact or just
// a forward of another of the user's clients
const QString contactJid = isSentByMe ? toJid : fromJid;
const QString contactJid = msg.sentByMe ? msg.recipient : msg.author;
// update the last message for this contact
emit rosterModel->setLastMessageRequested(contactJid, body);
......@@ -159,7 +156,7 @@ void MessageHandler::handleMessage(const gloox::Message &stanza, gloox::MessageS
// Increase unread message counter
// don't add new unread message if chat is opened or we wrote the message
if (!isCarbonMessage && chatPartner != contactJid && !isSentByMe)
if (!isCarbonMessage && chatPartner != contactJid && !msg.sentByMe)
newUnreadMessageForJid(contactJid);
}
......@@ -226,15 +223,20 @@ void MessageHandler::sendOnlyMessage(QString &toJid, QString &body, const std::s
}
void MessageHandler::addMessageToDb(QString &toJid, QString &body, QString id,
MessageType type)
MessageType type, QString mediaLocation)
{
// add the message to the database
const QString timestamp = QDateTime::currentDateTime().toUTC().toString(Qt::ISODate);
const QString fromJid = QString::fromStdString(client->jid().bare());
emit messageModel->addMessageRequested(
fromJid, toJid, timestamp, body, id, true, type
);
MessageModel::Message msg;
msg.timestamp = QDateTime::currentDateTime().toUTC().toString(Qt::ISODate);
msg.author = QString::fromStdString(client->jid().bare());
msg.recipient = toJid;
msg.message = body;
msg.id = id;
msg.sentByMe = true;
msg.type = type;
msg.mediaLocation = mediaLocation;
emit messageModel->addMessageRequested(msg);
// update the last message for this contact
emit rosterModel->setLastMessageRequested(toJid, body);
......
......@@ -86,8 +86,10 @@ public:
* @param body Message body / text of the message
* @param id Custom id used for the message
* @param type Custom message type (useful for media sharing / file uploads)
* @param mediaLocation Location on local storage to media file
*/
void addMessageToDb(QString &toJid, QString &body, QString id, MessageType type);
void addMessageToDb(QString &toJid, QString &body, QString id,
MessageType type, QString mediaLocation = "");
public slots:
void setChatPartner(QString chatPartner);
......
......@@ -59,6 +59,7 @@ MessageModel::MessageModel(QSqlDatabase *database, QObject *parent):
connect(this, &MessageModel::addMessageRequested, this, &MessageModel::addMessage);
connect(this, &MessageModel::setMessageAsSentRequested, this, &MessageModel::setMessageAsSent);
connect(this, &MessageModel::setMessageAsDeliveredRequested, this, &MessageModel::setMessageAsDelivered);
connect(this, &MessageModel::updateMessageRequested, this, &MessageModel::updateMessage);
}
void MessageModel::applyRecipientFilter(QString recipient)
......@@ -103,28 +104,66 @@ void MessageModel::setMessageAsDelivered(const QString msgId)
submitAll();
}
void MessageModel::addMessage(const QString author, const QString recipient,
const QString timestamp, const QString message,
const QString msgId, bool sentByMe, MessageType type,
const QString mediaUrl, const QString author_resource,
const QString recipient_resource)
void MessageModel::updateMessage(const QString id, Message msg)
{
QStringList list;
if (!msg.timestamp.isEmpty())
list << QString("'timestamp' = '%1'").arg(msg.timestamp);
if (!msg.message.isEmpty())
list << QString("'message' = '%1'").arg(msg.message);
if (!msg.id.isEmpty())
list << QString("'id' = '%1'").arg(msg.id);
if (!msg.mediaUrl.isEmpty())
list << QString("'mediaUrl' = '%1'").arg(msg.mediaUrl);
if (msg.mediaSize)
list << QString("'mediaSize' = %1").arg(msg.mediaSize);
if (!msg.mediaContentType.isEmpty())
list << QString("'mediaContentType' = '%1'").arg(msg.mediaContentType);
if (msg.mediaLastModified)
list << QString("'mediaLastModified' = %1").arg(msg.mediaLastModified);
if (!msg.mediaLocation.isEmpty())
list << QString("'mediaLocation' = '%1'").arg(msg.mediaLocation);
QString sets;
bool isFirst = true;
for (auto part : list) {
if (!isFirst)
sets.append(", ");
sets.append(part);
isFirst = false;
}
QString command = "UPDATE 'Messages' SET %1 WHERE 'id' = '%2'";
command = command.arg(sets, id);
QSqlQuery query(*database);
query.exec(command);
}
void MessageModel::addMessage(Message msg)
{
//
// add the new message
//
QSqlRecord record = this->record();
record.setValue("author", author);
record.setValue("author_resource", author_resource);
record.setValue("recipient", recipient);
record.setValue("recipient_resource", recipient_resource);
record.setValue("timestamp", timestamp);
record.setValue("message", message);
record.setValue("id", msgId);
record.setValue("isSent", !sentByMe);
record.setValue("isDelivered", !sentByMe);
record.setValue("type", (quint8) type);
record.setValue("mediaUrl", mediaUrl);
record.setValue("author", msg.author);
record.setValue("author_resource", msg.authorResource);
record.setValue("recipient", msg.recipient);
record.setValue("recipient_resource", msg.recipientResource);
record.setValue("timestamp", msg.timestamp);
record.setValue("message", msg.message);
record.setValue("id", msg.id);
record.setValue("isSent", !msg.sentByMe);
record.setValue("isDelivered", !msg.sentByMe);
record.setValue("type", (quint8) msg.type);
record.setValue("mediaUrl", msg.mediaUrl);
if (msg.mediaSize)
record.setValue("mediaSize", msg.mediaSize);
record.setValue("mediaContentType", msg.mediaContentType);
if (msg.mediaLastModified)
record.setValue("mediaLastModified", msg.mediaLastModified);
record.setValue("mediaLocation", msg.mediaLocation);
if (!insertRecord(rowCount(), record)) {
qWarning() << "Failed to add message to DB:" << lastError().text();
......
......@@ -51,6 +51,23 @@ public:
*/
void applyRecipientFilter(QString recipient);
struct Message {
QString author;
QString authorResource;
QString recipient;
QString recipientResource;
QString timestamp;
QString message;
QString id;
bool sentByMe;
MessageType type;
QString mediaUrl;
quint64 mediaSize;
QString mediaContentType;
quint64 mediaLastModified;
QString mediaLocation;
};
signals:
/**
* Emitted when the user opens another chat to apply a filter to the db
......@@ -62,24 +79,16 @@ signals:
*/
void ownJidChanged(QString &jid);
void addMessageRequested(const QString author, const QString recipient,
const QString timestamp, const QString message,
const QString msgId, bool sentByMe, MessageType type,
const QString mediaUrl = QString(),
const QString author_resource = QString(),
const QString recipient_resource = QString());
void addMessageRequested(Message msg);
void setMessageAsSentRequested(const QString msgId);
void setMessageAsDeliveredRequested(const QString msgId);
void updateMessageRequested(const QString id, Message msg);
private slots:
void addMessage(const QString author, const QString recipient,
const QString timestamp, const QString message,
const QString msgId, bool sentByMe, MessageType type,
const QString mediaUrl = QString(),
const QString author_resource = QString(),
const QString recipient_resource = QString());
void addMessage(Message msg);
void setMessageAsSent(const QString msgId);
void setMessageAsDelivered(const QString msgId);
void updateMessage(const QString id, Message msg);
private:
QSqlDatabase *database;
......
......@@ -46,8 +46,8 @@
#include <QDebug>
UploadHandler::UploadHandler(gloox::Client *client, MessageHandler *msgHandler,
QObject *parent)
: QObject(parent), client(client), msgHandler(msgHandler)
MessageModel *msgModel, QObject *parent)
: QObject(parent), client(client), msgHandler(msgHandler), msgModel(msgModel)
{
manager = new gloox::HttpUploadManager(client);
uploader = new QtHttpUploader(manager);
......@@ -61,6 +61,7 @@ void UploadHandler::uploadFile(QString jid, QString filePath)
QMimeDatabase mimeDb;
QMimeType mimeType = mimeDb.mimeTypeForFile(filePath);
QString mimeTypeStr = mimeType.name();
qDebug() << filePath;
int id = manager->uploadFile(filePath.toStdString(), true,
mimeTypeStr.toStdString());
......@@ -79,7 +80,7 @@ void UploadHandler::uploadFile(QString jid, QString filePath)
QString body = "";
msgHandler->addMessageToDb(
jid, body, QString::fromStdString(meta.msgId),
getMessageType(mimeType)
getMessageType(mimeType), filePath
);
}
}
......@@ -97,6 +98,17 @@ void UploadHandler::handleUploadFinished(int id, std::string &name,
unsigned long &size)
{
qDebug() << "[client] A file upload has finished.";
// save new information to database
MessageModel::Message msg;
msg.mediaUrl = QString::fromStdString(getUrl);
msg.mediaContentType = QString::fromStdString(contentType);
msg.mediaSize = size;
// TODO: mediaLastModified
emit msgModel->updateMessageRequested(
QString::fromStdString(mediaShares[id].msgId), msg
);
// TODO: send SIMS message (and optionally also create jingle object)
// for now only send a normal text message with the get URL.
QString body = QString::fromStdString(getUrl);
......
......@@ -46,6 +46,7 @@ namespace gloox {
class QMimeType;
class MessageHandler;
class MessageModel;
class QtHttpUploader;
using namespace Enums;
......@@ -62,7 +63,7 @@ public:
* Default constructor
*/
UploadHandler(gloox::Client *client, MessageHandler *msgHandler,
QObject *parent = nullptr);
MessageModel *msgModel, QObject *parent = nullptr);
gloox::HttpUploadManager* getUploadManager()
{
......@@ -149,6 +150,7 @@ private:
gloox::Client *client;
gloox::HttpUploadManager *manager;
MessageHandler *msgHandler;
MessageModel *msgModel;
QtHttpUploader *uploader;
QMap<int, MediaSharingMeta> mediaShares;
......
......@@ -130,6 +130,7 @@ int main(int argc, char *argv[])
// register qMetaTypes
qRegisterMetaType<RosterModel*>("RosterModel*");
qRegisterMetaType<MessageModel*>("MessageModel*");
qRegisterMetaType<MessageModel::Message>("Message");
qRegisterMetaType<AvatarFileStorage*>("AvatarFileStorage*");
qRegisterMetaType<ContactMap>("ContactMap");
qRegisterMetaType<PresenceCache*>("PresenceCache*");
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment