20 #include <kio/renamedialog.h>
21 #include <KPasswordDialog>
22 #include <knewpassworddialog.h>
24 #include <KPushButton>
28 #include <QStringList>
29 #include <QWeakPointer>
32 class KGpgTransactionPrivate {
35 ~KGpgTransactionPrivate();
40 KNewPasswordDialog *m_newPasswordDialog;
41 KPasswordDialog *m_passwordDialog;
44 QString m_description;
45 bool m_chainingAllowed;
47 QStringList m_idhints;
52 void slotProcessExited();
53 void slotProcessStarted();
54 void slotInputTransactionDone(
int result);
55 void slotPassphraseEntered(
const QString &passphrase);
61 void slotPassphraseAborted();
65 void handlePassphraseAborted();
68 bool m_inputProcessDone;
69 int m_inputProcessResult;
70 bool m_ownProcessFinished;
77 void write(
const QByteArray &a);
79 static const QStringList &hintNames(
void);
84 unsigned int m_quitTries;
85 QStringList m_quitLines;
88 KGpgTransactionPrivate::KGpgTransactionPrivate(
KGpgTransaction *parent,
bool allowChaining)
91 m_inputTransaction(NULL),
92 m_newPasswordDialog(NULL),
93 m_passwordDialog(NULL),
96 m_chainingAllowed(allowChaining),
97 m_inputProcessDone(false),
99 m_ownProcessFinished(false),
104 KGpgTransactionPrivate::~KGpgTransactionPrivate()
106 if (m_newPasswordDialog) {
107 m_newPasswordDialog->close();
108 m_newPasswordDialog->deleteLater();
110 if (m_process->state() == QProcess::Running) {
111 m_process->closeWriteChannel();
112 m_process->terminate();
114 delete m_inputTransaction;
120 d(new KGpgTransactionPrivate(this, allowChaining))
122 connect(d->m_process, SIGNAL(readReady()), SLOT(slotReadReady()));
123 connect(d->m_process, SIGNAL(processExited()), SLOT(slotProcessExited()));
124 connect(d->m_process, SIGNAL(started()), SLOT(slotProcessStarted()));
133 KGpgTransactionPrivate::slotReadReady()
136 QWeakPointer<GPGProc> process(m_process);
137 QWeakPointer<KGpgTransaction> par(m_parent);
139 while (!process.isNull() && (m_process->readln(line,
true) >= 0)) {
142 #ifdef KGPG_DEBUG_TRANSACTIONS
143 kDebug(2100) << m_parent << line;
146 static const QString getBool = QLatin1String(
"[GNUPG:] GET_BOOL ");
148 if (line.startsWith(QLatin1String(
"[GNUPG:] USERID_HINT "))) {
149 m_parent->addIdHint(line);
150 }
else if (line.startsWith(QLatin1String(
"[GNUPG:] BAD_PASSPHRASE "))) {
155 }
else if (line.startsWith(QLatin1String(
"[GNUPG:] GET_HIDDEN passphrase.enter"))) {
156 const bool goOn = m_parent->passphraseRequested();
159 if (!goOn || par.isNull())
162 }
else if (line.startsWith(QLatin1String(
"[GNUPG:] GOOD_PASSPHRASE"))) {
163 emit m_parent->statusMessage(i18n(
"Got Passphrase"));
165 if (m_passwordDialog != NULL) {
166 m_passwordDialog->close();
167 m_passwordDialog->deleteLater();
168 m_passwordDialog = NULL;
171 if (m_parent->passphraseReceived()) {
174 m_process->closeWriteChannel();
177 }
else if (line.startsWith(getBool)) {
178 static const QString overwrite = QLatin1String(
"openfile.overwrite.okay");
179 const QString question = line.mid(getBool.length());
183 if (question.startsWith(overwrite)) {
184 m_overwriteUrl.clear();
185 answer = m_parent->confirmOverwrite(m_overwriteUrl);
188 QPointer<KIO::RenameDialog> over =
new KIO::RenameDialog(qobject_cast<QWidget *>(m_parent->parent()),
189 i18n(
"File Already Exists"), KUrl(),
190 m_overwriteUrl, KIO::M_OVERWRITE);
192 m_overwriteUrl.clear();
194 switch (over->exec()) {
195 case KIO::R_OVERWRITE:
200 m_overwriteUrl = over->newDestUrl();
207 m_process->closeWriteChannel();
208 m_process->closeReadChannel(QProcess::StandardOutput);
218 answer = m_parent->boolQuestion(question);
230 m_parent->unexpectedLine(line);
233 }
else if (!m_overwriteUrl.isEmpty() && line.startsWith(QLatin1String(
"[GNUPG:] GET_LINE openfile.askoutname"))) {
234 write(m_overwriteUrl.toLocalFile().toUtf8() +
'\n');
235 m_overwriteUrl.clear();
236 }
else if (line.startsWith(QLatin1String(
"[GNUPG:] MISSING_PASSPHRASE"))) {
238 }
else if (line.startsWith(QLatin1String(
"[GNUPG:] CARDCTRL "))) {
243 bool matched =
false;
244 foreach (
const QString &hintName, hintNames()) {
246 if (!line.startsWith(hintName))
252 const int skip = hintName.length();
253 if (line.length() == skip) {
254 r = m_parent->hintLine(h, QString());
256 r = m_parent->hintLine(h, line.mid(skip + 1).trimmed());
268 if (m_parent->nextLine(line))
276 KGpgTransactionPrivate::slotProcessExited()
278 Q_ASSERT(m_parent->sender() == m_process);
279 m_ownProcessFinished =
true;
281 if (m_inputProcessDone)
286 KGpgTransactionPrivate::slotProcessStarted()
288 m_parent->postStart();
292 KGpgTransactionPrivate::sendQuit(
void)
296 #ifdef KGPG_DEBUG_TRANSACTIONS
297 if (m_quitTries == 0)
298 kDebug(2100) <<
"sending quit";
301 if (m_quitTries++ >= 3) {
302 kDebug(2100) <<
"tried" << m_quitTries <<
"times to quit the GnuPG session";
303 kDebug(2100) <<
"last input was" << m_quitLines;
304 kDebug(2100) <<
"please file a bug report at https://bugs.kde.org";
305 m_process->closeWriteChannel();
311 KGpgTransactionPrivate::slotInputTransactionDone(
int result)
313 Q_ASSERT(m_parent->sender() == m_inputTransaction);
315 m_inputProcessDone =
true;
316 m_inputProcessResult = result;
318 if (m_ownProcessFinished)
323 KGpgTransactionPrivate::slotPassphraseEntered(
const QString &passphrase)
326 m_process->write(passphrase.toUtf8() +
'\n');
327 if (m_parent->sender() == m_newPasswordDialog) {
328 m_newPasswordDialog->deleteLater();
329 m_newPasswordDialog = NULL;
330 m_parent->newPassphraseEntered();
332 Q_ASSERT(m_parent->sender() == m_passwordDialog);
337 KGpgTransactionPrivate::slotPassphraseAborted()
339 Q_ASSERT((m_parent->sender() == m_passwordDialog) ^ (m_parent->sender() == m_newPasswordDialog));
340 m_parent->sender()->deleteLater();
341 m_newPasswordDialog = NULL;
342 m_passwordDialog = NULL;
343 handlePassphraseAborted();
347 KGpgTransactionPrivate::handlePassphraseAborted()
350 m_process->closeWriteChannel();
355 KGpgTransactionPrivate::write(
const QByteArray &a)
358 #ifdef KGPG_DEBUG_TRANSACTIONS
359 kDebug(2100) << m_parent << a;
364 KGpgTransactionPrivate::hintNames (
void)
366 static QStringList hints;
368 if (hints.isEmpty()) {
379 KGpgTransactionPrivate::processDone()
382 emit m_parent->infoProgress(100, 100);
383 emit m_parent->done(m_success);
384 #ifdef KGPG_DEBUG_TRANSACTIONS
385 kDebug(2100) <<
this <<
"result:" << m_success;
392 d->m_inputProcessResult =
false;
393 d->m_inputProcessDone = (d->m_inputTransaction == NULL);
396 d->m_idhints.clear();
399 d->m_ownProcessFinished =
false;
400 if (d->m_inputTransaction != NULL)
401 d->m_inputTransaction->start();
402 #ifdef KGPG_DEBUG_TRANSACTIONS
403 kDebug(2100) <<
this << d->m_process->program();
405 d->m_process->start();
408 emit
done(d->m_success);
424 write(QByteArray::number(i));
432 d->m_newPasswordDialog =
new KNewPasswordDialog(qobject_cast<QWidget *>(parent()));
433 d->m_newPasswordDialog->setPrompt(text);
434 d->m_newPasswordDialog->setAllowEmptyPasswords(
false);
435 connect(d->m_newPasswordDialog, SIGNAL(newPassword(QString)), SLOT(slotPassphraseEntered(QString)));
436 connect(d->m_newPasswordDialog, SIGNAL(rejected()), SLOT(slotPassphraseAborted()));
437 connect(d->m_process, SIGNAL(processExited()), d->m_newPasswordDialog->button(KDialog::Cancel), SLOT(click()));
438 d->m_newPasswordDialog->show();
450 #ifdef KGPG_DEBUG_TRANSACTIONS
451 kDebug(2100) <<
"old" << d->m_success <<
"new" << v;
467 Q_UNUSED(currentFile)
477 return !args.isEmpty();
497 Q_ASSERT(d->m_inputTransaction != NULL);
499 if (d->m_inputProcessDone)
502 d->m_inputTransaction->waitForFinished();
508 kDebug(2100) <<
this <<
"unexpected input line" << line <<
"for command" << d->m_process->program();
537 int cut = txt.indexOf(QLatin1Char(
' ' ), 22, Qt::CaseInsensitive);
540 if (txt.contains(QLatin1Char(
'(' ), Qt::CaseInsensitive))
541 txt = txt.section(QLatin1Char(
'(' ), 0, 0) + txt.section(QLatin1Char(
')' ), -1);
543 txt.replace(QLatin1Char(
'<' ), QLatin1String(
"<" ));
545 if (!d->m_idhints.contains(txt))
552 return d->m_idhints.join( i18n(
" or " ));
564 int r = d->m_process->program().count();
566 *d->m_process << arg;
574 int r = d->m_process->program().count();
576 *d->m_process << args;
584 QStringList args(d->m_process->program());
585 d->m_process->clearProgram();
587 args.replace(pos, arg);
589 d->m_process->setProgram(args);
601 QStringList tmp(d->m_process->program());
604 foreach (
const QString &s, args) {
605 tmp.insert(tmppos++, s);
607 d->m_process->setProgram(tmp);
609 int move = args.count();
610 foreach (
int *ref, d->m_argRefs) {
619 d->m_argRefs.append(ref);
627 if (d->m_passwordDialog == NULL) {
628 d->m_passwordDialog =
new KPasswordDialog(qobject_cast<QWidget *>(parent()));
630 QString passdlgmessage;
631 if (message.isEmpty()) {
633 if (userIDs.isEmpty())
634 userIDs = i18n(
"[No user id found]");
636 userIDs.replace(QLatin1Char(
'<' ), QLatin1String(
"<" ));
638 passdlgmessage = i18n(
"Enter passphrase for <b>%1</b>", userIDs);
640 passdlgmessage = message;
643 d->m_passwordDialog->setPrompt(passdlgmessage);
645 connect(d->m_passwordDialog, SIGNAL(gotPassword(QString,
bool)), SLOT(slotPassphraseEntered(QString)));
646 connect(d->m_passwordDialog, SIGNAL(rejected()), SLOT(slotPassphraseAborted()));
647 connect(d->m_process, SIGNAL(processExited()), d->m_passwordDialog->button(KDialog::Cancel), SLOT(click()));
652 d->m_passwordDialog->showErrorMessage(i18np(
"<p><b>Bad passphrase</b>. You have 1 try left.</p>",
653 "<p><b>Bad passphrase</b>. You have %1 tries left.</p>", d->m_tries),
654 KPasswordDialog::PasswordError);
657 d->m_passwordDialog->show();
665 QStringList tmp(d->m_process->program());
667 Q_ASSERT(tmp.count() > 3);
668 int homepos = tmp.indexOf(QLatin1String(
"--options"), 1);
670 homepos = tmp.indexOf(QLatin1String(
"--homedir"), 1);
671 Q_ASSERT(homepos != -1);
672 Q_ASSERT(homepos + 1 < tmp.count());
674 tmp[homepos] = QLatin1String(
"--homedir");
675 tmp[homepos + 1] = home;
677 d->m_process->setProgram(tmp);
685 if (d->m_inputTransaction != NULL) {
686 int ret = d->m_inputTransaction->waitForFinished(msecs);
687 if ((ret !=
TS_OK) && (msecs != -1))
691 bool b = d->m_process->waitForFinished(msecs);
705 return d->m_description;
711 Q_ASSERT(d->m_chainingAllowed);
713 if (d->m_inputTransaction != NULL)
715 d->m_inputTransaction = ta;
718 proc->setStandardOutputProcess(d->m_process);
719 connect(ta, SIGNAL(
done(
int)), SLOT(slotInputTransactionDone(
int)));
725 disconnect(d->m_inputTransaction, SIGNAL(
done(
int)),
this, SLOT(slotInputTransactionDone(
int)));
726 d->m_inputTransaction = NULL;
732 return (d->m_inputTransaction != NULL);
738 d->m_process->kill();
746 #include "kgpgtransaction.moc"
virtual bool passphraseRequested()
called when GnuPG asks for a passphrase
int getSuccess() const
get the success value that will be returned with the done signal
void kill()
abort this operation as soon as possible
A interface to GnuPG handling UTF8 recoding correctly.
int addArgument(const QString &arg)
add a command line argument to gpg process
int addArguments(const QStringList &args)
add command line arguments to gpg process
void write(const QByteArray &a, const bool lf=true)
write data to standard input of gpg process
virtual void newPassphraseEntered()
called when the user entered a new passphrase
ts_hintType
the known hints sent by GnuPG
void infoProgress(qulonglong processedAmount, qulonglong totalAmount)
emits procentual status information
virtual void postStart()
Called when the gpg process is up and running.
virtual bool hintLine(const ts_hintType hint, const QString &args)
Called for a set of hint messages.
int waitForFinished(const int msecs=-1)
blocks until the transaction is complete
void replaceArgument(const int pos, const QString &arg)
replace the argument at the given position
bool askPassphrase(const QString &message=QString())
ask user for passphrase
void setInputTransaction(KGpgTransaction *ta)
connect the standard input of this transaction to another process
void start()
Start the operation.
GPGProc * getProcess()
get a reference to the gpg process object
void clearInputTransaction()
tell the process the standard input is no longer connected
KGpgTransaction(QObject *parent=0, const bool allowChaining=false)
KGpgTransaction constructor.
virtual bool preStart()
Called before the gpg process is started.
void done(int result)
Emitted when the operation was completed.
void statusMessage(const QString &msg)
emits textual status information
void insertArguments(const int pos, const QStringList &args)
insert arguments at the given position
bool hasInputTransaction() const
check if another transaction will sent input to this
message is encrypted for this key
void addArgumentRef(int *ref)
make sure the reference to a specific argument is kept up to date
void askNewPassphrase(const QString &text)
Ask user for passphrase and send it to gpg process.
virtual ts_boolanswer confirmOverwrite(KUrl ¤tFile)
called when GnuPG asks for confirmation for overwriting a file
virtual ts_boolanswer boolQuestion(const QString &line)
Called for every boolean question GnuPG answers.
QString getIdHints() const
get string of all userid hints
unexpected sequence of GnuPG messages
the question is not supported (this is an error)
ts_boolanswer
result codes for GnuPG boolean questions
virtual void finish()
Called when the gpg process finishes.
const QString & getDescription() const
return description of this transaction
the user aborted the transaction
virtual bool passphraseReceived()
called when GnuPG accepted the passphrase
void setSuccess(const int v)
set the success value that will be returned with the done signal
void addIdHint(QString txt)
add a userid hint
void unexpectedLine(const QString &line)
notify of an unexpected line
the passphrase was not correct
static const char description[]
void insertArgument(const int pos, const QString &arg)
insert an argument at the given position
Process one GnuPG operation.
void setDescription(const QString &description)
set the description returned in getDescription()
virtual ~KGpgTransaction()
KGpgTransaction destructor.
void setGnuPGHome(const QString &home)
sets the home directory of GnuPG called for this transaction
void waitForInputTransaction()
wait until the input transaction has finished