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 }