/* * Copyright (C) 2014-2017 CZ.NIC * * 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 . * * 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 #include #include #include #include /* qmlRegisterType */ #include "ios/src/url_opener.h" #include "src/auxiliaries/attachment_helper.h" #include "src/auxiliaries/email_helper.h" #include "src/common.h" #include "src/dialogues/dialogues.h" #include "src/files.h" #include "src/io/filesystem.h" #include "src/models/accountmodel.h" #include "src/net/xml_layer.h" #include "src/qml_interaction/attachment_data.h" #include "src/qml_interaction/message_envelope.h" #include "src/settings.h" #include "src/sqlite/file_db_container.h" #include "src/sqlite/message_db_container.h" #include "src/sqlite/dbs.h" #ifndef Q_OS_WIN #include "src/crypto/crypto.h" #endif Files::Files(QObject *parent) : QObject(parent) { } void Files::declareQML(void) { qmlRegisterType("cz.nic.mobileDatovka.files", 1, 0, "FileIdType"); qRegisterMetaType(); } void Files::attachmentSavingNotification(const QString &destPath) { 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())); } QString Files::fileSaveLocation(void) { return QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); } void Files::deleteExpiredFilesFromDbs(int days) { qDebug("%s()", __func__); QStringList msgIDList; QStringList userNameList = AccountListModel::globAccounts.keys(); foreach (const QString &userName, userNameList) { FileDb *fDb = NULL; fDb = globFileDbsPtr->accessFileDb(globSet.dbsLocation, userName, AccountListModel::globAccounts[userName].storeToDisk()); if (fDb == NULL) { qDebug() << "ERROR: File database cannot open!" << userName; return; } msgIDList = fDb->cleanFilesInDb(days); if (msgIDList.isEmpty()) { continue; } MessageDb *msgDb = NULL; msgDb = globMessageDbsPtr->accessMessageDb(globSet.dbsLocation, userName, AccountListModel::globAccounts[userName].storeToDisk()); if (msgDb == NULL) { qDebug() << "ERROR: Message database cannot open!"; return; } msgDb->beginTransaction(); foreach (const QString &msgId, msgIDList) { msgDb->setAttachmentDownloaded(msgId.toLongLong(), false); } msgDb->commitTransaction(); } } QString Files::getAttachmentFileIcon(const QString &fileName) { return getAttachmentFileIconFromFileExtension(fileName); } qint64 Files::getAttachmentSizeInBytes(const QString &filePath) { QFileInfo fileInfo(filePath); return fileInfo.size(); } QByteArray Files::getFileRawContentFromDb(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 QByteArray(); } FileDb *fDb = globFileDbsPtr->accessFileDb(globSet.dbsLocation, userName, AccountListModel::globAccounts[userName].storeToDisk()); if (fDb == Q_NULLPTR) { qCritical() << "Cannot open file database!"; return QByteArray(); } FileDb::FileData file = fDb->getFileContentFromDb(fileId); return base64ToRaw(file.content.toUtf8()); } 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; } FileDb *fDb = globFileDbsPtr->accessFileDb(globSet.dbsLocation, userName, AccountListModel::globAccounts[userName].storeToDisk()); if (fDb == Q_NULLPTR) { qCritical() << "Cannot open file database!"; return; } FileDb::FileData file = fDb->getFileContentFromDb(fileId); openAttachment(file.filename, file.content.toUtf8()); } void Files::openAttachment(const QString &fileName, const QByteArray &base64Data) { Q_ASSERT(!fileName.isEmpty()); Q_ASSERT(!base64Data.isEmpty()); if (fileName.isEmpty() || base64Data.isEmpty()) { qCritical() << "File name or its content is empty!"; return; } if (isZfoFile(fileName)) { /* Don't open zfo files from here. */ Q_ASSERT(0); qCritical() << "This should open ZFO files by itself."; return; } QString filePath(writeFileToTmpDir(fileName, TEMP_DIR_NAME, base64ToRaw(base64Data))); if (!filePath.isEmpty()) { qInfo() << "Creating temporary file" << filePath; 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; } #if defined Q_OS_IOS UrlOpener urlOpener; urlOpener.openFile(filePath); /* * TODO - we may check exceptions form objective-C (openFile) * and return as bool. */ #else /* !defined Q_OS_IOS */ 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 /* defined Q_OS_IOS */ } void Files::sendAttachmentsWithEmail(const QString &userName, qint64 msgId) { qDebug("%s()", __func__); if (userName.isEmpty() || msgId <= 0) { return; } QString body; QString subject; QStringList fileList; MessageDb *msgDb = globMessageDbsPtr->accessMessageDb(globSet.dbsLocation, userName, AccountListModel::globAccounts[userName].storeToDisk()); if (msgDb == NULL) { qDebug() << "ERROR: Message database cannot open!"; return; } if (!msgDb->getMessageDataForEmail(msgId, body, subject)) { qDebug() << "ERROR: Message data mssing!"; return; } FileDb *fDb = globFileDbsPtr->accessFileDb(globSet.dbsLocation, userName, AccountListModel::globAccounts[userName].storeToDisk()); if (fDb == NULL) { qDebug() << "ERROR: File database cannot open!"; return; } QList filelist; filelist = fDb->getFilesFromDb(msgId); if (filelist.isEmpty()) { qDebug() << "ERROR: File list is empty!"; return; } const QString boundary = generateBoundaryString(); QString emailMessage = createEmailMessage(body, subject, boundary); deleteFilesFromDir(EMAIL_DIR_NAME); foreach (const FileDb::FileData &file, filelist) { QString fileName = file.filename; if (fileName.isEmpty()) { qDebug() << "ERROR: File name is empty!"; return; } fileName = writeFileToEmailDir(fileName, msgId, base64ToRaw(file.content.toUtf8())); fileList.append(fileName); addAttachmentToEmailMessage(emailMessage, file.filename, file.content.toUtf8(), boundary); qDebug() << fileName; } finishEmailMessage(emailMessage, boundary); sendEmail(emailMessage, fileList, subject, body, msgId); } void Files::deleteFileDb(const QString &userName) { qDebug("%s()", __func__); int msgResponse = Dialogues::message(Dialogues::QUESTION, 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."), Dialogues::NO | Dialogues::YES, Dialogues::NO); if (msgResponse == Dialogues::NO) { return; } FileDb *fDb = NULL; fDb = globFileDbsPtr->accessFileDb(globSet.dbsLocation, userName, AccountListModel::globAccounts[userName].storeToDisk()); 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; msgDb = globMessageDbsPtr->accessMessageDb(globSet.dbsLocation, userName, AccountListModel::globAccounts[userName].storeToDisk()); if (msgDb == NULL) { qDebug() << "ERROR: Message database cannot open!"; return; } if (!msgDb->setAttachmentsDownloaded(false)) { qDebug() << "ERROR: Message data mssing!"; return; } } void Files::vacuumFileDbs(void) { qDebug("%s()", __func__); emit statusBarTextChanged(tr("Vacuum databases"), true); QStringList userNameList = AccountListModel::globAccounts.keys(); foreach (const QString &userName, userNameList) { FileDb *fDb = NULL; fDb = globFileDbsPtr->accessFileDb(globSet.dbsLocation, userName, AccountListModel::globAccounts[userName].storeToDisk()); if (fDb == NULL) { qDebug() << "ERROR: File database cannot open!" << userName; return; } fDb->vacuumFileDb(); } emit statusBarTextChanged(tr("Operation Vacuum has finished"), false); } bool Files::deleteAttachmentsFromDb(const QString &userName, qint64 msgId) { qDebug("%s()", __func__); FileDb *fDb = NULL; fDb = globFileDbsPtr->accessFileDb(globSet.dbsLocation, userName, AccountListModel::globAccounts[userName].storeToDisk()); if (fDb == NULL) { qDebug() << "ERROR: File database cannot open!" << userName; return false; } if (fDb->deleteFilesFromDb(msgId)) { MessageDb *msgDb = NULL; msgDb = globMessageDbsPtr->accessMessageDb(globSet.dbsLocation, userName, AccountListModel::globAccounts[userName].storeToDisk()); if (msgDb == NULL) { qDebug() << "ERROR: Message database cannot open!"; return false; } return msgDb->setAttachmentDownloaded(msgId, false); } return false; } 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); } MsgInfo *Files::zfoData(const QVariant &attachModelVariant, const QByteArray &rawZfoData) { enum MsgInfo::ZfoType type = MsgInfo::TYPE_UNKNOWN; QString idStr, annot, htmlDescr, emailBody; bool ret = parseXmlData(&type, &idStr, &annot, &htmlDescr, FileListModel::fromVariant(attachModelVariant), &emailBody, getXmlFromCms(rawZfoData)); return ret ? new (std::nothrow) MsgInfo(type, idStr, annot, htmlDescr, emailBody) : new (std::nothrow) MsgInfo(); } bool Files::setAttachmentModel(FileListModel &attachModel, const QString &userName, qint64 msgId) { qDebug("%s()", __func__); FileDb *fDb = globFileDbsPtr->accessFileDb(globSet.dbsLocation, userName, AccountListModel::globAccounts[userName].storeToDisk()); 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. */ const FileListModel *attachModel = FileListModel::fromVariant(attachModelVariant); 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; const QString boundary = generateBoundaryString(); QString emailMessage = createEmailMessage(body, subject, boundary); deleteFilesFromDir(EMAIL_DIR_NAME); 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()); QString filePath(writeFileToEmailDir(attachName, msgId, base64ToRaw(base64Data))); fileList.append(filePath); addAttachmentToEmailMessage(emailMessage, attachName, base64Data, boundary); } finishEmailMessage(emailMessage, boundary); sendEmail(emailMessage, fileList, subject, body, msgId); } 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 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) { destPath = writeFileToDir(fileSaveLocation(), file.filename, msgId, base64ToRaw(file.content.toUtf8())); } attachmentSavingNotification(destPath); } void Files::saveAttachmentsToDiskZfo(const QVariant &attachModelVariant, const QString &msgIdStr) { qDebug("%s()", __func__); /* Obtain pointer to attachment model. */ const FileListModel *attachModel = FileListModel::fromVariant(attachModelVariant); 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)); destPath = writeFileToDir(fileSaveLocation(), attachModel->data(idx, FileListModel::ROLE_FILE_NAME).toString(), msgId, base64ToRaw(attachModel->data(idx, FileListModel::ROLE_FILE_DATA).toByteArray())); } attachmentSavingNotification(destPath); } 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 */ } 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"; } Q_UNUSED(subject); Q_UNUSED(body); Q_UNUSED(emailMessage); Q_UNUSED(msgId); } QByteArray Files::getXmlFromCms(const QByteArray &rawData) { qDebug("%s()", __func__); if (rawData.isEmpty()) { Q_ASSERT(0); qCritical() << "File content is empty!"; return QByteArray(); } /* Decode CMS and obtain message XML data - uses OpenSSL. */ void *xmlContent = NULL; size_t xmlContentLen = 0; if (extract_cms_data(rawData.data(), rawData.length(), &xmlContent, &xmlContentLen) != 0) { return QByteArray(); } if (xmlContentLen == 0) { free(xmlContent); xmlContent = NULL; return QByteArray(); } QByteArray soap((char *)xmlContent, xmlContentLen); free(xmlContent); xmlContent = NULL; return soap; } QByteArray Files::decodeZfoFile(const QByteArray &base64ZfoData) { qDebug("%s()", __func__); if (base64ZfoData.isEmpty()) { Q_ASSERT(0); qCritical() << "File content is empty."; return QByteArray(); } /* decode signature from base64 and obtain something CMS message */ return getXmlFromCms(base64ToRaw(base64ZfoData)); } bool Files::parseXmlData(enum MsgInfo::ZfoType *type, QString *idStr, QString *annotation, QString *msgDescrHtml, FileListModel *attachModel, QString *emailBody, QByteArray xmlData) { qDebug("%s()", __func__); if (xmlData.isEmpty()) { qCritical() << "XML content is empty!"; return false; } xmlData.prepend("" "" ""); xmlData.append(""); /* Test if zfo is message, delivery info or unknown format */ if (xmlData.contains(QByteArray("MessageDownloadResponse"))) { if (type != Q_NULLPTR) { *type = MsgInfo::TYPE_MESSAGE; } return parseAndShowXmlData(MsgInfo::TYPE_MESSAGE, idStr, annotation, msgDescrHtml, attachModel, emailBody, xmlData); } else if (xmlData.contains(QByteArray("GetDeliveryInfoResponse"))) { if (type != Q_NULLPTR) { *type = MsgInfo::TYPE_DELIVERY_INFO; } return parseAndShowXmlData(MsgInfo::TYPE_DELIVERY_INFO, idStr, annotation, msgDescrHtml, attachModel, emailBody, xmlData); } else { if (type != Q_NULLPTR) { *type = MsgInfo::TYPE_UNKNOWN; } qCritical() << "Unknown ZFO format"; } return false; } bool Files::parseAndShowXmlData(enum MsgInfo::ZfoType type, QString *idStr, QString *annotation, QString *msgDescrHtml, FileListModel *attachModel, QString *emailBody, QByteArray &xmlData) { qDebug("%s()", __func__); QXmlStreamReader xml; XmlLayer xmlLayer; MsgEnvelope msg; QList fileList; QList eventList; if (type == MsgInfo::TYPE_UNKNOWN) { Q_ASSERT(0); return false; } /* parse message envelope and files */ xml.addData(xmlData); while(!xml.atEnd() && !xml.hasError()) { QXmlStreamReader::TokenType token = xml.readNext(); if (xml.error() != QXmlStreamReader::NoError) { return false; } if (token == QXmlStreamReader::StartDocument) { continue; } if (token == QXmlStreamReader::StartElement) { if (xml.name() == "dmDm") { xmlLayer.completeMessageParse(xml, msg, fileList); } } } if (type == MsgInfo::TYPE_DELIVERY_INFO) { /* 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)); } } } } QString html = divStart; html += "

