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 28 : static int edit_interactor_callback_impl(void *opaque, gpgme_status_code_t status, const char *args, int fd)
85 : {
86 28 : EditInteractor::Private *ei = (EditInteractor::Private *)opaque;
87 :
88 28 : Error err = status_to_error(status);
89 :
90 28 : if (!err) {
91 :
92 : // advance to next state based on input:
93 28 : const unsigned int oldState = ei->state;
94 28 : ei->state = ei->q->nextState(status, args, err);
95 28 : if (ei->debug) {
96 : 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 28 : if (err) {
100 0 : ei->state = oldState;
101 0 : goto error;
102 : }
103 :
104 35 : 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 21 : if (ei->debug) {
146 0 : std::fprintf(ei->debug, "EditInteractor: no action executed\n");
147 : }
148 : }
149 : }
150 :
151 : error:
152 28 : if (err) {
153 0 : ei->error = err;
154 0 : ei->state = EditInteractor::ErrorState;
155 : }
156 :
157 28 : if (ei->debug) {
158 : std::fprintf(ei->debug, "EditInteractor: error now %u (%s)\n",
159 0 : ei->error.encodedError(), gpgme_strerror(ei->error.encodedError()));
160 : }
161 :
162 28 : return ei->error.encodedError();
163 : }
164 : };
165 :
166 28 : static gpgme_error_t edit_interactor_callback(void *opaque, gpgme_status_code_t status, const char *args, int fd)
167 : {
168 28 : 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 2 : EditInteractor::~EditInteractor()
191 : {
192 2 : delete d;
193 2 : }
194 :
195 35 : unsigned int EditInteractor::state() const
196 : {
197 35 : return d->state;
198 : }
199 :
200 0 : Error EditInteractor::lastError() const
201 : {
202 0 : return d->error;
203 : }
204 :
205 28 : bool EditInteractor::needsNoResponse(unsigned int status) const
206 : {
207 28 : 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 7 : return false;
216 : default:
217 21 : return true;
218 : }
219 : }
220 :
221 : // static
222 28 : Error status_to_error(unsigned int status)
223 : {
224 28 : switch (status) {
225 : case GPGME_STATUS_MISSING_PASSPHRASE:
226 0 : return Error::fromCode(GPG_ERR_NO_PASSPHRASE);
227 : case GPGME_STATUS_ALREADY_SIGNED:
228 0 : return Error::fromCode(GPG_ERR_ALREADY_SIGNED);
229 : case GPGME_STATUS_SIGEXPIRED:
230 0 : return Error::fromCode(GPG_ERR_SIG_EXPIRED);
231 : }
232 28 : return Error();
233 : }
234 :
235 0 : void EditInteractor::setDebugChannel(std::FILE *debug)
236 : {
237 0 : d->debug = debug;
238 0 : }
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 0 : const char *status_to_string(unsigned int idx)
337 : {
338 0 : if (idx < num_status_strings) {
339 0 : return status_strings[idx];
340 : } else {
341 0 : return "(unknown)";
342 : }
343 : }
|