files.cpp 26.1 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"
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
#include "src/sqlite/dbs.h"
44 45
#include "src/sqlite/file_db_container.h"
#include "src/sqlite/message_db_container.h"
46
#include "src/sqlite/zfo_db.h"
47

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

56 57
Files::Files(QObject *parent)
    : QObject(parent)
58 59 60
{
}

61
void Files::attachmentSavingNotification(const QString &destPath)
62
{
63 64 65 66
	QFileInfo fi(destPath);

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

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

78 79
	if (Q_UNLIKELY((GlobInstcs::setPtr == Q_NULLPTR) ||
	        (GlobInstcs::messageDbsPtr == Q_NULLPTR) ||
80 81
	        (GlobInstcs::fileDbsPtr == Q_NULLPTR) ||
	        (GlobInstcs::acntMapPtr == Q_NULLPTR))) {
82 83 84 85
		Q_ASSERT(0);
		return;
	}

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

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

118 119 120 121 122 123 124 125 126 127 128
QString Files::getAttachmentFileIcon(const QString &fileName)
{
	return getAttachmentFileIconFromFileExtension(fileName);
}

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

129
QByteArray Files::getFileRawContentFromDb(const QString &userName, int fileId)
130 131 132
{
	qDebug("%s()", __func__);

133
	if (Q_UNLIKELY((GlobInstcs::setPtr == Q_NULLPTR) ||
134 135
	        (GlobInstcs::fileDbsPtr == Q_NULLPTR) ||
	        (GlobInstcs::acntMapPtr == Q_NULLPTR))) {
136 137 138 139
		Q_ASSERT(0);
		return QByteArray();
	}

140 141
	FileDb *fDb = GlobInstcs::fileDbsPtr->accessFileDb(
	    GlobInstcs::setPtr->dbsLocation, userName,
142
	    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
143

144 145 146 147 148
	if (fDb == Q_NULLPTR) {
		qCritical() << "Cannot open file database!";
		return QByteArray();
	}

149
	return base64ToRaw(fDb->getFileContentFromDb(fileId).content.toUtf8());
150 151
}

152
void Files::openAttachmentFromDb(const QString &userName, int fileId)
153 154 155
{
	qDebug("%s()", __func__);

156
	if (Q_UNLIKELY((GlobInstcs::setPtr == Q_NULLPTR) ||
157 158
	        (GlobInstcs::fileDbsPtr == Q_NULLPTR) ||
	        (GlobInstcs::acntMapPtr == Q_NULLPTR))) {
159 160 161 162
		Q_ASSERT(0);
		return;
	}

163 164
	FileDb *fDb = GlobInstcs::fileDbsPtr->accessFileDb(
	    GlobInstcs::setPtr->dbsLocation, userName,
165
	    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
166

167 168
	if (fDb == Q_NULLPTR) {
		qCritical() << "Cannot open file database!";
169 170 171
		return;
	}

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

174
	openAttachment(file.filename, file.content.toUtf8());
175 176
}

177 178
void Files::openAttachment(const QString &fileName,
    const QByteArray &base64Data)
179 180
{
	Q_ASSERT(!fileName.isEmpty());
181
	Q_ASSERT(!base64Data.isEmpty());
182

183 184
	if (fileName.isEmpty() || base64Data.isEmpty()) {
		qCritical() << "File name or its content is empty!";
185 186 187
		return;
	}

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

195
	QString filePath(writeFile(appTmpDirPath(), fileName,
196
	    base64ToRaw(base64Data)));
197 198

	if (!filePath.isEmpty()) {
199
		qInfo() << "Creating temporary file" << filePath;
200 201 202 203 204 205 206 207 208 209 210 211 212 213
		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)
{
	if (filePath.isEmpty()) {
		qCritical() << "File path is empty!";
214
		Q_ASSERT(0);
215 216 217 218 219 220
		return;
	}

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

225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
#ifdef Q_OS_IOS

	UrlOpener urlOpener;
	urlOpener.openFile(filePath);

#else

	if (!QDesktopServices::openUrl(QUrl::fromLocalFile(filePath))) {
		Dialogues::errorMessage(Dialogues::CRITICAL,
		    tr("Open attachment error"),
		    tr("There is no application to open this file format."),
		    tr("File: '%1'").arg(filePath));
	}

#endif
240 241
}

242 243
void Files::sendMsgFilesWithEmail(const QString &userName, qint64 msgId,
    enum Files::EmailAttachType attachType)
244 245 246
{
	qDebug("%s()", __func__);

247 248
	if (Q_UNLIKELY((GlobInstcs::setPtr == Q_NULLPTR) ||
	        (GlobInstcs::messageDbsPtr == Q_NULLPTR) ||
249
	        (GlobInstcs::fileDbsPtr == Q_NULLPTR) ||
250
	        (GlobInstcs::zfoDbPtr == Q_NULLPTR) ||
251
	        (GlobInstcs::acntMapPtr == Q_NULLPTR))) {
252 253 254 255
		Q_ASSERT(0);
		return;
	}

256 257 258 259 260 261 262
	if (userName.isEmpty() || msgId <= 0) {
		return;
	}

	QString body;
	QString subject;

263
	/* Fill email subject and body */
264 265
	MessageDb *msgDb = GlobInstcs::messageDbsPtr->accessMessageDb(
	    GlobInstcs::setPtr->dbsLocation, userName,
266
	    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
Martin Straka's avatar
Martin Straka committed
267
	if (msgDb == Q_NULLPTR) {
268
		qCritical() << "ERROR: Message database cannot open!";
269 270 271
		return;
	}
	if (!msgDb->getMessageDataForEmail(msgId, body, subject)) {
272
		qCritical() << "ERROR: Message data mssing!";
273 274 275 276
		return;
	}

	QList<FileDb::FileData> filelist;
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293

	/* Get attachment files from database if needed */
	if (Files::SEND_EMAIL_ZFO_ATTACHMENT == attachType
	    || Files::SEND_EMAIL_ATTACHMENT_ONLY == attachType ) {
		FileDb *fDb = GlobInstcs::fileDbsPtr->accessFileDb(
		    GlobInstcs::setPtr->dbsLocation, userName,
		    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
		if (fDb == Q_NULLPTR) {
			qCritical() << "ERROR: File database cannot open!";
			return;
		}

		filelist = fDb->getFilesFromDb(msgId);
		if (filelist.isEmpty()) {
			qCritical() << "ERROR: File list is empty!";
			return;
		}
294 295
	}

296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
	/* Get zfo file from database if needed */
	if (Files::SEND_EMAIL_ZFO_ATTACHMENT == attachType
	    || Files::SEND_EMAIL_ZFO_ONLY == attachType ) {
		FileDb::FileData msgData;
		msgData.content = GlobInstcs::zfoDbPtr->getZfoContentFromDb(msgId,
		    (*GlobInstcs::acntMapPtr)[userName].isTestAccount());
		if (msgData.content.isEmpty()) {
			/* TODO - show error dialogue */
			qCritical() << "ERROR: ZFO message data missing!";
			Q_ASSERT(0);
			return;
		}
		msgData.filename = QString("DZ_%1.zfo").arg(msgId);
		filelist.append(msgData);
	}
311

312
	/* Create email content, email attachment and send email */
313 314
	removeDirFromDocLoc(DATOVKA_MAIL_DIR_NAME);
	QString targetPath(appEmailDirPath(QString::number(msgId)));
315 316 317
	QStringList fileListPath;
	const QString boundary = generateBoundaryString();
	QString emailMessage = createEmailMessage(body, subject,  boundary);
318 319

	foreach (const FileDb::FileData &file, filelist) {
320
		QString fileName = file.filename;
321
		if (fileName.isEmpty()) {
322
			qCritical() << "ERROR: File name is empty!";
323 324
			return;
		}
325
		fileName = writeFile(targetPath, fileName,
326
		    base64ToRaw(file.content.toUtf8()));
327
		fileListPath.append(fileName);
328 329 330 331 332 333
		addAttachmentToEmailMessage(emailMessage, file.filename,
		    file.content.toUtf8(), boundary);
	}

	finishEmailMessage(emailMessage, boundary);

334
	sendEmail(emailMessage, fileListPath, subject, body, msgId);
335
}
336 337 338 339 340

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

341 342
	if (Q_UNLIKELY((GlobInstcs::setPtr == Q_NULLPTR) ||
	        (GlobInstcs::messageDbsPtr == Q_NULLPTR) ||
343 344
	        (GlobInstcs::fileDbsPtr == Q_NULLPTR) ||
	        (GlobInstcs::acntMapPtr == Q_NULLPTR))) {
345 346 347 348
		Q_ASSERT(0);
		return;
	}

349
	int msgResponse = Dialogues::message(Dialogues::QUESTION,
350 351 352
	    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."),
353 354
	    Dialogues::NO | Dialogues::YES, Dialogues::NO);
	if (msgResponse == Dialogues::NO) {
355 356 357
		return;
	}

358 359
	FileDb *fDb = GlobInstcs::fileDbsPtr->accessFileDb(
	    GlobInstcs::setPtr->dbsLocation, userName,
360
	    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
Martin Straka's avatar
Martin Straka committed
361
	if (fDb == Q_NULLPTR) {
362
		qCritical() << "ERROR: File database open error!";
363 364
		return;
	}
365
	if (!GlobInstcs::fileDbsPtr->deleteDb(fDb)) {
366
		qCritical() << "ERROR: File database could not delete!";
367 368 369
		return;
	}

370 371
	MessageDb *msgDb = GlobInstcs::messageDbsPtr->accessMessageDb(
	    GlobInstcs::setPtr->dbsLocation, userName,
372
	    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
Martin Straka's avatar
Martin Straka committed
373
	if (msgDb == Q_NULLPTR) {
374
		qCritical() << "ERROR: Message database cannot open!";
375 376 377
		return;
	}
	if (!msgDb->setAttachmentsDownloaded(false)) {
378
		qCritical() << "ERROR: Message data mssing!";
379 380 381
		return;
	}
}
382

Karel Slaný's avatar
Karel Slaný committed
383
void Files::vacuumFileDbs(void)
384 385 386
{
	qDebug("%s()", __func__);

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

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

396
	QStringList userNameList(GlobInstcs::acntMapPtr->keys());
397
	foreach (const QString &userName, userNameList) {
398 399
		FileDb *fDb = GlobInstcs::fileDbsPtr->accessFileDb(
		    GlobInstcs::setPtr->dbsLocation, userName,
400
		    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
Martin Straka's avatar
Martin Straka committed
401
		if (fDb == Q_NULLPTR) {
402 403
			qCritical() << "ERROR: File database cannot open!"
			    << userName;
404 405
			return;
		}
Karel Slaný's avatar
Karel Slaný committed
406
		fDb->vacuumFileDb();
407 408
	}

Karel Slaný's avatar
Karel Slaný committed
409
	emit statusBarTextChanged(tr("Operation Vacuum has finished"), false);
410
}
411 412 413 414 415

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

416 417
	if (Q_UNLIKELY((GlobInstcs::setPtr == Q_NULLPTR) ||
	        (GlobInstcs::messageDbsPtr == Q_NULLPTR) ||
418 419
	        (GlobInstcs::fileDbsPtr == Q_NULLPTR) ||
	        (GlobInstcs::acntMapPtr == Q_NULLPTR))) {
420 421 422 423
		Q_ASSERT(0);
		return false;
	}

424 425
	FileDb *fDb = GlobInstcs::fileDbsPtr->accessFileDb(
	    GlobInstcs::setPtr->dbsLocation, userName,
426
	    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
Martin Straka's avatar
Martin Straka committed
427
	if (fDb == Q_NULLPTR) {
428
		qCritical() << "ERROR: File database cannot open!" << userName;
429 430 431 432
		return false;
	}

	if (fDb->deleteFilesFromDb(msgId)) {
433 434
		MessageDb *msgDb = GlobInstcs::messageDbsPtr->accessMessageDb(
		    GlobInstcs::setPtr->dbsLocation, userName,
435
		    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
Martin Straka's avatar
Martin Straka committed
436
		if (msgDb == Q_NULLPTR) {
437
			qCritical() << "ERROR: Message database cannot open!";
438 439 440 441 442 443 444
			return false;
		}
		return msgDb->setAttachmentDownloaded(msgId, false);
	}

	return false;
}
445

446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
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);
}

493 494
MsgInfo *Files::zfoData(const QVariant &attachModelVariant,
    const QByteArray &rawZfoData)
495
{
496
	enum MsgInfo::ZfoType type = MsgInfo::TYPE_UNKNOWN;
497
	QString idStr, annot, htmlDescr, emailBody;
498

499
	bool ret = parseXmlData(&type, &idStr, &annot, &htmlDescr,
500
	    FileListModel::fromVariant(attachModelVariant),
501
	    &emailBody, getXmlFromCms(rawZfoData));
502

503
	return ret ?
504
	    new (std::nothrow) MsgInfo(type, idStr, annot, htmlDescr,
505
	        emailBody) :
506
	    new (std::nothrow) MsgInfo();
507 508 509 510 511 512 513
}

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

514
	if (Q_UNLIKELY((GlobInstcs::setPtr == Q_NULLPTR) ||
515 516
	        (GlobInstcs::fileDbsPtr == Q_NULLPTR) ||
	        (GlobInstcs::acntMapPtr == Q_NULLPTR))) {
517 518 519 520
		Q_ASSERT(0);
		return false;
	}

521 522
	FileDb *fDb = GlobInstcs::fileDbsPtr->accessFileDb(
	    GlobInstcs::setPtr->dbsLocation, userName,
523
	    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539

	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. */
540
	const FileListModel *attachModel =
541
	    FileListModel::fromVariant(attachModelVariant);
542 543 544 545 546 547 548 549 550 551 552 553 554 555
	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;

556 557
	const QString boundary = generateBoundaryString();
	QString emailMessage = createEmailMessage(body, subject,  boundary);
558

559 560
	removeDirFromDocLoc(DATOVKA_MAIL_DIR_NAME);
	QString targetPath(appEmailDirPath(msgIdStr));
561 562 563 564 565 566 567 568 569 570 571

	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());
572 573
		QString filePath(writeFile(targetPath, attachName,
		    base64ToRaw(base64Data)));
574 575 576 577 578 579 580 581 582 583
		fileList.append(filePath);
		addAttachmentToEmailMessage(emailMessage, attachName,
		    base64Data, boundary);
	}

	finishEmailMessage(emailMessage, boundary);

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

584 585 586 587 588
void Files::saveAttachmentsToDiskDb(const QString &userName,
    const QString &msgIdStr)
{
	qDebug("%s()", __func__);

589
	if (Q_UNLIKELY((GlobInstcs::setPtr == Q_NULLPTR) ||
590 591
	        (GlobInstcs::fileDbsPtr == Q_NULLPTR) ||
	        (GlobInstcs::acntMapPtr == Q_NULLPTR))) {
592 593 594 595
		Q_ASSERT(0);
		return;
	}

596 597 598 599 600 601 602 603 604 605 606 607 608
	/* 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;
	}

609 610
	FileDb *fDb = GlobInstcs::fileDbsPtr->accessFileDb(
	    GlobInstcs::setPtr->dbsLocation, userName,
611
	    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
612

Martin Straka's avatar
Martin Straka committed
613 614 615 616
	if (fDb == Q_NULLPTR) {
		qCritical() << "Cannot open file database!";
		return;
	}
617

Martin Straka's avatar
Martin Straka committed
618 619 620 621
	QList<FileDb::FileData> filelist = fDb->getFilesFromDb(msgId);
	if (filelist.isEmpty()) {
		qCritical() << "File list is empty!";
		return;
622 623
	}

624
	QString filePath(appMsgAttachDirPath(msgIdStr));
625

626 627
	QString destPath;
	foreach (const FileDb::FileData &file, filelist) {
628
		destPath = writeFile(filePath, file.filename,
629
		    base64ToRaw(file.content.toUtf8()));
630 631 632 633 634 635 636 637 638 639 640 641
	}

	attachmentSavingNotification(destPath);
}

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

	/* Obtain pointer to attachment model. */
	const FileListModel *attachModel =
642
	    FileListModel::fromVariant(attachModelVariant);
643 644 645 646 647
	if (attachModel == Q_NULLPTR) {
		Q_ASSERT(0);
		return;
	}

648
	QString targetPath(appMsgAttachDirPath(msgIdStr));
649 650 651 652

	QString destPath;
	for (int row = 0; row < attachModel->rowCount(); ++row) {
		QModelIndex idx(attachModel->index(row));
653 654
		destPath = writeFile(targetPath, attachModel->data(idx,
		    FileListModel::ROLE_FILE_NAME).toString(),
655
		    base64ToRaw(attachModel->data(idx,
656
		    FileListModel::ROLE_FILE_DATA).toByteArray()));
657 658 659 660 661
	}

	attachmentSavingNotification(destPath);
}

662 663 664 665 666 667 668
void Files::deleteTmpFileFromStorage(const QString &filePath)
{
#if defined Q_OS_IOS
	QFile file(filePath);
	file.remove();
#else
	Q_UNUSED(filePath);
669
#endif
670 671
}

672 673 674
void Files::sendEmail(const QString &emailMessage, const QStringList &fileList,
    const QString &subject, const QString &body, qint64 msgId)
{
675 676 677 678
	Q_UNUSED(subject);
	Q_UNUSED(body);
	Q_UNUSED(emailMessage);
	Q_UNUSED(msgId);
679
	Q_UNUSED(fileList);
680 681

#if defined Q_OS_IOS
682 683 684 685

	UrlOpener urlOpener;
	urlOpener.createEmail(body, subject, fileList);

686
#elif defined Q_OS_ANDROID
687 688 689 690

	QDesktopServices::openUrl(QUrl("mailto:?subject=" + subject +
	    "&body=" + body));

691 692
#else

693
	QString tmpEmailFile = writeFile(
694
	    appEmailDirPath(QString::number(msgId)),
695 696 697 698
	    QString::number(msgId) + "_mail.eml", emailMessage.toUtf8());
	QDesktopServices::openUrl(QUrl::fromLocalFile(tmpEmailFile));

#endif
699 700
}

701
QByteArray Files::getXmlFromCms(const QByteArray &rawData)
702 703 704
{
	qDebug("%s()", __func__);

705 706 707
	if (rawData.isEmpty()) {
		Q_ASSERT(0);
		qCritical() << "File content is empty!";
708 709 710
		return QByteArray();
	}

711
	/* Decode CMS and obtain message XML data - uses OpenSSL. */
Martin Straka's avatar
Martin Straka committed
712
	void *xmlContent = Q_NULLPTR;
713
	size_t xmlContentLen = 0;
714
	if (extract_cms_data(rawData.data(), rawData.length(), &xmlContent,
Martin Straka's avatar
Martin Straka committed
715
	        &xmlContentLen) != 0) {
716 717 718
		return QByteArray();
	}
	if (xmlContentLen == 0) {
Martin Straka's avatar
Martin Straka committed
719
		free(xmlContent); xmlContent = Q_NULLPTR;
720 721 722
		return QByteArray();
	}

723
	QByteArray soap((char *)xmlContent, xmlContentLen);
Martin Straka's avatar
Martin Straka committed
724
	free(xmlContent); xmlContent = Q_NULLPTR;
725 726 727 728

	return soap;
}

729
QByteArray Files::decodeZfoFile(const QByteArray &base64ZfoData)
Martin Straka's avatar
Martin Straka committed
730 731 732
{
	qDebug("%s()", __func__);

733 734 735
	if (base64ZfoData.isEmpty()) {
		Q_ASSERT(0);
		qCritical() << "File content is empty.";
Martin Straka's avatar
Martin Straka committed
736 737 738
		return QByteArray();
	}

739
	/* decode signature from base64 and obtain something CMS message */
740
	return getXmlFromCms(base64ToRaw(base64ZfoData));
Martin Straka's avatar
Martin Straka committed
741 742
}

743
bool Files::parseXmlData(enum MsgInfo::ZfoType *type, QString *idStr,
744 745
    QString *annotation, QString *msgDescrHtml, FileListModel *attachModel,
    QString *emailBody, QByteArray xmlData)
746 747 748
{
	qDebug("%s()", __func__);

749 750
	if (xmlData.isEmpty()) {
		qCritical() << "XML content is empty!";
751
		return false;
752 753
	}

754
	xmlData.prepend("<?xml version='1.0' encoding='utf-8'?>"
755 756 757 758
	      "<SOAP-ENV:Envelope "
	      "xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
	      "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">"
	      "<SOAP-ENV:Body>");
759
	xmlData.append("</SOAP-ENV:Body></SOAP-ENV:Envelope>");
760

761
	/* Test if zfo is message, delivery info or unknown format */
762
	if (xmlData.contains(QByteArray("MessageDownloadResponse"))) {
763 764
		if (type != Q_NULLPTR) {
			*type = MsgInfo::TYPE_MESSAGE;
765
		}
766 767
		return parseAndShowXmlData(MsgInfo::TYPE_MESSAGE, idStr,
		    annotation, msgDescrHtml, attachModel, emailBody, xmlData);
768
	} else if (xmlData.contains(QByteArray("GetDeliveryInfoResponse"))) {
769 770
		if (type != Q_NULLPTR) {
			*type = MsgInfo::TYPE_DELIVERY_INFO;
771
		}
772 773
		return parseAndShowXmlData(MsgInfo::TYPE_DELIVERY_INFO, idStr,
		    annotation, msgDescrHtml, attachModel, emailBody, xmlData);
774
	} else {
775 776 777 778
		if (type != Q_NULLPTR) {
			*type = MsgInfo::TYPE_UNKNOWN;
		}
		qCritical() << "Unknown ZFO format";
779 780
	}

781
	return false;
782 783
}

784
bool Files::parseAndShowXmlData(enum MsgInfo::ZfoType type, QString *idStr,
785 786
    QString *annotation, QString *msgDescrHtml, FileListModel *attachModel,
    QString *emailBody, QByteArray &xmlData)
787 788 789
{
	qDebug("%s()", __func__);

790
	QXmlStreamReader xml;
791
	MsgEnvelope msg;
792
	QList<AttachmentData> fileList;
793
	QList<Messages::Event> eventList;
794

795 796 797 798 799
	if (type == MsgInfo::TYPE_UNKNOWN) {
		Q_ASSERT(0);
		return false;
	}

800 801
	/* parse message envelope and files */
	xml.addData(xmlData);
802 803 804
	while(!xml.atEnd() && !xml.hasError()) {
		QXmlStreamReader::TokenType token = xml.readNext();
		if (xml.error() != QXmlStreamReader::NoError) {
805
			return false;
806 807 808 809 810 811
		}
		if (token == QXmlStreamReader::StartDocument) {
			continue;
		}
		if (token == QXmlStreamReader::StartElement) {
			if (xml.name() == "dmDm") {
812
				XmlLayer::completeMessageParse(xml, msg, fileList);
813 814 815 816
			}
		}
	}

817
	if (type == MsgInfo::TYPE_DELIVERY_INFO) {
818 819 820 821 822 823 824 825 826 827
		/* 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") {
828
					eventList.append(XmlLayer::parseEvent(xml));
829 830 831
				}
			}
		}
832 833 834 835
	}

	QString html = divStart;

836 837
	html += "<h3>" + tr("General") + "</h3>";
	html += strongInfoLine(tr("Subject"), msg.dmAnnotation());
838
	QString size = QString::number(msg.dmAttachmentSize());
839
	html += strongInfoLine(tr("Attachment size"),
840
	    (size == "0") ? "&lt;1 kB" : "~" + size + " kB");
841 842 843 844 845 846 847 848 849 850 851 852 853 854
	html += strongInfoLine(tr("Personal delivery"),
	    (msg.dmPersonalDelivery()) ? tr("Yes") : tr("No"));
	html += strongInfoLine(tr("Delivery by fiction"),
	    (msg.dmAllowSubstDelivery()) ? tr("Yes") : tr("No"));

	html += "<h3>" + tr("Sender") + "</h3>";
	html += strongInfoLine(tr("Databox ID"), msg.dbIDSender());
	html += strongInfoLine(tr("Name"), msg.dmSender());
	html += strongInfoLine(tr("Address"),msg.dmSenderAddress());

	html += "<h3>" + tr("Recipient") + "</h3>";
	html += strongInfoLine(tr("Databox ID"), msg.dbIDRecipient());
	html += strongInfoLine(tr("Name"), msg.dmRecipient());
	html += strongInfoLine(tr("Address"),msg.dmRecipientAddress());
855
	if (!msg.dmToHands().isEmpty()) {
856
		html += strongInfoLine(tr("To hands"), msg.dmToHands());
857
	}
858

859
	QString tmpHtml;
860
	if (!msg.dmSenderIdent().isEmpty()) {
861
		tmpHtml += strongInfoLine(tr("Our file mark"),
862
		    msg.dmSenderIdent());
863
	}
864
	if (!msg.dmSenderRefNumber().isEmpty()) {
865
		tmpHtml += strongInfoLine(tr("Our reference number"),
866
		    msg.dmSenderRefNumber());
867
	}
868
	if (!msg.dmRecipientIdent().isEmpty()) {
869
		tmpHtml += strongInfoLine(tr("Your file mark"),
870
		    msg.dmRecipientIdent());
871
	}
872
	if (!msg.dmRecipientRefNumber().isEmpty()) {
873
		tmpHtml += strongInfoLine(tr("Your reference number"),
874
		    msg.dmRecipientRefNumber());
875
	}
876
	if (!msg.dmLegalTitleLaw().isEmpty()) {
877
		tmpHtml += strongInfoLine(tr("Law"), msg.dmLegalTitleLaw());
878
	}
879
	if (!msg.dmLegalTitleYear().isEmpty()) {
880
		tmpHtml += strongInfoLine(tr("Year"), msg.dmLegalTitleYear());
881
	}
882
	if (!msg.dmLegalTitleSect().isEmpty()) {
883
		tmpHtml += strongInfoLine(tr("Section"), msg.dmLegalTitleSect());
884
	}
885
	if (!msg.dmLegalTitlePar().isEmpty()) {
886
		tmpHtml += strongInfoLine(tr("Paragraph"),
887
		    msg.dmLegalTitlePar());
888
	}
889
	if (!msg.dmLegalTitlePoint().isEmpty()) {
890
		tmpHtml += strongInfoLine(tr("Letter"), msg.dmLegalTitlePoint());
891 892
	}
	if (!tmpHtml.isEmpty()) {
893
		html += "<h3>" + tr("Additional info") + "</h3>";
894 895
		html += tmpHtml;
	}
896

897 898
	html += "<h3>" + tr("Message state") + "</h3>";
	html += strongInfoLine(tr("Delivery time"),
899
	    dateTimeStrFromDbFormat(
900
	        dateTimeStrToUTCDbFormat(msg.dmDeliveryTime()),
901
	        DATETIME_QML_FORMAT));
902
	html += strongInfoLine(tr("Accetance time"),
903
	    dateTimeStrFromDbFormat(
904
	        dateTimeStrToUTCDbFormat(msg.dmAcceptanceTime()),
905
	        DATETIME_QML_FORMAT));
906
	html += strongInfoLine(tr("Status"),
907
	    QString::number(msg.dmMessageStatus()));
908

909
	if (type == MsgInfo::TYPE_DELIVERY_INFO) {
910
		html += "<h3>" + tr("Events") + "</h3>";
911 912 913 914 915 916 917 918 919
		foreach (const Messages::Event &event, eventList) {
			html += divStart +
			    strongInfoLine(dateTimeStrFromDbFormat(
			        dateTimeStrToUTCDbFormat(event.dmEventTime),
			        DATETIME_QML_FORMAT), event.dmEventDescr)
			    + divEnd;
		}
	}

920 921
	html += divEnd;

922
	// Create body for email
923 924 925 926
	QString body = generateEmailBodyText(msg.dmID(), msg.dmSender(),
	    msg.dmRecipient(), dateTimeStrFromDbFormat(
	    dateTimeStrToUTCDbFormat(msg.dmAcceptanceTime()),
	    DATETIME_QML_FORMAT));
927

928
	if (idStr != Q_NULLPTR) {
929
		*idStr = QString::number(msg.dmID());
930
	}
931
	if (annotation != Q_NULLPTR) {
932
		*annotation = msg.dmAnnotation();
933 934 935 936 937
	}
	if (msgDescrHtml != Q_NULLPTR) {
		*msgDescrHtml = html;
	}
	if (attachModel != Q_NULLPTR) {
938 939
		attachModel->clearAll();
		foreach (const AttachmentData &file, fileList) {
Martin Straka's avatar
Martin Straka committed
940 941 942 943
			attachModel->appendFileEntry(
			    FileListModel::Entry(-1, file.dmFileDescr(),
			    file._dmFileSize(), file._icon(),
			    file.dmEncodedContent(), QString()));
944
		}
945 946 947 948
	}
	if (emailBody != Q_NULLPTR) {
		*emailBody = body;
	}
949

950 951
	return true;
}