" + QObject::tr("General") + "

"; html += strongInfoLine(QObject::tr("Subject"), msg.dmAnnotation()); QString size = QString::number(msg.dmAttachmentSize()); html += strongInfoLine(QObject::tr("Attachment size"), (size == "0") ? "<1 KB" : "~" + size + " KB"); html += strongInfoLine(QObject::tr("Personal delivery"), (msg.dmPersonalDelivery()) ? QObject::tr("Yes") : QObject::tr("No")); html += strongInfoLine(QObject::tr("Delivery by fiction"), (msg.dmAllowSubstDelivery()) ? QObject::tr("Yes") : QObject::tr("No")); html += "

" + QObject::tr("Sender") + "

"; html += strongInfoLine(QObject::tr("Databox ID"), msg.dbIDSender()); html += strongInfoLine(QObject::tr("Name"), msg.dmSender()); html += strongInfoLine(QObject::tr("Address"),msg.dmSenderAddress()); html += "

" + QObject::tr("Recipient") + "

"; 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()) { html += strongInfoLine(QObject::tr("To hands"), msg.dmToHands()); } QString tmpHtml; if (!msg.dmSenderIdent().isEmpty()) { tmpHtml += strongInfoLine(QObject::tr("Our file mark"), msg.dmSenderIdent()); } if (!msg.dmSenderRefNumber().isEmpty()) { tmpHtml += strongInfoLine(QObject::tr("Our reference number"), msg.dmSenderRefNumber()); } if (!msg.dmRecipientIdent().isEmpty()) { tmpHtml += strongInfoLine(QObject::tr("Your file mark"), msg.dmRecipientIdent()); } if (!msg.dmRecipientRefNumber().isEmpty()) { tmpHtml += strongInfoLine(QObject::tr("Your reference number"), msg.dmRecipientRefNumber()); } if (!msg.dmLegalTitleLaw().isEmpty()) { tmpHtml += strongInfoLine(QObject::tr("Law"), msg.dmLegalTitleLaw()); } if (!msg.dmLegalTitleYear().isEmpty()) { tmpHtml += strongInfoLine(QObject::tr("Year"), msg.dmLegalTitleYear()); } if (!msg.dmLegalTitleSect().isEmpty()) { tmpHtml += strongInfoLine(QObject::tr("Section"), msg.dmLegalTitleSect()); } if (!msg.dmLegalTitlePar().isEmpty()) { tmpHtml += strongInfoLine(QObject::tr("Paragraph"), msg.dmLegalTitlePar()); } if (!msg.dmLegalTitlePoint().isEmpty()) { tmpHtml += strongInfoLine(QObject::tr("Letter"), msg.dmLegalTitlePoint()); } if (!tmpHtml.isEmpty()) { html += "

