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