Introduce Encrypt and Decrypt message filters to automaticaly perform crypto operations on incoming emails. Differential Revision: https://phabricator.kde.org/D7041wilder
parent
f81e2ada5c
commit
bd19def5bd
34 changed files with 1539 additions and 2 deletions
@ -0,0 +1,108 @@ |
||||
/*
|
||||
* Copyright (c) 2017 Daniel Vrátil <dvratil@kde.org> |
||||
* |
||||
* This program is free software; you can redistribute it and/or modify it |
||||
* under the terms of the GNU General Public License, version 2, as |
||||
* published by the Free Software Foundation. |
||||
* |
||||
* 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, write to the Free Software Foundation, Inc., |
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#include "filteractiondecrypttest.h" |
||||
#include "../filteractions/filteractiondecrypt.h" |
||||
|
||||
#include <QTest> |
||||
|
||||
void FilterActionDecryptTest::initTestCase() |
||||
{ |
||||
mGpg = new GPGHelper(QString::fromUtf8(TEST_PATH) + QStringLiteral("/gpghome")); |
||||
QVERIFY(mGpg->isValid()); |
||||
} |
||||
|
||||
void FilterActionDecryptTest::cleanupTestCase() |
||||
{ |
||||
delete mGpg; |
||||
} |
||||
|
||||
void FilterActionDecryptTest::shouldDecrypt_data() |
||||
{ |
||||
QTest::addColumn<QByteArray>("content"); |
||||
QTest::addColumn<QByteArray>("encrypted"); |
||||
|
||||
QDir testDir(QString::fromUtf8(TEST_PATH) + QStringLiteral("/gpgdata")); |
||||
const auto tests = testDir.entryInfoList({ QStringLiteral("*.msg") }, QDir::Files, QDir::Name); |
||||
for (const auto test : tests) { |
||||
QFile plain(test.absoluteFilePath()); |
||||
QVERIFY(plain.open(QIODevice::ReadOnly)); |
||||
const auto plainData = plain.readAll(); |
||||
|
||||
QFile pgp(test.absoluteFilePath() + QStringLiteral(".pgp")); |
||||
QVERIFY(pgp.open(QIODevice::ReadOnly)); |
||||
QTest::newRow(QStringLiteral("PGP %1").arg(test.baseName()).toUtf8().constData()) |
||||
<< plainData << pgp.readAll(); |
||||
|
||||
QFile smime(test.absoluteFilePath() + QStringLiteral(".smime")); |
||||
QVERIFY(smime.open(QIODevice::ReadOnly)); |
||||
QTest::newRow(QStringLiteral("SMIME %1").arg(test.baseName()).toUtf8().constData()) |
||||
<< plainData << smime.readAll(); |
||||
|
||||
QTest::newRow(QStringLiteral("PLAIN %1").arg(test.baseName()).toUtf8().constData()) |
||||
<< plainData << plainData; |
||||
} |
||||
|
||||
} |
||||
|
||||
void FilterActionDecryptTest::shouldDecrypt() |
||||
{ |
||||
QFETCH(QByteArray, content); |
||||
QFETCH(QByteArray, encrypted); |
||||
|
||||
MailCommon::FilterActionDecrypt action(this); |
||||
|
||||
auto msg = KMime::Message::Ptr::create(); |
||||
msg->setContent(encrypted); |
||||
msg->parse(); |
||||
msg->assemble(); |
||||
|
||||
Akonadi::Item item; |
||||
item.setPayload(msg); |
||||
|
||||
MailCommon::ItemContext context(item, true); |
||||
const auto result = action.process(context, false); |
||||
QCOMPARE(result, MailCommon::FilterAction::GoOn); |
||||
if (content != encrypted) { |
||||
QVERIFY(context.needsPayloadStore()); |
||||
} else { |
||||
// the message is not encrypted, no change is needed
|
||||
QVERIFY(!context.needsPayloadStore()); |
||||
} |
||||
|
||||
auto newMsg = context.item().payload<KMime::Message::Ptr>(); |
||||
QCOMPARE(newMsg->from()->asUnicodeString(), msg->from()->asUnicodeString()); |
||||
QCOMPARE(newMsg->to()->asUnicodeString(), msg->to()->asUnicodeString()); |
||||
QCOMPARE(newMsg->date()->asUnicodeString(), msg->date()->asUnicodeString()); |
||||
QCOMPARE(newMsg->subject()->asUnicodeString(), msg->subject()->asUnicodeString()); |
||||
|
||||
auto decrypted = newMsg->encodedContent(); |
||||
KMime::Message decryptedContent; |
||||
decryptedContent.setContent(newMsg->encodedContent()); |
||||
decryptedContent.parse(); |
||||
KMime::Message expectedContent; |
||||
expectedContent.setContent(content); |
||||
expectedContent.parse(); |
||||
QCOMPARE(decryptedContent.from()->asUnicodeString(), expectedContent.from()->asUnicodeString()); |
||||
QCOMPARE(decryptedContent.to()->asUnicodeString(), expectedContent.to()->asUnicodeString()); |
||||
QCOMPARE(decryptedContent.date()->asUnicodeString(), expectedContent.date()->asUnicodeString()); |
||||
QCOMPARE(decryptedContent.subject()->asUnicodeString(), expectedContent.subject()->asUnicodeString()); |
||||
QCOMPARE(decryptedContent.contentType()->asUnicodeString(), expectedContent.contentType()->asUnicodeString()); |
||||
QCOMPARE(decryptedContent.encodedBody(), expectedContent.encodedBody()); |
||||
} |
||||
|
||||
QTEST_MAIN(FilterActionDecryptTest) |
||||
@ -0,0 +1,40 @@ |
||||
/*
|
||||
* Copyright (c) 2017 Daniel Vrátil <dvratil@kde.org> |
||||
* |
||||
* This program is free software; you can redistribute it and/or modify it |
||||
* under the terms of the GNU General Public License, version 2, as |
||||
* published by the Free Software Foundation. |
||||
* |
||||
* 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, write to the Free Software Foundation, Inc., |
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#ifndef FILTERACTIONDECRYPTTEST_H_ |
||||
#define FILTERACTIONDECRYPTTEST_H_ |
||||
|
||||
#include <QObject> |
||||
|
||||
#include "gpghelper.h" |
||||
|
||||
class FilterActionDecryptTest : public QObject |
||||
{ |
||||
Q_OBJECT |
||||
private Q_SLOTS: |
||||
void initTestCase(); |
||||
void cleanupTestCase(); |
||||
|
||||
void shouldDecrypt_data(); |
||||
void shouldDecrypt(); |
||||
|
||||
private: |
||||
GPGHelper *mGpg = {}; |
||||
}; |
||||
|
||||
#endif |
||||
|
||||
@ -0,0 +1,135 @@ |
||||
/*
|
||||
* Copyright (c) 2017 Daniel Vrátil <dvratil@kde.org> |
||||
* |
||||
* This program is free software; you can redistribute it and/or modify it |
||||
* under the terms of the GNU General Public License, version 2, as |
||||
* published by the Free Software Foundation. |
||||
* |
||||
* 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, write to the Free Software Foundation, Inc., |
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#include "filteractionencrypttest.h" |
||||
#include "../filteractions/filteractionencrypt.h" |
||||
|
||||
#include <QTest> |
||||
#include <QFileInfo> |
||||
|
||||
void FilterActionEncryptTest::initTestCase() |
||||
{ |
||||
mGpg = new GPGHelper(QString::fromUtf8(TEST_PATH) + QStringLiteral("/gpghome")); |
||||
QVERIFY(mGpg->isValid()); |
||||
} |
||||
|
||||
void FilterActionEncryptTest::cleanupTestCase() |
||||
{ |
||||
delete mGpg; |
||||
} |
||||
|
||||
void FilterActionEncryptTest::shouldEncrypt_data() |
||||
{ |
||||
QTest::addColumn<QString>("key"); |
||||
QTest::addColumn<QByteArray>("content"); // content for decryption
|
||||
QTest::addColumn<QByteArray>("expected"); // decrypted expected content
|
||||
|
||||
const auto smimeKey = QStringLiteral("SMIME:%1:0FDD972BCEFB5735DC7E8EE57DB7BA4E5FDBE218"); |
||||
const auto pgpKey = QStringLiteral("PGP:%1:818AE8DA30F81B0CEA4403BA358732559B8659B2"); |
||||
|
||||
QDir testDir(QString::fromUtf8(TEST_PATH) + QStringLiteral("/gpgdata")); |
||||
const auto tests = testDir.entryInfoList({ QStringLiteral("*.msg") }, QDir::Files, QDir::Name); |
||||
for (const auto test : tests) { |
||||
QFile plain(test.absoluteFilePath()); |
||||
QVERIFY(plain.open(QIODevice::ReadOnly)); |
||||
const auto plainData = plain.readAll(); |
||||
|
||||
QTest::newRow(QStringLiteral("PGP %1").arg(test.baseName()).toUtf8().constData()) |
||||
<< pgpKey.arg(0) << plainData << plainData; |
||||
QTest::newRow(QStringLiteral("SMIME %1").arg(test.baseName()).toUtf8().constData()) |
||||
<< smimeKey.arg(0) << plainData << plainData; |
||||
|
||||
QFile smimeFile(test.absoluteFilePath() + QStringLiteral(".smime")); |
||||
QVERIFY(smimeFile.open(QIODevice::ReadOnly)); |
||||
const auto smimeData = smimeFile.readAll(); |
||||
QFile pgpFile(test.absoluteFilePath() + QStringLiteral(".pgp")); |
||||
QVERIFY(pgpFile.open(QIODevice::ReadOnly)); |
||||
const auto pgpData = pgpFile.readAll(); |
||||
|
||||
QTest::newRow(QStringLiteral("PGP %1 re-encrypt").arg(test.baseName()).toUtf8().constData()) |
||||
<< pgpKey.arg(1) << smimeData << plainData; |
||||
QTest::newRow(QStringLiteral("SMIME %1 re-encrypt").arg(test.baseName()).toUtf8().constData()) |
||||
<< smimeKey.arg(1) << pgpData << plainData; |
||||
QTest::newRow(QStringLiteral("PGP %1 re-encrypt same key").arg(test.baseName()).toUtf8().constData()) |
||||
<< pgpKey.arg(1) << pgpData << plainData; |
||||
} |
||||
} |
||||
|
||||
void FilterActionEncryptTest::shouldEncrypt() |
||||
{ |
||||
QFETCH(QString, key); |
||||
QFETCH(QByteArray, content); |
||||
QFETCH(QByteArray, expected); |
||||
|
||||
MailCommon::FilterActionEncrypt action(this); |
||||
action.argsFromString(key); |
||||
QVERIFY(!action.key().isNull()); |
||||
QCOMPARE(action.reencrypt(), key.contains(QLatin1String(":1:"))); |
||||
|
||||
auto msg = KMime::Message::Ptr::create(); |
||||
msg->setContent(content); |
||||
msg->parse(); |
||||
msg->assemble(); |
||||
|
||||
Akonadi::Item item; |
||||
item.setPayload(msg); |
||||
|
||||
MailCommon::ItemContext context(item, true); |
||||
const auto result = action.process(context, false); |
||||
QCOMPARE(result, MailCommon::FilterAction::GoOn); |
||||
QVERIFY(context.needsPayloadStore()); |
||||
|
||||
auto newMsg = context.item().payload<KMime::Message::Ptr>(); |
||||
QCOMPARE(newMsg->from()->asUnicodeString(), msg->from()->asUnicodeString()); |
||||
QCOMPARE(newMsg->to()->asUnicodeString(), msg->to()->asUnicodeString()); |
||||
QCOMPARE(newMsg->date()->asUnicodeString(), msg->date()->asUnicodeString()); |
||||
QCOMPARE(newMsg->subject()->asUnicodeString(), msg->subject()->asUnicodeString()); |
||||
|
||||
QString gpgexe; |
||||
QByteArray resultContent; |
||||
GPGHelper::CryptoType crypto; |
||||
if (key.startsWith(QLatin1String("PGP"))) { |
||||
QCOMPARE(newMsg->contentType()->mimeType(), QByteArray("multipart/encrypted")); |
||||
resultContent = newMsg->encodedContent(); |
||||
crypto = GPGHelper::OpenPGP; |
||||
} else { |
||||
QCOMPARE(newMsg->contentType()->mimeType(), QByteArray("application/pkcs7-mime")); |
||||
resultContent = QByteArray::fromBase64(newMsg->encodedBody()); |
||||
crypto = GPGHelper::SMIME; |
||||
} |
||||
|
||||
// Check if the message is encrypted with the right key
|
||||
const auto usedKey = mGpg->encryptionKeyFp(resultContent, crypto); |
||||
QCOMPARE(usedKey, QString::fromLatin1(action.key().primaryFingerprint())); |
||||
|
||||
const auto actual = mGpg->decrypt(resultContent, crypto); |
||||
|
||||
KMime::Message actualContent; |
||||
actualContent.setContent(actual); |
||||
actualContent.parse(); |
||||
KMime::Message expectedContent; |
||||
expectedContent.setContent(expected); |
||||
expectedContent.parse(); |
||||
QCOMPARE(actualContent.from()->asUnicodeString(), expectedContent.from()->asUnicodeString()); |
||||
QCOMPARE(actualContent.to()->asUnicodeString(), expectedContent.to()->asUnicodeString()); |
||||
QCOMPARE(actualContent.date()->asUnicodeString(), expectedContent.date()->asUnicodeString()); |
||||
QCOMPARE(actualContent.subject()->asUnicodeString(), expectedContent.subject()->asUnicodeString()); |
||||
QCOMPARE(actualContent.contentType()->asUnicodeString(), expectedContent.contentType()->asUnicodeString()); |
||||
QCOMPARE(actualContent.encodedBody(), expectedContent.encodedBody()); |
||||
} |
||||
|
||||
QTEST_MAIN(FilterActionEncryptTest) |
||||
@ -0,0 +1,38 @@ |
||||
/*
|
||||
* Copyright (c) 2017 Daniel Vrátil <dvratil@kde.org> |
||||
* |
||||
* This program is free software; you can redistribute it and/or modify it |
||||
* under the terms of the GNU General Public License, version 2, as |
||||
* published by the Free Software Foundation. |
||||
* |
||||
* 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, write to the Free Software Foundation, Inc., |
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#ifndef FILTERACTIONENCRYPTTEST_H_ |
||||
#define FILTERACTIONENCRYPTTEST_H_ |
||||
|
||||
#include <QObject> |
||||
#include "gpghelper.h" |
||||
|
||||
class FilterActionEncryptTest : public QObject |
||||
{ |
||||
Q_OBJECT |
||||
private Q_SLOTS: |
||||
void initTestCase(); |
||||
void cleanupTestCase(); |
||||
|
||||
void shouldEncrypt_data(); |
||||
void shouldEncrypt(); |
||||
|
||||
private: |
||||
GPGHelper *mGpg = {}; |
||||
}; |
||||
|
||||
#endif |
||||
@ -0,0 +1,18 @@ |
||||
From: Daniel Vratil <dvratil@kde.org> |
||||
To: KMail Test <kmail@test.kde> |
||||
Date: Tue, 01 Aug 2017 07:50:04 +0000 |
||||
Subject: It's time to get schwifty! |
||||
MIME-Version: 1.0 |
||||
Content-Type: multipart/alternative; boundary="nextPart0" |
||||
|
||||
--nextPart0 |
||||
Content-Type: text/plain |
||||
|
||||
Wubba-lubba-dub-dub! |
||||
|
||||
--nextPart0 |
||||
Content-Type: text/html |
||||
|
||||
<html><body><strong>Wubba-lubba-dub-dub!</strong></body></html> |
||||
|
||||
--nextPart0-- |
||||
@ -0,0 +1,34 @@ |
||||
From: Daniel Vratil <dvratil@kde.org> |
||||
To: KMail Test <kmail@test.kde> |
||||
Date: Tue, 01 Aug 2017 07:50:04 +0000 |
||||
Subject: It's time to get schwifty! |
||||
MIME-Version: 1.0 |
||||
Content-Type: multipart/encrypted; boundary="nextPart0"; protocol="application/pgp-encrypted" |
||||
|
||||
--nextPart0 |
||||
Content-Type: application/pgp-encrypted |
||||
Content-Disposition: attachment |
||||
Content-Transfer-Encoding: 7Bit |
||||
|
||||
Version: 1 |
||||
--nextPart0 |
||||
Content-Type: application/octet-stream |
||||
Content-Disposition: inline; filename="msg.asc" |
||||
Content-Transfer-Encoding: 7Bit |
||||
|
||||
-----BEGIN PGP MESSAGE----- |
||||
|
||||
hQEMA3IXuHpXwGcWAQf/e5YrKiIPaaOU9zLeajnTipkHbGxgP/ZdDfxApfMMeWG+ |
||||
r2Ort8PflyvRB4MlFye7PpGTbK3BIFFNzWSlWwhAukbmj0OlJzgAxJZS7OlpX9mk |
||||
mae5XkkBnAqqzpwf0d9/JWaOvx4xhzYGjRBoInTxRI+I5iFS1TveQ0j3TsSut6ou |
||||
G84zyW2lMWNsXHsl8cGstPsI190LcpoWfdAwW5FTcPr7tqE8JDeNe5BntS3eRx4l |
||||
tCvxPQrDtbRdlh6CsMlUjZsRRe1p9l7Ab551YRQFeo+Zi6lP5gnLIImx0KErfiRG |
||||
Xcg4VB30jKBj7Q5AhQFCCcKCrLKLZcgs6KUYSb0YPdLAAgEEnoeRmQ6LbUAY9OQb |
||||
6+vwM+1vZ6efEkbaBUJwDbFaT9gbdNiS0MyQiNeZav19c/rkIb7JV07ps/d4yU+i |
||||
SzqmuwXOv77CjYU+pSmlFvpjVbeZw+YjS7asTEXID6vSE7XOzmjzkjLrJ+tBtBS3 |
||||
69dVFfOH/1kb1lFa+iKOkiwCnOo4/LfSGGLvx7ueJuZsqHE8rFIryc6MFtng5HlK |
||||
qP+apCdxSkbeG3kxQIC0gljhVQYJkGU25So3H6DyRa2U+B+V |
||||
=8G22 |
||||
-----END PGP MESSAGE----- |
||||
|
||||
--nextPart0-- |
||||
@ -0,0 +1,23 @@ |
||||
From: Daniel Vratil <dvratil@kde.org> |
||||
To: KMail Test <kmail@test.kde> |
||||
Date: Tue, 01 Aug 2017 07:50:04 +0000 |
||||
Subject: It's time to get schwifty! |
||||
Content-Type: application/pkcs7-mime; name="smime.p7m"; smime-type="enveloped-data" |
||||
Content-Transfer-Encoding: base64 |
||||
Content-Disposition: attachment; filename="smime.p7m" |
||||
MIME-Version: 1.0 |
||||
|
||||
MIAGCSqGSIb3DQEHA6CAMIACAQAxggFYMIIBVAIBADA8MDAxCzAJBgNVBAYTAkNa |
||||
MQwwCgYDVQQKEwNLREUxEzARBgNVBAMTCktNYWlsIFRlc3QCCDilUKgKqCPGMA0G |
||||
CSqGSIb3DQEBAQUABIIBAGqd1mJHm3G29UuwVYZM/TqjLa285LPTAHIqvz86ins+ |
||||
7cxsM8uu9R8PgYzpkR6HEeF3X/OY5K4YFj2BO2MS0jDh0u12IFeZSat83QHUCVP8 |
||||
hOQYbs6lB6duXrhSVQ/kGVL2l2Us7AWFqHbNzLZTq8sgHySfI5PRgKxmZRi+0/o+ |
||||
40zN43azW1RQkcDjxZIQh4etGHbFP/ywBMvW6lShFpz33DFGZzOopdlj834O1aVy |
||||
HadFGdqe7oKziQ1xSj05mblHMNeuYHwgB2+5RdemK4y4/BUwu+7LRuUeWjzo3i05 |
||||
A3mjz0jUtg6tVLx4sB9cw1Bh5kKL9G5o8FiidFkjAcAwgAYJKoZIhvcNAQcBMB0G |
||||
CWCGSAFlAwQBAgQQIRY9qD+NYirOMj+Aa4/JLqCABIHgngiipfaUSmGGW2O/YAdb |
||||
xHGYH0GwUwsWxEB2+TkSkLvzxmyXQaUoyLn2h+TJjj4ezEmH3Ir/9YQ85uUj2lpl |
||||
/KoXeN3QSsIbWaklyuca32pxSOVb5JWTTj03Kdnotnlg9wU97bcpEJ5pIlrZX4L0 |
||||
kTq+WQsK1198kxzhVCmT5LfAVdIMI1YQrRLkW7uZsa83IFMvqvkofGBKHx7lhSst |
||||
nil6+bStPyjfPGJChe0UOyhJicBcrm7CNLNiZPtYLBrAEbnSELqsU6a1OO+6wZ9L |
||||
l7CvTrvSj6ZV7JY90OhhhCsEENmHHKkBN0X+259Qkhgb1l4AAAAAAAAAAAAA |
||||
@ -0,0 +1,8 @@ |
||||
From: Daniel Vratil <dvratil@kde.org> |
||||
To: KMail Test <kmail@test.kde> |
||||
Date: Tue, 01 Aug 2017 07:50:04 +0000 |
||||
Subject: It's time to get schwifty! |
||||
Content-Type: text/plain |
||||
MIME-Version: 1.0 |
||||
|
||||
Show me what you got! |
||||
@ -0,0 +1,32 @@ |
||||
From: Daniel Vratil <dvratil@kde.org> |
||||
To: KMail Test <kmail@test.kde> |
||||
Date: Tue, 01 Aug 2017 07:50:04 +0000 |
||||
Subject: It's time to get schwifty! |
||||
Content-Type: multipart/encrypted; boundary="nextPart"; protocol="application/pgp-encrypted" |
||||
MIME-Version: 1.0 |
||||
|
||||
--nextPart |
||||
Content-Type: application/pgp-encrypted |
||||
Content-Disposition: attachment |
||||
Content-Transfer-Encoding: 7Bit |
||||
|
||||
Version: 1 |
||||
--nextPart |
||||
Content-Type: application/octet-stream |
||||
Content-Disposition: inline; filename="msg.asc" |
||||
Content-Transfer-Encoding: 7Bit |
||||
|
||||
-----BEGIN PGP MESSAGE----- |
||||
|
||||
hQEMA3IXuHpXwGcWAQf/dYk2TJpb6J6Ji9mA4RzMr5CUyiMkDlOe6+l4BVtRHsLm |
||||
g9Bwe4znqCenyf/B6MdGThPAFvYRcN2UPbLEElrZQ5vsCyYAG8sespy3sVQChA1H |
||||
063pLvYZDxV/FdA2ckduaxQND9kNdfSONn7+olTaKgDiKA1oSgZ+dvRHG5cr/xQ+ |
||||
AoYA/iQ65dT3MQyvWF1Iw+AKrbhR8XRfB77Hl+qL/vr62pbnVVA2rHjEu1Tjz04I |
||||
6oS55zoetonBVYiSIpQLRu5Uu0ys8jUY29qGgwd/VlBB2Y/YcYY3OTRbkvYBJ2Yd |
||||
hc1ca4YCEaJ/9e8BuhLfBzh4J9EZgJ3NYRTm3FZRadJrAftlxAnsG1HmvE3kNV6Q |
||||
zHWxlIQZQE+sSr7dbnpyObpHaiAaU1toaH22uKRh+9ZBhhiNYhCHk1KnkUKG7gdR |
||||
Wys2qPAhuMVZryoW0uQyX4lA5ZjsetBSxUVffjjHZKYP56dyLzpvMGqWvK0= |
||||
=DWvh |
||||
-----END PGP MESSAGE----- |
||||
|
||||
--nextPart-- |
||||
@ -0,0 +1,21 @@ |
||||
From: Daniel Vratil <dvratil@kde.org> |
||||
To: KMail Test <kmail@test.kde> |
||||
Date: Tue, 01 Aug 2017 07:50:04 +0000 |
||||
Subject: It's time to get schwifty! |
||||
Content-Type: application/pkcs7-mime; name="smime.p7m"; smime-type="enveloped-data" |
||||
Content-Transfer-Encoding: base64 |
||||
Content-Disposition: attachment; filename="smime.p7m" |
||||
MIME-Version: 1.0 |
||||
|
||||
MIAGCSqGSIb3DQEHA6CAMIACAQAxggFYMIIBVAIBADA8MDAxCzAJBgNVBAYTAkNa |
||||
MQwwCgYDVQQKEwNLREUxEzARBgNVBAMTCktNYWlsIFRlc3QCCDilUKgKqCPGMA0G |
||||
CSqGSIb3DQEBAQUABIIBAIGrbP6RHuRHdMhJpXuyRjzWPdApinxmUBtvPCxwfLK7 |
||||
0ZHa11jAwJbIvouuqblReZX/eCYbBYsgW5aSFn1dSV/lUQ49gOTRHnRXEBrQGK4z |
||||
gdNK+g+axqZO4zUtKyEDpKi7bnS2i/aCi5Q61PWFSkjgucrUuxyGP82iK5WQpcuk |
||||
0c7xR8cxaZf/tpQGnFRZ6OlUVLi2iSbv+ewbrZnaQgu3XAKQcJHrZlArgtR3cneA |
||||
X8vpCDE5R/DpZ9onJLwd/KI1EaoEjeZoa6VMVm324uxG2vAaubvg9p4lBR0Q69hL |
||||
MIpt8KGgz4psbAiE838U5jh77o5lxQmviolmuyEtZXswgAYJKoZIhvcNAQcBMB0G |
||||
CWCGSAFlAwQBAgQQrA0/KpKc4c8gmw1PZUNJXqCABDAgX5OMUtnouELP2pOKz4TN |
||||
pLHtscYX9+1XD4Rn+hqPOZ7aytiFOvpdn2qUqHtqhT8EEDumRT2M6mQ6rouGaXk7 |
||||
DDAAAAAAAAAAAAAA |
||||
|
||||
@ -0,0 +1,141 @@ |
||||
/*
|
||||
* Copyright (c) 2017 Daniel Vrátil <dvratil@kde.org> |
||||
* |
||||
* This program is free software; you can redistribute it and/or modify it |
||||
* under the terms of the GNU General Public License, version 2, as |
||||
* published by the Free Software Foundation. |
||||
* |
||||
* 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, write to the Free Software Foundation, Inc., |
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#include "gpghelper.h" |
||||
|
||||
#include <QFileInfo> |
||||
#include <QDebug> |
||||
#include <QProcess> |
||||
#include <QTest> |
||||
|
||||
namespace { |
||||
|
||||
bool copyRecursively(const QString &src, const QString &dest) |
||||
{ |
||||
QFileInfo srcInfo(src); |
||||
if (srcInfo.isDir()) { |
||||
QDir destDir(dest); |
||||
destDir.cdUp(); |
||||
if (!destDir.mkdir(QFileInfo(src).fileName())) { |
||||
qWarning() << "Failed to create directory" << QFileInfo(src).fileName() << "in" << destDir.path(); |
||||
return false; |
||||
} |
||||
QDir srcDir(src); |
||||
const auto srcFiles = srcDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System); |
||||
for (const auto &fileName : srcFiles) { |
||||
const QString srcFile = src + QLatin1Char('/') + fileName; |
||||
const QString dstFile = dest + QLatin1Char('/') + fileName; |
||||
if (!copyRecursively(srcFile, dstFile)) { |
||||
return false; |
||||
} |
||||
} |
||||
} else { |
||||
if (!QFile::copy(src, dest)) { |
||||
qWarning() << "Failed to copy" << src << "into" << dest; |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
QString gpgexe(GPGHelper::CryptoType crypto) |
||||
{ |
||||
return (crypto == GPGHelper::OpenPGP) ? QStringLiteral("gpg2") : QStringLiteral("gpgsm"); |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
GPGHelper::GPGHelper(const QString &templateGnupgHome) |
||||
: mValid(false) |
||||
{ |
||||
const auto home = gnupgHome(); |
||||
mValid = copyRecursively(templateGnupgHome, home); |
||||
if (mValid) { |
||||
qputenv("GNUPGHOME", home.toUtf8()); |
||||
} |
||||
} |
||||
|
||||
GPGHelper::~GPGHelper() |
||||
{ |
||||
// shutdown gpg-agent
|
||||
QProcess gpgshutdown; |
||||
auto env = gpgshutdown.processEnvironment(); |
||||
env.insert("GNUPGHOME", gnupgHome()); |
||||
gpgshutdown.setProcessEnvironment(env); |
||||
gpgshutdown.start(QStringLiteral("gpg-connect-agent")); |
||||
QVERIFY(gpgshutdown.waitForStarted()); |
||||
gpgshutdown.write("KILLAGENT"); |
||||
gpgshutdown.closeWriteChannel(); |
||||
QVERIFY(gpgshutdown.waitForFinished()); |
||||
} |
||||
|
||||
QString GPGHelper::gnupgHome() const |
||||
{ |
||||
return mTmpDir.path() + QStringLiteral("/gpghome"); |
||||
} |
||||
|
||||
QByteArray GPGHelper::runGpg(const QByteArray &in, GPGHelper::CryptoType crypto, |
||||
const QStringList &args) const |
||||
{ |
||||
QProcess gpg; |
||||
gpg.setReadChannel(QProcess::StandardOutput); |
||||
auto env = gpg.processEnvironment(); |
||||
env.insert("GNUPGHOME", gnupgHome()); |
||||
gpg.setProcessEnvironment(env); |
||||
gpg.start(gpgexe(crypto), args); |
||||
if (!gpg.waitForStarted()) { |
||||
return {}; |
||||
} |
||||
gpg.write(in); |
||||
gpg.closeWriteChannel(); |
||||
if (!gpg.waitForReadyRead()) { |
||||
return {}; |
||||
} |
||||
const auto out = gpg.readAllStandardOutput(); |
||||
|
||||
if (!gpg.waitForFinished()) { |
||||
return {}; |
||||
} |
||||
|
||||
return out; |
||||
} |
||||
|
||||
QByteArray GPGHelper::decrypt(const QByteArray &enc, GPGHelper::CryptoType crypto) const |
||||
{ |
||||
return runGpg(enc, crypto, { QStringLiteral("-d") }); |
||||
} |
||||
|
||||
QByteArray GPGHelper::encrypt(const QByteArray &dec, GPGHelper::CryptoType crypto) const |
||||
{ |
||||
return runGpg(dec, crypto, { QStringLiteral("-e") }); |
||||
} |
||||
|
||||
QString GPGHelper::encryptionKeyFp(const QByteArray &enc, GPGHelper::CryptoType crypto) const |
||||
{ |
||||
const auto data = runGpg(enc, crypto, { QStringLiteral("--fingerprint"), |
||||
QStringLiteral("--with-colons") }); |
||||
int idx = data.indexOf("\nfpr:"); |
||||
if (idx == -1) { |
||||
return {}; |
||||
} |
||||
|
||||
// Find first non-colon character after "fpr"
|
||||
for (idx = idx + 4; idx < data.size() && data[idx] == ':'; ++idx); |
||||
const int end = data.indexOf(':', idx); |
||||
|
||||
return QString::fromLatin1(data.constData() + idx, end - idx); |
||||
} |
||||
@ -0,0 +1,51 @@ |
||||
/*
|
||||
* Copyright (c) 2017 Daniel Vrátil <dvratil@kde.org> |
||||
* |
||||
* This program is free software; you can redistribute it and/or modify it |
||||
* under the terms of the GNU General Public License, version 2, as |
||||
* published by the Free Software Foundation. |
||||
* |
||||
* 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, write to the Free Software Foundation, Inc., |
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#ifndef GPGHELPER_H_ |
||||
#define GPGHELPER_H_ |
||||
|
||||
#include <QObject> |
||||
#include <QTemporaryDir> |
||||
|
||||
class GPGHelper |
||||
{ |
||||
public: |
||||
enum CryptoType { |
||||
OpenPGP, |
||||
SMIME |
||||
}; |
||||
|
||||
explicit GPGHelper(const QString &templateGnupgHome); |
||||
~GPGHelper(); |
||||
|
||||
bool isValid() const { return mValid; } |
||||
QString gnupgHome() const; |
||||
|
||||
QByteArray decrypt(const QByteArray &enc, CryptoType crypto) const; |
||||
QByteArray encrypt(const QByteArray &dec, CryptoType crypto) const; |
||||
|
||||
QString encryptionKeyFp(const QByteArray &encMsg, GPGHelper::CryptoType crypto) const; |
||||
|
||||
private: |
||||
QByteArray runGpg(const QByteArray &in, CryptoType crypt, const QStringList &args) const; |
||||
|
||||
bool mValid; |
||||
QTemporaryDir mTmpDir; |
||||
}; |
||||
|
||||
#endif |
||||
|
||||
@ -0,0 +1,32 @@ |
||||
This is a revocation certificate for the OpenPGP key: |
||||
|
||||
pub rsa2048 2017-08-01 [SC] [expires: 2019-08-01] |
||||
818AE8DA30F81B0CEA4403BA358732559B8659B2 |
||||
uid KMail Test <kmail@test.kde> |
||||
|
||||
A revocation certificate is a kind of "kill switch" to publicly |
||||
declare that a key shall not anymore be used. It is not possible |
||||
to retract such a revocation certificate once it has been published. |
||||
|
||||
Use it to revoke this key in case of a compromise or loss of |
||||
the secret key. However, if the secret key is still accessible, |
||||
it is better to generate a new revocation certificate and give |
||||
a reason for the revocation. For details see the description of |
||||
of the gpg command "--generate-revocation" in the GnuPG manual. |
||||
|
||||
To avoid an accidental use of this file, a colon has been inserted |
||||
before the 5 dashes below. Remove this colon with a text editor |
||||
before importing and publishing this revocation certificate. |
||||
|
||||
:-----BEGIN PGP PUBLIC KEY BLOCK----- |
||||
Comment: This is a revocation certificate |
||||
|
||||
iQE2BCABCAAgFiEEgYro2jD4GwzqRAO6NYcyVZuGWbIFAlmAfNQCHQAACgkQNYcy |
||||
VZuGWbJ5hQgAkNopkhsaO0yyu+xs3AymBzha/YDAqQLMEH6b3URyTyF7iyYD1HbG |
||||
fwYN/PcH5H6xreSTQiQoopER7hB0H42P1ASRyDyRxTk4EuiOfrgkewa8H+jCe6OK |
||||
HPZ3c1g6zo7LZFhIldXZ0aOVaHEuqldWnWJGhxfN2ew4xNUM+7yrA1N4WDRBzI/3 |
||||
CRzr+sG2wMZKixlpKqthvpH8doGFiNLpY4KmiKh9rjQ5f2KLSGcDDTNXsinRwOrr |
||||
jXIV4Y9u0ZugzmgWHlWV6bYX5ChsfI5OzjMCrCm+XuCsdFMca3tArQh/6KOY4zjW |
||||
L+iFSgdqd4gewSvpY8FTtKzkRlX2HMpYhg== |
||||
=rzUF |
||||
-----END PGP PUBLIC KEY BLOCK----- |
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,21 @@ |
||||
-----BEGIN CERTIFICATE----- |
||||
MIIDNTCCAh2gAwIBAgIIOKVQqAqoI8YwDQYJKoZIhvcNAQELBQAwMDELMAkGA1UE |
||||
BhMCQ1oxDDAKBgNVBAoTA0tERTETMBEGA1UEAxMKS01haWwgVGVzdDAgFw0xNzA4 |
||||
MDExNTQ0NThaGA8yMDYzMDQwNTE3MDAwMFowMDELMAkGA1UEBhMCQ1oxDDAKBgNV |
||||
BAoTA0tERTETMBEGA1UEAxMKS01haWwgVGVzdDCCASIwDQYJKoZIhvcNAQEBBQAD |
||||
ggEPADCCAQoCggEBAJlU6ix8++s4sVz+wuKV7krodegopIqq8JrdxmNzD0FsKDhP |
||||
dpabOGckCM3TMAQvE96PJaYKMV+QGdEvbe0qQOkABaEhjLucArBPWJAtymaEA+18 |
||||
qXGoslHGSeL/x5aBEzvCvRRkHYjLWb4CFv0PfV/GY6SppYUDxgDhnbraRN24/1vH |
||||
M2ix8PkBs3Nx8Ti9kY3Da3F0NnfIyGXSKCHbSFqjmnF9DHbNih9tEcgrFO0nJMG0 |
||||
56nzXKtLgIGe5RQE7jH+TaYALYOUS6FjGFxwiI22DsZmrFzzc12wgW/0jj7oFl5R |
||||
3fiUg0fNmqnwR+/pTNyJEwIVdGnQjrdfrDRb26sCAwEAAaNRME8wGQYDVR0RBBIw |
||||
EIEOa21haWxAdGVzdC5rZGUwEQYKKwYBBAHaRwICAQQDAQH/MA8GA1UdEwEB/wQF |
||||
MAMBAf8wDgYDVR0PAQH/BAQDAgTwMA0GCSqGSIb3DQEBCwUAA4IBAQBoUIiPFtvM |
||||
aQSLMh0FWfsSOUW6bmikClGGc7iykFlQbQPgxUiXr2EJlCoaLdTqsa52VdAeNiq1 |
||||
vGkUTsCMWnmqkhohew3Y4tBK+GkC3J6RFjBppJoPLN0tzR3Vw83JYmvKMnxegG48 |
||||
ko2qE74CH+ZCoToqrwn433FiA0wE4IQ018d9BcK936PpZbruj4yPDW4yqmnfWlME |
||||
g8lA5EbxR6oxWuT83g0LL2JIClyqr6O+dvF1u2PmaD39L3rzImaeENM0ht5sHlGM |
||||
O1WcA5efo16WOcqSX6F0cstVy7qE8WJTrzijB731afxTdsIuduHmvT6ywNUgUXeJ |
||||
ulcHRYZzTnHg |
||||
-----END CERTIFICATE----- |
||||
|
||||
Binary file not shown.
@ -0,0 +1,20 @@ |
||||
# This is the list of trusted keys. Comment lines, like this one, as |
||||
# well as empty lines are ignored. Lines have a length limit but this |
||||
# is not a serious limitation as the format of the entries is fixed and |
||||
# checked by gpg-agent. A non-comment line starts with optional white |
||||
# space, followed by the SHA-1 fingerpint in hex, followed by a flag |
||||
# which may be one of 'P', 'S' or '*' and optionally followed by a list of |
||||
# other flags. The fingerprint may be prefixed with a '!' to mark the |
||||
# key as not trusted. You should give the gpg-agent a HUP or run the |
||||
# command "gpgconf --reload gpg-agent" after changing this file. |
||||
|
||||
|
||||
# Include the default trust list |
||||
include-default |
||||
|
||||
|
||||
# CN=KMail Test |
||||
# O=KDE |
||||
# C=CZ |
||||
0F:DD:97:2B:CE:FB:57:35:DC:7E:8E:E5:7D:B7:BA:4E:5F:DB:E2:18 S relax |
||||
|
||||
@ -0,0 +1,93 @@ |
||||
/*
|
||||
* Copyright (c) 2017 Daniel Vrátil <dvratil@kde.org> |
||||
* |
||||
* 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 2 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, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
* |
||||
*/ |
||||
|
||||
#include "filteractiondecrypt.h" |
||||
#include "mailcommon_debug.h" |
||||
|
||||
#include <KLocalizedString> |
||||
|
||||
#include <KMime/Message> |
||||
|
||||
#include <QGpgME/DecryptJob> |
||||
#include <QGpgME/Protocol> |
||||
#include <gpgme++/decryptionresult.h> |
||||
|
||||
#include <QVector> |
||||
|
||||
#include <Akonadi/KMime/MessageFlags> |
||||
|
||||
using namespace MailCommon; |
||||
|
||||
FilterActionDecrypt::FilterActionDecrypt(QObject *parent) |
||||
: FilterActionWithCrypto(QStringLiteral("decrypt"), i18n("Decrypt"), parent) |
||||
{ |
||||
} |
||||
|
||||
FilterActionDecrypt::~FilterActionDecrypt() |
||||
{ |
||||
} |
||||
|
||||
FilterAction *FilterActionDecrypt::newAction() |
||||
{ |
||||
return new FilterActionDecrypt(); |
||||
} |
||||
|
||||
QString FilterActionDecrypt::displayString() const |
||||
{ |
||||
return i18n("Decrypt"); |
||||
} |
||||
|
||||
QString FilterActionDecrypt::argsAsString() const |
||||
{ |
||||
return {}; |
||||
} |
||||
|
||||
void FilterActionDecrypt::argsFromString(const QString &) |
||||
{ |
||||
} |
||||
|
||||
SearchRule::RequiredPart FilterActionDecrypt::requiredPart() const |
||||
{ |
||||
return SearchRule::CompleteMessage; |
||||
} |
||||
|
||||
FilterAction::ReturnCode FilterActionDecrypt::process(ItemContext &context, bool) const |
||||
{ |
||||
auto &item = context.item(); |
||||
if (!item.hasPayload<KMime::Message::Ptr>()) { |
||||
return ErrorNeedComplete; |
||||
} |
||||
|
||||
auto msg = item.payload<KMime::Message::Ptr>(); |
||||
if (!KMime::isEncrypted(msg.data())) { |
||||
return GoOn; |
||||
} |
||||
|
||||
bool wasEncrypted; |
||||
auto nec = decryptMessage(msg, wasEncrypted); |
||||
if (!nec) { |
||||
return wasEncrypted ? ErrorButGoOn: GoOn; |
||||
} |
||||
|
||||
context.item().setPayload(nec); |
||||
context.item().clearFlag(Akonadi::MessageFlags::Encrypted); |
||||
context.setNeedsPayloadStore(); |
||||
context.setNeedsFlagStore(); |
||||
return GoOn; |
||||
} |
||||
@ -0,0 +1,48 @@ |
||||
/*
|
||||
* Copyright (c) 2017 Daniel Vrátil <dvratil@kde.org> |
||||
* |
||||
* 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 2 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, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef MAILCOMMON_FILTERACTION_DECRYPT_H_ |
||||
#define MAILCOMMON_FILTERACTION_DECRYPT_H_ |
||||
|
||||
#include "filteractionwithcrypto.h" |
||||
|
||||
|
||||
namespace MailCommon { |
||||
|
||||
class FilterActionDecrypt : public FilterActionWithCrypto |
||||
{ |
||||
Q_OBJECT |
||||
public: |
||||
explicit FilterActionDecrypt(QObject *parent = nullptr); |
||||
~FilterActionDecrypt() override; |
||||
|
||||
static FilterAction *newAction(); |
||||
|
||||
QString displayString() const override; |
||||
|
||||
QString argsAsString() const override; |
||||
void argsFromString(const QString &argsStr) override; |
||||
|
||||
SearchRule::RequiredPart requiredPart() const override; |
||||
FilterAction::ReturnCode process(ItemContext &context, bool applyOnOutbound) const override; |
||||
}; |
||||
|
||||
} // namespace MailCommon
|
||||
|
||||
#endif |
||||
@ -0,0 +1,296 @@ |
||||
/*
|
||||
* Copyright (c) 2017 Daniel Vrátil <dvratil@kde.org> |
||||
* |
||||
* 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 2 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, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
* |
||||
*/ |
||||
|
||||
#include "filteractionencrypt.h" |
||||
#include "mailcommon_debug.h" |
||||
|
||||
#include <QEventLoop> |
||||
#include <QVBoxLayout> |
||||
#include <QCheckBox> |
||||
#include <QLabel> |
||||
|
||||
#include <KLocalizedString> |
||||
|
||||
#include <KMime/Message> |
||||
|
||||
#include <QGpgME/EncryptJob> |
||||
#include <QGpgME/KeyListJob> |
||||
#include <QGpgME/ListAllKeysJob> |
||||
#include <QGpgME/Protocol> |
||||
#include <gpgme++/encryptionresult.h> |
||||
#include <gpgme++/keylistresult.h> |
||||
|
||||
#include <Libkleo/KeySelectionCombo> |
||||
#include <Libkleo/DefaultKeyFilter> |
||||
|
||||
#include <MessageComposer/EncryptJob> |
||||
|
||||
#include <Akonadi/KMime/MessageFlags> |
||||
|
||||
#include <KColorScheme> |
||||
|
||||
using namespace MailCommon; |
||||
|
||||
#define LISTING_FINISHED "listingFinished" |
||||
|
||||
FilterActionEncrypt::FilterActionEncrypt(QObject *parent) |
||||
: FilterActionWithCrypto(QStringLiteral("encrypt"), i18n("Encrypt"), parent) |
||||
, mKeyCache(Kleo::KeyCache::instance()) |
||||
{ |
||||
} |
||||
|
||||
FilterActionEncrypt::~FilterActionEncrypt() |
||||
{ |
||||
} |
||||
|
||||
FilterAction *FilterActionEncrypt::newAction() |
||||
{ |
||||
return new FilterActionEncrypt(); |
||||
} |
||||
|
||||
QString FilterActionEncrypt::displayString() const |
||||
{ |
||||
return label(); |
||||
} |
||||
|
||||
QString FilterActionEncrypt::argsAsString() const |
||||
{ |
||||
if (mKey.isNull()) { |
||||
return {}; |
||||
} |
||||
|
||||
const auto proto = ((mKey.protocol() == GpgME::OpenPGP) ? QStringLiteral("PGP") |
||||
: QStringLiteral("SMIME")); |
||||
return QStringLiteral("%1:%2:%3").arg(proto, QString::number(int(mReencrypt)), |
||||
QString::fromLatin1(mKey.primaryFingerprint())); |
||||
} |
||||
|
||||
void FilterActionEncrypt::argsFromString(const QString &argsStr) |
||||
{ |
||||
const int pos = argsStr.indexOf(QLatin1Char(':')); |
||||
const auto protoStr = argsStr.leftRef(pos); |
||||
|
||||
QGpgME::Protocol *proto = {}; |
||||
if (protoStr == QLatin1String("PGP")) { |
||||
proto = QGpgME::openpgp(); |
||||
} else if (protoStr == QLatin1String("SMIME")) { |
||||
proto = QGpgME::smime(); |
||||
} else { |
||||
qCWarning(MAILCOMMON_LOG) << "Unknown protocol specified:" << protoStr; |
||||
return; |
||||
} |
||||
|
||||
mReencrypt = static_cast<bool>(argsStr.midRef(pos + 1, 1).toInt()); |
||||
|
||||
const auto fp = argsStr.mid(pos + 3); |
||||
auto listJob = proto->keyListJob(false, true, true); |
||||
|
||||
std::vector<GpgME::Key> keys; |
||||
auto result = listJob->exec({ fp }, true, keys); |
||||
listJob->deleteLater(); |
||||
|
||||
if (result.error()) { |
||||
qCWarning(MAILCOMMON_LOG) << "Failed to retrieve keys:" << result.error().asString(); |
||||
return; |
||||
} |
||||
|
||||
if (keys.empty()) { |
||||
qCWarning(MAILCOMMON_LOG) << "Could not obtain configured key: key expired or removed?"; |
||||
// TODO: Interactively ask user to re-configure the filter
|
||||
return; |
||||
} |
||||
|
||||
mKey = keys[0]; |
||||
} |
||||
|
||||
SearchRule::RequiredPart FilterActionEncrypt::requiredPart() const |
||||
{ |
||||
return SearchRule::CompleteMessage; |
||||
} |
||||
|
||||
FilterAction::ReturnCode FilterActionEncrypt::process(ItemContext &context, bool) const |
||||
{ |
||||
if (mKey.isNull()) { |
||||
qCWarning(MAILCOMMON_LOG) << "FilterActionEncrypt::process called without filter having a key!"; |
||||
return ErrorButGoOn; |
||||
} |
||||
|
||||
auto &item = context.item(); |
||||
if (!item.hasPayload<KMime::Message::Ptr>()) { |
||||
qCWarning(MAILCOMMON_LOG) << "Item" << item.id() << "does not contain KMime::Message payload!"; |
||||
return ErrorNeedComplete; |
||||
} |
||||
|
||||
auto msg = item.payload<KMime::Message::Ptr>(); |
||||
if (KMime::isEncrypted(msg.data())) { |
||||
if (mReencrypt) { |
||||
// Make sure the email is not already encrypted by the mKey - this is
|
||||
// a little expensive, but still much cheaper than modifying and
|
||||
// re-uploading the email to the server
|
||||
const auto encryptionKeys = getEncryptionKeysFromContent(msg, mKey.protocol()); |
||||
qCDebug(MAILCOMMON_LOG) << "Item" << item.id() << "encrypted by following keys: " << encryptionKeys; |
||||
if (!encryptionKeys.isEmpty()) { |
||||
if (mKey.protocol() == GpgME::OpenPGP) { |
||||
std::vector<std::string> ids; |
||||
ids.reserve(encryptionKeys.size()); |
||||
for (const auto key : encryptionKeys) { |
||||
ids.push_back(key.toStdString()); |
||||
} |
||||
for (const auto key : mKeyCache->findByKeyIDOrFingerprint(ids)) { |
||||
if (qstrcmp(key.primaryFingerprint(), mKey.primaryFingerprint()) == 0) { |
||||
// This email is already encrypted with the target key,
|
||||
// so there's no need to re-encrypt it
|
||||
qCDebug(MAILCOMMON_LOG) << "Item" << item.id() << "already encrypted with" << mKey.primaryFingerprint() << ", not re-encrypting"; |
||||
return GoOn; |
||||
} |
||||
} |
||||
} else if (mKey.protocol() == GpgME::CMS) { |
||||
// We are only able to get serial
|
||||
for (const auto key : mKeyCache->secretKeys()) { |
||||
if (qstrcmp(key.issuerSerial(), mKey.issuerSerial()) == 0) { |
||||
// This email is already encrypted with the target key,
|
||||
// so there's no need to re-encrypt it
|
||||
qCDebug(MAILCOMMON_LOG) << "Item" << item.id() << "already encrypted with" << mKey.primaryFingerprint() << ", not re-encrypting"; |
||||
return GoOn; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
bool dummy; // dummy
|
||||
const auto decrypted = decryptMessage(msg, dummy); |
||||
if (!decrypted) { |
||||
// We failed to decrypt the encrypted email - very likely we just don't
|
||||
// have the right key, so don't consider it an error
|
||||
return GoOn; |
||||
} else { |
||||
msg = decrypted; |
||||
} |
||||
} else { |
||||
return GoOn; |
||||
} |
||||
} |
||||
|
||||
MessageComposer::EncryptJob encrypt; |
||||
encrypt.setContent(msg.data()); |
||||
encrypt.setCryptoMessageFormat(mKey.protocol() == GpgME::OpenPGP ? Kleo::OpenPGPMIMEFormat : Kleo::SMIMEFormat); |
||||
encrypt.setEncryptionKeys({ mKey }); |
||||
encrypt.exec(); |
||||
if (encrypt.error()) { |
||||
qCWarning(MAILCOMMON_LOG) << "Encryption error:" << encrypt.errorString(); |
||||
return ErrorButGoOn; |
||||
} |
||||
|
||||
KMime::Content *result = encrypt.content(); |
||||
result->assemble(); |
||||
|
||||
auto nec = assembleMessage(msg, result); |
||||
context.item().setPayload(nec); |
||||
context.item().setFlag(Akonadi::MessageFlags::Encrypted); |
||||
context.setNeedsPayloadStore(); |
||||
context.setNeedsFlagStore(); |
||||
|
||||
delete result; |
||||
|
||||
return GoOn; |
||||
} |
||||
|
||||
bool FilterActionEncrypt::isEmpty() const |
||||
{ |
||||
return mKey.isNull(); |
||||
} |
||||
|
||||
QString FilterActionEncrypt::informationAboutNotValidAction() const |
||||
{ |
||||
return i18n("No encryption key has been selected"); |
||||
} |
||||
|
||||
QWidget *FilterActionEncrypt::createParamWidget(QWidget *parent) const |
||||
{ |
||||
auto w = new QWidget(parent); |
||||
auto l = new QVBoxLayout; |
||||
w->setLayout(l); |
||||
|
||||
auto combo = new Kleo::KeySelectionCombo(w); |
||||
combo->setDefaultKey(QString::fromLatin1(mKey.primaryFingerprint())); |
||||
|
||||
std::shared_ptr<Kleo::DefaultKeyFilter> filter(new Kleo::DefaultKeyFilter); |
||||
filter->setIsOpenPGP(Kleo::DefaultKeyFilter::DoesNotMatter); |
||||
filter->setCanEncrypt(Kleo::DefaultKeyFilter::Set); |
||||
filter->setHasSecret(Kleo::DefaultKeyFilter::Set); |
||||
combo->setKeyFilter(filter); |
||||
|
||||
combo->setProperty(LISTING_FINISHED, false); |
||||
connect(combo, &Kleo::KeySelectionCombo::keyListingFinished, |
||||
combo, [combo] { |
||||
combo->setProperty(LISTING_FINISHED, true); |
||||
}); |
||||
connect(combo, &Kleo::KeySelectionCombo::currentKeyChanged, |
||||
this, &FilterActionEncrypt::filterActionModified); |
||||
l->addWidget(combo); |
||||
|
||||
auto chkBox = new QCheckBox(w); |
||||
chkBox->setText(i18n("Re-encrypt encrypted emails with this key")); |
||||
chkBox->setChecked(mReencrypt); |
||||
l->addWidget(chkBox); |
||||
|
||||
auto lbl = new QLabel(w); |
||||
auto palette = lbl->palette(); |
||||
palette.setColor(lbl->foregroundRole(), KColorScheme(QPalette::Normal).foreground(KColorScheme::NegativeText).color()); |
||||
lbl->setPalette(palette); |
||||
lbl->setWordWrap(true); |
||||
lbl->setText(i18n("<b>Warning:</b> the encrypted emails will be uploaded back to the server!")); |
||||
lbl->setToolTip(i18n("<p>You will not be able to read the encrypted emails on any other computer " |
||||
"or email client unless you have your private key available there.</p>" |
||||
"<p>Also note that most webmail interfaces don't support encryption, so you " |
||||
"will not be able to read the encrypted emails there.</p>")); |
||||
l->addWidget(lbl); |
||||
|
||||
return w; |
||||
} |
||||
|
||||
void FilterActionEncrypt::setParamWidgetValue(QWidget *paramWidget) const |
||||
{ |
||||
if (auto combo = paramWidget->findChild<Kleo::KeySelectionCombo*>()) { |
||||
combo->setDefaultKey(QString::fromLatin1(mKey.primaryFingerprint())); |
||||
combo->setCurrentKey(QString::fromLatin1(mKey.primaryFingerprint())); |
||||
} |
||||
if (auto chkBox = paramWidget->findChild<QCheckBox*>()) { |
||||
chkBox->setChecked(mReencrypt); |
||||
} |
||||
} |
||||
|
||||
void FilterActionEncrypt::applyParamWidgetValue(QWidget *paramWidget) |
||||
{ |
||||
if (auto combo = paramWidget->findChild<Kleo::KeySelectionCombo*>()) { |
||||
// FIXME: This is super-ugly, but unfortunately the filtering code generates
|
||||
// several instances of this filter and passes the paramWidgets from one
|
||||
// instance to another to "copy" stuff in between, which in our case leads
|
||||
// to this method being called on an un-populated combobox
|
||||
if (!combo->property(LISTING_FINISHED).toBool()) { |
||||
QEventLoop ev; |
||||
connect(combo, &Kleo::KeySelectionCombo::keyListingFinished, |
||||
&ev, &QEventLoop::quit, Qt::QueuedConnection); |
||||
ev.exec(); |
||||
} |
||||
mKey = combo->currentKey(); |
||||
} |
||||
if (auto chkBox = paramWidget->findChild<QCheckBox*>()) { |
||||
mReencrypt = chkBox->isChecked(); |
||||
} |
||||
} |
||||
@ -0,0 +1,66 @@ |
||||
/*
|
||||
* Copyright (c) 2017 Daniel Vrátil <dvratil@kde.org> |
||||
* |
||||
* 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 2 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, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef MAILCOMMON_FILTERACTION_ENCRYPT_H_ |
||||
#define MAILCOMMON_FILTERACTION_ENCRYPT_H_ |
||||
|
||||
#include "filteractionwithcrypto.h" |
||||
|
||||
#include <gpgme++/key.h> |
||||
|
||||
#include <Libkleo/KeyCache> |
||||
|
||||
namespace MailCommon { |
||||
|
||||
class FilterActionEncrypt : public FilterActionWithCrypto |
||||
{ |
||||
Q_OBJECT |
||||
public: |
||||
explicit FilterActionEncrypt(QObject *parent = nullptr); |
||||
~FilterActionEncrypt() override; |
||||
|
||||
static FilterAction *newAction(); |
||||
|
||||
QString displayString() const override; |
||||
|
||||
QString argsAsString() const override; |
||||
void argsFromString(const QString &argsStr) override; |
||||
|
||||
SearchRule::RequiredPart requiredPart() const override; |
||||
FilterAction::ReturnCode process(ItemContext &context, bool applyOnOutbound) const override; |
||||
|
||||
bool isEmpty() const override; |
||||
|
||||
QString informationAboutNotValidAction() const override; |
||||
|
||||
QWidget *createParamWidget(QWidget *parent) const override; |
||||
void setParamWidgetValue(QWidget *paramWidget) const override; |
||||
void applyParamWidgetValue(QWidget *paramWidget) override; |
||||
|
||||
GpgME::Key key() const { return mKey; } |
||||
bool reencrypt() const { return mReencrypt; } |
||||
private: |
||||
std::shared_ptr<const Kleo::KeyCache> mKeyCache; |
||||
GpgME::Key mKey; |
||||
bool mReencrypt; |
||||
}; |
||||
|
||||
} // namespace MailCommon
|
||||
|
||||
#endif |
||||
@ -0,0 +1,228 @@ |
||||
/*
|
||||
* Copyright (c) 2017 Daniel Vrátil <dvratil@kde.org> |
||||
* |
||||
* 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 2 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, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
* |
||||
*/ |
||||
|
||||
#include "filteractionwithcrypto.h" |
||||
#include "mailcommon_debug.h" |
||||
|
||||
#include <QGpgME/Protocol> |
||||
#include <QGpgME/DecryptJob> |
||||
|
||||
#include <gpgme++/decryptionresult.h> |
||||
|
||||
#include <QProcess> |
||||
#include <QStandardPaths> |
||||
#include <QRegularExpression> |
||||
|
||||
using namespace MailCommon; |
||||
|
||||
bool FilterActionWithCrypto::isPGP(const KMime::Content *part, bool allowOctetStream) const |
||||
{ |
||||
const auto ct = static_cast<KMime::Headers::ContentType*>(part->headerByType("Content-Type")); |
||||
return ct &&(ct->isSubtype("pgp-encrypted") || ct->isSubtype("encrypted") |
||||
|| (allowOctetStream && ct->isMimeType("application/octet-stream"))); |
||||
} |
||||
|
||||
bool FilterActionWithCrypto::isSMIME(const KMime::Content *part) const |
||||
{ |
||||
const auto ct = static_cast<KMime::Headers::ContentType*>(part->headerByType("Content-Type")); |
||||
return ct && (ct->isSubtype("pkcs7-mime") || ct->isSubtype("x-pkcs7-mime")); |
||||
} |
||||
|
||||
|
||||
KMime::Message::Ptr FilterActionWithCrypto::decryptMessage(const KMime::Message::Ptr &msg, |
||||
bool &wasEncrypted) const |
||||
{ |
||||
QGpgME::Protocol *proto = {}; |
||||
if (msg->mainBodyPart("multipart/encrypted")) { |
||||
const auto subparts = msg->contents(); |
||||
for (auto subpart : subparts) { |
||||
if (isPGP(subpart, true)) { |
||||
proto = QGpgME::openpgp(); |
||||
break; |
||||
} else if (isSMIME(subpart)) { |
||||
proto = QGpgME::smime(); |
||||
break; |
||||
} |
||||
} |
||||
} else { |
||||
if (isPGP(msg.data())) { |
||||
proto = QGpgME::openpgp(); |
||||
} else if (isSMIME(msg.data())) { |
||||
proto = QGpgME::smime(); |
||||
} |
||||
} |
||||
|
||||
if (!proto) { |
||||
// Not encrypted, or we don't recognize the encryption
|
||||
wasEncrypted = false; |
||||
return {}; |
||||
} |
||||
|
||||
wasEncrypted = true; |
||||
QByteArray outData, inData; |
||||
if (proto == QGpgME::smime()) { |
||||
inData = QByteArray::fromBase64(msg->encodedBody()); |
||||
} else { |
||||
inData = msg->encodedContent(); |
||||
} |
||||
auto decrypt = proto->decryptJob(); |
||||
auto result = decrypt->exec(inData, outData); |
||||
if (result.error()) { |
||||
// unknown key, invalid algo, or general error
|
||||
qCWarning(MAILCOMMON_LOG) << "Failed to decrypt:" << result.error().asString(); |
||||
return {}; |
||||
} |
||||
|
||||
KMime::Content decCt; |
||||
decCt.setContent(outData); |
||||
decCt.parse(); |
||||
decCt.assemble(); |
||||
|
||||
return assembleMessage(msg, &decCt); |
||||
} |
||||
|
||||
void FilterActionWithCrypto::copyHeader(const KMime::Headers::Base *header, |
||||
KMime::Message::Ptr msg) const |
||||
{ |
||||
auto newHdr = KMime::Headers::createHeader(header->type()); |
||||
if (!newHdr) { |
||||
newHdr = new KMime::Headers::Generic(header->type()); |
||||
} |
||||
newHdr->fromUnicodeString(header->asUnicodeString(), "UTF-8"); |
||||
msg->appendHeader(newHdr); |
||||
} |
||||
|
||||
bool FilterActionWithCrypto::isContentHeader(const KMime::Headers::Base *header) const |
||||
{ |
||||
return header->is("Content-Type") |
||||
|| header->is("Content-Transfer-Encoding") |
||||
|| header->is("Content-Disposition"); |
||||
} |
||||
|
||||
KMime::Message::Ptr FilterActionWithCrypto::assembleMessage(const KMime::Message::Ptr &orig, |
||||
const KMime::Content *newContent) const |
||||
{ |
||||
auto out = KMime::Message::Ptr::create(); |
||||
// Use the new content as message content
|
||||
out->setBody(const_cast<KMime::Content*>(newContent)->encodedBody()); |
||||
out->parse(); |
||||
|
||||
// Copy over headers from the original message, except for CT, CTE and CD
|
||||
// headers, we want to preserve those from the new content
|
||||
QVector<KMime::Headers::Base*> headers = orig->headers(); |
||||
for (const auto hdr : qAsConst(headers)) { |
||||
if (isContentHeader(hdr)) { |
||||
continue; |
||||
} |
||||
|
||||
copyHeader(hdr, out); |
||||
} |
||||
|
||||
// Overwrite some headers by those provided by the new content
|
||||
headers = newContent->headers(); |
||||
for (const auto hdr : qAsConst(headers)) { |
||||
if (isContentHeader(hdr)) { |
||||
copyHeader(hdr, out); |
||||
} |
||||
} |
||||
|
||||
out->assemble(); |
||||
out->parse(); |
||||
|
||||
return out; |
||||
} |
||||
|
||||
QStringList FilterActionWithCrypto::getEncryptionKeysFromContent(const KMime::Message::Ptr &msg, |
||||
GpgME::Protocol protocol) const |
||||
{ |
||||
if (protocol == GpgME::CMS && mGpgSmPath.isNull()) { |
||||
auto path = QStandardPaths::findExecutable(QStringLiteral("gpgsm")); |
||||
mGpgSmPath = path.isEmpty() ? QStringLiteral("") : path; |
||||
} else if (protocol == GpgME::OpenPGP && mGpgPath.isNull()) { |
||||
auto path = QStandardPaths::findExecutable(QStringLiteral("gpg2")); |
||||
if (path.isEmpty()) { |
||||
path = QStandardPaths::findExecutable(QStringLiteral("gpg")); |
||||
mGpgPath = path.isEmpty() ? QStringLiteral("") : path; |
||||
} else { |
||||
mGpgPath = path; |
||||
} |
||||
} |
||||
|
||||
if ((protocol == GpgME::CMS && mGpgSmPath.isEmpty()) |
||||
|| (protocol == GpgME::OpenPGP && mGpgPath.isEmpty())) { |
||||
return {}; |
||||
} |
||||
|
||||
QProcess gpg; |
||||
QStringList keyIds; |
||||
// TODO: contribute an API for this into gpgme
|
||||
if (protocol == GpgME::OpenPGP) { |
||||
gpg.setProgram(mGpgPath); |
||||
// --list-packets will give us list of keys used to encrypt the message
|
||||
// --batch will prevent gpg from asking for decryption password (we don't need it yet)
|
||||
gpg.setArguments({ QStringLiteral("--list-packets"), QStringLiteral("--batch") }); |
||||
gpg.start(QIODevice::ReadWrite); |
||||
gpg.waitForStarted(); |
||||
gpg.write(msg->encodedContent()); |
||||
gpg.closeWriteChannel(); |
||||
gpg.waitForFinished(); |
||||
while (!gpg.atEnd()) { |
||||
const auto l = gpg.readLine(); |
||||
if (l.startsWith(":pubkey")) { |
||||
const int pos = l.indexOf("keyid "); |
||||
if (pos < 0) { |
||||
continue; |
||||
} |
||||
const int start = pos + 6; // strlen("keyid ")
|
||||
const int len = l.size() - start - 1; // -1 to skip trailing \n
|
||||
keyIds << QString::fromUtf8(l.mid(start, len)); |
||||
} |
||||
} |
||||
} else if (protocol == GpgME::CMS) { |
||||
gpg.setProgram(mGpgSmPath); |
||||
// --decrypt - the only way how to get the keys from gpgsm, sadly, is to decrypt the email
|
||||
// --status-fd 2 - make sure the status output is not mangled with the decrypted content
|
||||
// --assume-base64 - so that we don't have to decode it ourselves
|
||||
gpg.setArguments({ QStringLiteral("--decrypt"), |
||||
QStringLiteral("--status-fd"), QStringLiteral("2"), |
||||
QStringLiteral("--debug-level"), QStringLiteral("basic"), |
||||
QStringLiteral("--assume-base64") }); |
||||
gpg.start(QIODevice::ReadWrite); |
||||
gpg.waitForStarted(); |
||||
gpg.write(msg->encodedBody()); // just the body!
|
||||
gpg.closeWriteChannel(); |
||||
gpg.waitForFinished(); |
||||
gpg.setReadChannel(QProcess::StandardError); |
||||
while (!gpg.atEnd()) { |
||||
const auto l = gpg.readLine(); |
||||
if (l.startsWith("gpgsm: DBG: recp ")) { |
||||
const int pos = l.indexOf("serial: "); |
||||
if (pos < 0) { |
||||
continue; |
||||
} |
||||
const int start = pos + 8; // strlen("serial: ")
|
||||
const int len = l.size() - start - 1; // -1 to skip trailing \n
|
||||
keyIds << QString::fromUtf8(l.mid(start, len)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
return keyIds; |
||||
} |
||||
|
||||
@ -0,0 +1,59 @@ |
||||
/*
|
||||
* Copyright (c) 2017 Daniel Vrátil <dvratil@kde.org> |
||||
* |
||||
* 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 2 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, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef MAILCOMMON_FILTERACTION_WITH_CRYPTO_H_ |
||||
#define MAILCOMMON_FILTERACTION_WITH_CRYPTO_H_ |
||||
|
||||
#include "filteraction.h" |
||||
|
||||
#include <gpgme++/global.h> |
||||
|
||||
namespace MailCommon { |
||||
|
||||
class FilterActionWithCrypto : public FilterAction |
||||
{ |
||||
Q_OBJECT |
||||
|
||||
protected: |
||||
using FilterAction::FilterAction; |
||||
|
||||
KMime::Message::Ptr assembleMessage(const KMime::Message::Ptr &orig, |
||||
const KMime::Content *newContent) const; |
||||
KMime::Message::Ptr decryptMessage(const KMime::Message::Ptr &decrypt, |
||||
bool &wasEncrypted) const; |
||||
|
||||
bool isPGP(const KMime::Content *content, bool allowOctetStream = false) const; |
||||
bool isSMIME(const KMime::Content *content) const; |
||||
|
||||
QStringList getEncryptionKeysFromContent(const KMime::Message::Ptr &msg, GpgME::Protocol proto) const; |
||||
|
||||
private: |
||||
void copyHeader(const KMime::Headers::Base *header, |
||||
KMime::Message::Ptr destMsg) const; |
||||
bool isContentHeader(const KMime::Headers::Base *header) const; |
||||
|
||||
private: |
||||
// cached values
|
||||
mutable QString mGpgSmPath; |
||||
mutable QString mGpgPath; |
||||
}; |
||||
|
||||
} // namespace MailCommon
|
||||
|
||||
#endif |
||||
Loading…
Reference in new issue