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