files.cpp 25.8 KB
Newer Older
1
/*
2
 * Copyright (C) 2014-2018 CZ.NIC
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 *
 * This program 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.
 *
 * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of portions of this program with the
 * OpenSSL library under certain conditions as described in each
 * individual source file, and distribute linked combinations including
 * the two.
 */

#include <QDebug>
#include <QDesktopServices>
26
#include <QFileInfo>
Martin Straka's avatar
Martin Straka committed
27
#include <QQmlEngine>
28

29
#include "ios/src/url_opener.h"
30
#include "src/auxiliaries/attachment_helper.h"
Martin Straka's avatar
Martin Straka committed
31
#include "src/auxiliaries/email_helper.h"
32
#include "src/common.h"
33
#include "src/crypto/crypto.h"
34
#include "src/dialogues/dialogues.h"
35
#include "src/files.h"
36
#include "src/global.h"
37 38
#include "src/io/filesystem.h"
#include "src/models/accountmodel.h"
39
#include "src/net/xml_layer.h"
40
#include "src/qml_interaction/attachment_data.h"
41
#include "src/qml_interaction/message_envelope.h"
42
#include "src/settings.h"
43 44
#include "src/sqlite/file_db_container.h"
#include "src/sqlite/message_db_container.h"
45 46
#include "src/sqlite/dbs.h"

Martin Straka's avatar
Martin Straka committed
47 48 49 50 51 52
void Files::declareQML(void)
{
	qmlRegisterType<Files>("cz.nic.mobileDatovka.files", 1, 0, "FileIdType");
	qRegisterMetaType<Files::FileIdType>();
}

53 54 55 56
Files::Files(QObject *parent) : QObject(parent)
{
}

Martin Straka's avatar
Martin Straka committed
57
void Files::attachmentSavingNotification(const QString &destPath)
58
{
Martin Straka's avatar
Martin Straka committed
59 60 61 62 63 64 65 66
	QFileInfo fi(destPath);

	Dialogues::errorMessage(
	    !destPath.isEmpty() ? Dialogues::INFORMATION : Dialogues::CRITICAL,
	    QObject::tr("Attachment saving"),
	    !destPath.isEmpty() ? QObject::tr("Attachments have been saved.") :
	        QObject::tr("Attachments have not been saved!"),
	    QObject::tr("Path: '%1'").arg(fi.absolutePath()));
67 68 69 70 71 72
}

