qt: Add generic flag support for keylistjobs
[gpgme.git] / lang / qt / src / qgpgmekeylistjob.cpp
1 /*
2     qgpgmekeylistjob.cpp
3
4     This file is part of qgpgme, the Qt API binding for gpgme
5     Copyright (c) 2004,2008 Klarälvdalens Datakonsult AB
6     Copyright (c) 2016 Intevation GmbH
7
8     QGpgME 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     QGpgME 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
19     along with this program; if not, write to the Free Software
20     Foundation, Inc., 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 #include "qgpgmekeylistjob.h"
35
36 #include "key.h"
37 #include "context.h"
38 #include "keylistresult.h"
39 #include <gpg-error.h>
40
41 #include <QStringList>
42
43 #include <algorithm>
44
45 #include <cstdlib>
46 #include <cstring>
47 #include <cassert>
48
49 using namespace QGpgME;
50 using namespace GpgME;
51
52 QGpgMEKeyListJob::QGpgMEKeyListJob(Context *context)
53     : mixin_type(context),
54       mResult(), mSecretOnly(false)
55 {
56     lateInitialization();
57 }
58
59 QGpgMEKeyListJob::~QGpgMEKeyListJob() {}
60
61 static KeyListResult do_list_keys(Context *ctx, const QStringList &pats, std::vector<Key> &keys, bool secretOnly)
62 {
63
64     const _detail::PatternConverter pc(pats);
65
66     if (const Error err = ctx->startKeyListing(pc.patterns(), secretOnly)) {
67         return KeyListResult(0, err);
68     }
69
70     Error err;
71     do {
72         keys.push_back(ctx->nextKey(err));
73     } while (!err);
74
75     keys.pop_back();
76
77     const KeyListResult result = ctx->endKeyListing();
78     ctx->cancelPendingOperation();
79     return result;
80 }
81
82 static QGpgMEKeyListJob::result_type list_keys(Context *ctx, QStringList pats, bool secretOnly)
83 {
84     if (pats.size() < 2) {
85         std::vector<Key> keys;
86         const KeyListResult r = do_list_keys(ctx, pats, keys, secretOnly);
87         return std::make_tuple(r, keys, QString(), Error());
88     }
89
90     // The communication channel between gpgme and gpgsm is limited in
91     // the number of patterns that can be transported, but they won't
92     // say to how much, so we need to find out ourselves if we get a
93     // LINE_TOO_LONG error back...
94
95     // We could of course just feed them single patterns, and that would
96     // probably be easier, but the performance penalty would currently
97     // be noticeable.
98
99     unsigned int chunkSize = pats.size();
100 retry:
101     std::vector<Key> keys;
102     keys.reserve(pats.size());
103     KeyListResult result;
104     do {
105         const KeyListResult this_result = do_list_keys(ctx, pats.mid(0, chunkSize), keys, secretOnly);
106         if (this_result.error().code() == GPG_ERR_LINE_TOO_LONG) {
107             // got LINE_TOO_LONG, try a smaller chunksize:
108             chunkSize /= 2;
109             if (chunkSize < 1)
110                 // chunks smaller than one can't be -> return the error.
111             {
112                 return std::make_tuple(this_result, keys, QString(), Error());
113             } else {
114                 goto retry;
115             }
116         } else if (this_result.error().code() == GPG_ERR_EOF) {
117             // early end of keylisting (can happen when ~/.gnupg doesn't
118             // exist). Fakeing an empty result:
119             return std::make_tuple(KeyListResult(), std::vector<Key>(), QString(), Error());
120         }
121         // ok, that seemed to work...
122         result.mergeWith(this_result);
123         if (result.error().code()) {
124             break;
125         }
126         pats = pats.mid(chunkSize);
127     } while (!pats.empty());
128     return std::make_tuple(result, keys, QString(), Error());
129 }
130
131 Error QGpgMEKeyListJob::start(const QStringList &patterns, bool secretOnly)
132 {
133     mSecretOnly = secretOnly;
134     run(std::bind(&list_keys, std::placeholders::_1, patterns, secretOnly));
135     return Error();
136 }
137
138 KeyListResult QGpgMEKeyListJob::exec(const QStringList &patterns, bool secretOnly, std::vector<Key> &keys)
139 {
140     mSecretOnly = secretOnly;
141     const result_type r = list_keys(context(), patterns, secretOnly);
142     resultHook(r);
143     keys = std::get<1>(r);
144     return std::get<0>(r);
145 }
146
147 void QGpgMEKeyListJob::resultHook(const result_type &tuple)
148 {
149     mResult = std::get<0>(tuple);
150     Q_FOREACH (const Key &key, std::get<1>(tuple)) {
151         Q_EMIT nextKey(key);
152     }
153 }
154
155 void QGpgMEKeyListJob::addMode(KeyListMode mode)
156 {
157     context()->addKeyListMode(mode);
158 }
159 #if 0
160 void QGpgMEKeyListJob::showErrorDialog(QWidget *parent, const QString &caption) const
161 {
162     if (!mResult.error() || mResult.error().isCanceled()) {
163         return;
164     }
165     const QString msg = i18n("<qt><p>An error occurred while fetching "
166                              "the keys from the backend:</p>"
167                              "<p><b>%1</b></p></qt>",
168                              QString::fromLocal8Bit(mResult.error().asString()));
169     KMessageBox::error(parent, msg, caption);
170 }
171 #endif
172 #include "qgpgmekeylistjob.moc"