" + QObject::tr("Additional info") + "

"; html += tmpHtml; } html += "

" + QObject::tr("Message state") + "

"; html += strongInfoLine(QObject::tr("Delivery time"), dateTimeStrFromDbFormat( dateTimeStrToUTCDbFormat(msg.dmDeliveryTime()), DATETIME_QML_FORMAT)); html += strongInfoLine(QObject::tr("Accetance time"), dateTimeStrFromDbFormat( dateTimeStrToUTCDbFormat(msg.dmAcceptanceTime()), DATETIME_QML_FORMAT)); html += strongInfoLine(QObject::tr("Status"), QString::number(msg.dmMessageStatus())); if (type == MsgInfo::TYPE_DELIVERY_INFO) { html += "

" + QObject::tr("Events") + "

"; foreach (const Messages::Event &event, eventList) { html += divStart + strongInfoLine(dateTimeStrFromDbFormat( dateTimeStrToUTCDbFormat(event.dmEventTime), DATETIME_QML_FORMAT), event.dmEventDescr) + divEnd; } } html += divEnd; // Create body for email QString body = generateEmailBodyText(msg.dmID(), msg.dmSender(), msg.dmRecipient(), dateTimeStrFromDbFormat( dateTimeStrToUTCDbFormat(msg.dmAcceptanceTime()), DATETIME_QML_FORMAT)); if (idStr != Q_NULLPTR) { *idStr = QString::number(msg.dmID()); } if (annotation != Q_NULLPTR) { *annotation = msg.dmAnnotation(); } if (msgDescrHtml != Q_NULLPTR) { *msgDescrHtml = html; } if (attachModel != Q_NULLPTR) { attachModel->clearAll(); foreach (const AttachmentData &file, fileList) { attachModel->appendFileEntry(FileListModel::Entry(-1, file.dmFileDescr(), file._dmFileSize(), file._icon(), file.dmEncodedContent(), QString())); } } if (emailBody != Q_NULLPTR) { *emailBody = body; } return true; }