files.cpp 24.2 KB
Newer Older
1
/*
2
 * Copyright (C) 2014-2017 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 27
#include <QFileInfo>
#include <QUrl>
28
#include <QQmlEngine> /* qmlRegisterType */
29

30
#include "ios/src/url_opener.h"
31
#include "src/auxiliaries/attachment_helper.h"
32
#include "src/auxiliaries/email_helper.h"
33
#include "src/common.h"
34
#include "src/dialogues/dialogues.h"
35
#include "src/files.h"
36 37
#include "src/io/filesystem.h"
#include "src/models/accountmodel.h"
38
#include "src/net/xml_layer.h"
39
#include "src/qml_interaction/attachment_data.h"
40
#include "src/qml_interaction/message_envelope.h"
41
#include "src/settings.h"
42 43
#include "src/sqlite/file_db_container.h"
#include "src/sqlite/message_db_container.h"
44 45 46 47 48 49
#include "src/sqlite/dbs.h"

#ifndef Q_OS_WIN
#include "src/crypto/crypto.h"
#endif

50
Files::Files(QObject *parent) : QObject(parent)
51 52 53
{
}

Martin Straka's avatar
Martin Straka committed
54 55 56 57 58 59
void Files::declareQML(void)
{
	qmlRegisterType<Files>("cz.nic.mobileDatovka.files", 1, 0, "FileIdType");
	qRegisterMetaType<Files::FileIdType>();
}

60
void Files::attachmentSavingNotification(const QString &destPath)
61
{
62 63 64 65 66 67 68 69
	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()));
70 71
}

72
QString Files::fileSaveLocation(void)
73
{
74
	return QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
75 76
}

77 78 79 80 81 82 83 84
void Files::deleteExpiredFilesFromDbs(int days)
{
	qDebug("%s()", __func__);

	QStringList msgIDList;
	QStringList userNameList = AccountListModel::globAccounts.keys();
	foreach (const QString &userName, userNameList) {
		FileDb *fDb = NULL;
85 86
		fDb = globFileDbsPtr->accessFileDb(globSet.dbsLocation, userName,
		    AccountListModel::globAccounts[userName].storeToDisk());
87 88 89 90 91 92 93 94 95 96
		if (fDb == NULL) {
			qDebug() << "ERROR: File database cannot open!" << userName;
			return;
		}
		msgIDList = fDb->cleanFilesInDb(days);
		if (msgIDList.isEmpty()) {
			continue;
		}

		MessageDb *msgDb = NULL;
97 98
		msgDb = globMessageDbsPtr->accessMessageDb(globSet.dbsLocation,
		    userName, AccountListModel::globAccounts[userName].storeToDisk());
99 100 101 102
		if (msgDb == NULL) {
			qDebug() << "ERROR: Message database cannot open!";
			return;
		}
103
		msgDb->beginTransaction();
104 105 106 107
		foreach (const QString &msgId, msgIDList) {
			msgDb->setAttachmentDownloaded(msgId.toLongLong(),
			    false);
		}
108
		msgDb->commitTransaction();
109 110 111
	}
}

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

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

123
QByteArray Files::getFileRawContentFromDb(const QString &userName,
124 125 126 127 128 129 130 131 132 133 134
    const QString &msgIdStr, int fileId)
{
	qDebug("%s()", __func__);

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

135 136
	FileDb *fDb = globFileDbsPtr->accessFileDb(globSet.dbsLocation,
	    userName, AccountListModel::globAccounts[userName].storeToDisk());
137

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

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

	return base64ToRaw(file.content.toUtf8());
146 147 148 149 150 151 152 153 154 155 156 157 158 159
}

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

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

160 161
	FileDb *fDb = globFileDbsPtr->accessFileDb(globSet.dbsLocation,
	    userName, AccountListModel::globAccounts[userName].storeToDisk());
162

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

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

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

