File indexing completed on 2024-04-14 05:43:30

0001 /*
0002     SPDX-FileCopyrightText: 2008-2022 Rolf Eike Beer <kde@opensource.sf-tec.de>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "kgpgimport.h"
0007 #include "kgpg_general_debug.h"
0008 
0009 #include "model/kgpgitemmodel.h"
0010 #include "core/KGpgKeyNode.h"
0011 
0012 
0013 
0014 #include <KLocalizedString>
0015 
0016 KGpgImport::KGpgImport(QObject *parent, const QString &text)
0017     : KGpgTextOrFileTransaction(parent, text, true)
0018 {
0019 }
0020 
0021 KGpgImport::KGpgImport(QObject *parent, const QList<QUrl> &files)
0022     : KGpgTextOrFileTransaction(parent, files, true)
0023 {
0024 }
0025 
0026 QStringList
0027 KGpgImport::command() const
0028 {
0029     QStringList ret;
0030 
0031     ret << QLatin1String( "--import" ) << QLatin1String( "--allow-secret-key-import" );
0032 
0033     return ret;
0034 }
0035 
0036 QStringList
0037 KGpgImport::getImportedKeys() const
0038 {
0039     QStringList res;
0040 
0041     for (const QString &str : getMessages())
0042         if (str.startsWith(QLatin1String("[GNUPG:] IMPORTED ")))
0043             res << str.mid(18);
0044 
0045     return res;
0046 }
0047 
0048 QStringList
0049 KGpgImport::getImportedIds(const QStringList &log, const int reason)
0050 {
0051     QStringList res;
0052 
0053     for (const QString &str : log) {
0054         if (!str.startsWith(QLatin1String("[GNUPG:] IMPORT_OK ")))
0055             continue;
0056 
0057         QString tmpstr(str.mid(19).simplified());
0058 
0059         int space = tmpstr.indexOf(QLatin1Char( ' ' ));
0060         if (space <= 0) {
0061             qCDebug(KGPG_LOG_GENERAL) << __LINE__ << "invalid format:" << str;
0062             continue;
0063         }
0064 
0065         bool ok;
0066         unsigned char code = QStringView(tmpstr).left(space).toUInt(&ok);
0067         if (!ok) {
0068             qCDebug(KGPG_LOG_GENERAL) << __LINE__ << "invalid format:" << str << space << QStringView(tmpstr).left(space - 1);
0069             continue;
0070         }
0071 
0072         if ((reason == -1) || ((reason == 0) && (code == 0)) || ((reason & code) != 0))
0073             res << tmpstr.mid(space + 1);
0074     }
0075 
0076     return res;
0077 }
0078 
0079 QStringList
0080 KGpgImport::getImportedIds(const int reason) const
0081 {
0082     return getImportedIds(getMessages(), reason);
0083 }
0084 
0085 QString
0086 KGpgImport::getImportMessage() const
0087 {
0088     return getImportMessage(getMessages());
0089 }
0090 
0091 QString
0092 KGpgImport::getImportMessage(const QStringList &log)
0093 {
0094 #define RESULT_PARTS_MIN 14
0095 #define RESULT_PARTS_MAX 15
0096     unsigned long rcode[RESULT_PARTS_MAX];
0097     int line = 0;
0098     bool fine = false;
0099 
0100     memset(rcode, 0, sizeof(rcode));
0101 
0102     for (const QString &str : log) {
0103         line++;
0104         if (!str.startsWith(QLatin1String("[GNUPG:] IMPORT_RES ")))
0105             continue;
0106 
0107         const QStringList rstr = str.mid(20).simplified().split(QLatin1Char(' '));
0108 
0109         bool syn = (rstr.count() >= RESULT_PARTS_MIN);
0110 
0111         for (int i = std::min<int>(rstr.count(), RESULT_PARTS_MAX) - 1; (i >= 0) && syn; i--) {
0112             rcode[i] += rstr.at(i).toULong(&syn);
0113             fine |= (rcode[i] != 0);
0114         }
0115 
0116         if (!syn)
0117             return xi18nc("@info", "The import result string has an unsupported format in line %1.<nl/>Please see the detailed log for more information.", line);
0118     }
0119 
0120     if (!fine)
0121         return i18n("No key imported.<br />Please see the detailed log for more information.");
0122 
0123     QString resultMessage(xi18ncp("@info", "<para>%1 key processed.</para>", "<para>%1 keys processed.</para>", rcode[0]));
0124 
0125     if (rcode[1])
0126         resultMessage += xi18ncp("@info", "<para>One key without ID.</para>", "<para>%1 keys without ID.</para>", rcode[1]);
0127     if (rcode[2])
0128         resultMessage += xi18ncp("@info", "<para><emphasis strong='true'>One key imported:</emphasis></para>", "<para><emphasis strong='true'>%1 keys imported:</emphasis></para>", rcode[2]);
0129     if (rcode[3])
0130         resultMessage += xi18ncp("@info", "<para>One RSA key imported.</para>", "<para>%1 RSA keys imported.</para>", rcode[3]);
0131     if (rcode[4])
0132         resultMessage += xi18ncp("@info", "<para>One key unchanged.</para>", "<para>%1 keys unchanged.</para>", rcode[4]);
0133     if (rcode[5])
0134         resultMessage += xi18ncp("@info", "<para>One user ID imported.</para>", "<para>%1 user IDs imported.</para>", rcode[5]);
0135     if (rcode[6])
0136         resultMessage += xi18ncp("@info", "<para>One subkey imported.</para>", "<para>%1 subkeys imported.</para>", rcode[6]);
0137     if (rcode[7])
0138         resultMessage += xi18ncp("@info", "<para>One signature imported.</para>", "<para>%1 signatures imported.</para>", rcode[7]);
0139     if (rcode[8])
0140         resultMessage += xi18ncp("@info", "<para>One revocation certificate imported.</para>", "<para>%1 revocation certificates imported.</para>", rcode[8]);
0141     if (rcode[9])
0142         resultMessage += xi18ncp("@info", "<para>One secret key processed.</para>", "<para>%1 secret keys processed.</para>", rcode[9]);
0143     if (rcode[10])
0144         resultMessage +=  xi18ncp("@info", "<para><emphasis strong='true'>One secret key imported.</emphasis></para>", "<para><emphasis strong='true'>%1 secret keys imported.</emphasis></para>", rcode[10]);
0145     if (rcode[11])
0146         resultMessage += xi18ncp("@info", "<para>One secret key unchanged.</para>", "<para>%1 secret keys unchanged.</para>", rcode[11]);
0147     if (rcode[12])
0148         resultMessage += xi18ncp("@info", "<para>One secret key not imported.</para>", "<para>%1 secret keys not imported.</para>", rcode[12]);
0149 
0150     if (rcode[9])
0151         resultMessage += xi18nc("@info", "<para><emphasis strong='true'>You have imported a secret key.</emphasis><nl/>"
0152         "Please note that imported secret keys are not trusted by default.<nl/>"
0153         "To fully use this secret key for signing and encryption, you must edit the key (double click on it) and set its trust to Full or Ultimate.</para>");
0154 
0155     return resultMessage;
0156 }
0157 
0158 static QString
0159 beautifyKeyList(const QStringList &keyIds, const KGpgItemModel *model)
0160 {
0161     QString result;
0162 
0163     result.append(QLatin1String("\n"));
0164     if (model == nullptr) {
0165         result.append(QLatin1Char(' ') + keyIds.join(QLatin1String("\n ")));
0166     } else {
0167         for (const QString &changed : keyIds) {
0168             const KGpgKeyNode *node = model->findKeyNode(changed);
0169             QString line;
0170 
0171             if (node == nullptr) {
0172                 line = changed;
0173             } else {
0174                 if (node->getEmail().isEmpty())
0175                     line = xi18nc("@item ID: Name", "%1: %2", node->getFingerprint(), node->getName());
0176                 else
0177                     line = xi18nc("@item ID: Name <Email>", "%1: %2 <email>%3</email>", node->getFingerprint(), node->getName(), node->getEmail());
0178             }
0179 
0180             result.append(QLatin1Char(' ') + line + QLatin1String("\n"));
0181         }
0182     }
0183 
0184     return result;
0185 }
0186 
0187 QString
0188 KGpgImport::getDetailedImportMessage(const QStringList &log, const KGpgItemModel *model)
0189 {
0190     QString result;
0191     QMap<QString, unsigned int> resultcodes;
0192 
0193     for (const QString &keyresult : log) {
0194         if (!keyresult.startsWith(QLatin1String("[GNUPG:] IMPORT_OK ")))
0195             continue;
0196 
0197         QStringList rc(keyresult.mid(19).split(QLatin1Char( ' ' )));
0198         if (rc.count() < 2) {
0199             qCDebug(KGPG_LOG_GENERAL) << "unexpected syntax:" << keyresult;
0200             continue;
0201         }
0202 
0203         resultcodes[rc.at(1)] = rc.at(0).toUInt();
0204     }
0205 
0206     const QMap<QString, unsigned int>::const_iterator iterend = resultcodes.constEnd();
0207 
0208     for (unsigned int flag = 1; flag <= 16; flag <<= 1) {
0209         QStringList thischanged;
0210 
0211         for (QMap<QString, unsigned int>::const_iterator iter = resultcodes.constBegin(); iter != iterend; ++iter) {
0212             if (iter.value() & flag)
0213                 thischanged << iter.key();
0214         }
0215 
0216         if (thischanged.isEmpty())
0217             continue;
0218 
0219         switch (flag) {
0220         case 1:
0221             result.append(i18np("New Key", "New Keys", thischanged.count()));
0222             break;
0223         case 2:
0224             result.append(i18np("Key with new User Id", "Keys with new User Ids", thischanged.count()));
0225             break;
0226         case 4:
0227             result.append(i18np("Key with new Signatures", "Keys with new Signatures", thischanged.count()));
0228             break;
0229         case 8:
0230             result.append(i18np("Key with new Subkeys", "Keys with new Subkeys", thischanged.count()));
0231             break;
0232         case 16:
0233             result.append(i18np("New Private Key", "New Private Keys", thischanged.count()));
0234             break;
0235         default:
0236             Q_ASSERT(flag == 1);
0237         }
0238 
0239         result.append(beautifyKeyList(thischanged, model));
0240         result.append(QLatin1String("\n\n"));
0241     }
0242 
0243     QStringList unchanged(resultcodes.keys(0));
0244 
0245     if (unchanged.isEmpty()) {
0246         // remove empty line at end
0247         result.chop(1);
0248     } else {
0249         result.append(i18np("Unchanged Key", "Unchanged Keys", unchanged.count()));
0250         result.append(beautifyKeyList(unchanged, model));
0251         result.append(QLatin1String("\n"));
0252     }
0253 
0254     return result;
0255 }
0256 
0257 int
0258 KGpgImport::isKey(const QString &text, const bool incomplete)
0259 {
0260     int markpos = text.indexOf(QLatin1String("-----BEGIN PGP PUBLIC KEY BLOCK-----"));
0261     if (markpos >= 0) {
0262         markpos = text.indexOf(QLatin1String("-----END PGP PUBLIC KEY BLOCK-----"), markpos);
0263         return ((markpos > 0) || incomplete) ? 1 : 0;
0264     }
0265 
0266     markpos = text.indexOf(QLatin1String("-----BEGIN PGP PRIVATE KEY BLOCK-----"));
0267     if (markpos < 0)
0268         return 0;
0269 
0270     markpos = text.indexOf(QLatin1String("-----END PGP PRIVATE KEY BLOCK-----"), markpos);
0271     if ((markpos < 0) && !incomplete)
0272         return 0;
0273 
0274     return 2;
0275 }