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