173
void Files::openAttachment(const QString &fileName, const QByteArray &base64Data)
174 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 192
	QString filePath(writeFileToTmpDir(fileName, TEMP_DIR_NAME,
	    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 */
}

238
void Files::sendAttachmentsWithEmail(const QString &userName, qint64 msgId)
239 240 241 242 243 244 245 246 247 248 249
{
	qDebug("%s()", __func__);

	if (userName.isEmpty() || msgId <= 0) {
		return;
	}

	QString body;
	QString subject;
	QStringList fileList;

250 251
	MessageDb *msgDb = globMessageDbsPtr->accessMessageDb(globSet.dbsLocation,
	    userName, AccountListModel::globAccounts[userName].storeToDisk());
252 253 254 255 256 257 258 259 260
	if (msgDb == NULL) {
		qDebug() << "ERROR: Message database cannot open!";
		return;
	}
	if (!msgDb->getMessageDataForEmail(msgId, body, subject)) {
		qDebug() << "ERROR: Message data mssing!";
		return;
	}

261 262
	FileDb *fDb = globFileDbsPtr->accessFileDb(globSet.dbsLocation,
	    userName, AccountListModel::globAccounts[userName].storeToDisk());
263 264 265 266 267 268 269 270 271 272 273 274
	if (fDb == NULL) {
		qDebug() << "ERROR: File database cannot open!";
		return;
	}

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

275 276
	const QString boundary = generateBoundaryString();
	QString emailMessage = createEmailMessage(body, subject,  boundary);
277 278 279 280

	deleteFilesFromDir(EMAIL_DIR_NAME);

	foreach (const FileDb::FileData &file, filelist) {
281
		QString fileName = file.filename;
282 283 284 285
		if (fileName.isEmpty()) {
			qDebug() << "ERROR: File name is empty!";
			return;
		}
286 287
		fileName = writeFileToEmailDir(fileName, msgId,
		    base64ToRaw(file.content.toUtf8()));
288 289 290 291 292 293 294 295
		fileList.append(fileName);
		addAttachmentToEmailMessage(emailMessage, file.filename,
		    file.content.toUtf8(), boundary);
		qDebug() << fileName;
	}

	finishEmailMessage(emailMessage, boundary);

296 297
	sendEmail(emailMessage, fileList, subject, body, msgId);
}
298 299 300 301 302

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

303
	int msgResponse = Dialogues::message(Dialogues::QUESTION,
304 305 306
	    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."),
307 308
	    Dialogues::NO | Dialogues::YES, Dialogues::NO);
	if (msgResponse == Dialogues::NO) {
309 310 311 312
		return;
	}

	FileDb *fDb = NULL;
313 314
	fDb = globFileDbsPtr->accessFileDb(globSet.dbsLocation, userName,
	    AccountListModel::globAccounts[userName].storeToDisk());
315 316 317 318 319 320 321 322 323 324
	if (fDb == NULL) {
		qDebug() << "ERROR: File database open error!";
		return;
	}
	if (!globFileDbsPtr->deleteDb(fDb)) {
		qDebug() << "ERROR: File database could not delete!";
		return;
	}

	MessageDb *msgDb = NULL;
325 326
	msgDb = globMessageDbsPtr->accessMessageDb(globSet.dbsLocation, userName,
	    AccountListModel::globAccounts[userName].storeToDisk());
327 328 329 330 331 332 333 334 335
	if (msgDb == NULL) {
		qDebug() << "ERROR: Message database cannot open!";
		return;
	}
	if (!msgDb->setAttachmentsDownloaded(false)) {
		qDebug() << "ERROR: Message data mssing!";
		return;
	}
}
336

