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 | |
45 | using namespace GpgME; |
46 | |
47 | static const char * status_to_string( unsigned int status ); |
48 | static Error status_to_error( unsigned int status ); |
49 | |
50 | class EditInteractor::Private { |
51 | friend class ::GpgME::EditInteractor; |
52 | friend class ::GpgME::CallbackHelper; |
53 | EditInteractor * const q; |
54 | public: |
55 | explicit Private( EditInteractor * qq ); |
56 | ~Private(); |
57 | |
58 | private: |
59 | unsigned int state; |
60 | Error error; |
61 | std::FILE * debug; |
62 | }; |
63 | |
64 | class GpgME::CallbackHelper { |
65 | private: |
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 | |
89 | public: |
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 | |
180 | static 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 | |
185 | gpgme_edit_cb_t GpgME::edit_interactor_callback = ::edit_interactor_callback; |
186 | |
187 | EditInteractor::Private::Private( EditInteractor * qq ) |
188 | : q( qq ), |
189 | state( StartState ), |
190 | error(), |
191 | debug(0) |
192 | { |
193 | |
194 | } |
195 | |
196 | EditInteractor::Private::~Private() {} |
197 | |
198 | EditInteractor::EditInteractor() |
199 | : d( new Private( this ) ) |
200 | { |
201 | |
202 | } |
203 | |
204 | EditInteractor::~EditInteractor() { |
205 | delete d; |
206 | } |
207 | |
208 | unsigned int EditInteractor::state() const { |
209 | return d->state; |
210 | } |
211 | |
212 | Error EditInteractor::lastError() const { |
213 | return d->error; |
214 | } |
215 | |
216 | bool 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 |
234 | Error 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 | |
248 | void EditInteractor::setDebugChannel( std::FILE * debug ) { |
249 | d->debug = debug; |
250 | } |
251 | |
252 | static 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 | }; |
346 | static const unsigned int num_status_strings = sizeof status_strings / sizeof *status_strings ; |
347 | |
348 | const 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 | |