Add QGpgME code from libkleo
[gpgme.git] / lang / qt / src / qgpgmerefreshkeysjob.cpp
1 /*
2     qgpgmerefreshkeysjob.cpp
3
4     This file is part of qgpgme, the Qt API binding for gpgme
5     Copyright (c) 2004 Klar´┐Żvdalens Datakonsult AB
6     Copyright (c) 2016 Intevation GmbH
7
8     Libkleopatra is free software; you can redistribute it and/or
9     modify it under the terms of the GNU General Public License as
10     published by the Free Software Foundation; either version 2 of the
11     License, or (at your option) any later version.
12
13     Libkleopatra is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16     General Public License for more details.
17
18     You should have received a copy of the GNU General Public License along
19     with this program; if not, write to the Free Software Foundation, Inc.,
20     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21
22     In addition, as a special exception, the copyright holders give
23     permission to link the code of this program with any edition of
24     the Qt library by Trolltech AS, Norway (or with modified versions
25     of Qt that use the same license as Qt), and distribute linked
26     combinations including the two.  You must obey the GNU General
27     Public License in all respects for all of the code used other than
28     Qt.  If you modify this file, you may extend this exception to
29     your version of the file, but you are not obligated to do so.  If
30     you do not wish to do so, delete this exception statement from
31     your version.
32 */
33
34 #define MAX_CMD_LENGTH 32768
35
36 #include "qgpgmerefreshkeysjob.h"
37
38 #include <QDebug>
39 #include "gpgme_backend_debug.h"
40
41 #include "context.h"
42
43 #include <QByteArray>
44 #include <QStringList>
45
46 #include <gpg-error.h>
47
48 #include <assert.h>
49
50 QGpgME::QGpgMERefreshKeysJob::QGpgMERefreshKeysJob()
51     : RefreshKeysJob(0),
52       mProcess(0),
53       mError(0)
54 {
55
56 }
57
58 QGpgME::QGpgMERefreshKeysJob::~QGpgMERefreshKeysJob()
59 {
60
61 }
62
63 GpgME::Error QGpgME::QGpgMERefreshKeysJob::start(const QStringList &patterns)
64 {
65     assert(mPatternsToDo.empty());
66
67     mPatternsToDo = patterns;
68     if (mPatternsToDo.empty()) {
69         mPatternsToDo.push_back(QStringLiteral(" "));    // empty list means all -> mae
70     }
71     // sure to fail the first
72     // startAProcess() guard clause
73
74     return startAProcess();
75 }
76
77 #if MAX_CMD_LENGTH < 65 + 128
78 #error MAX_CMD_LENGTH is too low
79 #endif
80
81 GpgME::Error QGpgME::QGpgMERefreshKeysJob::startAProcess()
82 {
83     if (mPatternsToDo.empty()) {
84         return GpgME::Error();
85     }
86     // create and start gpgsm process:
87     mProcess = new QProcess(this);
88     mProcess->setObjectName(QStringLiteral("gpgsm -k --with-validation --force-crl-refresh --enable-crl-checks"));
89
90     // FIXME: obbtain the path to gpgsm from gpgme, so we use the same instance.
91     mProcess->setProgram(QStringLiteral("gpgsm"));
92     QStringList arguments;
93     arguments << QStringLiteral("-k")
94               << QStringLiteral("--with-validation")
95               << QStringLiteral("--force-crl-refresh")
96               << QStringLiteral("--enable-crl-checks");
97     unsigned int commandLineLength = MAX_CMD_LENGTH;
98     commandLineLength -=
99         strlen("gpgsm") + 1 + strlen("-k") + 1 +
100         strlen("--with-validation") + 1 + strlen("--force-crl-refresh") + 1 +
101         strlen("--enable-crl-checks") + 1;
102     while (!mPatternsToDo.empty()) {
103         const QByteArray pat = mPatternsToDo.front().toUtf8().trimmed();
104         const unsigned int patLength = pat.length();
105         if (patLength >= commandLineLength) {
106             break;
107         }
108         mPatternsToDo.pop_front();
109         if (pat.isEmpty()) {
110             continue;
111         }
112         arguments << QLatin1String(pat);
113         commandLineLength -= patLength + 1;
114     }
115
116     mProcess->setArguments(arguments);
117
118     connect(mProcess, SIGNAL(finished(int,QProcess::ExitStatus)),
119             SLOT(slotProcessExited(int,QProcess::ExitStatus)));
120     connect(mProcess, SIGNAL(readyReadStandardOutput()),
121             SLOT(slotStdout()));
122     connect(mProcess, &QProcess::readyReadStandardError,
123             this, &QGpgMERefreshKeysJob::slotStderr);
124
125     mProcess->start();
126     if (!mProcess->waitForStarted()) {
127         mError = GpgME::Error::fromCode(GPG_ERR_ENOENT, GPG_ERR_SOURCE_GPGSM);   // what else?
128         deleteLater();
129         return mError;
130     } else {
131         return GpgME::Error();
132     }
133 }
134
135 void QGpgME::QGpgMERefreshKeysJob::slotCancel()
136 {
137     if (mProcess) {
138         mProcess->kill();
139     }
140     mProcess = 0;
141     mError = GpgME::Error::fromCode(GPG_ERR_CANCELED, GPG_ERR_SOURCE_GPGSM);
142 }
143
144 void QGpgME::QGpgMERefreshKeysJob::slotStatus(QProcess *proc, const QString &type, const QStringList &args)
145 {
146     if (proc != mProcess) {
147         return;
148     }
149     QStringList::const_iterator it = args.begin();
150     bool ok = false;
151
152     if (type == QLatin1String("ERROR")) {
153
154         if (args.size() < 2) {
155             qCDebug(GPGPME_BACKEND_LOG) << "not recognising ERROR with < 2 args!";
156             return;
157         }
158         const int source = (*++it).toInt(&ok);
159         if (!ok) {
160             qCDebug(GPGPME_BACKEND_LOG) << "expected number for first ERROR arg, got something else";
161             return;
162         }
163         ok = false;
164         const int code = (*++it).toInt(&ok);
165         if (!ok) {
166             qCDebug(GPGPME_BACKEND_LOG) << "expected number for second ERROR arg, got something else";
167             return;
168         }
169         mError = GpgME::Error::fromCode(code, source);
170
171     } else if (type == QLatin1String("PROGRESS")) {
172
173         if (args.size() < 4) {
174             qCDebug(GPGPME_BACKEND_LOG) << "not recognising PROGRESS with < 4 args!";
175             return;
176         }
177         const QString what = *++it;
178         ok = false;
179         const int typ = (*++it).toInt(&ok);
180         if (!ok) {
181             qCDebug(GPGPME_BACKEND_LOG) << "expected number for \"type\", got something else";
182             return;
183         }
184         ok = false;
185         const int cur = (*++it).toInt(&ok);
186         if (!ok) {
187             qCDebug(GPGPME_BACKEND_LOG) << "expected number for \"cur\", got something else";
188             return;
189         }
190         ok = false;
191         const int total = (*++it).toInt(&ok);
192         if (!ok) {
193             qCDebug(GPGPME_BACKEND_LOG) << "expected number for \"total\", got something else";
194             return;
195         }
196         // TODO port
197         Q_EMIT progress(QString(), cur, total);
198
199     }
200 }
201
202 void QGpgME::QGpgMERefreshKeysJob::slotStderr()
203 {
204     // implement? or not?
205 }
206
207 void QGpgME::QGpgMERefreshKeysJob::slotProcessExited(int exitCode, QProcess::ExitStatus exitStatus)
208 {
209     if (!mError && !mPatternsToDo.empty()) {
210         if (const GpgME::Error err = startAProcess()) {
211             mError = err;
212         } else {
213             return;
214         }
215     }
216
217     Q_EMIT done();
218     if (!mError &&
219             (exitStatus != QProcess::NormalExit || exitCode != 0)) {
220         mError = GpgME::Error::fromCode(GPG_ERR_GENERAL, GPG_ERR_SOURCE_GPGSM);
221     }
222     Q_EMIT result(mError);
223     deleteLater();
224 }