File indexing completed on 2022-01-05 13:38:25

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 }