UploadHandler.cpp 7.5 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 29 30 31 32
/*
 *  Kaidan - A user-friendly XMPP client for every device!
 *
 *  Copyright (C) 2018 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 <http://www.gnu.org/licenses/>.
 */

#include "UploadHandler.h"
#include "QtHttpUploader.h"
33
#include "MessageHandler.h"
34
// gloox
35
#include <gloox/message.h>
36
#include <gloox/base64.h>
37 38 39 40 41 42
#include "gloox-extensions/httpuploadmanager.h"
#include "gloox-extensions/reference.h"
#include "gloox-extensions/sims.h"
#include "gloox-extensions/processinghints.h"
#include "gloox-extensions/jinglefile.h"
#include "gloox-extensions/hash.h"
43 44
#include "gloox-extensions/thumb.h"
#include "gloox-extensions/bitsofbinarydata.h"
45 46
// Qt
#include <QMimeDatabase>
47 48
#include <QMimeType>
#include <QDateTime>
49 50
#include <QBuffer>
#include <QImage>
51 52
#include <QDebug>

53
UploadHandler::UploadHandler(gloox::Client *client, MessageHandler *msgHandler,
54 55
                             MessageModel *msgModel, QObject *parent)
	: QObject(parent), client(client), msgHandler(msgHandler), msgModel(msgModel)
56 57 58 59 60 61 62
{
	manager = new gloox::HttpUploadManager(client);
	uploader = new QtHttpUploader(manager);

	manager->registerHttpUploadHandler(this);
}

Linus Jahn's avatar
Linus Jahn committed
63
void UploadHandler::uploadFile(QString jid, QString filePath, QString message)
64 65 66
{
	// get MIME-type
	QMimeDatabase mimeDb;
67 68
	QMimeType mimeType = mimeDb.mimeTypeForFile(filePath);
	QString mimeTypeStr = mimeType.name();
69 70

	int id = manager->uploadFile(filePath.toStdString(), true,
71
	                             mimeTypeStr.toStdString());
72 73 74 75 76 77 78 79

	if (id < 0) {
		// failure
		// TODO: send passive notification
	} else {
		// save for later processing/sending
		MediaSharingMeta meta;
		meta.jid = jid;
80
		meta.msgId = client->getID();
Linus Jahn's avatar
Linus Jahn committed
81
		meta.message = message;
82 83
		meta.filePath = filePath;
		meta.type = msgHandler->getMessageType(mimeType);
84 85

		mediaShares[id] = meta;
86 87

		msgHandler->addMessageToDb(
Linus Jahn's avatar
Linus Jahn committed
88 89
			jid, message, QString::fromStdString(meta.msgId),
			MessageType::MessageFile, filePath
90
		);
91
	}
92 93 94 95 96 97 98
}

void UploadHandler::handleUploadFailed(int id, gloox::HttpUploadError error,
                                       const std::string &text,
                                       const std::string &stamp)
{
	qDebug() << "[client] A file upload has failed.";
99 100
	// the media meta isn't needed anymore, so delete it
	mediaShares.remove(id);
101 102 103 104 105 106 107 108
}