void Files::deleteExpiredFilesFromDbs(int days)
{
	qDebug("%s()", __func__);

73 74
	if (Q_UNLIKELY((GlobInstcs::setPtr == Q_NULLPTR) ||
	        (GlobInstcs::messageDbsPtr == Q_NULLPTR) ||
75 76
	        (GlobInstcs::fileDbsPtr == Q_NULLPTR) ||
	        (GlobInstcs::acntMapPtr == Q_NULLPTR))) {
77 78 79 80
		Q_ASSERT(0);
		return;
	}

81
	QStringList msgIDList;
82
	QStringList userNameList(GlobInstcs::acntMapPtr->keys());
83
	foreach (const QString &userName, userNameList) {
84 85
		FileDb *fDb = GlobInstcs::fileDbsPtr->accessFileDb(
		    GlobInstcs::setPtr->dbsLocation, userName,
86
		    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
Martin Straka's avatar
Martin Straka committed
87 88 89
		if (fDb == Q_NULLPTR) {
			qCritical() << "ERROR: File database cannot open!"
			    << userName;
90 91 92 93 94 95 96
			return;
		}
		msgIDList = fDb->cleanFilesInDb(days);
		if (msgIDList.isEmpty()) {
			continue;
		}

97 98
		MessageDb *msgDb = GlobInstcs::messageDbsPtr->accessMessageDb(
		    GlobInstcs::setPtr->dbsLocation, userName,
99
		    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
Martin Straka's avatar
Martin Straka committed
100
		if (msgDb == Q_NULLPTR) {
101
			qCritical() << "ERROR: Message database cannot open!";
102 103
			return;
		}
104
		msgDb->beginTransaction();
105 106 107 108
		foreach (const QString &msgId, msgIDList) {
			msgDb->setAttachmentDownloaded(msgId.toLongLong(),
			    false);
		}
109
		msgDb->commitTransaction();
110 111 112
	}
}

113 114 115 116 117 118 119 120 121 122 123
QString Files::getAttachmentFileIcon(const QString &fileName)
{
	return getAttachmentFileIconFromFileExtension(fileName);
}

qint64 Files::getAttachmentSizeInBytes(const QString &filePath)
{
	QFileInfo fileInfo(filePath);
	return fileInfo.size();
}

124
QByteArray Files::getFileRawContentFromDb(const QString &userName, int fileId)
125 126 127
{
	qDebug("%s()", __func__);

128
	if (Q_UNLIKELY((GlobInstcs::setPtr == Q_NULLPTR) ||
129 130
	        (GlobInstcs::fileDbsPtr == Q_NULLPTR) ||
	        (GlobInstcs::acntMapPtr == Q_NULLPTR))) {
131 132 133 134
		Q_ASSERT(0);
		return QByteArray();
	}

135 136
	FileDb *fDb = GlobInstcs::fileDbsPtr->accessFileDb(
	    GlobInstcs::setPtr->dbsLocation, userName,
137
	    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
138

139 140 141 142 143 144 145 146
	if (fDb == Q_NULLPTR) {
		qCritical() << "Cannot open file database!";
		return QByteArray();
	}

	FileDb::FileData file = fDb->getFileContentFromDb(fileId);

	return base64ToRaw(file.content.toUtf8());
147 148
}

149
void Files::openAttachmentFromDb(const QString &userName, int fileId)
150 151 152
{
	qDebug("%s()", __func__);

153
	if (Q_UNLIKELY((GlobInstcs::setPtr == Q_NULLPTR) ||
154 155
	        (GlobInstcs::fileDbsPtr == Q_NULLPTR) ||
	        (GlobInstcs::acntMapPtr == Q_NULLPTR))) {
156 157 158 159
		Q_ASSERT(0);
		return;
	}

160 161
	FileDb *fDb = GlobInstcs::fileDbsPtr->accessFileDb(
	    GlobInstcs::setPtr->dbsLocation, userName,
162
	    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
163

164 165
	if (fDb == Q_NULLPTR) {
		qCritical() << "Cannot open file database!";
166 167 168
		return;
	}

169 170
	FileDb::FileData file = fDb->getFileContentFromDb(fileId);

171
	openAttachment(file.filename, file.content.toUtf8());
172 173
}

174
void Files::openAttachment(const QString &fileName, const QByteArray &base64Data)
175 176
{
	Q_ASSERT(!fileName.isEmpty());
177
	Q_ASSERT(!base64Data.isEmpty());
178

179 180
	if (fileName.isEmpty() || base64Data.isEmpty()) {
		qCritical() << "File name or its content is empty!";
181 182 183
		return;
	}

184 185 186 187
	if (isZfoFile(fileName)) {
		/* Don't open zfo files from here. */
		Q_ASSERT(0);
		qCritical() << "This should open ZFO files by itself.";
188 189 190
		return;
	}

191
	QString filePath(writeFileToDir(TEMP_DIR_NAME, -1, fileName,
192
	    base64ToRaw(base64Data)));
193 194

	if (!filePath.isEmpty()) {
195
		qInfo() << "Creating temporary file" << filePath;
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
		openAttachmentFromPath(filePath);
	} else {
		qCritical() << "Cannot create temporary file for" << fileName;
		Dialogues::errorMessage(Dialogues::CRITICAL,
		    tr("Open attachment error"),
		    tr("Cannot save selected file to disk for opening."),
		    QString());
	}
}

void Files::openAttachmentFromPath(const QString &filePath)
{
	Q_ASSERT(!filePath.isEmpty());
	if (filePath.isEmpty()) {
		qCritical() << "File path is empty!";
		return;
	}

	if (isZfoFile(filePath)) {
		/* Don't open zfo files from here. */
		Q_ASSERT(0);
		qCritical() << "This should open ZFO files by itself.";
		return;
	}
220 221 222

#if defined Q_OS_IOS
		UrlOpener urlOpener;
223
		urlOpener.openFile(filePath);
224 225 226 227
		/*
		 * TODO - we may check exceptions form objective-C (openFile)
		 * and return as bool.
		 */
228
#else /* !defined Q_OS_IOS */
229
		if (!QDesktopServices::openUrl(QUrl::fromLocalFile(filePath))) {
230
			Dialogues::errorMessage(Dialogues::CRITICAL,
231 232
			    tr("Open attachment error"),
			    tr("There is no application to open this file format."),
233
			    tr("File: '%1'").arg(filePath));
234
		}
235 236 237
#endif /* defined Q_OS_IOS */
}

Martin Straka's avatar
Martin Straka committed
238
void Files::sendAttachmentsWithEmail(const QString &userName, qint64 msgId)
239 240 241
{
	qDebug("%s()", __func__);

242 243
	if (Q_UNLIKELY((GlobInstcs::setPtr == Q_NULLPTR) ||
	        (GlobInstcs::messageDbsPtr == Q_NULLPTR) ||
244 245
	        (GlobInstcs::fileDbsPtr == Q_NULLPTR) ||
	        (GlobInstcs::acntMapPtr == Q_NULLPTR))) {
246 247 248 249
		Q_ASSERT(0);
		return;
	}

250 251 252 253 254 255 256 257
	if (userName.isEmpty() || msgId <= 0) {
		return;
	}

	QString body;
	QString subject;
	QStringList fileList;

258 259
	MessageDb *msgDb = GlobInstcs::messageDbsPtr->accessMessageDb(
	    GlobInstcs::setPtr->dbsLocation, userName,
260
	    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
Martin Straka's avatar
Martin Straka committed
261
	if (msgDb == Q_NULLPTR) {
262
		qCritical() << "ERROR: Message database cannot open!";
263 264 265
		return;
	}
	if (!msgDb->getMessageDataForEmail(msgId, body, subject)) {
266
		qCritical() << "ERROR: Message data mssing!";
267 268 269
		return;
	}

270 271
	FileDb *fDb = GlobInstcs::fileDbsPtr->accessFileDb(
	    GlobInstcs::setPtr->dbsLocation, userName,
272
	    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
Martin Straka's avatar
Martin Straka committed
273
	if (fDb == Q_NULLPTR) {
274
		qCritical() << "ERROR: File database cannot open!";
275 276 277 278 279 280
		return;
	}

	QList<FileDb::FileData> filelist;
	filelist = fDb->getFilesFromDb(msgId);
	if (filelist.isEmpty()) {
281
		qCritical() << "ERROR: File list is empty!";
282 283 284
		return;
	}

Martin Straka's avatar
Martin Straka committed
285 286
	const QString boundary = generateBoundaryString();
	QString emailMessage = createEmailMessage(body, subject,  boundary);
287 288 289 290

	deleteFilesFromDir(EMAIL_DIR_NAME);

	foreach (const FileDb::FileData &file, filelist) {
Martin Straka's avatar
Martin Straka committed
291
		QString fileName = file.filename;
292
		if (fileName.isEmpty()) {
293
			qCritical() << "ERROR: File name is empty!";
294 295
			return;
		}
296
		fileName = writeFileToDir(EMAIL_DIR_NAME, msgId, fileName,
297
		    base64ToRaw(file.content.toUtf8()));
298 299 300 301 302 303 304
		fileList.append(fileName);
		addAttachmentToEmailMessage(emailMessage, file.filename,
		    file.content.toUtf8(), boundary);
	}

	finishEmailMessage(emailMessage, boundary);

305 306
	sendEmail(emailMessage, fileList, subject, body, msgId);
}
307 308 309 310 311

void Files::deleteFileDb(const QString &userName)
{
	qDebug("%s()", __func__);

312 313
	if (Q_UNLIKELY((GlobInstcs::setPtr == Q_NULLPTR) ||
	        (GlobInstcs::messageDbsPtr == Q_NULLPTR) ||
314 315
	        (GlobInstcs::fileDbsPtr == Q_NULLPTR) ||
	        (GlobInstcs::acntMapPtr == Q_NULLPTR))) {
316 317 318 319
		Q_ASSERT(0);
		return;
	}

320
	int msgResponse = Dialogues::message(Dialogues::QUESTION,
321 322 323
	    tr("Delete files: %1").arg(userName),
	    tr("Do you want to clean up the file database of account '%1'?").arg(userName),
	    tr("Note: All attachment files of messages will be removed from the database."),
324 325
	    Dialogues::NO | Dialogues::YES, Dialogues::NO);
	if (msgResponse == Dialogues::NO) {
326 327 328
		return;
	}

329 330
	FileDb *fDb = GlobInstcs::fileDbsPtr->accessFileDb(
	    GlobInstcs::setPtr->dbsLocation, userName,
331
	    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
Martin Straka's avatar
Martin Straka committed
332
	if (fDb == Q_NULLPTR) {
333
		qCritical() << "ERROR: File database open error!";
334 335
		return;
	}
336
	if (!GlobInstcs::fileDbsPtr->deleteDb(fDb)) {
337
		qCritical() << "ERROR: File database could not delete!";
338 339 340
		return;
	}

341 342
	MessageDb *msgDb = GlobInstcs::messageDbsPtr->accessMessageDb(
	    GlobInstcs::setPtr->dbsLocation, userName,
343
	    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
Martin Straka's avatar
Martin Straka committed
344
	if (msgDb == Q_NULLPTR) {
345
		qCritical() << "ERROR: Message database cannot open!";
346 347 348
		return;
	}
	if (!msgDb->setAttachmentsDownloaded(false)) {
349
		qCritical() << "ERROR: Message data mssing!";
350 351 352
		return;
	}
}
353

Karel Slaný's avatar
Karel Slaný committed
354
void Files::vacuumFileDbs(void)
355 356 357
{
	qDebug("%s()", __func__);

358
	if (Q_UNLIKELY((GlobInstcs::setPtr == Q_NULLPTR) ||
359 360
	        (GlobInstcs::fileDbsPtr == Q_NULLPTR) ||
	        (GlobInstcs::acntMapPtr == Q_NULLPTR))) {
361 362 363 364
		Q_ASSERT(0);
		return;
	}

Karel Slaný's avatar
Karel Slaný committed
365
	emit statusBarTextChanged(tr("Vacuum databases"), true);
366

367
	QStringList userNameList(GlobInstcs::acntMapPtr->keys());
368
	foreach (const QString &userName, userNameList) {
369 370
		FileDb *fDb = GlobInstcs::fileDbsPtr->accessFileDb(
		    GlobInstcs::setPtr->dbsLocation, userName,
371
		    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
Martin Straka's avatar
Martin Straka committed
372
		if (fDb == Q_NULLPTR) {
373 374
			qCritical() << "ERROR: File database cannot open!"
			    << userName;
375 376
			return;
		}
Karel Slaný's avatar
Karel Slaný committed
377
		fDb->vacuumFileDb();
378 379
	}

Karel Slaný's avatar
Karel Slaný committed
380
	emit statusBarTextChanged(tr("Operation Vacuum has finished"), false);
381
}
382 383 384 385 386

bool Files::deleteAttachmentsFromDb(const QString &userName, qint64 msgId)
{
	qDebug("%s()", __func__);

387 388
	if (Q_UNLIKELY((GlobInstcs::setPtr == Q_NULLPTR) ||
	        (GlobInstcs::messageDbsPtr == Q_NULLPTR) ||
389 390
	        (GlobInstcs::fileDbsPtr == Q_NULLPTR) ||
	        (GlobInstcs::acntMapPtr == Q_NULLPTR))) {
391 392 393 394
		Q_ASSERT(0);
		return false;
	}

395 396
	FileDb *fDb = GlobInstcs::fileDbsPtr->accessFileDb(
	    GlobInstcs::setPtr->dbsLocation, userName,
397
	    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
Martin Straka's avatar
Martin Straka committed
398
	if (fDb == Q_NULLPTR) {
399
		qCritical() << "ERROR: File database cannot open!" << userName;
400 401 402 403
		return false;
	}

	if (fDb->deleteFilesFromDb(msgId)) {
404 405
		MessageDb *msgDb = GlobInstcs::messageDbsPtr->accessMessageDb(
		    GlobInstcs::setPtr->dbsLocation, userName,
406
		    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
Martin Straka's avatar
Martin Straka committed
407
		if (msgDb == Q_NULLPTR) {
408
			qCritical() << "ERROR: Message database cannot open!";
409 410 411 412 413 414 415
			return false;
		}
		return msgDb->setAttachmentDownloaded(msgId, false);
	}

	return false;
}
416

417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
bool Files::fileReadable(const QString &filePath)
{
	if (filePath.isEmpty()) {
		Q_ASSERT(0);
		qCritical() << "Target ZFO path is empty!";
		return false;
	}

	{
		QFileInfo fileInfo(filePath);
		if (!fileInfo.isFile() || !fileInfo.isReadable()) {
			qCritical() << "Cannot open ZFO file from" << filePath;
			return false;
		}
	}

	return true;
}

bool Files::isZfoFile(const QString &fileName)
{
	return QFileInfo(fileName).suffix().toLower() == QStringLiteral("zfo");
}

QByteArray Files::rawFileContent(const QString &filePath)
{
	if (!fileReadable(filePath)) {
		return QByteArray();
	}

	QFile file(filePath);
	if (!file.open(QIODevice::ReadOnly)) {
		Q_ASSERT(0);
		qCritical() << "Cannot open file" << filePath;
		return QByteArray();
	}

	QByteArray rawData(file.readAll());
	file.close();
	return rawData;
}

QByteArray Files::base64ToRaw(const QByteArray &base64Data)
{
	return QByteArray::fromBase64(base64Data);
}

464 465
MsgInfo *Files::zfoData(const QVariant &attachModelVariant,
    const QByteArray &rawZfoData)
466
{
467
	enum MsgInfo::ZfoType type = MsgInfo::TYPE_UNKNOWN;
468
	QString idStr, annot, htmlDescr, emailBody;
469

470
	bool ret = parseXmlData(&type, &idStr, &annot, &htmlDescr,
471
	    FileListModel::fromVariant(attachModelVariant),
472
	    &emailBody, getXmlFromCms(rawZfoData));
473

474
	return ret ?
475
	    new (std::nothrow) MsgInfo(type, idStr, annot, htmlDescr,
476
	        emailBody) :
477
	    new (std::nothrow) MsgInfo();
478 479 480 481 482 483 484
}

bool Files::setAttachmentModel(FileListModel &attachModel,
    const QString &userName, qint64 msgId)
{
	qDebug("%s()", __func__);

485
	if (Q_UNLIKELY((GlobInstcs::setPtr == Q_NULLPTR) ||
486 487
	        (GlobInstcs::fileDbsPtr == Q_NULLPTR) ||
	        (GlobInstcs::acntMapPtr == Q_NULLPTR))) {
488 489 490 491
		Q_ASSERT(0);
		return false;
	}

492 493
	FileDb *fDb = GlobInstcs::fileDbsPtr->accessFileDb(
	    GlobInstcs::setPtr->dbsLocation, userName,
494
	    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510

	if (fDb == Q_NULLPTR) {
		qCritical() << "Cannot open file database!";
		return false;
	}
	attachModel.clearAll();
	fDb->getFileListFromDb(attachModel, msgId);
	return true;
}

void Files::sendAttachmentEmailZfo(const QVariant &attachModelVariant,
    const QString &msgIdStr, QString subject, QString body)
{
	qDebug("%s()", __func__);

	/* Obtain pointer to attachment model. */
511
	const FileListModel *attachModel =
512
	    FileListModel::fromVariant(attachModelVariant);
513 514 515 516 517 518 519 520 521 522 523 524 525 526
	if (attachModel == Q_NULLPTR) {
		Q_ASSERT(0);
		return;
	}

	/* Obtain message identifier. */
	bool ok = false;
	qint64 msgId = msgIdStr.toLongLong(&ok);
	if (!ok || (msgId < 0)) {
		return;
	}

	QStringList fileList;

Martin Straka's avatar
Martin Straka committed
527 528
	const QString boundary = generateBoundaryString();
	QString emailMessage = createEmailMessage(body, subject,  boundary);
529

Martin Straka's avatar
Martin Straka committed
530
	deleteFilesFromDir(EMAIL_DIR_NAME);
531 532 533 534 535 536 537 538 539 540 541

	for (int row = 0; row < attachModel->rowCount(); ++row) {
		QModelIndex idx(attachModel->index(row));
		/*
		 * On Android the attachment must be saved and the explicitly
		 * add into the email message.
		 */
		QByteArray base64Data(attachModel->data(idx,
		    FileListModel::ROLE_FILE_DATA).toByteArray());
		QString attachName(attachModel->data(idx,
		    FileListModel::ROLE_FILE_NAME).toString());
542 543
		QString filePath(writeFileToDir(EMAIL_DIR_NAME, msgId,
		    attachName, base64ToRaw(base64Data)));
544 545 546 547 548 549 550 551 552 553
		fileList.append(filePath);
		addAttachmentToEmailMessage(emailMessage, attachName,
		    base64Data, boundary);
	}

	finishEmailMessage(emailMessage, boundary);

	sendEmail(emailMessage, fileList, subject, body, msgId);
}

554 555 556 557 558
void Files::saveAttachmentsToDiskDb(const QString &userName,
    const QString &msgIdStr)
{
	qDebug("%s()", __func__);

559
	if (Q_UNLIKELY((GlobInstcs::setPtr == Q_NULLPTR) ||
560 561
	        (GlobInstcs::fileDbsPtr == Q_NULLPTR) ||
	        (GlobInstcs::acntMapPtr == Q_NULLPTR))) {
562 563 564 565
		Q_ASSERT(0);
		return;
	}

566 567 568 569 570 571 572 573 574 575 576 577 578
	/* User name must be supplied. */
	if (userName.isEmpty()) {
		Q_ASSERT(0);
		return;
	}

	/* Obtain message identifier. */
	bool ok = false;
	qint64 msgId = msgIdStr.toLongLong(&ok);
	if (!ok || (msgId < 0)) {
		return;
	}

579 580
	FileDb *fDb = GlobInstcs::fileDbsPtr->accessFileDb(
	    GlobInstcs::setPtr->dbsLocation, userName,
581
	    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
582

Martin Straka's avatar
Martin Straka committed
583 584 585 586
	if (fDb == Q_NULLPTR) {
		qCritical() << "Cannot open file database!";
		return;
	}
587

Martin Straka's avatar
Martin Straka committed
588 589 590 591
	QList<FileDb::FileData> filelist = fDb->getFilesFromDb(msgId);
	if (filelist.isEmpty()) {
		qCritical() << "File list is empty!";
		return;
592 593 594 595
	}

	QString destPath;
	foreach (const FileDb::FileData &file, filelist) {
596 597
		destPath = writeFileToDir(QString(), msgId, file.filename,
		    base64ToRaw(file.content.toUtf8()));
598 599 600 601 602 603 604 605 606 607 608 609
	}

	attachmentSavingNotification(destPath);
}

void Files::saveAttachmentsToDiskZfo(const QVariant &attachModelVariant,
    const QString &msgIdStr)
{
	qDebug("%s()", __func__);

	/* Obtain pointer to attachment model. */
	const FileListModel *attachModel =
610
	    FileListModel::fromVariant(attachModelVariant);
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
	if (attachModel == Q_NULLPTR) {
		Q_ASSERT(0);
		return;
	}

	/* Obtain message identifier. */
	bool ok = false;
	qint64 msgId = msgIdStr.toLongLong(&ok);
	if (!ok || (msgId < 0)) {
		return;
	}

	QString destPath;
	for (int row = 0; row < attachModel->rowCount(); ++row) {
		QModelIndex idx(attachModel->index(row));
626
		destPath = writeFileToDir(QString(), msgId,
627 628
		    attachModel->data(idx,
		        FileListModel::ROLE_FILE_NAME).toString(),
629 630
		    base64ToRaw(attachModel->data(idx,
		        FileListModel::ROLE_FILE_DATA).toByteArray()));
631 632 633 634 635
	}

	attachmentSavingNotification(destPath);
}

636 637 638 639 640 641 642 643 644 645
void Files::deleteTmpFileFromStorage(const QString &filePath)
{
#if defined Q_OS_IOS
	QFile file(filePath);
	file.remove();
#else
	Q_UNUSED(filePath);
#endif /* defined Q_OS_IOS */
}

646 647 648
void Files::sendEmail(const QString &emailMessage, const QStringList &fileList,
    const QString &subject, const QString &body, qint64 msgId)
{
649 650 651 652 653
	Q_UNUSED(subject);
	Q_UNUSED(body);
	Q_UNUSED(emailMessage);
	Q_UNUSED(msgId);

654 655 656 657 658 659 660 661 662 663 664 665
	if (!fileList.isEmpty()) {

#if defined Q_OS_IOS
		UrlOpener urlOpener;
		urlOpener.createEmail(body, subject, fileList);
#elif defined Q_OS_ANDROID
		QDesktopServices::openUrl(QUrl("mailto:?subject=" +
		    subject + "&body=" + body));
#elif defined Q_OS_WINRT
		QDesktopServices::openUrl(QUrl("mailto:?subject=" +
		    subject + "&body=" + body));
#else
666 667
		QString tmpEmailFile = writeFileToDir(EMAIL_DIR_NAME, msgId,
		   QString::number(msgId) + "_mail.eml", emailMessage.toUtf8());
668 669 670 671 672
		QDesktopServices::openUrl(QUrl::fromLocalFile(tmpEmailFile));
#endif
		/* TODO -- Handle openUrl() return value. */

	} else {
673
		qWarning() << "Tmp file save error";
674 675 676
	}
}

677
QByteArray Files::getXmlFromCms(const QByteArray &rawData)
678 679 680
{
	qDebug("%s()", __func__);

681 682 683
	if (rawData.isEmpty()) {
		Q_ASSERT(0);
		qCritical() << "File content is empty!";
684 685 686
		return QByteArray();
	}

687
	/* Decode CMS and obtain message XML data - uses OpenSSL. */
Martin Straka's avatar
Martin Straka committed
688
	void *xmlContent = Q_NULLPTR;
689
	size_t xmlContentLen = 0;
690
	if (extract_cms_data(rawData.data(), rawData.length(), &xmlContent,
Martin Straka's avatar
Martin Straka committed
691
	        &xmlContentLen) != 0) {
692 693 694
		return QByteArray();
	}
	if (xmlContentLen == 0) {
Martin Straka's avatar
Martin Straka committed
695
		free(xmlContent); xmlContent = Q_NULLPTR;
696 697 698
		return QByteArray();
	}

699
	QByteArray soap((char *)xmlContent, xmlContentLen);
Martin Straka's avatar
Martin Straka committed
700
	free(xmlContent); xmlContent = Q_NULLPTR;
701 702 703 704

	return soap;
}

705
QByteArray Files::decodeZfoFile(const QByteArray &base64ZfoData)
Martin Straka's avatar
Martin Straka committed
706 707 708
{
	qDebug("%s()", __func__);

709 710 711
	if (base64ZfoData.isEmpty()) {
		Q_ASSERT(0);
		qCritical() << "File content is empty.";
Martin Straka's avatar
Martin Straka committed
712 713 714
		return QByteArray();
	}

715
	/* decode signature from base64 and obtain something CMS message */
716
	return getXmlFromCms(base64ToRaw(base64ZfoData));
Martin Straka's avatar
Martin Straka committed
717 718
}

719
bool Files::parseXmlData(enum MsgInfo::ZfoType *type, QString *idStr,
720 721
    QString *annotation, QString *msgDescrHtml, FileListModel *attachModel,
    QString *emailBody, QByteArray xmlData)
722 723 724
{
	qDebug("%s()", __func__);

725 726
	if (xmlData.isEmpty()) {
		qCritical() << "XML content is empty!";
727
		return false;
728 729
	}

730
	xmlData.prepend("<?xml version='1.0' encoding='utf-8'?>"
731 732 733 734
	      "<SOAP-ENV:Envelope "
	      "xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
	      "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">"
	      "<SOAP-ENV:Body>");
735
	xmlData.append("</SOAP-ENV:Body></SOAP-ENV:Envelope>");
736

737
	/* Test if zfo is message, delivery info or unknown format */
738
	if (xmlData.contains(QByteArray("MessageDownloadResponse"))) {
739 740
		if (type != Q_NULLPTR) {
			*type = MsgInfo::TYPE_MESSAGE;
741
		}
742 743
		return parseAndShowXmlData(MsgInfo::TYPE_MESSAGE, idStr,
		    annotation, msgDescrHtml, attachModel, emailBody, xmlData);
744
	} else if (xmlData.contains(QByteArray("GetDeliveryInfoResponse"))) {
745 746
		if (type != Q_NULLPTR) {
			*type = MsgInfo::TYPE_DELIVERY_INFO;
747
		}
748 749
		return parseAndShowXmlData(MsgInfo::TYPE_DELIVERY_INFO, idStr,
		    annotation, msgDescrHtml, attachModel, emailBody, xmlData);
750
	} else {
751 752 753 754
		if (type != Q_NULLPTR) {
			*type = MsgInfo::TYPE_UNKNOWN;
		}
		qCritical() << "Unknown ZFO format";
755 756
	}

757
	return false;
758 759
}

760
bool Files::parseAndShowXmlData(enum MsgInfo::ZfoType type, QString *idStr,
761 762
    QString *annotation, QString *msgDescrHtml, FileListModel *attachModel,
    QString *emailBody, QByteArray &xmlData)
763 764 765
{
	qDebug("%s()", __func__);

766
	QXmlStreamReader xml;
767
	MsgEnvelope msg;
768
	QList<AttachmentData> fileList;
769
	QList<Messages::Event> eventList;
770

771 772 773 774 775
	if (type == MsgInfo::TYPE_UNKNOWN) {
		Q_ASSERT(0);
		return false;
	}

776 777
	/* parse message envelope and files */
	xml.addData(xmlData);
778 779 780
	while(!xml.atEnd() && !xml.hasError()) {
		QXmlStreamReader::TokenType token = xml.readNext();
		if (xml.error() != QXmlStreamReader::NoError) {
781
			return false;
782 783 784 785 786 787
		}
		if (token == QXmlStreamReader::StartDocument) {
			continue;
		}
		if (token == QXmlStreamReader::StartElement) {
			if (xml.name() == "dmDm") {
788
				XmlLayer::completeMessageParse(xml, msg, fileList);
789 790 791 792
			}
		}
	}

793
	if (type == MsgInfo::TYPE_DELIVERY_INFO) {
794 795 796 797 798 799 800 801 802 803
		/* parse delivery info */
		xml.clear();
		xml.addData(xmlData);
		while(!xml.atEnd() && !xml.hasError()){
			QXmlStreamReader::TokenType token = xml.readNext();
			if (token == QXmlStreamReader::StartDocument) {
				continue;
			}
			if (token == QXmlStreamReader::StartElement) {
				if (xml.name() == "dmEvent") {
804
					eventList.append(XmlLayer::parseEvent(xml));
805 806 807
				}
			}
		}
808 809 810 811 812
	}

	QString html = divStart;

	html += "<h3>" + QObject::tr("General") + "</h3>";
813 814
	html += strongInfoLine(QObject::tr("Subject"), msg.dmAnnotation());
	QString size = QString::number(msg.dmAttachmentSize());
815
	html += strongInfoLine(QObject::tr("Attachment size"),
816
	    (size == "0") ? "&lt;1 kB" : "~" + size + " kB");
817
	html += strongInfoLine(QObject::tr("Personal delivery"),
818
	    (msg.dmPersonalDelivery()) ? QObject::tr("Yes") : QObject::tr("No"));
819
	html += strongInfoLine(QObject::tr("Delivery by fiction"),
820
	    (msg.dmAllowSubstDelivery()) ? QObject::tr("Yes") : QObject::tr("No"));
821 822

	html += "<h3>" + QObject::tr("Sender") + "</h3>";
823 824 825
	html += strongInfoLine(QObject::tr("Databox ID"), msg.dbIDSender());
	html += strongInfoLine(QObject::tr("Name"), msg.dmSender());
	html += strongInfoLine(QObject::tr("Address"),msg.dmSenderAddress());
826 827

	html += "<h3>" + QObject::tr("Recipient") + "</h3>";
828 829 830 831
	html += strongInfoLine(QObject::tr("Databox ID"), msg.dbIDRecipient());
	html += strongInfoLine(QObject::tr("Name"), msg.dmRecipient());
	html += strongInfoLine(QObject::tr("Address"),msg.dmRecipientAddress());
	if (!msg.dmToHands().isEmpty()) {
832
		html += strongInfoLine(QObject::tr("To hands"),
833
		    msg.dmToHands());
834
	}
835

836
	QString tmpHtml;
837
	if (!msg.dmSenderIdent().isEmpty()) {
838
		tmpHtml += strongInfoLine(QObject::tr("Our file mark"),
839
		    msg.dmSenderIdent());
840
	}
841
	if (!msg.dmSenderRefNumber().isEmpty()) {
842
		tmpHtml += strongInfoLine(QObject::tr("Our reference number"),
843
		    msg.dmSenderRefNumber());
844
	}
845
	if (!msg.dmRecipientIdent().isEmpty()) {
846
		tmpHtml += strongInfoLine(QObject::tr("Your file mark"),
847
		    msg.dmRecipientIdent());
848
	}
849
	if (!msg.dmRecipientRefNumber().isEmpty()) {
850
		tmpHtml += strongInfoLine(QObject::tr("Your reference number"),
851
		    msg.dmRecipientRefNumber());
852
	}
853
	if (!msg.dmLegalTitleLaw().isEmpty()) {
854
		tmpHtml += strongInfoLine(QObject::tr("Law"),
855
		    msg.dmLegalTitleLaw());
856
	}
857
	if (!msg.dmLegalTitleYear().isEmpty()) {
858
		tmpHtml += strongInfoLine(QObject::tr("Year"),
859
		    msg.dmLegalTitleYear());
860
	}
861
	if (!msg.dmLegalTitleSect().isEmpty()) {
862
		tmpHtml += strongInfoLine(QObject::tr("Section"),
863
		    msg.dmLegalTitleSect());
864
	}
865
	if (!msg.dmLegalTitlePar().isEmpty()) {
866
		tmpHtml += strongInfoLine(QObject::tr("Paragraph"),
867
		    msg.dmLegalTitlePar());
868
	}
869
	if (!msg.dmLegalTitlePoint().isEmpty()) {
870
		tmpHtml += strongInfoLine(QObject::tr("Letter"),
871
		    msg.dmLegalTitlePoint());
872 873 874 875 876
	}
	if (!tmpHtml.isEmpty()) {
		html += "<h3>" + QObject::tr("Additional info") + "</h3>";
		html += tmpHtml;
	}
877 878 879 880

	html += "<h3>" + QObject::tr("Message state") + "</h3>";
	html += strongInfoLine(QObject::tr("Delivery time"),
	    dateTimeStrFromDbFormat(
881
	        dateTimeStrToUTCDbFormat(msg.dmDeliveryTime()),
882 883 884
	        DATETIME_QML_FORMAT));
	html += strongInfoLine(QObject::tr("Accetance time"),
	    dateTimeStrFromDbFormat(
885
	        dateTimeStrToUTCDbFormat(msg.dmAcceptanceTime()),
886 887
	        DATETIME_QML_FORMAT));
	html += strongInfoLine(QObject::tr("Status"),
888
	    QString::number(msg.dmMessageStatus()));
889

890
	if (type == MsgInfo::TYPE_DELIVERY_INFO) {
891 892 893 894 895 896 897 898 899 900
		html += "<h3>" + QObject::tr("Events") + "</h3>";
		foreach (const Messages::Event &event, eventList) {
			html += divStart +
			    strongInfoLine(dateTimeStrFromDbFormat(
			        dateTimeStrToUTCDbFormat(event.dmEventTime),
			        DATETIME_QML_FORMAT), event.dmEventDescr)
			    + divEnd;
		}
	}

901 902
	html += divEnd;

903
	// Create body for email
Martin Straka's avatar
Martin Straka committed
904 905 906 907
	QString body = generateEmailBodyText(msg.dmID(), msg.dmSender(),
	    msg.dmRecipient(), dateTimeStrFromDbFormat(
	    dateTimeStrToUTCDbFormat(msg.dmAcceptanceTime()),
	    DATETIME_QML_FORMAT));
908

909
	if (idStr != Q_NULLPTR) {
910
		*idStr = QString::number(msg.dmID());
911
	}
912
	if (annotation != Q_NULLPTR) {
913
		*annotation = msg.dmAnnotation();
914 915 916 917 918
	}
	if (msgDescrHtml != Q_NULLPTR) {
		*msgDescrHtml = html;
	}
	if (attachModel != Q_NULLPTR) {
Martin Straka's avatar
Martin Straka committed
919 920
		attachModel->clearAll();
		foreach (const AttachmentData &file, fileList) {
Martin Straka's avatar
Martin Straka committed
921 922 923 924
			attachModel->appendFileEntry(
			    FileListModel::Entry(-1, file.dmFileDescr(),
			    file._dmFileSize(), file._icon(),
			    file.dmEncodedContent(), QString()));
Martin Straka's avatar
Martin Straka committed
925
		}
926 927 928 929
	}
	if (emailBody != Q_NULLPTR) {
		*emailBody = body;
	}
930

931 932
	return true;
}