Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34252416
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
11 KB
Subscribers
None
View Options
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
Details
Attached
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
Attached To
rOJ GpgOL.js
Event Timeline
Log In to Comment