/* * 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 #include #include #include #include "src/files.h" #include "src/common.h" #include "src/settings.h" #include "src/io/filesystem.h" #include "ios/src/url_opener.h" #include "src/models/accountmodel.h" #include "src/sqlite/file_db_container.h" #include "src/sqlite/message_db_container.h" #include "src/net/xml_layer.h" #include "src/sqlite/dbs.h" #ifndef Q_OS_WIN #include "src/crypto/crypto.h" #endif /* TODO -- These MUST be removed. */ QStack> stackFileList; QStack stackMessageDetail; /*! * @brief Creates email header and message body. * * @param[in,out] message String to append data to. * @param[in] body Email body. * @param[in] subj Subject text. * @param[in] boundary Boundary to be used, */ static void createEmailMessage(QString &message, const QString &body, const QString &subj, const QString &boundary) { message.clear(); const QString newLine("\n"); /* "\r\n" ? */ /* Rudimentary header. */ message += "Subject: " + subj + newLine; message += "MIME-Version: 1.0" + newLine; message += "Content-Type: multipart/mixed;" + newLine + " boundary=\"" + boundary + "\"" + newLine; /* Body. */ message += newLine; message += "--" + boundary + newLine; message += "Content-Type: text/plain; charset=UTF-8" + newLine; message += "Content-Transfer-Encoding: 8bit" + newLine; message += newLine; message += "-- " + newLine; /* Must contain the space. */ message += body + newLine; } /*! * @brief Adds attachment into email. * * @param[in,out] message String to append data to. * @param[in] attachName Attachment name. * @param[in] base64 Base64-encoded attachment. * @param[in] boundary Boundary to be used. */ static void addAttachmentToEmailMessage(QString &message, const QString &attachName, const QByteArray &base64, const QString &boundary) { const QString newLine("\n"); /* "\r\n" ? */ QMimeDatabase mimeDb; QMimeType mimeType( mimeDb.mimeTypeForData(QByteArray::fromBase64(base64))); message += newLine; message += "--" + boundary + newLine; message += "Content-Type: " + mimeType.name() + "; charset=UTF-8;" + newLine + " name=\"" + attachName + "\"" + newLine; message += "Content-Transfer-Encoding: base64" + newLine; message += "Content-Disposition: attachment;" + newLine + " filename=\"" + attachName + "\"" + newLine; for (int i = 0; i < base64.size(); ++i) { if ((i % 60) == 0) { message += newLine; } message += base64.at(i); } message += newLine; } /*! * @brief Adds last line into email. * * @param[in,out] message String to append data to. * @param[in] boundary Boundary to be used. */ static void finishEmailMessage(QString &message, const QString &boundary) { const QString newLine("\n"); /* "\r\n" ? */ message += newLine + "--" + boundary + "--" + newLine; } Files::Files(QObject *parent) : QObject(parent) { } void Files::clearFileModel(void) { globFilesModel.clearAll(); } /*! * @brief Set attachment model content. * * @param[in,out] model Model to be set. * @param[in] fileList Attachment list to be added into the model. */ static void setZfoFilesToModel(FileListModel &model, const QList &fileList) { model.clearAll(); foreach (const Files::File &file, fileList) { model.appendFileEntry(FileListModel::Entry(-1, file.dmFileDescr, file._dmFileSize, file._icon, file.dmEncodedContent)); } } 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(); } } void Files::openAttachmentFromDb(const QString &userName, qint64 msgId, int fileId) { qDebug("%s()", __func__); if (userName.isEmpty() || msgId <= 0 || fileId < 0) { return; } FileDb *fDb = NULL; fDb = globFileDbsPtr->accessFileDb(globSet.dbsLocation, userName, AccountListModel::globAccounts[userName].storeToDisk()); if (fDb == NULL) { qDebug() << "ERROR: File database cannot open!"; return; } FileDb::FileData file = fDb->getFileContentFromDb(fileId); QString fileName = file.filename; Q_ASSERT(!fileName.isEmpty()); if (fileName.isEmpty()) { qDebug() << "ERROR: File name is empty!"; return; } openAttachment(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; } QByteArray rawData(QByteArray::fromBase64(base64Data)); QString filePath(writeFileToTmpDir(fileName, TEMP_DIR_NAME, rawData)); if (!filePath.isEmpty()) { qInfo() << "Creating temporary file" << filePath; #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))) { QMessageBox msgBox; msgBox.setIcon(QMessageBox::Critical); msgBox.setWindowTitle(tr("Open attachment error")); msgBox.setText(tr("There is no application to open this file format.")); msgBox.setInformativeText(tr("File: '%1'").arg(filePath)); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); msgBox.exec(); } #endif /* defined Q_OS_IOS */ } else { qCritical() << "Cannot create temporary file for" << fileName; QMessageBox msgBox; msgBox.setIcon(QMessageBox::Critical); msgBox.setWindowTitle(tr("Open attachment error")); msgBox.setText(tr("Cannot save selected file to disk for opening.")); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); msgBox.exec(); } } void Files::sendAttachmentsWithEmailFromDb(const QString &userName, qint64 msgId) { qDebug("%s()", __func__); if (userName.isEmpty() || msgId <= 0) { return; } QString body; QString subject; QStringList fileList; QString emailMessage; const QString boundary("-----123456789asdfghj_" + QDateTime::currentDateTimeUtc().toString( "dd.MM.yyyy-HH:mm:ss.zzz")); 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->getMessageDataForEmail(msgId, body, subject)) { qDebug() << "ERROR: Message data mssing!"; return; } FileDb *fDb = NULL; 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; } createEmailMessage(emailMessage, body, subject, boundary); deleteFilesFromDir(EMAIL_DIR_NAME); QString fileName; foreach (const FileDb::FileData &file, filelist) { fileName = file.filename; if (fileName.isEmpty()) { qDebug() << "ERROR: File name is empty!"; return; } QByteArray data = QByteArray::fromBase64(file.content.toUtf8()); fileName = writeFileToEmailDir(fileName, msgId, data); fileList.append(fileName); addAttachmentToEmailMessage(emailMessage, file.filename, file.content.toUtf8(), boundary); qDebug() << fileName; } finishEmailMessage(emailMessage, boundary); sendEmail(emailMessage, fileList, subject, body, msgId); } void Files::saveAttachmentsToDisk(const QString &userName, qint64 msgId) { qDebug("%s()", __func__); QList filelist; if (!userName.isEmpty()) { FileDb *fDb = NULL; fDb = globFileDbsPtr->accessFileDb(globSet.dbsLocation, userName, AccountListModel::globAccounts[userName].storeToDisk()); if (fDb == NULL) { qDebug() << "ERROR: File database cannot open!"; return; } filelist = fDb->getFilesFromDb(msgId); if (filelist.isEmpty()) { qDebug() << "ERROR: File list is empty!"; return; } } QString path = getBasePathBasedOnPlatform(); #if defined Q_OS_IOS /* There is only one location where files or data can store * permanently and share with Desktop. * See: https://developer.apple.com/library/content/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html */ path = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); #else /* Another platform can select destination for attachment saving. */ path = QFileDialog::getExistingDirectory(0, tr("Select directory"), path, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); #endif QString destPath; if (userName.isEmpty()) { for (int i = 0; i < globFilesModelZfo.rowCount(); ++i) { QModelIndex index = globFilesModelZfo.index(i); QByteArray data = QByteArray::fromBase64( globFilesModelZfo.data( index, FileListModel::ROLE_FILE_DATA).toByteArray()); destPath = writeFileToDir(path, globFilesModelZfo.data( index, FileListModel::ROLE_FILE_NAME).toString(), msgId, data); } } else { foreach (const FileDb::FileData &file, filelist) { QByteArray data = QByteArray::fromBase64(file.content.toUtf8()); destPath = writeFileToDir(path, file.filename, msgId, data); } } QFileInfo fi(destPath); QMessageBox msgBox; msgBox.setWindowTitle(tr("Attachment saving")); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); if (!destPath.isEmpty()) { msgBox.setIcon(QMessageBox::Information); msgBox.setText(tr("Attachments have been saved.")); msgBox.setInformativeText(tr("Path: '%1'").arg(fi.absolutePath())); } else { msgBox.setIcon(QMessageBox::Critical); msgBox.setText(tr("Attachments have not been saved!")); msgBox.setInformativeText(tr("Path: '%1'").arg(fi.absolutePath())); } msgBox.exec(); } void Files::deleteFileDb(const QString &userName) { qDebug("%s()", __func__); QMessageBox msgBox; msgBox.setWindowTitle(tr("Delete files: %1").arg(userName)); msgBox.setIcon(QMessageBox::Question); msgBox.setText(tr("Do you want to clean up the file database " "of account '%1'?").arg(userName)); msgBox.setInformativeText(tr("Note: All attachment files of messages " "will be removed from the database.")); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::No); if (msgBox.exec() == QMessageBox::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); } QString Files::msgIdString(const QByteArray &rawZfoData) { QString msgId; bool ret = parseXmlData(&msgId, Q_NULLPTR, Q_NULLPTR, Q_NULLPTR, Q_NULLPTR, getXmlFromCms(rawZfoData)); return ret ? msgId : QString(); } QString Files::msgAnnotation(const QByteArray &rawZfoData) { QString annotation; bool ret = parseXmlData(Q_NULLPTR, &annotation, Q_NULLPTR, Q_NULLPTR, Q_NULLPTR, getXmlFromCms(rawZfoData)); return ret ? annotation : QString(); } QString Files::msgDescriptionHtml(const QByteArray &rawZfoData) { QString htmlDescr; bool ret = parseXmlData(Q_NULLPTR, Q_NULLPTR, &htmlDescr, Q_NULLPTR, Q_NULLPTR, getXmlFromCms(rawZfoData)); return ret ? htmlDescr : QString(); } QString Files::msgEmailBody(const QByteArray &rawZfoData) { QString emailBody; bool ret = parseXmlData(Q_NULLPTR, Q_NULLPTR, Q_NULLPTR, Q_NULLPTR, &emailBody, getXmlFromCms(rawZfoData)); return ret ? emailBody : QString(); } bool Files::setAttachmentModel(FileListModel &attachModel, const QByteArray &rawZfoData) { return parseXmlData(Q_NULLPTR, Q_NULLPTR, Q_NULLPTR, &attachModel, Q_NULLPTR, getXmlFromCms(rawZfoData)); } bool Files::setAttachmentModel(FileListModel &attachModel, const QString &userName, qint64 msgId) { qDebug("%s()", __func__); FileDb *fDb = Q_NULLPTR; 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 = Q_NULLPTR; { if (!attachModelVariant.canConvert()) { return; } QObject *obj = qvariant_cast(attachModelVariant); attachModel = qobject_cast(obj); } 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; QString emailMessage; QString boundary("-----123456789asdfghj_" + QDateTime::currentDateTimeUtc().toString( "dd.MM.yyyy-HH:mm:ss.zzz")); createEmailMessage(emailMessage, body, subject, boundary); deleteFilesFromDir(EMAIL_DIR_NAME); QString filePath; QByteArray data; 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()); QByteArray rawData(QByteArray::fromBase64(base64Data)); QString attachName(attachModel->data(idx, FileListModel::ROLE_FILE_NAME).toString()); QString filePath( writeFileToEmailDir(attachName, msgId, rawData)); fileList.append(filePath); addAttachmentToEmailMessage(emailMessage, attachName, base64Data, boundary); } finishEmailMessage(emailMessage, boundary); sendEmail(emailMessage, fileList, subject, body, msgId); } 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) { Q_UNUSED(subject); Q_UNUSED(body); Q_UNUSED(emailMessage); Q_UNUSED(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"; } } 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(QByteArray::fromBase64(base64ZfoData)); } bool Files::parseXmlData(QString *idStr, QString *annotation, QString *msgDescrHtml, FileListModel *attachModel, QString *emailBody, QByteArray xmlData) { qDebug("%s()", __func__); bool success = false; if (xmlData.isEmpty()) { Q_ASSERT(0); qCritical() << "XML content is empty!"; return success; } xmlData.prepend("" "" ""); xmlData.append(""); //qDebug() << xmlData; QXmlStreamReader xml; xml.addData(xmlData); XmlLayer xmlLayer; Messages::Message msg; QList fileList; while(!xml.atEnd() && !xml.hasError()) { QXmlStreamReader::TokenType token = xml.readNext(); if (xml.error() != QXmlStreamReader::NoError) { return success; } if (token == QXmlStreamReader::StartDocument) { continue; } if (token == QXmlStreamReader::StartElement) { if (xml.name() == "dmDm") { success = xmlLayer.completeMessageParse(xml, msg, fileList); } } } if (!success) { return success; } 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)); html += divEnd; // Create body for email QString body = QObject::tr("ID") + ": "; body += QString::number(msg.dmID) + "\n"; body += QObject::tr("FROM") + ": "; body += msg.dmSender + "\n"; body += QObject::tr("TO") + ": "; body += msg.dmRecipient + "\n"; body += QObject::tr("DELIVERED") + ": "; body += dateTimeStrFromDbFormat(dateTimeStrToUTCDbFormat(msg.dmAcceptanceTime), DATETIME_QML_FORMAT) + "\n\n---\n"; body += QObject::tr("This email has been generated with Datovka " "application based on a data message (%1) delivered " "to databox.").arg(msg.dmID); body += "\n"; 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) { setZfoFilesToModel(*attachModel, fileList); } if (emailBody != Q_NULLPTR) { *emailBody = body; } // remember file list and message detail stackFileList.push(fileList); stackMessageDetail.push(html); return success; }