File indexing completed on 2025-02-16 05:21:30
0001 /* 0002 SPDX-FileCopyrightText: 2002 Jean-Baptiste Mardelle <bj@altern.org> 0003 SPDX-FileCopyrightText: 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2016, 2017 Rolf Eike Beer <kde@opensource.sf-tec.de> 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kgpginterface.h" 0008 0009 #include "gpgproc.h" 0010 #include "core/convert.h" 0011 #include "core/KGpgKeyNode.h" 0012 #include "core/KGpgSignNode.h" 0013 #include "core/KGpgSubkeyNode.h" 0014 #include "core/KGpgUatNode.h" 0015 #include "core/KGpgUidNode.h" 0016 #include "kgpgsettings.h" 0017 0018 #include <gpgme.h> 0019 #include <KLocalizedString> 0020 #include <KMessageBox> 0021 #include <KProcess> 0022 #include <QFile> 0023 #include <QString> 0024 #include <QTextStream> 0025 0026 using namespace KgpgCore; 0027 0028 QString KgpgInterface::getGpgSetting(const QString &name, const QString &configfile) 0029 { 0030 const QString tmp = name.simplified() + QLatin1Char(' '); 0031 QFile qfile(configfile); 0032 0033 if (qfile.exists() && qfile.open(QIODevice::ReadOnly)) { 0034 QTextStream t(&qfile); 0035 while (!t.atEnd()) { 0036 QString result(t.readLine().simplified()); 0037 if (result.startsWith(tmp)) { 0038 result = result.mid(tmp.length()).simplified(); 0039 return result.section(QLatin1Char( ' ' ), 0, 0); 0040 } 0041 } 0042 qfile.close(); 0043 } 0044 0045 return QString(); 0046 } 0047 0048 void KgpgInterface::setGpgSetting(const QString &name, const QString &value, const QString &url) 0049 { 0050 QFile qfile(url); 0051 0052 if (qfile.exists() && qfile.open(QIODevice::ReadOnly)) { 0053 const QString temp = name + QLatin1Char(' '); 0054 QString texttowrite; 0055 bool found = false; 0056 QTextStream tin(&qfile); 0057 0058 while (!tin.atEnd()) { 0059 QString result = tin.readLine(); 0060 if (result.simplified().startsWith(temp)) { 0061 if (!value.isEmpty()) 0062 result = temp + QLatin1Char( ' ' ) + value; 0063 else 0064 result.clear(); 0065 found = true; 0066 } 0067 0068 texttowrite += result + QLatin1Char( '\n' ); 0069 } 0070 0071 qfile.close(); 0072 if ((!found) && (!value.isEmpty())) 0073 texttowrite += QLatin1Char( '\n' ) + temp + QLatin1Char( ' ' ) + value; 0074 0075 if (qfile.open(QIODevice::WriteOnly)) { 0076 QTextStream tout(&qfile); 0077 tout << texttowrite; 0078 qfile.close(); 0079 } 0080 } 0081 } 0082 0083 bool KgpgInterface::getGpgBoolSetting(const QString &name, const QString &configfile) 0084 { 0085 QFile qfile(configfile); 0086 if (qfile.exists() && qfile.open(QIODevice::ReadOnly)) { 0087 QTextStream t(&qfile); 0088 while (!t.atEnd()) { 0089 if (t.readLine().simplified().startsWith(name)) 0090 return true; 0091 } 0092 qfile.close(); 0093 } 0094 return false; 0095 } 0096 0097 void KgpgInterface::setGpgBoolSetting(const QString &name, const bool enable, const QString &url) 0098 { 0099 QFile qfile(url); 0100 0101 if (qfile.exists() && qfile.open(QIODevice::ReadOnly)) { 0102 QString texttowrite; 0103 bool found = false; 0104 QTextStream tin(&qfile); 0105 0106 while (!tin.atEnd()) { 0107 QString result(tin.readLine()); 0108 0109 if (result.simplified().startsWith(name)) { 0110 if (enable) 0111 result = name; 0112 else 0113 result.clear(); 0114 0115 found = true; 0116 } 0117 0118 texttowrite += result + QLatin1Char( '\n' ); 0119 } 0120 qfile.close(); 0121 0122 if ((!found) && (enable)) 0123 texttowrite += name; 0124 0125 if (qfile.open(QIODevice::WriteOnly)) { 0126 QTextStream tout(&qfile); 0127 tout << texttowrite; 0128 qfile.close(); 0129 } 0130 } 0131 } 0132 0133 /** 0134 * @param p the process that reads the GnuPG data 0135 * @param readNode the node where the signatures are read for 0136 */ 0137 static KgpgCore::KgpgKeyList 0138 readPublicKeysProcess(GPGProc &p, KGpgKeyNode *readNode) 0139 { 0140 QStringList lsp; 0141 int items; 0142 KgpgCore::KgpgKeyList publiclistkeys; 0143 KgpgCore::KgpgKey *publickey = nullptr; 0144 unsigned int idIndex = 0; 0145 QString log; 0146 KGpgSignableNode *currentSNode = nullptr; ///< the current (sub)node signatures are read for 0147 0148 while ((items = p.readln(lsp)) >= 0) { 0149 if ((lsp.at(0) == QLatin1String( "pub" )) && (items >= 10)) { 0150 KgpgSubKeyType subtype; 0151 KgpgSubKeyType keytype; 0152 bool enabled = true; 0153 if (items > 11) { 0154 const QString &caps = lsp.at(11); 0155 0156 enabled = !caps.contains(QLatin1Char('D'), Qt::CaseSensitive); 0157 0158 subtype = Convert::toSubType(caps, false); 0159 keytype = Convert::toSubType(caps, true); 0160 } 0161 0162 const QString curve = (items > 16) ? lsp.at(16) : QString(); 0163 publiclistkeys << KgpgKey(lsp.at(4), lsp.at(2).toUInt(), Convert::toTrust(lsp.at(1)), 0164 Convert::toAlgo(lsp.at(3)), subtype, keytype, 0165 Convert::toDateTime(lsp.at(5)), curve); 0166 0167 publickey = &publiclistkeys.last(); 0168 0169 const QString &owTrust = lsp.at(8); 0170 if (owTrust.isEmpty()) 0171 publickey->setOwnerTrust(GPGME_VALIDITY_UNDEFINED); 0172 else 0173 publickey->setOwnerTrust(Convert::toOwnerTrust(owTrust[0])); 0174 0175 publickey->setExpiration(Convert::toDateTime(lsp.at(6))); 0176 0177 publickey->setValid(enabled); // disabled key 0178 0179 // GnuPG since 2.1 can flag immediately if a key has a secret key 0180 if (items > 14 && lsp.at(14).contains(QLatin1Char('+'))) 0181 publickey->setSecret(true); 0182 0183 idIndex = 0; 0184 } else if (publickey && (lsp.at(0) == QLatin1String("fpr")) && (items >= 10)) { 0185 const QString &fingervalue = lsp.at(9); 0186 0187 if ((currentSNode != nullptr) && (currentSNode->getType() == ITYPE_SUB)) 0188 static_cast<KGpgSubkeyNode *>(currentSNode)->setFingerprint(fingervalue); 0189 else if (publickey->fingerprint().isEmpty()) 0190 publickey->setFingerprint(fingervalue); 0191 } else if (publickey && (lsp.at(0) == QLatin1String( "sub" )) && (items >= 7)) { 0192 KgpgSubKeyType subtype; 0193 0194 if (items > 11) 0195 subtype = Convert::toSubType(lsp.at(11), false); 0196 0197 const QString curve = (items > 16) ? lsp.at(16) : QString(); 0198 KgpgKeySub sub(lsp.at(4), lsp.at(2).toUInt(), Convert::toTrust(lsp.at(1)), 0199 Convert::toAlgo(lsp.at(3)), subtype, Convert::toDateTime(lsp.at(5)), 0200 curve); 0201 0202 // FIXME: Please see kgpgkey.h, KgpgSubKey class 0203 if (items <= 11) 0204 sub.setValid(true); 0205 else 0206 sub.setValid(!lsp.at(11).contains(QLatin1Char( 'D' ))); 0207 0208 sub.setExpiration(Convert::toDateTime(lsp.at(6))); 0209 0210 publickey->subList()->append(sub); 0211 if (readNode == nullptr) 0212 currentSNode = nullptr; 0213 else 0214 currentSNode = new KGpgSubkeyNode(readNode, sub); 0215 } else if (publickey && (lsp.at(0) == QLatin1String( "uat" ))) { 0216 idIndex++; 0217 if (readNode != nullptr) { 0218 currentSNode = new KGpgUatNode(readNode, idIndex, lsp); 0219 } 0220 } else if (publickey && (lsp.at(0) == QLatin1String( "uid" )) && (items >= 10)) { 0221 if (idIndex == 0) { 0222 QString fullname(lsp.at(9)); 0223 QString kmail; 0224 if (fullname.contains(QLatin1Char( '<' )) ) { 0225 kmail = fullname; 0226 0227 if (fullname.contains(QLatin1Char( ')' )) ) 0228 kmail = kmail.section(QLatin1Char( ')' ), 1); 0229 0230 kmail = kmail.section(QLatin1Char( '<' ), 1); 0231 kmail.chop(1); 0232 0233 if (kmail.contains(QLatin1Char( '<' ))) { 0234 // several email addresses in the same key 0235 kmail.replace(QLatin1Char( '>' ), QLatin1Char( ';' )); 0236 kmail.remove(QLatin1Char( '<' )); 0237 } 0238 } 0239 0240 QString kname(fullname.section( QLatin1String( " <" ), 0, 0)); 0241 QString comment; 0242 if (fullname.contains(QLatin1Char( '(' )) ) { 0243 kname = kname.section( QLatin1String( " (" ), 0, 0); 0244 comment = fullname.section(QLatin1Char( '(' ), 1, 1); 0245 comment = comment.section(QLatin1Char( ')' ), 0, 0); 0246 } 0247 0248 idIndex++; 0249 publickey->setEmail(kmail); 0250 publickey->setComment(comment); 0251 publickey->setName(kname); 0252 0253 currentSNode = readNode; 0254 } else { 0255 idIndex++; 0256 if (readNode != nullptr) { 0257 currentSNode = new KGpgUidNode(readNode, idIndex, lsp); 0258 } 0259 } 0260 } else if (publickey && ((lsp.at(0) == QLatin1String( "sig" )) || (lsp.at(0) == QLatin1String( "rev" ))) && (items >= 11)) { 0261 if (currentSNode != nullptr) 0262 (void) new KGpgSignNode(currentSNode, lsp); 0263 } else { 0264 log += lsp.join(QString(QLatin1Char( ':' ))) + QLatin1Char( '\n' ); 0265 } 0266 } 0267 0268 if (p.exitCode() != 0) { 0269 KMessageBox::detailedError(nullptr, i18n("An error occurred while scanning your keyring"), log); 0270 log.clear(); 0271 } 0272 0273 return publiclistkeys; 0274 } 0275 0276 KgpgKeyList KgpgInterface::readPublicKeys(const QStringList &ids) 0277 { 0278 GPGProc process; 0279 process << 0280 QLatin1String("--with-colons") << 0281 QLatin1String("--with-fingerprint") << 0282 QLatin1String("--fixed-list-mode") << 0283 QLatin1String("--list-keys"); 0284 0285 if (GPGProc::gpgVersion(GPGProc::gpgVersionString(KGpgSettings::gpgBinaryPath())) > 0x20100) 0286 process << QLatin1String("--with-secret"); 0287 0288 process << ids; 0289 0290 process.setOutputChannelMode(KProcess::MergedChannels); 0291 0292 process.start(); 0293 process.waitForFinished(-1); 0294 return readPublicKeysProcess(process, nullptr); 0295 } 0296 0297 void KgpgInterface::readSignatures(KGpgKeyNode *node) 0298 { 0299 GPGProc process; 0300 process << 0301 QLatin1String("--with-colons") << 0302 QLatin1String("--with-fingerprint") << 0303 QLatin1String("--fixed-list-mode") << 0304 QLatin1String("--list-sigs") << 0305 node->getId(); 0306 0307 process.setOutputChannelMode(KProcess::MergedChannels); 0308 0309 process.start(); 0310 process.waitForFinished(-1); 0311 0312 readPublicKeysProcess(process, node); 0313 } 0314 0315 static KgpgCore::KgpgKeyList 0316 readSecretKeysProcess(GPGProc &p) 0317 { 0318 QStringList lsp; 0319 int items; 0320 bool hasuid = false; 0321 KgpgCore::KgpgKeyList result; 0322 KgpgCore::KgpgKey *secretkey = nullptr; 0323 0324 while ( (items = p.readln(lsp)) >= 0 ) { 0325 if ((lsp.at(0) == QLatin1String( "sec" )) && (items >= 10)) { 0326 KgpgSubKeyType subtype; 0327 KgpgSubKeyType keytype; 0328 0329 if (items >= 11) { 0330 const QString &caps = lsp.at(11); 0331 0332 subtype = Convert::toSubType(caps, false); 0333 keytype = Convert::toSubType(caps, true); 0334 } 0335 0336 const QString curve = (items > 16) ? lsp.at(16) : QString(); 0337 result << KgpgKey(lsp.at(4), lsp.at(2).toUInt(), Convert::toTrust(lsp.at(1)), 0338 Convert::toAlgo(lsp.at(3)), subtype, keytype, 0339 Convert::toDateTime(lsp.at(5)), curve); 0340 0341 secretkey = &result.last(); 0342 0343 secretkey->setSecret(true); 0344 0345 secretkey->setExpiration(Convert::toDateTime(lsp.at(6))); 0346 0347 hasuid = true; 0348 } else if ((lsp.at(0) == QLatin1String( "uid" )) && (items >= 10)) { 0349 if (hasuid) 0350 continue; 0351 0352 hasuid = true; 0353 0354 const QString &fullname = lsp.at(9); 0355 if (fullname.contains(QLatin1Char( '<' ) )) { 0356 QString kmail(fullname); 0357 0358 if (fullname.contains(QLatin1Char( ')' ) )) 0359 kmail = kmail.section(QLatin1Char( ')' ), 1); 0360 0361 kmail = kmail.section(QLatin1Char( '<' ), 1); 0362 kmail.chop(1); 0363 0364 if (kmail.contains(QLatin1Char( '<' ) )) { // several email addresses in the same key 0365 kmail.replace(QLatin1Char( '>' ), QLatin1Char( ';' )); 0366 kmail.remove(QLatin1Char( '<' )); 0367 } 0368 0369 secretkey->setEmail(kmail); 0370 } else { 0371 secretkey->setEmail(QString()); 0372 } 0373 0374 QString kname(fullname.section( QLatin1String( " <" ), 0, 0)); 0375 if (fullname.contains(QLatin1Char( '(' ) )) { 0376 kname = kname.section( QLatin1String( " (" ), 0, 0); 0377 QString comment = fullname.section(QLatin1Char( '(' ), 1, 1); 0378 comment = comment.section(QLatin1Char( ')' ), 0, 0); 0379 0380 secretkey->setComment(comment); 0381 } else { 0382 secretkey->setComment(QString()); 0383 } 0384 secretkey->setName(kname); 0385 } else if ((lsp.at(0) == QLatin1String( "fpr" )) && (items >= 10)) { 0386 secretkey->setFingerprint(lsp.at(9)); 0387 } 0388 } 0389 0390 return result; 0391 } 0392 0393 KgpgKeyList KgpgInterface::readSecretKeys(const QStringList &ids) 0394 { 0395 GPGProc process; 0396 process << 0397 QLatin1String("--with-colons") << 0398 QLatin1String("--list-secret-keys") << 0399 QLatin1String("--with-fingerprint") << 0400 QLatin1String("--fixed-list-mode") << 0401 ids; 0402 0403 process.start(); 0404 process.waitForFinished(-1); 0405 return readSecretKeysProcess(process); 0406 }