void UploadHandler::handleUploadFinished(int id, std::string &name,
                                         std::string &getUrl,
                                         std::string &contentType,
                                         unsigned long &size)
{
	qDebug() << "[client] A file upload has finished.";
109
	// prepare new database message
110 111 112 113 114
	MessageModel::Message msg;
	msg.mediaUrl = QString::fromStdString(getUrl);
	msg.mediaContentType = QString::fromStdString(contentType);
	msg.mediaSize = size;

Linus Jahn's avatar
Linus Jahn committed
115
	// TODO: lastModified / date
116 117

	// Hashes
118 119 120 121
	QtHttpUploader::HashResult hashResults = uploader->getHashResults(id);
	std::list<gloox::Hash> hashes;
	hashes.emplace_back(gloox::Hash("sha-256", hashResults.sha256.toBase64().toStdString()));
	hashes.emplace_back(gloox::Hash("sha3-256", hashResults.sha3_256.toBase64().toStdString()));
122 123 124
	// save hashes in database field
	for (gloox::Hash &hash : hashes)
		msg.mediaHashes.append(QString::fromStdString(hash.tag()->xml()));
125

126
	// last modified date
Linus Jahn's avatar
Linus Jahn committed
127
	std::string date = "";
128

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
	// thumbnail
	QSize thumbSize = generateMediaThumb(mediaShares[id].filePath,
	                                     mediaShares[id].type, &msg.mediaThumb);
	gloox::Jingle::Thumb *thumb = nullptr;
	gloox::BitsOfBinaryData *thumbBob = nullptr;
	if (!msg.mediaThumb.isEmpty()) {
		// encode to base64
		std::string thumbData = gloox::Base64::encode64(
			std::string(msg.mediaThumb.constData(), msg.mediaThumb.length())
		);
		// create BoB content identifier
		std::string thumbCid = gloox::BitsOfBinaryData::generateContentId(thumbData);
		// create BoB data object
		thumbBob = new gloox::BitsOfBinaryData(thumbData, thumbCid, "image/jpeg");
		// create thumb object (links to BoB data)
		thumb = new gloox::Jingle::Thumb(
			"cid:" + thumbCid, thumbSize.width(), thumbSize.height(), "image/jpeg"
		);
	}

149
	// file meta information
Linus Jahn's avatar
Linus Jahn committed
150
	gloox::Jingle::File *fileInfo = new gloox::Jingle::File(
151
		name, size, hashes, contentType, date,
152
		mediaShares[id].message.toStdString(), thumb
Linus Jahn's avatar
Linus Jahn committed
153 154 155 156 157 158
	);

	// list of sources for the file (TODO: jingle as fallback)
	gloox::StringList sources;
	sources.emplace_back(getUrl);

159
	// create message
Linus Jahn's avatar
Linus Jahn committed
160 161 162 163 164 165 166 167 168 169 170 171 172
	gloox::SIMS *sims = new gloox::SIMS(fileInfo, sources);
	gloox::Reference *simsRef = new gloox::Reference(gloox::Reference::Data);
	simsRef->embedSIMS(sims);

	gloox::JID to(mediaShares[id].jid.toStdString());
	// message body for clients without SIMS support
	std::string msgBody = getUrl;
	if (!mediaShares[id].message.isEmpty())
		msgBody = msgBody.append("\n").append(mediaShares[id].message.toStdString());

	gloox::Message message(gloox::Message::Chat, to.bareJID(), msgBody);
	message.setID(mediaShares[id].msgId);
	message.addExtension((gloox::StanzaExtension*) simsRef);
173 174
	if (thumbBob)
		message.addExtension(thumbBob); // thumbnail BoB data
Linus Jahn's avatar
Linus Jahn committed
175 176

	client->send(message);
177

178 179 180 181 182
	// save to database
	emit msgModel->updateMessageRequested(
		QString::fromStdString(mediaShares[id].msgId), msg
	);

183 184
	// the media meta isn't needed anymore, so delete it
	mediaShares.remove(id);
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
}

void UploadHandler::handleUploadProcess(int id, long unsigned int sent,
                                        long unsigned int total)
{
}

void UploadHandler::handleUploadServiceRemoved(const gloox::JID &jid)
{
}

void UploadHandler::handleUploadServiceAdded(const gloox::JID &jid,
                                             unsigned long maxFileSize)
{
}

void UploadHandler::handleFileSizeLimitChanged(unsigned long maxFileSize)
{
}
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225

QSize UploadHandler::generateMediaThumb(QString &filePath, MessageType type,
                                       QByteArray *bytes)
{
	if (type != MessageType::MessageImage)
		return QSize(0, 0);

	// results should be about 1-3 kB large
	int finalSize = 40;

	QImage img(filePath);
	// Qt::FastTransformation is used because quality doesn't matter at this
	// size; you won't be able to really see a large difference
	img = img.scaled(finalSize, finalSize, Qt::KeepAspectRatio, Qt::FastTransformation);

	// save image to byte array
	QBuffer buffer(bytes);
	buffer.open(QIODevice::WriteOnly);
	img.save(&buffer, "JPEG", 90);

	return img.size();
}