Page MenuHome GnuPG

No OneTemporary

diff --git a/client/reencrypt/reencryptjob.cpp b/client/reencrypt/reencryptjob.cpp
index 64d93e3..5c11ba4 100644
--- a/client/reencrypt/reencryptjob.cpp
+++ b/client/reencrypt/reencryptjob.cpp
@@ -1,313 +1,311 @@
// SPDX-FileCopyrightText: 2025 g10 code GmbH
// SPDX-FileContributor: Carl Schwan <carl.schwan@gnupg.com>
// SPDX-License-Identifier: GPL-2.0-or-later
#include "reencryptjob.h"
#include "../ews/ewsfinditemrequest.h"
#include "../ews/ewsgetitemrequest.h"
#include "../ews/ewsitem.h"
#include "choosekeydialog.h"
#include "ews/ewsupdateitemrequest.h"
#include <QGpgME/DecryptJob>
#include <QGpgME/EncryptJob>
#include <QGpgME/Protocol>
#include <gpgme++/context.h>
#include <gpgme++/decryptionresult.h>
#include <gpgme++/encryptionresult.h>
#include <QListView>
#include <QLocale>
#include <QPlainTextEdit>
#include <QSaveFile>
#include <QStandardItemModel>
#include <QStyledItemDelegate>
#include <QVBoxLayout>
#include <KLocalizedString>
#include <KMessageBox>
#include <KMime/Message>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
using namespace Qt::StringLiterals;
static QString ewsHash(const QString &value)
{
return QString::number(qHash(value), 36);
};
ReencryptJob::ReencryptJob(WebsocketClient *websocketClient, const EwsId &folderId, const EwsClient &client)
: KJob()
, m_itemModel(new QStandardItemModel(this))
, m_websocketClient(websocketClient)
, m_folderId(folderId)
, m_ewsClient(client)
{
}
void ReencryptJob::start()
{
auto dialog = new ChooseKeyDialog(m_folderId.type() == EwsId::Real ? EwsClient::folderHash.value(m_folderId.id(), ewsHash(m_folderId.id())) : u""_s);
dialog->setAttribute(Qt::WA_DeleteOnClose);
connect(dialog, &QDialog::accepted, this, [this, dialog] {
m_currentKey = dialog->currentKey();
m_backupFolder = dialog->backup();
auto dialog = new QDialog;
auto layout = new QVBoxLayout(dialog);
layout->setSpacing(0);
layout->setContentsMargins({});
auto listView = new QListView;
layout->addWidget(listView);
m_debugLog = new QPlainTextEdit;
layout->addWidget(m_debugLog);
listView->setModel(m_itemModel);
listView->setItemDelegate(new QStyledItemDelegate);
dialog->show();
fetchItems();
});
connect(dialog, &QDialog::rejected, this, [this] {
emitResult();
});
dialog->show();
}
void ReencryptJob::emailListFetched(KJob *job)
{
auto findItemRequest = qobject_cast<EwsFindItemRequest *>(job);
Q_ASSERT(findItemRequest);
const auto items = findItemRequest->items();
EwsId::List ids;
for (const auto &item : items) {
ids << item[EwsItemFieldItemId].value<EwsId>();
}
EwsItemShape itemShape(EwsShapeIdOnly);
itemShape.setFlags(EwsItemShape::IncludeMimeContent);
auto getItemRequest = new EwsGetItemRequest(m_ewsClient, this);
getItemRequest->setItemIds(ids);
getItemRequest->setItemShape(itemShape);
connect(getItemRequest, &EwsGetItemRequest::finished, this, &ReencryptJob::emailContentFetched);
getItemRequest->start();
// fetch more items
if (!findItemRequest->includesLastItem()) {
EwsItemShape itemShape(EwsShapeIdOnly);
auto nextFindItemRequest = new EwsFindItemRequest(m_ewsClient, this);
nextFindItemRequest->setFolderId(m_folderId);
nextFindItemRequest->setItemShape(itemShape);
nextFindItemRequest->setTraversal(EwsTraversalType::EwsTraversalShallow);
nextFindItemRequest->setPagination(EwsBasePointBeginning, findItemRequest->nextOffset(), 20);
connect(nextFindItemRequest, &EwsFindItemRequest::finished, this, &ReencryptJob::emailListFetched);
nextFindItemRequest->start();
}
}
void ReencryptJob::emailContentFetched(KJob *job)
{
auto getItemRequest = qobject_cast<EwsGetItemRequest *>(job);
Q_ASSERT(getItemRequest);
const auto responses = getItemRequest->responses();
for (const auto &response : responses) {
if (!error()) {
reencrypt(response.item());
}
}
}
template<typename T>
const T *findHeader(KMime::Content *content)
{
auto header = content->header<T>();
if (header || !content->parent()) {
return header;
}
return findHeader<T>(content->parent());
}
void ReencryptJob::reencrypt(const EwsItem &item)
{
const auto mimeContent = item[EwsItemFieldMimeContent].toString().toUtf8();
const auto mailData = KMime::CRLFtoLF(mimeContent);
const auto msg = KMime::Message::Ptr(new KMime::Message);
msg->setContent(mailData);
msg->parse();
bool encrypted = false;
auto subject = findHeader<KMime::Headers::Subject>(msg.get());
auto modelItem = new QStandardItem(subject ? subject->asUnicodeString() : u"no subject"_s);
m_itemModel->appendRow(modelItem);
parseParts(item, msg, msg.get(), encrypted, modelItem);
if (modelItem->icon().isNull()) {
modelItem->setIcon(QIcon::fromTheme(u"data-success"_s));
}
}
bool ReencryptJob::saveContent(const EwsItem &item, const QByteArray &content, bool original)
{
if (m_backupFolder.isEmpty()) {
return true;
}
const auto id = item[EwsItemFieldItemId].value<EwsId>();
const QString name =
EwsClient::folderHash.value(id.id(), ewsHash(id.id())) + u'-' + ewsHash(id.changeKey()) + (original ? QString{} : u"-reencrypted"_s) + u".eml"_s;
QSaveFile saveFile(m_backupFolder.toLocalFile() + u'/' + name);
if (!saveFile.open(QSaveFile::WriteOnly)) {
KMessageBox::error(nullptr, i18nc("@info", "Unable to save backup to %1", saveFile.fileName()));
return false;
}
saveFile.write(content);
if (!saveFile.commit()) {
KMessageBox::error(nullptr, i18nc("@info", "Unable to save backup to %1", saveFile.fileName()));
return false;
}
return true;
}
void ReencryptJob::parseParts(const EwsItem &item, const KMime::Message::Ptr &message, KMime::Content *content, bool &encrypted, QStandardItem *modelItem)
{
if (const auto contentType = content->contentType(); contentType) {
const auto mimeType = contentType->mimeType();
if (mimeType == "application/pgp-encrypted") {
const auto parent = content->parent();
if (!parent) {
return;
}
const auto siblings = parent->contents();
for (const auto sibling : siblings) {
if (const auto contentType = sibling->contentType(); contentType) {
const auto mimeType = contentType->mimeType();
if (mimeType == "application/octet-stream") {
const auto encryptedContent = sibling->decodedContent();
auto job = QGpgME::openpgp()->encryptJob(true, true);
-#if 0 // requires unreleased code from gpgmepp
job->setEncryptionFlags(GpgME::Context::AddRecipient);
-#endif
job->setProperty("message", QVariant::fromValue(message));
job->setProperty("oldContent", QVariant::fromValue(encryptedContent));
job->setProperty("oldPart", QVariant::fromValue(sibling));
job->setProperty("modelItem", QVariant::fromValue(modelItem));
modelItem->setIcon(QIcon::fromTheme(u"view-refresh-symbolic"_s));
if (!saveContent(item, message->encodedContent(), true)) {
modelItem->setIcon(QIcon::fromTheme(u"data-error"_s));
setError(KJob::UserDefinedError);
emitResult();
return;
}
connect(job,
&QGpgME::EncryptJob::result,
this,
[this, item, job](const GpgME::EncryptionResult &result,
const QByteArray &cipherText,
const QString &auditLogAsHtml,
const GpgME::Error &auditLogError) {
Q_UNUSED(auditLogAsHtml);
Q_UNUSED(auditLogError);
encryptionFinished(item, job, result, cipherText);
});
job->start({m_currentKey}, encryptedContent);
m_encryptJobs.append(job);
}
}
}
encrypted = true;
}
}
for (const auto &content : content->contents()) {
parseParts(item, message, content, encrypted, modelItem);
}
}
void ReencryptJob::fetchItems()
{
EwsItemShape itemShape(EwsShapeIdOnly);
auto findItemRequest = new EwsFindItemRequest(m_ewsClient, this);
findItemRequest->setFolderId(m_folderId);
findItemRequest->setItemShape(itemShape);
findItemRequest->setTraversal(EwsTraversalType::EwsTraversalShallow);
findItemRequest->setPagination(EwsBasePointBeginning, 0, 20);
connect(findItemRequest, &EwsFindItemRequest::finished, this, &ReencryptJob::emailListFetched);
findItemRequest->start();
}
void ReencryptJob::encryptionFinished(const EwsItem &item, QGpgME::EncryptJob *job, const GpgME::EncryptionResult &result, const QByteArray &cipherText)
{
const auto index = m_encryptJobs.indexOf(job);
if (index != -1) {
m_encryptJobs.remove(index);
}
const auto modelItem = job->property("modelItem").value<QStandardItem *>();
if (result.error()) {
setError(KJob::UserDefinedError);
setErrorText(errorText() + Kleo::Formatting::errorAsString(result.error()) + u'\n');
modelItem->setIcon(QIcon::fromTheme(u"data-error"_s));
m_debugLog->appendPlainText(Kleo::Formatting::errorAsString(result.error()));
return;
}
const auto message = job->property("message").value<KMime::Message::Ptr>();
const auto oldPart = job->property("oldPart").value<KMime::Content *>();
const auto oldContent = job->property("oldContent").toString().toUtf8();
oldPart->setBody(cipherText);
message->assemble();
const auto newMessage = message->encodedContent();
if (!saveContent(item, newMessage, false)) {
setError(KJob::UserDefinedError);
setErrorText(errorText() + u"Unable to save file. \n"_s);
m_debugLog->appendPlainText(u"Unable to save file."_s);
modelItem->setIcon(QIcon::fromTheme(u"data-error"_s));
return;
}
auto updateItemRequest = new EwsUpdateItemRequest(m_ewsClient, this);
EwsUpdateItemRequest::ItemChange itemChange(item[EwsItemFieldItemId].value<EwsId>(), EwsItemTypeMessage);
EwsPropertyField field(u"item:MimeContent"_s);
itemChange.addUpdate(new EwsUpdateItemRequest::SetUpdate(field, QString::fromUtf8(newMessage.toBase64())));
updateItemRequest->addItemChange(itemChange);
updateItemRequest->setSavedFolderId(m_folderId);
updateItemRequest->setConflictResolution(EwsConflictResolution::EwsResolAlwaysOverwrite);
connect(updateItemRequest, &EwsUpdateItemRequest::finished, this, [this, modelItem, updateItemRequest](KJob *job) {
if (job->error()) {
setError(KJob::UserDefinedError);
setErrorText(errorText() + job->errorText() + u'\n');
modelItem->setIcon(QIcon::fromTheme(u"data-error"_s));
modelItem->setText(modelItem->text() + u' ' + job->errorText());
m_debugLog->appendPlainText(job->errorText());
emitResult();
return;
}
modelItem->setIcon(QIcon::fromTheme(u"data-success"_s));
});
updateItemRequest->start();
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Dec 24, 10:50 PM (1 d, 22 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
c7/2d/67de62e9141c27448a5a3106686f

Event Timeline