Karel Slaný's avatar
Karel Slaný committed
337
void Files::vacuumFileDbs(void)
338 339 340
{
	qDebug("%s()", __func__);

Karel Slaný's avatar
Karel Slaný committed
341
	emit statusBarTextChanged(tr("Vacuum databases"), true);
342 343 344 345

	QStringList userNameList = AccountListModel::globAccounts.keys();
	foreach (const QString &userName, userNameList) {
		FileDb *fDb = NULL;
346 347
		fDb = globFileDbsPtr->accessFileDb(globSet.dbsLocation, userName,
		    AccountListModel::globAccounts[userName].storeToDisk());
348 349 350 351
		if (fDb == NULL) {
			qDebug() << "ERROR: File database cannot open!" << userName;
			return;
		}
Karel Slaný's avatar
Karel Slaný committed
352
		fDb->vacuumFileDb();
353 354
	}

Karel Slaný's avatar
Karel Slaný committed
355
	emit statusBarTextChanged(tr("Operation Vacuum has finished"), false);
356
}
357 358 359 360 361 362

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

	FileDb *fDb = NULL;
363 364
	fDb = globFileDbsPtr->accessFileDb(globSet.dbsLocation, userName,
	    AccountListModel::globAccounts[userName].storeToDisk());
365 366 367 368 369 370 371 372
	if (fDb == NULL) {
		qDebug() << "ERROR: File database cannot open!" << userName;
		return false;
	}

	if (fDb->deleteFilesFromDb(msgId)) {
		MessageDb *msgDb = NULL;
		msgDb = globMessageDbsPtr->accessMessageDb(globSet.dbsLocation,
373
		    userName, AccountListModel::globAccounts[userName].storeToDisk());
374 375 376 377 378 379 380 381 382
		if (msgDb == NULL) {
			qDebug() << "ERROR: Message database cannot open!";
			return false;
		}
		return msgDb->setAttachmentDownloaded(msgId, false);
	}

	return false;
}
383

384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
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);
}

431 432
MsgInfo *Files::zfoData(const QVariant &attachModelVariant,
    const QByteArray &rawZfoData)
433
{
434
	enum MsgInfo::ZfoType type = MsgInfo::TYPE_UNKNOWN;
435
	QString idStr, annot, htmlDescr, emailBody;
436

437
	bool ret = parseXmlData(&type, &idStr, &annot, &htmlDescr,
438
	    FileListModel::fromVariant(attachModelVariant),
439
	    &emailBody, getXmlFromCms(rawZfoData));
440

441
	return ret ?
442
	    new (std::nothrow) MsgInfo(type, idStr, annot, htmlDescr,
443
	        emailBody) :
444
	    new (std::nothrow) MsgInfo();
445 446 447 448 449 450 451
}

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

452 453
	FileDb *fDb = globFileDbsPtr->accessFileDb(globSet.dbsLocation,
	    userName, AccountListModel::globAccounts[userName].storeToDisk());
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469

	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. */
470
	const FileListModel *attachModel =
471
	    FileListModel::fromVariant(attachModelVariant);
472 473 474 475 476 477 478 479 480 481 482 483 484 485
	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;

486 487
	const QString boundary = generateBoundaryString();
	QString emailMessage = createEmailMessage(body, subject,  boundary);
488

489
	deleteFilesFromDir(EMAIL_DIR_NAME);
490 491 492 493 494 495 496 497 498 499 500

	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());
501 502
		QString filePath(writeFileToEmailDir(attachName, msgId,
		    base64ToRaw(base64Data)));
503 504 505 506 507 508 509 510 511 512
		fileList.append(filePath);
		addAttachmentToEmailMessage(emailMessage, attachName,
		    base64Data, boundary);
	}

	finishEmailMessage(emailMessage, boundary);

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

513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
void Files::saveAttachmentsToDiskDb(const QString &userName,
    const QString &msgIdStr)
{
	qDebug("%s()", __func__);

	/* 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;
	}

	QList<FileDb::FileData> filelist;
	{
		FileDb *fDb = Q_NULLPTR;
		fDb = globFileDbsPtr->accessFileDb(globSet.dbsLocation, userName,
		    AccountListModel::globAccounts[userName].storeToDisk());

		if (fDb == Q_NULLPTR) {
			qCritical() << "Cannot open file database!";
			return;
		}

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

	}

	QString destPath;
	foreach (const FileDb::FileData &file, filelist) {
552
		destPath = writeFileToDir(fileSaveLocation(), file.filename,
553
		    msgId, base64ToRaw(file.content.toUtf8()));
554 555 556 557 558 559 560 561 562 563 564 565
	}

	attachmentSavingNotification(destPath);
}

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

	/* Obtain pointer to attachment model. */
	const FileListModel *attachModel =
566
	    FileListModel::fromVariant(attachModelVariant);
567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
	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));
