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