File indexing completed on 2024-04-21 05:50:43

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 "kgpgtransaction.h"
0007 #include "kgpg_transactions_debug.h"
0008 #include "kgpg_general_debug.h"
0009 #include "kgpgtransactionprivate.h"
0010 
0011 #include "gpgproc.h"
0012 #include "kgpginterface.h"
0013 
0014 #include <QByteArray>
0015 
0016 #include <QUrl>
0017 #include <QWidget>
0018 
0019 #include <KConfigGroup>
0020 #include <KNewPasswordDialog>
0021 #include <KLocalizedString>
0022 #include <KPasswordDialog>
0023 
0024 KGpgTransaction::KGpgTransaction(QObject *parent, const bool allowChaining)
0025     : QObject(parent),
0026     d(new KGpgTransactionPrivate(this, allowChaining))
0027 {
0028 }
0029 
0030 KGpgTransaction::~KGpgTransaction()
0031 {
0032     delete d;
0033 }
0034 
0035 void
0036 KGpgTransaction::start()
0037 {
0038     d->m_inputProcessResult = false;
0039     d->m_inputProcessDone = (d->m_inputTransaction == nullptr);
0040 
0041     setSuccess(TS_OK);
0042     d->m_idhints.clear();
0043     d->m_tries = 3;
0044     if (preStart()) {
0045         d->m_ownProcessFinished = false;
0046         if (d->m_inputTransaction != nullptr)
0047             d->m_inputTransaction->start();
0048 #ifdef KGPG_DEBUG_TRANSACTIONS
0049         qCDebug(KGPG_LOG_TRANSACTIONS) << this << d->m_process->program();
0050 #endif /* KGPG_DEBUG_TRANSACTIONS */
0051         d->m_process->start();
0052         Q_EMIT infoProgress(0, 1);
0053     } else {
0054         Q_EMIT done(d->m_success);
0055     }
0056 }
0057 
0058 void
0059 KGpgTransaction::write(const QByteArray &a, const bool lf)
0060 {
0061     if (lf)
0062         d->write(a + '\n');
0063     else
0064         d->write(a);
0065 }
0066 
0067 void
0068 KGpgTransaction::write(const int i)
0069 {
0070     write(QByteArray::number(i));
0071 }
0072 
0073 void
0074 KGpgTransaction::askNewPassphrase(const QString& text)
0075 {
0076     Q_EMIT statusMessage(i18n("Requesting Passphrase"));
0077 
0078     d->m_newPasswordDialog = new KNewPasswordDialog(qobject_cast<QWidget *>(parent()));
0079     d->m_newPasswordDialog->setPrompt(text);
0080     d->m_newPasswordDialog->setAllowEmptyPasswords(false);
0081     connect(d->m_newPasswordDialog, &KNewPasswordDialog::newPassword, d, &KGpgTransactionPrivate::slotPassphraseEntered);
0082     connect(d->m_newPasswordDialog, &KNewPasswordDialog::rejected, d, &KGpgTransactionPrivate::slotPassphraseAborted);
0083     connect(d->m_process, &GPGProc::processExited, d->m_newPasswordDialog, &KNewPasswordDialog::rejected);
0084     d->m_newPasswordDialog->show();
0085 }
0086 
0087 int
0088 KGpgTransaction::getSuccess() const
0089 {
0090     return d->m_success;
0091 }
0092 
0093 void
0094 KGpgTransaction::setSuccess(const int v)
0095 {
0096 #ifdef KGPG_DEBUG_TRANSACTIONS
0097     qCDebug(KGPG_LOG_TRANSACTIONS) << "old" << d->m_success << "new" << v;
0098 #endif /* KGPG_DEBUG_TRANSACTIONS */
0099     d->m_success = v;
0100 }
0101 
0102 KGpgTransaction::ts_boolanswer
0103 KGpgTransaction::boolQuestion(const QString& line)
0104 {
0105     Q_UNUSED(line)
0106 
0107     return BA_UNKNOWN;
0108 }
0109 
0110 KGpgTransaction::ts_boolanswer
0111 KGpgTransaction::confirmOverwrite(QUrl &currentFile)
0112 {
0113     Q_UNUSED(currentFile)
0114 
0115     return BA_UNKNOWN;
0116 }
0117 
0118 bool
0119 KGpgTransaction::hintLine(const ts_hintType hint, const QString &args)
0120 {
0121     switch (hint) {
0122     case HT_KEYEXPIRED:
0123     case HT_PINENTRY_LAUNCHED:
0124         return !args.isEmpty();
0125     default:
0126         return true;
0127     }
0128 }
0129 
0130 void
0131 KGpgTransaction::finish()
0132 {
0133 }
0134 
0135 void
0136 KGpgTransaction::setDescription(const QString &description)
0137 {
0138     d->m_description = description;
0139 }
0140 
0141 void
0142 KGpgTransaction::waitForInputTransaction()
0143 {
0144     Q_ASSERT(d->m_inputTransaction != nullptr);
0145 
0146     if (d->m_inputProcessDone)
0147         return;
0148 
0149     d->m_inputTransaction->waitForFinished();
0150 }
0151 
0152 void
0153 KGpgTransaction::unexpectedLine(const QString &line)
0154 {
0155     qCDebug(KGPG_LOG_GENERAL) << this << "unexpected input line" << line << "for command" << d->m_process->program();
0156 }
0157 
0158 bool
0159 KGpgTransaction::passphraseRequested()
0160 {
0161     return askPassphrase();
0162 }
0163 
0164 bool
0165 KGpgTransaction::passphraseReceived()
0166 {
0167     return true;
0168 }
0169 
0170 bool
0171 KGpgTransaction::preStart()
0172 {
0173     return true;
0174 }
0175 
0176 void
0177 KGpgTransaction::postStart()
0178 {
0179 }
0180 
0181 void
0182 KGpgTransaction::addIdHint(QString txt)
0183 {
0184     int cut = txt.indexOf(QLatin1Char( ' ' ), 22, Qt::CaseInsensitive);
0185     txt.remove(0, cut);
0186 
0187     if (txt.contains(QLatin1Char( '(' ), Qt::CaseInsensitive))
0188         txt = txt.section(QLatin1Char( '(' ), 0, 0) + txt.section(QLatin1Char( ')' ), -1);
0189 
0190     txt.replace(QLatin1Char( '<' ), QLatin1String( "&lt;" ));
0191 
0192     if (!d->m_idhints.contains(txt))
0193         d->m_idhints << txt;
0194 }
0195 
0196 QString
0197 KGpgTransaction::getIdHints() const
0198 {
0199     return d->m_idhints.join( i18n(" or " ));
0200 }
0201 
0202 GPGProc *
0203 KGpgTransaction::getProcess()
0204 {
0205     return d->m_process;
0206 }
0207 
0208 int
0209 KGpgTransaction::addArgument(const QString &arg)
0210 {
0211     int r = d->m_process->program().count();
0212 
0213     *d->m_process << arg;
0214 
0215     return r;
0216 }
0217 
0218 void
0219 KGpgTransaction::addArguments(const QStringList &args)
0220 {
0221     *d->m_process << args;
0222 }
0223 
0224 void
0225 KGpgTransaction::replaceArgument(const int pos, const QString &arg)
0226 {
0227     QStringList args(d->m_process->program());
0228     d->m_process->clearProgram();
0229 
0230     args.replace(pos, arg);
0231 
0232     d->m_process->setProgram(args);
0233 }
0234 
0235 void
0236 KGpgTransaction::insertArgument(const int pos, const QString &arg)
0237 {
0238     insertArguments(pos, QStringList(arg));
0239 }
0240 
0241 void
0242 KGpgTransaction::insertArguments(const int pos, const QStringList &args)
0243 {
0244     QStringList tmp(d->m_process->program());
0245 
0246     int tmppos = pos;
0247     for (const QString &s : args) {
0248         tmp.insert(tmppos++, s);
0249     }
0250     d->m_process->setProgram(tmp);
0251 
0252     int move = args.count();
0253     for (int *ref : std::as_const(d->m_argRefs)) {
0254         if (*ref >= pos)
0255             *ref += move;
0256     }
0257 }
0258 
0259 void
0260 KGpgTransaction::addArgumentRef(int *ref)
0261 {
0262     d->m_argRefs.append(ref);
0263 }
0264 
0265 bool
0266 KGpgTransaction::askPassphrase(const QString &message)
0267 {
0268     Q_EMIT statusMessage(i18n("Requesting Passphrase"));
0269 
0270     if (d->m_passwordDialog == nullptr) {
0271         d->m_passwordDialog = new KPasswordDialog(qobject_cast<QWidget *>(parent()));
0272 
0273         QString passdlgmessage;
0274         if (message.isEmpty()) {
0275             QString userIDs(getIdHints());
0276             if (userIDs.isEmpty())
0277                 userIDs = i18n("[No user id found]");
0278             else
0279                 userIDs.replace(QLatin1Char( '<' ), QLatin1String( "&lt;" ));
0280 
0281             passdlgmessage = i18n("Enter passphrase for <b>%1</b>", userIDs);
0282         } else {
0283             passdlgmessage = message;
0284         }
0285 
0286         d->m_passwordDialog->setPrompt(passdlgmessage);
0287 
0288         connect(d->m_passwordDialog, &KPasswordDialog::gotPassword, d, &KGpgTransactionPrivate::slotPassphraseEntered);
0289         connect(d->m_passwordDialog, &KPasswordDialog::rejected, d, &KGpgTransactionPrivate::slotPassphraseAborted);
0290         connect(d->m_process, &GPGProc::processExited, d->m_passwordDialog, &KPasswordDialog::rejected);
0291     } else {
0292         // we already have a dialog, so this is a "bad passphrase" situation
0293         --d->m_tries;
0294 
0295         d->m_passwordDialog->showErrorMessage(i18np("<p><b>Bad passphrase</b>. You have 1 try left.</p>",
0296                 "<p><b>Bad passphrase</b>. You have %1 tries left.</p>", d->m_tries),
0297                 KPasswordDialog::PasswordError);
0298     }
0299 
0300     d->m_passwordDialog->show();
0301 
0302     return true;
0303 }
0304 
0305 void
0306 KGpgTransaction::setExpectedFingerprints(const QStringList &fingerprints)
0307 {
0308     d->m_expectedFingerprints = fingerprints;
0309 }
0310 
0311 void
0312 KGpgTransaction::setGnuPGHome(const QString &home)
0313 {
0314     QStringList tmp(d->m_process->program());
0315 
0316     Q_ASSERT(tmp.count() > 3);
0317     int homepos = tmp.indexOf(QLatin1String("--options"), 1);
0318     if (homepos == -1)
0319         homepos = tmp.indexOf(QLatin1String("--homedir"), 1);
0320     Q_ASSERT(homepos != -1);
0321     Q_ASSERT(homepos + 1 < tmp.count());
0322 
0323     tmp[homepos] = QLatin1String("--homedir");
0324     tmp[homepos + 1] = home;
0325 
0326     d->m_process->setProgram(tmp);
0327 }
0328 
0329 int
0330 KGpgTransaction::waitForFinished(const int msecs)
0331 {
0332     int ret = TS_OK;
0333 
0334     if (d->m_inputTransaction != nullptr) {
0335         ret = d->m_inputTransaction->waitForFinished(msecs);
0336         if ((ret != TS_OK) && (msecs != -1))
0337             return ret;
0338     }
0339 
0340     bool b = d->m_process->waitForFinished(msecs);
0341 
0342     if (ret != TS_OK)
0343         return ret;
0344 
0345     if (!b)
0346         return TS_USER_ABORTED;
0347     else
0348         return getSuccess();
0349 }
0350 
0351 const QString &
0352 KGpgTransaction::getDescription() const
0353 {
0354     return d->m_description;
0355 }
0356 
0357 void
0358 KGpgTransaction::setInputTransaction(KGpgTransaction *ta)
0359 {
0360     Q_ASSERT(d->m_chainingAllowed);
0361 
0362     if (d->m_inputTransaction != nullptr)
0363         clearInputTransaction();
0364     d->m_inputTransaction = ta;
0365 
0366     GPGProc *proc = ta->getProcess();
0367     proc->setStandardOutputProcess(d->m_process);
0368     connect(ta, &KGpgTransaction::done, d, &KGpgTransactionPrivate::slotInputTransactionDone);
0369 }
0370 
0371 void
0372 KGpgTransaction::clearInputTransaction()
0373 {
0374     disconnect(d->m_inputTransaction, &KGpgTransaction::done, d, &KGpgTransactionPrivate::slotInputTransactionDone);
0375     d->m_inputTransaction = nullptr;
0376 }
0377 
0378 bool
0379 KGpgTransaction::hasInputTransaction() const
0380 {
0381     return (d->m_inputTransaction != nullptr);
0382 }
0383 
0384 void
0385 KGpgTransaction::kill()
0386 {
0387     d->m_process->kill();
0388 }
0389 
0390 void
0391 KGpgTransaction::newPassphraseEntered()
0392 {
0393 }