582
		destPath = writeFileToDir(fileSaveLocation(),
583 584
		    attachModel->data(idx,
		        FileListModel::ROLE_FILE_NAME).toString(),
585 586 587
		    msgId,
		    base64ToRaw(attachModel->data(idx,
		        FileListModel::ROLE_FILE_DATA).toByteArray()));
588 589 590 591 592
	}

	attachmentSavingNotification(destPath);
}

593 594 595 596 597 598 599 600 601 602
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 */
}

603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
void Files::sendEmail(const QString &emailMessage, const QStringList &fileList,
    const QString &subject, const QString &body, qint64 msgId)
{
	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
		QString tmpEmailFile = writeFileToEmailDir(QString::number(msgId)
		    + "_mail.eml", msgId, emailMessage.toUtf8());
		QDesktopServices::openUrl(QUrl::fromLocalFile(tmpEmailFile));
#endif
		/* TODO -- Handle openUrl() return value. */

	} else {
		qDebug() << "Tmp file save error";
	}
627 628 629 630 631

	Q_UNUSED(subject);
	Q_UNUSED(body);
	Q_UNUSED(emailMessage);
	Q_UNUSED(msgId);
632 633
}

634
QByteArray Files::getXmlFromCms(const QByteArray &rawData)
635 636 637
{
	qDebug("%s()", __func__);

638 639 640
	if (rawData.isEmpty()) {
		Q_ASSERT(0);
		qCritical() << "File content is empty!";
641 642 643
		return QByteArray();
	}

644
	/* Decode CMS and obtain message XML data - uses OpenSSL. */
645 646
	void *xmlContent = NULL;
	size_t xmlContentLen = 0;
647
	if (extract_cms_data(rawData.data(), rawData.length(), &xmlContent,
Martin Straka's avatar
Martin Straka committed
648
	        &xmlContentLen) != 0) {
649 650 651 652 653 654 655
		return QByteArray();
	}
	if (xmlContentLen == 0) {
		free(xmlContent); xmlContent = NULL;
		return QByteArray();
	}

656
	QByteArray soap((char *)xmlContent, xmlContentLen);
657 658 659 660 661
	free(xmlContent); xmlContent = NULL;

	return soap;
}

662
QByteArray Files::decodeZfoFile(const QByteArray &base64ZfoData)
Martin Straka's avatar
Martin Straka committed
663 664 665
{
	qDebug("%s()", __func__);

666 667 668
	if (base64ZfoData.isEmpty()) {
		Q_ASSERT(0);
		qCritical() << "File content is empty.";
Martin Straka's avatar
Martin Straka committed
669 670 671
		return QByteArray();
	}

672
	/* decode signature from base64 and obtain something CMS message */
673
	return getXmlFromCms(base64ToRaw(base64ZfoData));
Martin Straka's avatar
Martin Straka committed
674 675
}

676
bool Files::parseXmlData(enum MsgInfo::ZfoType *type, QString *idStr,
677 678
    QString *annotation, QString *msgDescrHtml, FileListModel *attachModel,
    QString *emailBody, QByteArray xmlData)
