diff --git a/messageviewer/src/dkim-verify/dkimmanagerkey.cpp b/messageviewer/src/dkim-verify/dkimmanagerkey.cpp index 0522056b..2be6b1de 100644 --- a/messageviewer/src/dkim-verify/dkimmanagerkey.cpp +++ b/messageviewer/src/dkim-verify/dkimmanagerkey.cpp @@ -1,131 +1,131 @@ /* Copyright (C) 2018-2019 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dkimmanagerkey.h" - +#include "dkimutil.h" #include #include #include #include #ifdef USE_DKIM_CHECKER #include #endif using namespace MessageViewer; DKIMManagerKey::DKIMManagerKey(QObject *parent) : QObject(parent) { #ifdef USE_DKIM_CHECKER mQcaInitializer = new QCA::Initializer(QCA::Practical, 64); #endif loadKeys(); } DKIMManagerKey::~DKIMManagerKey() { #ifdef USE_DKIM_CHECKER delete mQcaInitializer; #endif saveKeys(); } DKIMManagerKey *DKIMManagerKey::self() { static DKIMManagerKey s_self; return &s_self; } QString DKIMManagerKey::keyValue(const QString &selector, const QString &domain) { for (const KeyInfo &keyInfo : qAsConst(mKeys)) { if (keyInfo.selector == selector && keyInfo.domain == domain) { return keyInfo.keyValue; } } return {}; } void DKIMManagerKey::addKey(const KeyInfo &key) { if (!mKeys.contains(key)) { mKeys.append(key); } } void DKIMManagerKey::removeKey(const QString &key) { for (const KeyInfo &keyInfo : qAsConst(mKeys)) { if (keyInfo.keyValue == key) { mKeys.removeAll(keyInfo); break; } } } QVector DKIMManagerKey::keys() const { return mKeys; } void DKIMManagerKey::loadKeys() { - const KSharedConfig::Ptr &config = KSharedConfig::openConfig(QStringLiteral("dkimsettingsrc"), KConfig::NoGlobals); + const KSharedConfig::Ptr &config = KSharedConfig::openConfig(MessageViewer::DKIMUtil::defaultConfigFileName(), KConfig::NoGlobals); const QStringList keyGroups = config->groupList().filter(QRegularExpression(QStringLiteral("DKIM Key Record #\\d+"))); mKeys.clear(); for (const QString &groupName : keyGroups) { KConfigGroup group = config->group(groupName); const QString selector = group.readEntry(QLatin1String("Selector"), QString()); const QString domain = group.readEntry(QLatin1String("Domain"), QString()); const QString key = group.readEntry(QLatin1String("Key"), QString()); mKeys.append(KeyInfo{key, selector, domain}); } } void DKIMManagerKey::saveKeys() { - const KSharedConfig::Ptr &config = KSharedConfig::openConfig(QStringLiteral("dkimsettingsrc"), KConfig::NoGlobals); + const KSharedConfig::Ptr &config = KSharedConfig::openConfig(MessageViewer::DKIMUtil::defaultConfigFileName(), KConfig::NoGlobals); const QStringList filterGroups = config->groupList().filter(QRegularExpression(QStringLiteral("DKIM Key Record #\\d+"))); for (const QString &group : filterGroups) { config->deleteGroup(group); } for (int i = 0, total = mKeys.count(); i < total; ++i) { const QString groupName = QStringLiteral("DKIM Key Record #%1").arg(i); KConfigGroup group = config->group(groupName); const KeyInfo &info = mKeys.at(i); group.writeEntry(QLatin1String("Selector"), info.selector); group.writeEntry(QLatin1String("Domain"), info.domain); group.writeEntry(QLatin1String("Key"), info.keyValue); } } void DKIMManagerKey::saveKeys(const QVector &lst) { mKeys = lst; saveKeys(); } bool KeyInfo::operator ==(const KeyInfo &other) const { return keyValue == other.keyValue && selector == other.selector && domain == other.domain; } diff --git a/messageviewer/src/dkim-verify/dkimmanagerrules.cpp b/messageviewer/src/dkim-verify/dkimmanagerrules.cpp index d6c61dff..bc9846cb 100644 --- a/messageviewer/src/dkim-verify/dkimmanagerrules.cpp +++ b/messageviewer/src/dkim-verify/dkimmanagerrules.cpp @@ -1,129 +1,129 @@ /* Copyright (C) 2019 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dkimmanagerrules.h" - +#include "dkimutil.h" #include #include #include using namespace MessageViewer; DKIMManagerRules::DKIMManagerRules(QObject *parent) : QObject(parent) { loadRules(); } DKIMManagerRules::~DKIMManagerRules() { save(); } DKIMManagerRules *DKIMManagerRules::self() { static DKIMManagerRules s_self; return &s_self; } void DKIMManagerRules::addRule(const DKIMRule &rule) { if (!mRules.contains(rule)) { mRules.append(rule); save(); } } QVector DKIMManagerRules::rules() const { return mRules; } QStringList DKIMManagerRules::ruleGroups(const KSharedConfig::Ptr &config) const { return config->groupList().filter(QRegularExpression(QStringLiteral("DKIM Rule #\\d+"))); } void DKIMManagerRules::loadRules(const QString &fileName) { - const KSharedConfig::Ptr &config = KSharedConfig::openConfig(fileName.isEmpty() ? QStringLiteral("dkimsettingsrc") : fileName, KConfig::NoGlobals); + const KSharedConfig::Ptr &config = KSharedConfig::openConfig(fileName.isEmpty() ? MessageViewer::DKIMUtil::defaultConfigFileName() : fileName, KConfig::NoGlobals); const QStringList rulesGroups = ruleGroups(config); mRules.clear(); for (const QString &groupName : rulesGroups) { KConfigGroup group = config->group(groupName); const QStringList signedDomainIdentifier = group.readEntry(QLatin1String("SignedDomainIdentifier"), QStringList()); const QString from = group.readEntry(QLatin1String("From"), QString()); const QString domain = group.readEntry(QLatin1String("Domain"), QString()); const bool enabled = group.readEntry(QLatin1String("Enabled"), true); const int ruleType = group.readEntry(QLatin1String("RuleType"), 0); const QString listId = group.readEntry(QLatin1String("List-Id"), QString()); DKIMRule rule; rule.setEnabled(enabled); rule.setDomain(domain); rule.setFrom(from); rule.setListId(listId); rule.setSignedDomainIdentifier(signedDomainIdentifier); rule.setRuleType(static_cast(ruleType)); mRules.append(rule); } } void DKIMManagerRules::saveRules(const QVector &lst) { mRules = lst; save(); } void DKIMManagerRules::clear() { mRules.clear(); save(); } void DKIMManagerRules::importRules(const QString &fileName) { loadRules(fileName); } void DKIMManagerRules::exportRules(const QString &fileName) { save(fileName); } void DKIMManagerRules::save(const QString &fileName) { - const KSharedConfig::Ptr &config = KSharedConfig::openConfig(fileName.isEmpty() ? QStringLiteral("dkimsettingsrc") : fileName, KConfig::NoGlobals); + const KSharedConfig::Ptr &config = KSharedConfig::openConfig(fileName.isEmpty() ? MessageViewer::DKIMUtil::defaultConfigFileName() : fileName, KConfig::NoGlobals); const QStringList rulesGroups = ruleGroups(config); for (const QString &group : rulesGroups) { config->deleteGroup(group); } for (int i = 0, total = mRules.count(); i < total; ++i) { const QString groupName = QStringLiteral("DKIM Rule #%1").arg(i); KConfigGroup group = config->group(groupName); const DKIMRule &rule = mRules.at(i); group.writeEntry(QLatin1String("SignedDomainIdentifier"), rule.signedDomainIdentifier()); group.writeEntry(QLatin1String("From"), rule.from()); group.writeEntry(QLatin1String("Domain"), rule.domain()); group.writeEntry(QLatin1String("Enabled"), rule.enabled()); group.writeEntry(QLatin1String("RuleType"), static_cast(rule.ruleType())); group.writeEntry(QLatin1String("List-Id"), rule.listId()); } } diff --git a/messageviewer/src/dkim-verify/dkimutil.cpp b/messageviewer/src/dkim-verify/dkimutil.cpp index a2034251..f3b04c48 100644 --- a/messageviewer/src/dkim-verify/dkimutil.cpp +++ b/messageviewer/src/dkim-verify/dkimutil.cpp @@ -1,160 +1,165 @@ /* Copyright (C) 2018-2019 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dkimutil.h" #include #include #include QString MessageViewer::DKIMUtil::bodyCanonizationRelaxed(QString body) { /* * canonicalize the body using the relaxed algorithm * specified in Section 3.4.4 of RFC 6376 */ /* a. Reduce whitespace: * Ignore all whitespace at the end of lines. Implementations MUST NOT remove the CRLF at the end of the line. * Reduce all sequences of WSP within a line to a single SP character. b. Ignore all empty lines at the end of the message body. "Empty line" is defined in Section 3.4.3. If the body is non-empty but does not end with a CRLF, a CRLF is added. (For email, this is only possible when using extensions to SMTP or non-SMTP transport mechanisms.) */ body.replace(QLatin1String("\n"), QLatin1String("\r\n")); body.replace(QRegularExpression(QLatin1String("[ \t]+\r\n")), QLatin1String("\r\n")); body.replace(QRegularExpression(QLatin1String("[ \t]+")), QStringLiteral(" ")); body.replace(QRegularExpression(QLatin1String("((\r\n)+?)$")), QLatin1String("\r\n")); if (body == QLatin1String("\r\n")) { body.clear(); } return body; } QString MessageViewer::DKIMUtil::bodyCanonizationSimple(QString body) { // The "simple" body canonicalization algorithm ignores all empty lines // at the end of the message body. An empty line is a line of zero // length after removal of the line terminator. If there is no body or // no trailing CRLF on the message body, a CRLF is added. It makes no // other changes to the message body. In more formal terms, the // "simple" body canonicalization algorithm converts "*CRLF" at the end // of the body to a single "CRLF". // Note that a completely empty or missing body is canonicalized as a // single "CRLF"; that is, the canonicalized length will be 2 octets. body.replace(QLatin1String("\n"), QLatin1String("\r\n")); body.replace(QRegularExpression(QLatin1String("((\r\n)+)?$")), QLatin1String("\r\n")); if (body.endsWith(QLatin1String("\r\n"))) { //Remove it from start body.chop(2); } if (body.isEmpty()) { body = QLatin1String("\r\n"); } return body; } QByteArray MessageViewer::DKIMUtil::generateHash(const QByteArray &body, QCryptographicHash::Algorithm algo) { return QCryptographicHash::hash(body, algo).toBase64(); } QString MessageViewer::DKIMUtil::headerCanonizationSimple(const QString &headerName, const QString &headerValue) { //TODO verify it lower it ? return headerName + QLatin1Char(':') + headerValue; } QString MessageViewer::DKIMUtil::headerCanonizationRelaxed(const QString &headerName, const QString &headerValue, bool removeQuoteOnContentType) { // The "relaxed" header canonicalization algorithm MUST apply the // following steps in order: // o Convert all header field names (not the header field values) to // lowercase. For example, convert "SUBJect: AbC" to "subject: AbC". // o Unfold all header field continuation lines as described in // [RFC5322]; in particular, lines with terminators embedded in // continued header field values (that is, CRLF sequences followed by // WSP) MUST be interpreted without the CRLF. Implementations MUST // NOT remove the CRLF at the end of the header field value. // o Convert all sequences of one or more WSP characters to a single SP // character. WSP characters here include those before and after a // line folding boundary. // o Delete all WSP characters at the end of each unfolded header field // value. // o Delete any WSP characters remaining before and after the colon // separating the header field name from the header field value. The // colon separator MUST be retained. QString newHeaderName = headerName.toLower(); QString newHeaderValue = headerValue; newHeaderValue.replace(QRegularExpression(QStringLiteral("\r\n[ \t]+")), QStringLiteral(" ")); newHeaderValue.replace(QRegularExpression(QStringLiteral("[ \t]+")), QStringLiteral(" ")); newHeaderValue.replace(QRegularExpression(QStringLiteral("[ \t]+\r\n")), QStringLiteral("\r\n")); //Perhaps remove tab after headername and before value name //newHeaderValue.replace(QRegularExpression(QStringLiteral("[ \t]*:[ \t]")), QStringLiteral(":")); if (newHeaderName == QLatin1String("content-type") && removeQuoteOnContentType) { //Remove quote in charset if (newHeaderValue.contains(QLatin1String("charset=\""))) { newHeaderValue.remove(QLatin1Char('"')); } } //Remove extra space. newHeaderValue = newHeaderValue.trimmed(); return newHeaderName + QLatin1Char(':') + newHeaderValue; } QString MessageViewer::DKIMUtil::cleanString(QString str) { //Move as static ? // WSP help pattern as specified in Section 2.8 of RFC 6376 const QString pattWSP = QStringLiteral("[ \t]"); // FWS help pattern as specified in Section 2.8 of RFC 6376 const QString pattFWS = QStringLiteral("(?:") + pattWSP + QStringLiteral("*(?:\r\n)?") + pattWSP + QStringLiteral("+)"); str.replace(QRegularExpression(pattFWS), QString()); return str; } QString MessageViewer::DKIMUtil::emailDomain(const QString &emailDomain) { return emailDomain.right(emailDomain.length() - emailDomain.indexOf(QLatin1Char('@')) - 1); } QString MessageViewer::DKIMUtil::emailSubDomain(const QString &emailDomain) { int dotNumber = 0; for (int i = emailDomain.length() - 1; i >= 0; --i) { if (emailDomain.at(i) == QLatin1Char('.')) { dotNumber++; if (dotNumber == 2) { return emailDomain.right(emailDomain.length() - i - 1); } } } return emailDomain; } + +QString MessageViewer::DKIMUtil::defaultConfigFileName() +{ + return QStringLiteral("dkimsettingsrc"); +} diff --git a/messageviewer/src/dkim-verify/dkimutil.h b/messageviewer/src/dkim-verify/dkimutil.h index c4333cea..400c3967 100644 --- a/messageviewer/src/dkim-verify/dkimutil.h +++ b/messageviewer/src/dkim-verify/dkimutil.h @@ -1,38 +1,39 @@ /* Copyright (C) 2018-2019 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DKIMUTIL_H #define DKIMUTIL_H #include #include #include "messageviewer_private_export.h" namespace MessageViewer { namespace DKIMUtil { MESSAGEVIEWER_TESTS_EXPORT Q_REQUIRED_RESULT QString bodyCanonizationRelaxed(QString body); MESSAGEVIEWER_TESTS_EXPORT Q_REQUIRED_RESULT QString bodyCanonizationSimple(QString body); MESSAGEVIEWER_TESTS_EXPORT Q_REQUIRED_RESULT QByteArray generateHash(const QByteArray &body, QCryptographicHash::Algorithm algo); MESSAGEVIEWER_TESTS_EXPORT Q_REQUIRED_RESULT QString headerCanonizationSimple(const QString &headerName, const QString &headerValue); MESSAGEVIEWER_TESTS_EXPORT Q_REQUIRED_RESULT QString headerCanonizationRelaxed(const QString &headerName, const QString &headerValue, bool removeQuoteOnContentType); MESSAGEVIEWER_TESTS_EXPORT Q_REQUIRED_RESULT QString cleanString(QString str); MESSAGEVIEWER_TESTS_EXPORT Q_REQUIRED_RESULT QString emailDomain(const QString &emailDomain); MESSAGEVIEWER_TESTS_EXPORT Q_REQUIRED_RESULT QString emailSubDomain(const QString &emailDomain); +MESSAGEVIEWER_TESTS_EXPORT Q_REQUIRED_RESULT QString defaultConfigFileName(); } } #endif // DKIMUTIL_H