cpp, qt: Include config.h
[gpgme.git] / lang / cpp / src / editinteractor.cpp
1 /*
2   editinteractor.cpp - Interface for edit interactors
3   Copyright (C) 2007 Klarälvdalens Datakonsult AB
4
5   This file is part of GPGME++.
6
7   GPGME++ is free software; you can redistribute it and/or
8   modify it under the terms of the GNU Library General Public
9   License as published by the Free Software Foundation; either
10   version 2 of the License, or (at your option) any later version.
11
12   GPGME++ is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU Library General Public License for more details.
16
17   You should have received a copy of the GNU Library General Public License
18   along with GPGME++; see the file COPYING.LIB.  If not, write to the
19   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20   Boston, MA 02110-1301, USA.
21 */
22
23 #ifdef HAVE_CONFIG_H
24  #include "config.h"
25 #endif
26
27 #include "editinteractor.h"
28 #include "callbacks.h"
29 #include "error.h"
30
31 #include <gpgme.h>
32
33 #ifdef _WIN32
34 # include <io.h>
35 #include <windows.h>
36 #else
37 # include <unistd.h>
38 #endif
39
40 #include <cerrno>
41 #include <cstring>
42
43 #ifndef GPG_ERR_ALREADY_SIGNED
44 # define GPG_ERR_ALREADY_SIGNED GPG_ERR_USER_1
45 #endif
46
47 using namespace GpgME;
48
49 static const char *status_to_string(unsigned int status);
50 static Error status_to_error(unsigned int status);
51
52 class EditInteractor::Private
53 {
54     friend class ::GpgME::EditInteractor;
55     friend class ::GpgME::CallbackHelper;
56     EditInteractor *const q;
57 public:
58     explicit Private(EditInteractor *qq);
59     ~Private();
60
61 private:
62     unsigned int state;
63     Error error;
64     std::FILE *debug;
65 };
66
67 class GpgME::CallbackHelper
68 {
69 private:
70     static int writeAll(int fd, const void *buf, size_t count)
71     {
72         size_t toWrite = count;
73         while (toWrite > 0) {
74             const int n = gpgme_io_write(fd, buf, toWrite);
75             if (n < 0) {
76                 return n;
77             }
78             toWrite -= n;
79         }
80         return count;
81     }
82
83 public:
84     static int edit_interactor_callback_impl(void *opaque, gpgme_status_code_t status, const char *args, int fd)
85     {
86         EditInteractor::Private *ei = (EditInteractor::Private *)opaque;
87
88         Error err = status_to_error(status);
89
90         if (!err) {
91
92             // advance to next state based on input:
93             const unsigned int oldState = ei->state;
94             ei->state = ei->q->nextState(status, args, err);
95             if (ei->debug) {
96                 std::fprintf(ei->debug, "EditInteractor: %u -> nextState( %s, %s ) -> %u\n",
97                              oldState, status_to_string(status), args ? args : "<null>", ei->state);
98             }
99             if (err) {
100                 ei->state = oldState;
101                 goto error;
102             }
103
104             if (ei->state != oldState &&
105                     // if there was an error from before, we stop here (### this looks weird, can this happen at all?)
106                     ei->error.code() == GPG_ERR_NO_ERROR) {
107
108                 // successful state change -> call action
109                 if (const char *const result = ei->q->action(err)) {
110                     if (err) {
111                         goto error;
112                     }
113                     if (ei->debug) {
114                         std::fprintf(ei->debug, "EditInteractor: action result \"%s\"\n", result);
115                     }
116                     // if there's a result, write it:
117                     if (*result) {
118                         gpgme_err_set_errno(0);
119                         const ssize_t len = std::strlen(result);
120                         if (writeAll(fd, result, len) != len) {
121                             err = Error::fromSystemError();
122                             if (ei->debug) {
123                                 std::fprintf(ei->debug, "EditInteractor: Could not write to fd %d (%s)\n", fd, err.asString());
124                             }
125                             goto error;
126                         }
127                     }
128                     gpgme_err_set_errno(0);
129                     if (writeAll(fd, "\n", 1) != 1) {
130                         err = Error::fromSystemError();
131                         if (ei->debug) {
132                             std::fprintf(ei->debug, "EditInteractor: Could not write to fd %d (%s)\n", fd, err.asString());
133                         }
134                         goto error;
135                     }
136                 } else {
137                     if (err) {
138                         goto error;
139                     }
140                     if (ei->debug) {
141                         std::fprintf(ei->debug, "EditInteractor: no action result\n");
142                     }
143                 }
144             } else {
145                 if (ei->debug) {
146                     std::fprintf(ei->debug, "EditInteractor: no action executed\n");
147                 }
148             }
149         }
150
151     error:
152         if (err) {
153             ei->error = err;
154             ei->state = EditInteractor::ErrorState;
155         }
156
157         if (ei->debug) {
158             std::fprintf(ei->debug, "EditInteractor: error now %u (%s)\n",
159                          ei->error.encodedError(), gpgme_strerror(ei->error.encodedError()));
160         }
161
162         return ei->error.encodedError();
163     }
164 };
165
166 static gpgme_error_t edit_interactor_callback(void *opaque, gpgme_status_code_t status, const char *args, int fd)
167 {
168     return CallbackHelper::edit_interactor_callback_impl(opaque, status, args, fd);
169 }
170
171 const gpgme_edit_cb_t GpgME::edit_interactor_callback = ::edit_interactor_callback;
172
173 EditInteractor::Private::Private(EditInteractor *qq)
174     : q(qq),
175       state(StartState),
176       error(),
177       debug(0)
178 {
179
180 }
181
182 EditInteractor::Private::~Private() {}
183
184 EditInteractor::EditInteractor()
185     : d(new Private(this))
186 {
187
188 }
189
190 EditInteractor::~EditInteractor()
191 {
192     delete d;
193 }
194
195 unsigned int EditInteractor::state() const
196 {
197     return d->state;
198 }
199
200 Error EditInteractor::lastError() const
201 {
202     return d->error;
203 }
204
205 bool EditInteractor::needsNoResponse(unsigned int status) const
206 {
207     switch (status) {
208     case GPGME_STATUS_ALREADY_SIGNED:
209     case GPGME_STATUS_ERROR:
210     case GPGME_STATUS_GET_BOOL:
211     case GPGME_STATUS_GET_LINE:
212     case GPGME_STATUS_KEY_CREATED:
213     case GPGME_STATUS_NEED_PASSPHRASE_SYM:
214     case GPGME_STATUS_SC_OP_FAILURE:
215         return false;
216     default:
217         return true;
218     }
219 }
220
221 // static
222 Error status_to_error(unsigned int status)
223 {
224     switch (status) {
225     case GPGME_STATUS_MISSING_PASSPHRASE:
226         return Error::fromCode(GPG_ERR_NO_PASSPHRASE);
227     case GPGME_STATUS_ALREADY_SIGNED:
228         return Error::fromCode(GPG_ERR_ALREADY_SIGNED);
229     case GPGME_STATUS_SIGEXPIRED:
230         return Error::fromCode(GPG_ERR_SIG_EXPIRED);
231     }
232     return Error();
233 }
234
235 void EditInteractor::setDebugChannel(std::FILE *debug)
236 {
237     d->debug = debug;
238 }
239
240 static const char *const status_strings[] = {
241     "EOF",
242     /* mkstatus processing starts here */
243     "ENTER",
244     "LEAVE",
245     "ABORT",
246
247     "GOODSIG",
248     "BADSIG",
249     "ERRSIG",
250
251     "BADARMOR",
252
253     "RSA_OR_IDEA",
254     "KEYEXPIRED",
255     "KEYREVOKED",
256
257     "TRUST_UNDEFINED",
258     "TRUST_NEVER",
259     "TRUST_MARGINAL",
260     "TRUST_FULLY",
261     "TRUST_ULTIMATE",
262
263     "SHM_INFO",
264     "SHM_GET",
265     "SHM_GET_BOOL",
266     "SHM_GET_HIDDEN",
267
268     "NEED_PASSPHRASE",
269     "VALIDSIG",
270     "SIG_ID",
271     "ENC_TO",
272     "NODATA",
273     "BAD_PASSPHRASE",
274     "NO_PUBKEY",
275     "NO_SECKEY",
276     "NEED_PASSPHRASE_SYM",
277     "DECRYPTION_FAILED",
278     "DECRYPTION_OKAY",
279     "MISSING_PASSPHRASE",
280     "GOOD_PASSPHRASE",
281     "GOODMDC",
282     "BADMDC",
283     "ERRMDC",
284     "IMPORTED",
285     "IMPORT_OK",
286     "IMPORT_PROBLEM",
287     "IMPORT_RES",
288     "FILE_START",
289     "FILE_DONE",
290     "FILE_ERROR",
291
292     "BEGIN_DECRYPTION",
293     "END_DECRYPTION",
294     "BEGIN_ENCRYPTION",
295     "END_ENCRYPTION",
296
297     "DELETE_PROBLEM",
298     "GET_BOOL",
299     "GET_LINE",
300     "GET_HIDDEN",
301     "GOT_IT",
302     "PROGRESS",
303     "SIG_CREATED",
304     "SESSION_KEY",
305     "NOTATION_NAME",
306     "NOTATION_DATA",
307     "POLICY_URL",
308     "BEGIN_STREAM",
309     "END_STREAM",
310     "KEY_CREATED",
311     "USERID_HINT",
312     "UNEXPECTED",
313     "INV_RECP",
314     "NO_RECP",
315     "ALREADY_SIGNED",
316     "SIGEXPIRED",
317     "EXPSIG",
318     "EXPKEYSIG",
319     "TRUNCATED",
320     "ERROR",
321     "NEWSIG",
322     "REVKEYSIG",
323     "SIG_SUBPACKET",
324     "NEED_PASSPHRASE_PIN",
325     "SC_OP_FAILURE",
326     "SC_OP_SUCCESS",
327     "CARDCTRL",
328     "BACKUP_KEY_CREATED",
329     "PKA_TRUST_BAD",
330     "PKA_TRUST_GOOD",
331
332     "PLAINTEXT",
333 };
334 static const unsigned int num_status_strings = sizeof status_strings / sizeof * status_strings ;
335
336 const char *status_to_string(unsigned int idx)
337 {
338     if (idx < num_status_strings) {
339         return status_strings[idx];
340     } else {
341         return "(unknown)";
342     }
343 }