679 680 681
{
	qDebug("%s()", __func__);

682 683
	if (xmlData.isEmpty()) {
		qCritical() << "XML content is empty!";
684
		return false;
685 686
	}

687
	xmlData.prepend("<?xml version='1.0' encoding='utf-8'?>"
688 689 690 691
	      "<SOAP-ENV:Envelope "
	      "xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
	      "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">"
	      "<SOAP-ENV:Body>");
692
	xmlData.append("</SOAP-ENV:Body></SOAP-ENV:Envelope>");
693

694
	/* Test if zfo is message, delivery info or unknown format */
695
	if (xmlData.contains(QByteArray("MessageDownloadResponse"))) {
696 697
		if (type != Q_NULLPTR) {
			*type = MsgInfo::TYPE_MESSAGE;
698
		}
699 700
		return parseAndShowXmlData(MsgInfo::TYPE_MESSAGE, idStr,
		    annotation, msgDescrHtml, attachModel, emailBody, xmlData);
701
	} else if (xmlData.contains(QByteArray("GetDeliveryInfoResponse"))) {
702 703
		if (type != Q_NULLPTR) {
			*type = MsgInfo::TYPE_DELIVERY_INFO;
704
		}
705 706
		return parseAndShowXmlData(MsgInfo::TYPE_DELIVERY_INFO, idStr,
		    annotation, msgDescrHtml, attachModel, emailBody, xmlData);
707
	} else {
708 709 710 711
		if (type != Q_NULLPTR) {
			*type = MsgInfo::TYPE_UNKNOWN;
		}
		qCritical() << "Unknown ZFO format";
712 713
	}

714
	return false;
715 716
}

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

723 724
	QXmlStreamReader xml;
	XmlLayer xmlLayer;
725
	MsgEnvelope msg;
726
	QList<AttachmentData> fileList;
727
	QList<Messages::Event> eventList;
728

729 730 731 732 733
	if (type == MsgInfo::TYPE_UNKNOWN) {
		Q_ASSERT(0);
		return false;
	}

734 735
	/* parse message envelope and files */
	xml.addData(xmlData);
736 737 738
	while(!xml.atEnd() && !xml.hasError()) {
		QXmlStreamReader::TokenType token = xml.readNext();
		if (xml.error() != QXmlStreamReader::NoError) {
739
			return false;
740 741 742 743 744 745
		}
		if (token == QXmlStreamReader::StartDocument) {
			continue;
		}
		if (token == QXmlStreamReader::StartElement) {
			if (xml.name() == "dmDm") {
746
				xmlLayer.completeMessageParse(xml, msg, fileList);
747 748 749 750
			}
		}
	}

