diff options
Diffstat (limited to 'kgpg/transactions/kgpgtransaction.cpp')
-rw-r--r-- | kgpg/transactions/kgpgtransaction.cpp | 558 |
1 files changed, 558 insertions, 0 deletions
diff --git a/kgpg/transactions/kgpgtransaction.cpp b/kgpg/transactions/kgpgtransaction.cpp new file mode 100644 index 0000000..241252c --- /dev/null +++ b/kgpg/transactions/kgpgtransaction.cpp @@ -0,0 +1,558 @@ +/* + * Copyright (C) 2008,2009,2010,2011,2012 Rolf Eike Beer <[email protected]> + */ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kgpgtransaction.h" + +#include "../gpgproc.h" +#include "../kgpginterface.h" + +//#include <KDebug> +#include <QDebug> +//#include <knewpassworddialog.h> +//#include <KLocale> +//#include <KPushButton> +#include <QByteArray> +#include <QStringList> +#include <QWeakPointer> +#include <QWidget> + + + +KGpgTransactionPrivate::KGpgTransactionPrivate(KGpgTransaction *parent, bool allowChaining) + : QObject(parent), + m_parent(parent), + m_process(new GPGProc()), + m_inputTransaction(NULL), + //m_passwordDialog(NULL), + m_success(KGpgTransaction::TS_OK), + m_tries(3), + m_chainingAllowed(allowChaining), + m_passphraseAction(KGpgTransaction::PA_NONE), + m_inputProcessDone(false), + m_inputProcessResult(KGpgTransaction::TS_OK), + m_ownProcessFinished(false), + m_quitTries(0) +{ +} + +KGpgTransactionPrivate::~KGpgTransactionPrivate() +{ + /*if (m_passwordDialog) { + m_passwordDialog->close(); + m_passwordDialog->deleteLater(); + }*/ + if (m_process->state() == QProcess::Running) { + m_process->closeWriteChannel(); + m_process->terminate(); + } + delete m_inputTransaction; + delete m_process; +} + +KGpgTransaction::KGpgTransaction(QObject *parent, const bool allowChaining) + : QObject(parent), + d(new KGpgTransactionPrivate(this, allowChaining)) +{ + connect(d->m_process, SIGNAL(readReady()), SLOT(slotReadReady())); + connect(d->m_process, SIGNAL(processExited()), SLOT(slotProcessExited())); + connect(d->m_process, SIGNAL(started()), SLOT(slotProcessStarted())); +} + +KGpgTransaction::~KGpgTransaction() +{ + delete d; +} + +void +KGpgTransactionPrivate::slotReadReady() +{ + QString line; + + QWeakPointer<GPGProc> process(m_process); + QWeakPointer<KGpgTransaction> par(m_parent); + + while (!process.isNull() && (m_process->readln(line, true) >= 0)) { + if (m_quitTries) + m_quitLines << line; +#ifdef KGPG_DEBUG_TRANSACTIONS + qDebug() << m_parent << line; +#endif /* KGPG_DEBUG_TRANSACTIONS */ + //qDebug() << "trans-read: " << m_parent << line; + + + if (line.startsWith(QLatin1String("[GNUPG:] USERID_HINT "))) { + m_parent->addIdHint(line); + } else if (line.startsWith(QLatin1String("[GNUPG:] BAD_PASSPHRASE "))) { + m_success = KGpgTransaction::TS_BAD_PASSPHRASE; + } else if (line.startsWith(QLatin1String("[GNUPG:] GET_HIDDEN passphrase.enter"))) { + // Do not directly assign to the member here, the object could + // get deleted while waiting for the result + const KGpgTransaction::ts_passphrase_actions action = m_parent->passphraseRequested(); + + if (par.isNull()) + return; + + m_passphraseAction = action; + + switch (action) { + case KGpgTransaction::PA_USER_ABORTED: + m_parent->setSuccess(KGpgTransaction::TS_USER_ABORTED); + // sending "quit" here is useless as it would be interpreted as the passphrase + m_process->closeWriteChannel(); + break; + default: + break; + } + } else if ((m_passphraseAction == KGpgTransaction::PA_CLOSE_GOOD) && + line.startsWith(QLatin1String("[GNUPG:] GOOD_PASSPHRASE"))) { + // signal GnuPG that there will be no further input and it can + // begin sending output. + + // except when signing text... + if(QString::fromAscii(m_parent->metaObject()->className()) != "KGpgSignText"){ + m_process->closeWriteChannel(); + } + } else if (line.startsWith(QLatin1String("[GNUPG:] GET_BOOL "))) { + switch (m_parent->boolQuestion(line.mid(18))) { + case KGpgTransaction::BA_YES: + write("YES\n"); + break; + case KGpgTransaction::BA_NO: + write("NO\n"); + break; + case KGpgTransaction::BA_UNKNOWN: + m_parent->setSuccess(KGpgTransaction::TS_MSG_SEQUENCE); + m_parent->unexpectedLine(line); + sendQuit(); + } + } else if (line.startsWith(QLatin1String("[GNUPG:] CARDCTRL "))) { + // just ignore them, pinentry should handle that + } else if (m_parent->nextLine(line)) { + sendQuit(); + } + } +} + +void +KGpgTransactionPrivate::slotProcessExited() +{ + Q_ASSERT(m_parent->sender() == m_process); + m_ownProcessFinished = true; + + if (m_inputProcessDone) + processDone(); +} + +void +KGpgTransactionPrivate::slotProcessStarted() +{ + m_parent->postStart(); +} + +void +KGpgTransactionPrivate::sendQuit(void) +{ + write("quit\n"); + +#ifdef KGPG_DEBUG_TRANSACTIONS + if (m_quitTries == 0) + qDebug() << "sending quit"; +#endif /* KGPG_DEBUG_TRANSACTIONS */ + + if (m_quitTries++ >= 3) { + qDebug() << "tried" << m_quitTries << "times to quit the GnuPG session"; + qDebug() << "last input was" << m_quitLines; + qDebug() << "please file a bug report at https://bugs.kde.org"; + m_process->closeWriteChannel(); + m_success = KGpgTransaction::TS_MSG_SEQUENCE; + } +} + +void +KGpgTransactionPrivate::slotInputTransactionDone(int result) +{ + Q_ASSERT(m_parent->sender() == m_inputTransaction); + + m_inputProcessDone = true; + m_inputProcessResult = result; + + if (m_ownProcessFinished) + processDone(); +} + +void +KGpgTransactionPrivate::slotPasswordEntered(const QString &password) +{ + sender()->deleteLater(); + //m_passwordDialog = NULL; + m_process->write(password.toUtf8() + '\n'); + m_parent->newPasswordEntered(); +} + +void +KGpgTransactionPrivate::slotPasswordAborted() +{ + sender()->deleteLater(); + //m_passwordDialog = NULL; + m_process->closeWriteChannel(); + m_success = KGpgTransaction::TS_USER_ABORTED; +} + +void +KGpgTransactionPrivate::write(const QByteArray &a) +{ + m_process->write(a); +#ifdef KGPG_DEBUG_TRANSACTIONS + qDebug() << m_parent << a; +#endif /* KGPG_DEBUG_TRANSACTIONS */ + qDebug() << "trans-write: " << m_parent << a; +} + +void +KGpgTransactionPrivate::processDone() +{ + m_parent->finish(); + emit m_parent->infoProgress(100, 100); + emit m_parent->done(m_success); +} + +void +KGpgTransaction::start() +{ + d->m_inputProcessResult = false; + d->m_inputProcessDone = (d->m_inputTransaction == NULL); + + setSuccess(TS_OK); + d->m_idhints.clear(); + d->m_tries = 3; + if (preStart()) { + d->m_ownProcessFinished = false; + if (d->m_inputTransaction != NULL) + d->m_inputTransaction->start(); +#ifdef KGPG_DEBUG_TRANSACTIONS + kDebug(2100) << this << d->m_process->program(); +#endif /* KGPG_DEBUG_TRANSACTIONS */ + d->m_process->start(); + emit infoProgress(0, 1); + } else { + emit done(d->m_success); + } +} + +void +KGpgTransaction::write(const QByteArray &a, const bool lf) +{ + if (lf) + d->write(a + '\n'); + else + d->write(a); +} + +void +KGpgTransaction::write(const int i) +{ + write(QByteArray::number(i)); +} + +void +KGpgTransaction::askNewPassphrase(const QString& text) +{ + qDebug() << "KGpgTransaction::askNewPassphrase called"; + + emit statusMessage(tr("Requesting Passphrase")); + + /*d->m_passwordDialog = new KNewPasswordDialog(qobject_cast<QWidget *>(parent())); + d->m_passwordDialog->setPrompt(text); + d->m_passwordDialog->setAllowEmptyPasswords(false); + connect(d->m_passwordDialog, SIGNAL(newPassword(QString)), SLOT(slotPasswordEntered(QString))); + connect(d->m_passwordDialog, SIGNAL(rejected()), SLOT(slotPasswordAborted())); + connect(d->m_process, SIGNAL(processExited()), d->m_passwordDialog->button(KDialog::Cancel), SLOT(clicked())); + d->m_passwordDialog->show();*/ + + +} + +int +KGpgTransaction::getSuccess() const +{ + return d->m_success; +} + +void +KGpgTransaction::setSuccess(const int v) +{ +#ifdef KGPG_DEBUG_TRANSACTIONS + qDebug() << d->m_success << v; +#endif /* KGPG_DEBUG_TRANSACTIONS */ + d->m_success = v; +} + +KGpgTransaction::ts_boolanswer +KGpgTransaction::boolQuestion(const QString& line) +{ + Q_UNUSED(line); + + return BA_UNKNOWN; +} + +void +KGpgTransaction::finish() +{ +} + +void +KGpgTransaction::setDescription(const QString &description) +{ + d->m_description = description; +} + +void +KGpgTransaction::waitForInputTransaction() +{ + Q_ASSERT(d->m_inputTransaction != NULL); + + if (d->m_inputProcessDone) + return; + + d->m_inputTransaction->waitForFinished(); +} + +void +KGpgTransaction::unexpectedLine(const QString &line) +{ + qDebug() << this << "unexpected input line" << line << "for command" << d->m_process->program(); +} + +KGpgTransaction::ts_passphrase_actions +KGpgTransaction::passphraseRequested() +{ + qDebug() << "KGpgTransaction::passphraseRequested called"; + if (!askPassphrase()) + return PA_USER_ABORTED; + else + return PA_CLOSE_GOOD; +} + +bool +KGpgTransaction::preStart() +{ + return true; +} + +void +KGpgTransaction::postStart() +{ +} + +void +KGpgTransaction::addIdHint(QString txt) +{ + int cut = txt.indexOf(QLatin1Char( ' ' ), 22, Qt::CaseInsensitive); + txt.remove(0, cut); + + if (txt.contains(QLatin1Char( '(' ), Qt::CaseInsensitive)) + txt = txt.section(QLatin1Char( '(' ), 0, 0) + txt.section(QLatin1Char( ')' ), -1); + + txt.replace(QLatin1Char( '<' ), QLatin1String( "<" )); + + if (!d->m_idhints.contains(txt)) + d->m_idhints << txt; +} + +QString +KGpgTransaction::getIdHints() const +{ + return d->m_idhints.join( tr(" or " )); +} + +GPGProc * +KGpgTransaction::getProcess() +{ + return d->m_process; +} + +int +KGpgTransaction::addArgument(const QString &arg) +{ + int r = d->m_process->program().count(); + + *d->m_process << arg; + + return r; +} + +int +KGpgTransaction::addArguments(const QStringList &args) +{ + int r = d->m_process->program().count(); + + *d->m_process << args; + + return r; +} + +void +KGpgTransaction::replaceArgument(const int pos, const QString &arg) +{ + QStringList args(d->m_process->program()); + d->m_process->clearProgram(); + + args.replace(pos, arg); + + d->m_process->setProgram(args); +} + +void +KGpgTransaction::insertArgument(const int pos, const QString &arg) +{ + insertArguments(pos, QStringList(arg)); +} + +void +KGpgTransaction::insertArguments(const int pos, const QStringList &args) +{ + QStringList tmp(d->m_process->program()); + + int tmppos = pos; + foreach (const QString &s, args) { + tmp.insert(tmppos++, s); + } + d->m_process->setProgram(tmp); + + int move = args.count(); + foreach (int *ref, d->m_argRefs) { + if (*ref >= pos) + *ref += move; + } +} + +void +KGpgTransaction::addArgumentRef(int *ref) +{ + d->m_argRefs.append(ref); +} + +bool +KGpgTransaction::askPassphrase(const QString &message) +{ + qDebug() << "KGpgTransaction::askPassphrase called"; + + if (d->m_passphraseAction == PA_USER_ABORTED) + return false; + + QString passdlgmessage; + QString userIDs(getIdHints()); + if (userIDs.isEmpty()) + userIDs = tr("[No user id found]"); + else + userIDs.replace(QLatin1Char( '<' ), QLatin1String( "<" )); + + if (d->m_tries < 3) + passdlgmessage = tr("<p><b>Bad passphrase</b>. You have 1 try left.</p>", "<p><b>Bad passphrase</b>. You have %1 tries left.</p>", d->m_tries); + if (message.isEmpty()) { + passdlgmessage += tr("Enter passphrase for <b>%1</b>").arg(userIDs); + } else { + passdlgmessage += message; + } + + --d->m_tries; + + emit statusMessage(tr("Requesting Passphrase")); + return (KgpgInterface::sendPassphrase(passdlgmessage, d->m_process, qobject_cast<QWidget *>(parent())) == 0); +} + +void +KGpgTransaction::setGnuPGHome(const QString &home) +{ + QStringList tmp(d->m_process->program()); + + Q_ASSERT(tmp.count() > 3); + int homepos = tmp.indexOf(QLatin1String("--options"), 1); + if (homepos == -1) + homepos = tmp.indexOf(QLatin1String("--homedir"), 1); + Q_ASSERT(homepos != -1); + Q_ASSERT(homepos + 1 < tmp.count()); + + tmp[homepos] = QLatin1String("--homedir"); + tmp[homepos + 1] = home; + + d->m_process->setProgram(tmp); +} + +int +KGpgTransaction::waitForFinished(const int msecs) +{ + int ret = TS_OK; + + if (d->m_inputTransaction != NULL) { + int ret = d->m_inputTransaction->waitForFinished(msecs); + if ((ret != TS_OK) && (msecs != -1)) + return ret; + } + + bool b = d->m_process->waitForFinished(msecs); + + if (ret != TS_OK) + return ret; + + if (!b) + return TS_USER_ABORTED; + else + return getSuccess(); +} + +const QString & +KGpgTransaction::getDescription() const +{ + return d->m_description; +} + +void +KGpgTransaction::setInputTransaction(KGpgTransaction *ta) +{ + Q_ASSERT(d->m_chainingAllowed); + + if (d->m_inputTransaction != NULL) + clearInputTransaction(); + d->m_inputTransaction = ta; + + GPGProc *proc = ta->getProcess(); + proc->setStandardOutputProcess(d->m_process); + connect(ta, SIGNAL(done(int)), SLOT(slotInputTransactionDone(int))); +} + +void +KGpgTransaction::clearInputTransaction() +{ + disconnect(d->m_inputTransaction, SIGNAL(done(int)), this, SLOT(slotInputTransactionDone(int))); + d->m_inputTransaction = NULL; +} + +bool +KGpgTransaction::hasInputTransaction() const +{ + return (d->m_inputTransaction != NULL); +} + +void +KGpgTransaction::kill() +{ + d->m_process->kill(); +} + +void +KGpgTransaction::newPasswordEntered() +{ +} + +//#include "moc_kgpgtransaction.cpp" |