751
	if (type == MsgInfo::TYPE_DELIVERY_INFO) {
752 753 754 755 756 757 758 759 760 761 762 763 764 765
		/* 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") {
					eventList.append(xmlLayer.parseEvent(xml));
				}
			}
		}
766 767 768 769 770
	}

	QString html = divStart;

	html += "<h3>" + QObject::tr("General") + "</h3>";
771 772
	html += strongInfoLine(QObject::tr("Subject"), msg.dmAnnotation());
	QString size = QString::number(msg.dmAttachmentSize());
773 774 775
	html += strongInfoLine(QObject::tr("Attachment size"),
	    (size == "0") ? "&lt;1 KB" : "~" + size + " KB");
	html += strongInfoLine(QObject::tr("Personal delivery"),
776
	    (msg.dmPersonalDelivery()) ? QObject::tr("Yes") : QObject::tr("No"));
777
	html += strongInfoLine(QObject::tr("Delivery by fiction"),
778
	    (msg.dmAllowSubstDelivery()) ? QObject::tr("Yes") : QObject::tr("No"));
779 780

	html += "<h3>" + QObject::tr("Sender") + "</h3>";
781 782 783
	html += strongInfoLine(QObject::tr("Databox ID"), msg.dbIDSender());
	html += strongInfoLine(QObject::tr("Name"), msg.dmSender());
	html += strongInfoLine(QObject::tr("Address"),msg.dmSenderAddress());
784 785

	html += "<h3>" + QObject::tr("Recipient") + "</h3>";
786 787 788 789
	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()) {
790
		html += strongInfoLine(QObject::tr("To hands"),
791
		    msg.dmToHands());
792
	}
793

794
	QString tmpHtml;
795
	if (!msg.dmSenderIdent().isEmpty()) {
796
		tmpHtml += strongInfoLine(QObject::tr("Our file mark"),
797
		    msg.dmSenderIdent());
798
	}
799
	if (!msg.dmSenderRefNumber().isEmpty()) {
800
		tmpHtml += strongInfoLine(QObject::tr("Our reference number"),
801
		    msg.dmSenderRefNumber());
802
	}
803
	if (!msg.dmRecipientIdent().isEmpty()) {
804
		tmpHtml += strongInfoLine(QObject::tr("Your file mark"),
805
		    msg.dmRecipientIdent());
806
	}
807
	if (!msg.dmRecipientRefNumber().isEmpty()) {
808
		tmpHtml += strongInfoLine(QObject::tr("Your reference number"),
809
		    msg.dmRecipientRefNumber());
810
	}
811
	if (!msg.dmLegalTitleLaw().isEmpty()) {
812
		tmpHtml += strongInfoLine(QObject::tr("Law"),
813
		    msg.dmLegalTitleLaw());
814
	}
815
	if (!msg.dmLegalTitleYear().isEmpty()) {
816
		tmpHtml += strongInfoLine(QObject::tr("Year"),
817
		    msg.dmLegalTitleYear());
818
	}
819
	if (!msg.dmLegalTitleSect().isEmpty()) {
820
		tmpHtml += strongInfoLine(QObject::tr("Section"),
821
		    msg.dmLegalTitleSect());
822
	}
823
	if (!msg.dmLegalTitlePar().isEmpty()) {
824
		tmpHtml += strongInfoLine(QObject::tr("Paragraph"),
825
		    msg.dmLegalTitlePar());
826
	}
827
	if (!msg.dmLegalTitlePoint().isEmpty()) {
828
		tmpHtml += strongInfoLine(QObject::tr("Letter"),
829
		    msg.dmLegalTitlePoint());
830 831 832 833 834
	}
	if (!tmpHtml.isEmpty()) {
		html += "<h3>" + QObject::tr("Additional info") + "</h3>";
		html += tmpHtml;
	}
835 836 837 838

	html += "<h3>" + QObject::tr("Message state") + "</h3>";
	html += strongInfoLine(QObject::tr("Delivery time"),
	    dateTimeStrFromDbFormat(
839
	        dateTimeStrToUTCDbFormat(msg.dmDeliveryTime()),
840 841 842
	        DATETIME_QML_FORMAT));
	html += strongInfoLine(QObject::tr("Accetance time"),
	    dateTimeStrFromDbFormat(
843
	        dateTimeStrToUTCDbFormat(msg.dmAcceptanceTime()),
844 845
	        DATETIME_QML_FORMAT));
	html += strongInfoLine(QObject::tr("Status"),
846
	    QString::number(msg.dmMessageStatus()));
847

848
	if (type == MsgInfo::TYPE_DELIVERY_INFO) {
849 850 851 852 853 854 855 856 857 858
		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;
		}
	}

859 860
	html += divEnd;

861
	// Create body for email
862 863 864 865
	QString body = generateEmailBodyText(msg.dmID(), msg.dmSender(),
	    msg.dmRecipient(), dateTimeStrFromDbFormat(
	    dateTimeStrToUTCDbFormat(msg.dmAcceptanceTime()),
	    DATETIME_QML_FORMAT));
866

867
	if (idStr != Q_NULLPTR) {
868
		*idStr = QString::number(msg.dmID());
869
	}
870
	if (annotation != Q_NULLPTR) {
871
		*annotation = msg.dmAnnotation();
872 873 874 875 876
	}
	if (msgDescrHtml != Q_NULLPTR) {
		*msgDescrHtml = html;
	}
	if (attachModel != Q_NULLPTR) {
877 878 879 880 881
		attachModel->clearAll();
		foreach (const AttachmentData &file, fileList) {
			attachModel->appendFileEntry(FileListModel::Entry(-1, file.dmFileDescr(),
			    file._dmFileSize(), file._icon(), file.dmEncodedContent(), QString()));
		}
882 883 884 885
	}
	if (emailBody != Q_NULLPTR) {
		*emailBody = body;
	}
886

887 888
	return true;
}