1 | /* |
2 | context.cpp - wraps a gpgme key context |
3 | Copyright (C) 2003, 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 <gpgme++/context.h> |
26 | #include <gpgme++/eventloopinteractor.h> |
27 | #include <gpgme++/trustitem.h> |
28 | #include <gpgme++/assuanresult.h> |
29 | #include <gpgme++/keylistresult.h> |
30 | #include <gpgme++/keygenerationresult.h> |
31 | #include <gpgme++/importresult.h> |
32 | #include <gpgme++/decryptionresult.h> |
33 | #include <gpgme++/verificationresult.h> |
34 | #include <gpgme++/signingresult.h> |
35 | #include <gpgme++/encryptionresult.h> |
36 | #include <gpgme++/engineinfo.h> |
37 | #include <gpgme++/editinteractor.h> |
38 | #include <gpgme++/vfsmountresult.h> |
39 | |
40 | #include <gpgme++/interfaces/assuantransaction.h> |
41 | #include <gpgme++/defaultassuantransaction.h> |
42 | |
43 | #include "callbacks.h" |
44 | #include "data_p.h" |
45 | #include "context_p.h" |
46 | #include "util.h" |
47 | |
48 | #include <gpgme.h> |
49 | |
50 | #include <boost/scoped_array.hpp> |
51 | |
52 | #include <istream> |
53 | #ifndef NDEBUG |
54 | #include <iostream> |
55 | using std::cerr; |
56 | using std::endl; |
57 | #endif |
58 | |
59 | #include <cassert> |
60 | |
61 | #include <qglobal.h> |
62 | |
63 | namespace GpgME { |
64 | |
65 | static inline unsigned int xtoi_1( const char * str ) { |
66 | const unsigned int ch = *str; |
67 | const unsigned int result = |
68 | ch <= '9' ? ch - '0' : |
69 | ch <= 'F' ? ch - 'A' + 10 : |
70 | /* else */ ch - 'a' + 10 ; |
71 | return result < 16 ? result : 0 ; |
72 | } |
73 | static inline int xtoi_2( const char * str ) { |
74 | return xtoi_1( str ) * 16U + xtoi_1( str + 1 ); |
75 | } |
76 | |
77 | #ifdef HAVE_GPGME_ASSUAN_ENGINE |
78 | static void ( std::string & s, bool plus2space ) { |
79 | std::string::iterator src = s.begin(), dest = s.begin(), end = s.end(); |
80 | while ( src != end ) { |
81 | if ( *src == '%' && end - src > 2 ) { |
82 | *dest++ = xtoi_2( &*++src ); |
83 | src += 2; |
84 | } else if ( *src == '+' && plus2space ) { |
85 | *dest++ = ' '; |
86 | ++src; |
87 | } else { |
88 | *dest++ = *src++; |
89 | } |
90 | } |
91 | s.erase( dest, end ); |
92 | } |
93 | #endif |
94 | |
95 | void initializeLibrary() { |
96 | gpgme_check_version( 0 ); |
97 | } |
98 | |
99 | Error initializeLibrary( int ) { |
100 | if ( gpgme_check_version( GPGME_VERSION ) ) { |
101 | return Error(); |
102 | } else { |
103 | return Error::fromCode( GPG_ERR_USER_1 ); |
104 | } |
105 | } |
106 | |
107 | static void ( gpgme_error_t err, std::string & str ) { |
108 | char buffer[ 1024 ]; |
109 | gpgme_strerror_r( err, buffer, sizeof buffer ); |
110 | buffer[ sizeof buffer - 1 ] = '\0'; |
111 | str = buffer; |
112 | } |
113 | |
114 | const char * Error::source() const { |
115 | return gpgme_strsource( (gpgme_error_t)mErr ); |
116 | } |
117 | |
118 | const char * Error::asString() const { |
119 | if ( mMessage.empty() ) { |
120 | format_error( static_cast<gpgme_error_t>( mErr ), mMessage ); |
121 | } |
122 | return mMessage.c_str(); |
123 | } |
124 | |
125 | int Error::code() const { |
126 | return gpgme_err_code( mErr ); |
127 | } |
128 | |
129 | int Error::sourceID() const { |
130 | return gpgme_err_source( mErr ); |
131 | } |
132 | |
133 | bool Error::isCanceled() const { |
134 | return code() == GPG_ERR_CANCELED; |
135 | } |
136 | |
137 | int Error::toErrno() const { |
138 | //#ifdef HAVE_GPGME_GPG_ERROR_WRAPPERS |
139 | return gpgme_err_code_to_errno( static_cast<gpgme_err_code_t>( code() ) ); |
140 | //#else |
141 | // return gpg_err_code_to_errno( static_cast<gpg_err_code_t>( code() ) ); |
142 | //#endif |
143 | } |
144 | |
145 | // static |
146 | bool Error::hasSystemError() { |
147 | #ifdef HAVE_GPGME_GPG_ERROR_WRAPPERS |
148 | return gpgme_err_code_from_syserror() == GPG_ERR_MISSING_ERRNO ; |
149 | #else |
150 | return gpg_err_code_from_syserror() == GPG_ERR_MISSING_ERRNO ; |
151 | #endif |
152 | } |
153 | |
154 | // static |
155 | void Error::setSystemError( gpg_err_code_t err ) { |
156 | setErrno( gpgme_err_code_to_errno( err ) ); |
157 | } |
158 | |
159 | // static |
160 | void Error::setErrno( int err ) { |
161 | #ifdef HAVE_GPGME_GPG_ERROR_WRAPPERS |
162 | gpgme_err_set_errno( err ); |
163 | #else |
164 | gpg_err_set_errno( err ); |
165 | #endif |
166 | } |
167 | |
168 | // static |
169 | Error Error::fromSystemError( unsigned int src ) { |
170 | #ifdef HAVE_GPGME_GPG_ERROR_WRAPPERS |
171 | return Error( gpgme_err_make( static_cast<gpgme_err_source_t>( src ), gpgme_err_code_from_syserror() ) ); |
172 | #else |
173 | return Error( gpg_err_make( static_cast<gpg_err_source_t>( src ), gpg_err_code_from_syserror() ) ); |
174 | #endif |
175 | } |
176 | |
177 | // static |
178 | Error Error::fromErrno( int err, unsigned int src ) { |
179 | //#ifdef HAVE_GPGME_GPG_ERROR_WRAPPERS |
180 | return Error( gpgme_err_make( static_cast<gpgme_err_source_t>( src ), gpgme_err_code_from_errno( err ) ) ); |
181 | //#else |
182 | // return Error( gpg_err_make( static_cast<gpg_err_source_t>( src ), gpg_err_from_from_errno( err ) ) ); |
183 | //#endif |
184 | } |
185 | |
186 | // static |
187 | Error Error::fromCode( unsigned int err, unsigned int src ) { |
188 | //#ifdef HAVE_GPGME_GPG_ERROR_WRAPPERS |
189 | return Error( gpgme_err_make( static_cast<gpgme_err_source_t>( src ), static_cast<gpgme_err_code_t>( err ) ) ); |
190 | //#else |
191 | // return Error( gpg_err_make( static_cast<gpg_err_source_t>( src ), static_cast<gpgme_err_code_t>( err ) ) ); |
192 | //#endif |
193 | } |
194 | |
195 | std::ostream & operator<<( std::ostream & os, const Error & err ) { |
196 | return os << "GpgME::Error(" << err.encodedError() << " (" << err.asString() << "))" ; |
197 | } |
198 | |
199 | Context::Context( gpgme_ctx_t ctx ) : d( new Private( ctx ) ) { |
200 | } |
201 | |
202 | Context::~Context() { |
203 | delete d; |
204 | } |
205 | |
206 | Context * Context::createForProtocol( Protocol proto ) { |
207 | gpgme_ctx_t ctx = 0; |
208 | if ( gpgme_new ( &ctx ) != 0 ) { |
209 | return 0; |
210 | } |
211 | |
212 | switch ( proto ) { |
213 | case OpenPGP: |
214 | if ( gpgme_set_protocol( ctx, GPGME_PROTOCOL_OpenPGP ) != 0 ) { |
215 | gpgme_release( ctx ); |
216 | return 0; |
217 | } |
218 | break; |
219 | case CMS: |
220 | if ( gpgme_set_protocol( ctx, GPGME_PROTOCOL_CMS ) != 0 ) { |
221 | gpgme_release( ctx ); |
222 | return 0; |
223 | } |
224 | break; |
225 | default: |
226 | return 0; |
227 | } |
228 | |
229 | return new Context( ctx ); |
230 | } |
231 | |
232 | std::auto_ptr<Context> Context::createForEngine( Engine eng, Error * error ) { |
233 | gpgme_ctx_t ctx = 0; |
234 | if ( const gpgme_error_t err = gpgme_new( &ctx ) ) { |
235 | if ( error ) { |
236 | *error = Error( err ); |
237 | } |
238 | return std::auto_ptr<Context>(); |
239 | } |
240 | |
241 | switch ( eng ) { |
242 | case AssuanEngine: |
243 | #ifdef HAVE_GPGME_ASSUAN_ENGINE |
244 | if ( const gpgme_error_t err = gpgme_set_protocol( ctx, GPGME_PROTOCOL_ASSUAN ) ) { |
245 | gpgme_release( ctx ); |
246 | if ( error ) { |
247 | *error = Error( err ); |
248 | } |
249 | return std::auto_ptr<Context>(); |
250 | } |
251 | break; |
252 | #else |
253 | if ( error ) { |
254 | *error = Error::fromCode( GPG_ERR_NOT_SUPPORTED ); |
255 | } |
256 | return std::auto_ptr<Context>(); |
257 | #endif |
258 | case G13Engine: |
259 | #ifdef HAVE_GPGME_G13_VFS |
260 | if ( const gpgme_error_t err = gpgme_set_protocol( ctx, GPGME_PROTOCOL_G13 ) ) { |
261 | gpgme_release( ctx ); |
262 | if ( error ) { |
263 | *error = Error( err ); |
264 | } |
265 | return std::auto_ptr<Context>(); |
266 | } |
267 | break; |
268 | #else |
269 | if ( error ) { |
270 | *error = Error::fromCode( GPG_ERR_NOT_SUPPORTED ); |
271 | } |
272 | return std::auto_ptr<Context>(); |
273 | #endif |
274 | default: |
275 | if ( error ) { |
276 | *error = Error::fromCode( GPG_ERR_INV_ARG ); |
277 | } |
278 | return std::auto_ptr<Context>(); |
279 | } |
280 | |
281 | if ( error ) { |
282 | *error = Error(); |
283 | } |
284 | |
285 | return std::auto_ptr<Context>( new Context( ctx ) ); |
286 | } |
287 | |
288 | // |
289 | // |
290 | // Context::Private |
291 | // |
292 | // |
293 | |
294 | Context::Private::Private( gpgme_ctx_t c ) |
295 | : ctx( c ), |
296 | iocbs( 0 ), |
297 | lastop( None ), |
298 | lasterr( GPG_ERR_NO_ERROR ), |
299 | lastAssuanInquireData( Data::null ), |
300 | lastAssuanTransaction(), |
301 | lastEditInteractor(), |
302 | lastCardEditInteractor() |
303 | { |
304 | |
305 | } |
306 | |
307 | Context::Private::~Private() { |
308 | if ( ctx ) { |
309 | gpgme_release( ctx ); |
310 | } |
311 | ctx = 0; |
312 | delete iocbs; |
313 | } |
314 | |
315 | // |
316 | // |
317 | // Context attributes: |
318 | // |
319 | // |
320 | |
321 | Protocol Context::protocol() const { |
322 | gpgme_protocol_t p = gpgme_get_protocol( d->ctx ); |
323 | switch ( p ) { |
324 | case GPGME_PROTOCOL_OpenPGP: return OpenPGP; |
325 | case GPGME_PROTOCOL_CMS: return CMS; |
326 | default: return UnknownProtocol; |
327 | } |
328 | } |
329 | |
330 | |
331 | void Context::setArmor( bool useArmor ) { |
332 | gpgme_set_armor( d->ctx, int( useArmor ) ); |
333 | } |
334 | bool Context::armor() const { |
335 | return gpgme_get_armor( d->ctx ); |
336 | } |
337 | |
338 | void Context::setTextMode( bool useTextMode ) { |
339 | gpgme_set_textmode( d->ctx, int( useTextMode ) ); |
340 | } |
341 | bool Context::textMode() const { |
342 | return gpgme_get_textmode( d->ctx ); |
343 | } |
344 | |
345 | void Context::setIncludeCertificates( int which ) { |
346 | if ( which == DefaultCertificates ) { |
347 | #ifdef HAVE_GPGME_INCLUDE_CERTS_DEFAULT |
348 | which = GPGME_INCLUDE_CERTS_DEFAULT; |
349 | #else |
350 | which = 1; |
351 | #endif |
352 | } |
353 | gpgme_set_include_certs( d->ctx, which ); |
354 | } |
355 | |
356 | int Context::includeCertificates() const { |
357 | return gpgme_get_include_certs( d->ctx ); |
358 | } |
359 | |
360 | void Context::setKeyListMode( unsigned int mode ) { |
361 | gpgme_set_keylist_mode( d->ctx, add_to_gpgme_keylist_mode_t( 0, mode ) ); |
362 | } |
363 | |
364 | void Context::addKeyListMode( unsigned int mode ) { |
365 | const unsigned int cur = gpgme_get_keylist_mode( d->ctx ); |
366 | gpgme_set_keylist_mode( d->ctx, add_to_gpgme_keylist_mode_t( cur, mode ) ); |
367 | } |
368 | |
369 | |
370 | unsigned int Context::keyListMode() const { |
371 | return convert_from_gpgme_keylist_mode_t( gpgme_get_keylist_mode( d->ctx ) ); |
372 | } |
373 | |
374 | void Context::setProgressProvider( ProgressProvider * provider ) { |
375 | gpgme_set_progress_cb( d->ctx, provider ? &progress_callback : 0, provider ); |
376 | } |
377 | ProgressProvider * Context::progressProvider() const { |
378 | void * pp = 0; |
379 | gpgme_progress_cb_t pcb = &progress_callback; |
380 | gpgme_get_progress_cb( d->ctx, &pcb, &pp ); |
381 | return static_cast<ProgressProvider*>( pp ); |
382 | } |
383 | |
384 | void Context::setPassphraseProvider( PassphraseProvider * provider ) { |
385 | gpgme_set_passphrase_cb( d->ctx, provider ? &passphrase_callback : 0, provider ); |
386 | } |
387 | |
388 | PassphraseProvider * Context::passphraseProvider() const { |
389 | void * pp = 0; |
390 | gpgme_passphrase_cb_t pcb = &passphrase_callback; |
391 | gpgme_get_passphrase_cb( d->ctx, &pcb, &pp ); |
392 | return static_cast<PassphraseProvider*>( pp ); |
393 | } |
394 | |
395 | void Context::setManagedByEventLoopInteractor( bool manage ) { |
396 | if ( !EventLoopInteractor::instance() ) { |
397 | #ifndef NDEBUG |
398 | cerr << "Context::setManagedByEventLoopInteractor(): " |
399 | "You must create an instance of EventLoopInteractor " |
400 | "before using anything that needs one." << endl; |
401 | #endif |
402 | return; |
403 | } |
404 | if ( manage ) { |
405 | EventLoopInteractor::instance()->manage( this ); |
406 | } else { |
407 | EventLoopInteractor::instance()->unmanage( this ); |
408 | } |
409 | } |
410 | bool Context::managedByEventLoopInteractor() const { |
411 | return d->iocbs != 0; |
412 | } |
413 | |
414 | |
415 | void Context::installIOCallbacks( gpgme_io_cbs * iocbs ) { |
416 | if ( !iocbs ) { |
417 | uninstallIOCallbacks(); |
418 | return; |
419 | } |
420 | gpgme_set_io_cbs( d->ctx, iocbs ); |
421 | delete d->iocbs; d->iocbs = iocbs; |
422 | } |
423 | |
424 | void Context::uninstallIOCallbacks() { |
425 | static gpgme_io_cbs noiocbs = { 0, 0, 0, 0, 0 }; |
426 | // io.add == 0 means disable io callbacks: |
427 | gpgme_set_io_cbs( d->ctx, &noiocbs ); |
428 | delete d->iocbs; d->iocbs = 0; |
429 | } |
430 | |
431 | Error Context::setLocale( int cat, const char * val ) { |
432 | return Error( d->lasterr = gpgme_set_locale( d->ctx, cat, val ) ); |
433 | } |
434 | |
435 | EngineInfo Context::engineInfo() const { |
436 | #ifdef HAVE_GPGME_CTX_GETSET_ENGINE_INFO |
437 | return EngineInfo( gpgme_ctx_get_engine_info( d->ctx ) ); |
438 | #else |
439 | return EngineInfo(); |
440 | #endif |
441 | } |
442 | |
443 | Error Context::setEngineFileName( const char * filename ) { |
444 | #ifdef HAVE_GPGME_CTX_GETSET_ENGINE_INFO |
445 | const char * const home_dir = engineInfo().homeDirectory(); |
446 | return Error( gpgme_ctx_set_engine_info( d->ctx, gpgme_get_protocol( d->ctx ), filename, home_dir ) ); |
447 | #else |
448 | return Error::fromCode( GPG_ERR_NOT_IMPLEMENTED ); |
449 | #endif |
450 | } |
451 | |
452 | Error Context::setEngineHomeDirectory( const char * home_dir ) { |
453 | #ifdef HAVE_GPGME_CTX_GETSET_ENGINE_INFO |
454 | const char * const filename = engineInfo().fileName(); |
455 | return Error( gpgme_ctx_set_engine_info( d->ctx, gpgme_get_protocol( d->ctx ), filename, home_dir ) ); |
456 | #else |
457 | return Error::fromCode( GPG_ERR_NOT_IMPLEMENTED ); |
458 | #endif |
459 | } |
460 | |
461 | // |
462 | // |
463 | // Key Management |
464 | // |
465 | // |
466 | |
467 | Error Context::startKeyListing( const char * pattern, bool secretOnly ) { |
468 | d->lastop = Private::KeyList; |
469 | return Error( d->lasterr = gpgme_op_keylist_start( d->ctx, pattern, int( secretOnly ) ) ); |
470 | } |
471 | |
472 | Error Context::startKeyListing( const char * patterns[], bool secretOnly ) { |
473 | d->lastop = Private::KeyList; |
474 | #ifndef HAVE_GPGME_EXT_KEYLIST_MODE_EXTERNAL_NONBROKEN |
475 | if ( !patterns || !patterns[0] || !patterns[1] ) { |
476 | // max. one pattern -> use the non-ext version |
477 | return startKeyListing( patterns ? patterns[0] : 0, secretOnly ); |
478 | } |
479 | #endif |
480 | return Error( d->lasterr = gpgme_op_keylist_ext_start( d->ctx, patterns, int( secretOnly ), 0 ) ); |
481 | } |
482 | |
483 | Key Context::nextKey( GpgME::Error & e ) { |
484 | d->lastop = Private::KeyList; |
485 | gpgme_key_t key; |
486 | e = Error( d->lasterr = gpgme_op_keylist_next( d->ctx, &key ) ); |
487 | return Key( key, false ); |
488 | } |
489 | |
490 | KeyListResult Context::endKeyListing() { |
491 | d->lasterr = gpgme_op_keylist_end( d->ctx ); |
492 | return keyListResult(); |
493 | } |
494 | |
495 | KeyListResult Context::keyListResult() const { |
496 | return KeyListResult( d->ctx, Error( d->lasterr ) ); |
497 | } |
498 | |
499 | Key Context::key( const char * fingerprint, GpgME::Error & e , bool secret /*, bool forceUpdate*/ ) { |
500 | d->lastop = Private::KeyList; |
501 | gpgme_key_t key; |
502 | e = Error( d->lasterr = gpgme_get_key( d->ctx, fingerprint, &key, int( secret )/*, int( forceUpdate )*/ ) ); |
503 | return Key( key, false ); |
504 | } |
505 | |
506 | KeyGenerationResult Context::generateKey( const char * parameters, Data & pubKey ) { |
507 | d->lastop = Private::KeyGen; |
508 | Data::Private * const dp = pubKey.impl(); |
509 | d->lasterr = gpgme_op_genkey( d->ctx, parameters, dp ? dp->data : 0, 0 ); |
510 | return KeyGenerationResult( d->ctx, Error( d->lasterr ) ); |
511 | } |
512 | |
513 | Error Context::startKeyGeneration( const char * parameters, Data & pubKey ) { |
514 | d->lastop = Private::KeyGen; |
515 | Data::Private * const dp = pubKey.impl(); |
516 | return Error( d->lasterr = gpgme_op_genkey_start( d->ctx, parameters, dp ? dp->data : 0, 0 ) ); |
517 | } |
518 | |
519 | KeyGenerationResult Context::keyGenerationResult() const { |
520 | if ( d->lastop & Private::KeyGen ) { |
521 | return KeyGenerationResult( d->ctx, Error( d->lasterr ) ); |
522 | } else { |
523 | return KeyGenerationResult(); |
524 | } |
525 | } |
526 | |
527 | Error Context::exportPublicKeys( const char * pattern, Data & keyData ) { |
528 | d->lastop = Private::Export; |
529 | Data::Private * const dp = keyData.impl(); |
530 | return Error( d->lasterr = gpgme_op_export( d->ctx, pattern, 0, dp ? dp->data : 0 ) ); |
531 | } |
532 | |
533 | Error Context::exportPublicKeys( const char * patterns[], Data & keyData ) { |
534 | d->lastop = Private::Export; |
535 | #ifndef HAVE_GPGME_EXT_KEYLIST_MODE_EXTERNAL_NONBROKEN |
536 | if ( !patterns || !patterns[0] || !patterns[1] ) { |
537 | // max. one pattern -> use the non-ext version |
538 | return exportPublicKeys( patterns ? patterns[0] : 0, keyData ); |
539 | } |
540 | #endif |
541 | Data::Private * const dp = keyData.impl(); |
542 | return Error( d->lasterr = gpgme_op_export_ext( d->ctx, patterns, 0, dp ? dp->data : 0 ) ); |
543 | } |
544 | |
545 | Error Context::startPublicKeyExport( const char * pattern, Data & keyData ) { |
546 | d->lastop = Private::Export; |
547 | Data::Private * const dp = keyData.impl(); |
548 | return Error( d->lasterr = gpgme_op_export_start( d->ctx, pattern, 0, dp ? dp->data : 0 ) ); |
549 | } |
550 | |
551 | Error Context::startPublicKeyExport( const char * patterns[], Data & keyData ) { |
552 | d->lastop = Private::Export; |
553 | #ifndef HAVE_GPGME_EXT_KEYLIST_MODE_EXTERNAL_NONBROKEN |
554 | if ( !patterns || !patterns[0] || !patterns[1] ) { |
555 | // max. one pattern -> use the non-ext version |
556 | return startPublicKeyExport( patterns ? patterns[0] : 0, keyData ); |
557 | } |
558 | #endif |
559 | Data::Private * const dp = keyData.impl(); |
560 | return Error( d->lasterr = gpgme_op_export_ext_start( d->ctx, patterns, 0, dp ? dp->data : 0 ) ); |
561 | } |
562 | |
563 | |
564 | ImportResult Context::importKeys( const Data & data ) { |
565 | d->lastop = Private::Import; |
566 | const Data::Private * const dp = data.impl(); |
567 | d->lasterr = gpgme_op_import( d->ctx, dp ? dp->data : 0 ); |
568 | return ImportResult( d->ctx, Error( d->lasterr ) ); |
569 | } |
570 | |
571 | ImportResult Context::importKeys( const std::vector<Key> & kk ) { |
572 | d->lastop = Private::Import; |
573 | d->lasterr = make_error( GPG_ERR_NOT_IMPLEMENTED ); |
574 | |
575 | bool shouldHaveResult = false; |
576 | #ifdef HAVE_GPGME_OP_IMPORT_KEYS |
577 | const boost::scoped_array<gpgme_key_t> keys( new gpgme_key_t[ kk.size() + 1 ] ); |
578 | gpgme_key_t * keys_it = &keys[0]; |
579 | for ( std::vector<Key>::const_iterator it = kk.begin(), end = kk.end() ; it != end ; ++it ) { |
580 | if ( it->impl() ) { |
581 | *keys_it++ = it->impl(); |
582 | } |
583 | } |
584 | *keys_it++ = 0; |
585 | d->lasterr = gpgme_op_import_keys( d->ctx, keys.get() ); |
586 | shouldHaveResult = true; |
587 | #endif |
588 | if ( ( gpgme_err_code( d->lasterr ) == GPG_ERR_NOT_IMPLEMENTED || |
589 | gpgme_err_code( d->lasterr ) == GPG_ERR_NOT_SUPPORTED ) && |
590 | protocol() == CMS ) { |
591 | // ok, try the workaround (export+import): |
592 | std::vector<const char*> fprs; |
593 | for ( std::vector<Key>::const_iterator it = kk.begin(), end = kk.end() ; it != end ; ++it ) { |
594 | if ( const char * fpr = it->primaryFingerprint() ) { |
595 | if ( *fpr ) { |
596 | fprs.push_back( fpr ); |
597 | } |
598 | } else if ( const char * keyid = it->keyID() ) { |
599 | if ( *keyid ) { |
600 | fprs.push_back( keyid ); |
601 | } |
602 | } |
603 | } |
604 | fprs.push_back( 0 ); |
605 | Data data; |
606 | Data::Private * const dp = data.impl(); |
607 | const gpgme_keylist_mode_t oldMode = gpgme_get_keylist_mode( d->ctx ); |
608 | gpgme_set_keylist_mode( d->ctx, GPGME_KEYLIST_MODE_EXTERN ); |
609 | d->lasterr = gpgme_op_export_ext( d->ctx, &fprs[0], 0, dp ? dp->data : 0 ); |
610 | gpgme_set_keylist_mode( d->ctx, oldMode ); |
611 | if ( !d->lasterr ) { |
612 | data.seek( 0, SEEK_SET ); |
613 | d->lasterr = gpgme_op_import( d->ctx, dp ? dp->data : 0 ); |
614 | shouldHaveResult = true; |
615 | } |
616 | } |
617 | if ( shouldHaveResult ) { |
618 | return ImportResult( d->ctx, Error( d->lasterr ) ); |
619 | } else { |
620 | return ImportResult( Error( d->lasterr ) ); |
621 | } |
622 | } |
623 | |
624 | Error Context::startKeyImport( const Data & data ) { |
625 | d->lastop = Private::Import; |
626 | const Data::Private * const dp = data.impl(); |
627 | return Error( d->lasterr = gpgme_op_import_start( d->ctx, dp ? dp->data : 0 ) ); |
628 | } |
629 | |
630 | Error Context::startKeyImport( const std::vector<Key> & kk ) { |
631 | d->lastop = Private::Import; |
632 | #ifdef HAVE_GPGME_OP_IMPORT_KEYS |
633 | const boost::scoped_array<gpgme_key_t> keys( new gpgme_key_t[ kk.size() + 1 ] ); |
634 | gpgme_key_t * keys_it = &keys[0]; |
635 | for ( std::vector<Key>::const_iterator it = kk.begin(), end = kk.end() ; it != end ; ++it ) { |
636 | if ( it->impl() ) { |
637 | *keys_it++ = it->impl(); |
638 | } |
639 | } |
640 | *keys_it++ = 0; |
641 | return Error( d->lasterr = gpgme_op_import_keys_start( d->ctx, keys.get() ) ); |
642 | #else |
643 | (void)kk; |
644 | return Error( d->lasterr = make_error( GPG_ERR_NOT_IMPLEMENTED ) ); |
645 | #endif |
646 | } |
647 | |
648 | ImportResult Context::importResult() const { |
649 | if ( d->lastop & Private::Import ) { |
650 | return ImportResult( d->ctx, Error( d->lasterr ) ); |
651 | } else { |
652 | return ImportResult(); |
653 | } |
654 | } |
655 | |
656 | Error Context::deleteKey( const Key & key, bool allowSecretKeyDeletion ) { |
657 | d->lastop = Private::Delete; |
658 | return Error( d->lasterr = gpgme_op_delete( d->ctx, key.impl(), int( allowSecretKeyDeletion ) ) ); |
659 | } |
660 | |
661 | Error Context::startKeyDeletion( const Key & key, bool allowSecretKeyDeletion ) { |
662 | d->lastop = Private::Delete; |
663 | return Error( d->lasterr = gpgme_op_delete_start( d->ctx, key.impl(), int( allowSecretKeyDeletion ) ) ); |
664 | } |
665 | |
666 | Error Context::passwd( const Key & key ) { |
667 | d->lastop = Private::Passwd; |
668 | #ifdef HAVE_GPGME_OP_PASSWD |
669 | return Error( d->lasterr = gpgme_op_passwd( d->ctx, key.impl(), 0U ) ); |
670 | #else |
671 | (void)key; |
672 | return Error( d->lasterr = make_error( GPG_ERR_NOT_IMPLEMENTED ) ); |
673 | #endif |
674 | } |
675 | |
676 | Error Context::startPasswd( const Key & key ) { |
677 | d->lastop = Private::Passwd; |
678 | #ifdef HAVE_GPGME_OP_PASSWD |
679 | return Error( d->lasterr = gpgme_op_passwd_start( d->ctx, key.impl(), 0U ) ); |
680 | #else |
681 | (void)key; |
682 | return Error( d->lasterr = make_error( GPG_ERR_NOT_IMPLEMENTED ) ); |
683 | #endif |
684 | } |
685 | |
686 | Error Context::edit( const Key & key, std::auto_ptr<EditInteractor> func, Data & data ) { |
687 | d->lastop = Private::Edit; |
688 | d->lastEditInteractor = func; |
689 | Data::Private * const dp = data.impl(); |
690 | return Error( d->lasterr = gpgme_op_edit( d->ctx, key.impl(), |
691 | d->lastEditInteractor.get() ? edit_interactor_callback : 0, |
692 | d->lastEditInteractor.get() ? d->lastEditInteractor->d : 0, |
693 | dp ? dp->data : 0 ) ); |
694 | } |
695 | |
696 | Error Context::startEditing( const Key & key, std::auto_ptr<EditInteractor> func, Data & data ) { |
697 | d->lastop = Private::Edit; |
698 | d->lastEditInteractor = func; |
699 | Data::Private * const dp = data.impl(); |
700 | return Error( d->lasterr = gpgme_op_edit_start( d->ctx, key.impl(), |
701 | d->lastEditInteractor.get() ? edit_interactor_callback : 0, |
702 | d->lastEditInteractor.get() ? d->lastEditInteractor->d : 0, |
703 | dp ? dp->data : 0 ) ); |
704 | } |
705 | |
706 | EditInteractor * Context::lastEditInteractor() const { |
707 | return d->lastEditInteractor.get(); |
708 | } |
709 | |
710 | std::auto_ptr<EditInteractor> Context::takeLastEditInteractor() { |
711 | return d->lastEditInteractor; |
712 | } |
713 | |
714 | Error Context::cardEdit( const Key & key, std::auto_ptr<EditInteractor> func, Data & data ) { |
715 | d->lastop = Private::CardEdit; |
716 | d->lastCardEditInteractor = func; |
717 | Data::Private * const dp = data.impl(); |
718 | return Error( d->lasterr = gpgme_op_card_edit( d->ctx, key.impl(), |
719 | d->lastCardEditInteractor.get() ? edit_interactor_callback : 0, |
720 | d->lastCardEditInteractor.get() ? d->lastCardEditInteractor->d : 0, |
721 | dp ? dp->data : 0 ) ); |
722 | } |
723 | |
724 | Error Context::startCardEditing( const Key & key, std::auto_ptr<EditInteractor> func, Data & data ) { |
725 | d->lastop = Private::CardEdit; |
726 | d->lastCardEditInteractor = func; |
727 | Data::Private * const dp = data.impl(); |
728 | return Error( d->lasterr = gpgme_op_card_edit_start( d->ctx, key.impl(), |
729 | d->lastCardEditInteractor.get() ? edit_interactor_callback : 0, |
730 | d->lastCardEditInteractor.get() ? d->lastCardEditInteractor->d : 0, |
731 | dp ? dp->data : 0 ) ); |
732 | } |
733 | |
734 | EditInteractor * Context::lastCardEditInteractor() const { |
735 | return d->lastCardEditInteractor.get(); |
736 | } |
737 | |
738 | std::auto_ptr<EditInteractor> Context::takeLastCardEditInteractor() { |
739 | return d->lastCardEditInteractor; |
740 | } |
741 | |
742 | Error Context::startTrustItemListing( const char * pattern, int maxLevel ) { |
743 | d->lastop = Private::TrustList; |
744 | return Error( d->lasterr = gpgme_op_trustlist_start( d->ctx, pattern, maxLevel ) ); |
745 | } |
746 | |
747 | TrustItem Context::nextTrustItem( Error & e ) { |
748 | gpgme_trust_item_t ti = 0; |
749 | e = Error( d->lasterr = gpgme_op_trustlist_next( d->ctx, &ti ) ); |
750 | return TrustItem( ti ); |
751 | } |
752 | |
753 | Error Context::endTrustItemListing() { |
754 | return Error( d->lasterr = gpgme_op_trustlist_end( d->ctx ) ); |
755 | } |
756 | |
757 | #ifdef HAVE_GPGME_ASSUAN_ENGINE |
758 | static gpgme_error_t assuan_transaction_data_callback( void * opaque, const void * data, size_t datalen ) { |
759 | assert( opaque ); |
760 | AssuanTransaction * t = static_cast<AssuanTransaction*>( opaque ); |
761 | return t->data( static_cast<const char*>( data ), datalen ).encodedError(); |
762 | } |
763 | |
764 | static gpgme_error_t assuan_transaction_inquire_callback( void * opaque, const char * name, const char * args, gpgme_data_t * r_data ) { |
765 | assert( opaque ); |
766 | Context::Private * p = static_cast<Context::Private*>( opaque ); |
767 | AssuanTransaction * t = p->lastAssuanTransaction.get(); |
768 | assert( t ); |
769 | Error err; |
770 | if ( name ) { |
771 | p->lastAssuanInquireData = t->inquire( name, args, err ); |
772 | } else { |
773 | p->lastAssuanInquireData = Data::null; |
774 | } |
775 | if ( !p->lastAssuanInquireData.isNull() ) { |
776 | *r_data = p->lastAssuanInquireData.impl()->data; |
777 | } |
778 | return err.encodedError(); |
779 | } |
780 | |
781 | static gpgme_error_t assuan_transaction_status_callback( void * opaque, const char * status, const char * args ) { |
782 | assert( opaque ); |
783 | AssuanTransaction * t = static_cast<AssuanTransaction*>( opaque ); |
784 | std::string a = args; |
785 | percent_unescape( a, true ); // ### why doesn't gpgme do this?? |
786 | return t->status( status, a.c_str() ).encodedError(); |
787 | } |
788 | #endif |
789 | |
790 | AssuanResult Context::assuanTransact( const char * command ) { |
791 | return assuanTransact( command, std::auto_ptr<AssuanTransaction>( new DefaultAssuanTransaction ) ); |
792 | } |
793 | |
794 | AssuanResult Context::assuanTransact( const char * command, std::auto_ptr<AssuanTransaction> transaction ) { |
795 | d->lastop = Private::AssuanTransact; |
796 | d->lastAssuanTransaction = transaction; |
797 | if ( !d->lastAssuanTransaction.get() ) { |
798 | return AssuanResult( Error( d->lasterr = make_error( GPG_ERR_INV_ARG ) ) ); |
799 | } |
800 | #ifdef HAVE_GPGME_ASSUAN_ENGINE |
801 | d->lasterr = gpgme_op_assuan_transact( d->ctx, command, |
802 | assuan_transaction_data_callback, |
803 | d->lastAssuanTransaction.get(), |
804 | assuan_transaction_inquire_callback, |
805 | d, // sic! |
806 | assuan_transaction_status_callback, |
807 | d->lastAssuanTransaction.get() ); |
808 | #else |
809 | (void)command; |
810 | d->lasterr = make_error( GPG_ERR_NOT_SUPPORTED ); |
811 | #endif |
812 | return AssuanResult( d->ctx, d->lasterr ); |
813 | } |
814 | |
815 | Error Context::startAssuanTransaction( const char * command ) { |
816 | return startAssuanTransaction( command, std::auto_ptr<AssuanTransaction>( new DefaultAssuanTransaction ) ); |
817 | } |
818 | |
819 | Error Context::startAssuanTransaction( const char * command, std::auto_ptr<AssuanTransaction> transaction ) { |
820 | d->lastop = Private::AssuanTransact; |
821 | d->lastAssuanTransaction = transaction; |
822 | if ( !d->lastAssuanTransaction.get() ) { |
823 | return Error( d->lasterr = make_error( GPG_ERR_INV_ARG ) ); |
824 | } |
825 | #ifdef HAVE_GPGME_ASSUAN_ENGINE |
826 | return Error( d->lasterr = gpgme_op_assuan_transact_start( d->ctx, command, |
827 | assuan_transaction_data_callback, |
828 | d->lastAssuanTransaction.get(), |
829 | assuan_transaction_inquire_callback, |
830 | d, // sic! |
831 | assuan_transaction_status_callback, |
832 | d->lastAssuanTransaction.get() ) ); |
833 | #else |
834 | (void)command; |
835 | return Error( d->lasterr = make_error( GPG_ERR_NOT_SUPPORTED ) ); |
836 | #endif |
837 | } |
838 | |
839 | AssuanResult Context::assuanResult() const { |
840 | if ( d->lastop & Private::AssuanTransact ) { |
841 | return AssuanResult( d->ctx, d->lasterr ); |
842 | } else { |
843 | return AssuanResult(); |
844 | } |
845 | } |
846 | |
847 | AssuanTransaction * Context::lastAssuanTransaction() const { |
848 | return d->lastAssuanTransaction.get(); |
849 | } |
850 | |
851 | std::auto_ptr<AssuanTransaction> Context::takeLastAssuanTransaction() { |
852 | return d->lastAssuanTransaction; |
853 | } |
854 | |
855 | DecryptionResult Context::decrypt( const Data & cipherText, Data & plainText ) { |
856 | d->lastop = Private::Decrypt; |
857 | const Data::Private * const cdp = cipherText.impl(); |
858 | Data::Private * const pdp = plainText.impl(); |
859 | d->lasterr = gpgme_op_decrypt( d->ctx, cdp ? cdp->data : 0, pdp ? pdp->data : 0 ); |
860 | return DecryptionResult( d->ctx, Error( d->lasterr ) ); |
861 | } |
862 | |
863 | Error Context::startDecryption( const Data & cipherText, Data & plainText ) { |
864 | d->lastop = Private::Decrypt; |
865 | const Data::Private * const cdp = cipherText.impl(); |
866 | Data::Private * const pdp = plainText.impl(); |
867 | return Error( d->lasterr = gpgme_op_decrypt_start( d->ctx, cdp ? cdp->data : 0, pdp ? pdp->data : 0 ) ); |
868 | } |
869 | |
870 | DecryptionResult Context::decryptionResult() const { |
871 | if ( d->lastop & Private::Decrypt ) { |
872 | return DecryptionResult( d->ctx, Error( d->lasterr ) ); |
873 | } else { |
874 | return DecryptionResult(); |
875 | } |
876 | } |
877 | |
878 | |
879 | |
880 | VerificationResult Context::verifyDetachedSignature( const Data & signature, const Data & signedText ) { |
881 | d->lastop = Private::Verify; |
882 | const Data::Private * const sdp = signature.impl(); |
883 | const Data::Private * const tdp = signedText.impl(); |
884 | d->lasterr = gpgme_op_verify( d->ctx, sdp ? sdp->data : 0, tdp ? tdp->data : 0, 0 ); |
885 | return VerificationResult( d->ctx, Error( d->lasterr ) ); |
886 | } |
887 | |
888 | VerificationResult Context::verifyOpaqueSignature( const Data & signedData, Data & plainText ) { |
889 | d->lastop = Private::Verify; |
890 | const Data::Private * const sdp = signedData.impl(); |
891 | Data::Private * const pdp = plainText.impl(); |
892 | d->lasterr = gpgme_op_verify( d->ctx, sdp ? sdp->data : 0, 0, pdp ? pdp->data : 0 ); |
893 | return VerificationResult( d->ctx, Error( d->lasterr ) ); |
894 | } |
895 | |
896 | Error Context::startDetachedSignatureVerification( const Data & signature, const Data & signedText ) { |
897 | d->lastop = Private::Verify; |
898 | const Data::Private * const sdp = signature.impl(); |
899 | const Data::Private * const tdp = signedText.impl(); |
900 | return Error( d->lasterr = gpgme_op_verify_start( d->ctx, sdp ? sdp->data : 0, tdp ? tdp->data : 0, 0 ) ); |
901 | } |
902 | |
903 | Error Context::startOpaqueSignatureVerification( const Data & signedData, Data & plainText ) { |
904 | d->lastop = Private::Verify; |
905 | const Data::Private * const sdp = signedData.impl(); |
906 | Data::Private * const pdp = plainText.impl(); |
907 | return Error( d->lasterr = gpgme_op_verify_start( d->ctx, sdp ? sdp->data : 0, 0, pdp ? pdp->data : 0 ) ); |
908 | } |
909 | |
910 | VerificationResult Context::verificationResult() const { |
911 | if ( d->lastop & Private::Verify ) { |
912 | return VerificationResult( d->ctx, Error( d->lasterr ) ); |
913 | } else { |
914 | return VerificationResult(); |
915 | } |
916 | } |
917 | |
918 | std::pair<DecryptionResult,VerificationResult> Context::decryptAndVerify( const Data & cipherText, Data & plainText ) { |
919 | d->lastop = Private::DecryptAndVerify; |
920 | const Data::Private * const cdp = cipherText.impl(); |
921 | Data::Private * const pdp = plainText.impl(); |
922 | d->lasterr = gpgme_op_decrypt_verify( d->ctx, cdp ? cdp->data : 0, pdp ? pdp->data : 0 ); |
923 | return std::make_pair( DecryptionResult( d->ctx, Error( d->lasterr ) ), |
924 | VerificationResult( d->ctx, Error( d->lasterr ) ) ); |
925 | } |
926 | |
927 | Error Context::startCombinedDecryptionAndVerification( const Data & cipherText, Data & plainText ) { |
928 | d->lastop = Private::DecryptAndVerify; |
929 | const Data::Private * const cdp = cipherText.impl(); |
930 | Data::Private * const pdp = plainText.impl(); |
931 | return Error( d->lasterr = gpgme_op_decrypt_verify_start( d->ctx, cdp ? cdp->data : 0, pdp ? pdp->data : 0 ) ); |
932 | } |
933 | |
934 | #ifdef HAVE_GPGME_OP_GETAUDITLOG |
935 | unsigned int to_auditlog_flags( unsigned int flags ) { |
936 | unsigned int result = 0; |
937 | if ( flags & Context::HtmlAuditLog ) { |
938 | result |= GPGME_AUDITLOG_HTML; |
939 | } |
940 | if ( flags & Context::AuditLogWithHelp ) { |
941 | result |= GPGME_AUDITLOG_WITH_HELP; |
942 | } |
943 | return result; |
944 | } |
945 | #endif // HAVE_GPGME_OP_GETAUDITLOG |
946 | |
947 | Error Context::startGetAuditLog( Data & output, unsigned int flags ) { |
948 | d->lastop = Private::GetAuditLog; |
949 | #ifdef HAVE_GPGME_OP_GETAUDITLOG |
950 | Data::Private * const odp = output.impl(); |
951 | return Error( d->lasterr = gpgme_op_getauditlog_start( d->ctx, odp ? odp->data : 0, to_auditlog_flags( flags ) ) ); |
952 | #else |
953 | (void)output; (void)flags; |
954 | return Error( d->lasterr = make_error( GPG_ERR_NOT_IMPLEMENTED ) ); |
955 | #endif |
956 | } |
957 | |
958 | Error Context::getAuditLog( Data & output, unsigned int flags ) { |
959 | d->lastop = Private::GetAuditLog; |
960 | #ifdef HAVE_GPGME_OP_GETAUDITLOG |
961 | Data::Private * const odp = output.impl(); |
962 | return Error( d->lasterr = gpgme_op_getauditlog( d->ctx, odp ? odp->data : 0, to_auditlog_flags( flags ) ) ); |
963 | #else |
964 | (void)output; (void)flags; |
965 | return Error( d->lasterr = make_error( GPG_ERR_NOT_IMPLEMENTED ) ); |
966 | #endif |
967 | } |
968 | |
969 | void Context::clearSigningKeys() { |
970 | gpgme_signers_clear( d->ctx ); |
971 | } |
972 | |
973 | Error Context::addSigningKey( const Key & key ) { |
974 | return Error( d->lasterr = gpgme_signers_add( d->ctx, key.impl() ) ); |
975 | } |
976 | |
977 | Key Context::signingKey( unsigned int idx ) const { |
978 | gpgme_key_t key = gpgme_signers_enum( d->ctx, idx ); |
979 | return Key( key, false ); |
980 | } |
981 | |
982 | std::vector<Key> Context::signingKeys() const { |
983 | std::vector<Key> result; |
984 | gpgme_key_t key; |
985 | for ( unsigned int i = 0 ; ( key = gpgme_signers_enum( d->ctx, i ) ) ; ++i ) { |
986 | result.push_back( Key( key, false ) ); |
987 | } |
988 | return result; |
989 | } |
990 | |
991 | void Context::clearSignatureNotations() { |
992 | #ifdef HAVE_GPGME_SIG_NOTATION_CLEARADDGET |
993 | gpgme_sig_notation_clear( d->ctx ); |
994 | #endif |
995 | } |
996 | |
997 | GpgME::Error Context::addSignatureNotation( const char * name, const char * value, unsigned int flags ) { |
998 | #ifdef HAVE_GPGME_SIG_NOTATION_CLEARADDGET |
999 | return Error( gpgme_sig_notation_add( d->ctx, name, value, add_to_gpgme_sig_notation_flags_t( 0, flags ) ) ); |
1000 | #else |
1001 | (void)name; (void)value; (void)flags; |
1002 | return Error( make_error( GPG_ERR_NOT_IMPLEMENTED ) ); |
1003 | #endif |
1004 | } |
1005 | |
1006 | GpgME::Error Context::addSignaturePolicyURL( const char * url, bool critical ) { |
1007 | #ifdef HAVE_GPGME_SIG_NOTATION_CLEARADDGET |
1008 | return Error( gpgme_sig_notation_add( d->ctx, 0, url, critical ? GPGME_SIG_NOTATION_CRITICAL : 0 ) ); |
1009 | #else |
1010 | (void)url; (void)critical; |
1011 | return Error( make_error( GPG_ERR_NOT_IMPLEMENTED ) ); |
1012 | #endif |
1013 | } |
1014 | |
1015 | const char * Context::signaturePolicyURL() const { |
1016 | #ifdef HAVE_GPGME_SIG_NOTATION_CLEARADDGET |
1017 | for ( gpgme_sig_notation_t n = gpgme_sig_notation_get( d->ctx ) ; n ; n = n->next ) { |
1018 | if ( !n->name ) { |
1019 | return n->value; |
1020 | } |
1021 | } |
1022 | #endif |
1023 | return 0; |
1024 | } |
1025 | |
1026 | Notation Context::signatureNotation( unsigned int idx ) const { |
1027 | #ifdef HAVE_GPGME_SIG_NOTATION_CLEARADDGET |
1028 | for ( gpgme_sig_notation_t n = gpgme_sig_notation_get( d->ctx ) ; n ; n = n->next ) { |
1029 | if ( n->name ) { |
1030 | if ( idx-- == 0 ) { |
1031 | return Notation( n ); |
1032 | } |
1033 | } |
1034 | } |
1035 | #endif |
1036 | return Notation(); |
1037 | } |
1038 | |
1039 | std::vector<Notation> Context::signatureNotations() const { |
1040 | std::vector<Notation> result; |
1041 | #ifdef HAVE_GPGME_SIG_NOTATION_CLEARADDGET |
1042 | for ( gpgme_sig_notation_t n = gpgme_sig_notation_get( d->ctx ) ; n ; n = n->next ) { |
1043 | if ( n->name ) { |
1044 | result.push_back( Notation( n ) ); |
1045 | } |
1046 | } |
1047 | #endif |
1048 | return result; |
1049 | } |
1050 | |
1051 | static gpgme_sig_mode_t sigmode2sigmode( SignatureMode mode ) { |
1052 | switch ( mode ) { |
1053 | default: |
1054 | case NormalSignatureMode: return GPGME_SIG_MODE_NORMAL; |
1055 | case Detached: return GPGME_SIG_MODE_DETACH; |
1056 | case Clearsigned: return GPGME_SIG_MODE_CLEAR; |
1057 | } |
1058 | } |
1059 | |
1060 | SigningResult Context::sign( const Data & plainText, Data & signature, SignatureMode mode ) { |
1061 | d->lastop = Private::Sign; |
1062 | const Data::Private * const pdp = plainText.impl(); |
1063 | Data::Private * const sdp = signature.impl(); |
1064 | d->lasterr = gpgme_op_sign( d->ctx, pdp ? pdp->data : 0, sdp ? sdp->data : 0, sigmode2sigmode( mode ) ); |
1065 | return SigningResult( d->ctx, Error( d->lasterr ) ); |
1066 | } |
1067 | |
1068 | |
1069 | Error Context::startSigning( const Data & plainText, Data & signature, SignatureMode mode ) { |
1070 | d->lastop = Private::Sign; |
1071 | const Data::Private * const pdp = plainText.impl(); |
1072 | Data::Private * const sdp = signature.impl(); |
1073 | return Error( d->lasterr = gpgme_op_sign_start( d->ctx, pdp ? pdp->data : 0, sdp ? sdp->data : 0, sigmode2sigmode( mode ) ) ); |
1074 | } |
1075 | |
1076 | SigningResult Context::signingResult() const { |
1077 | if ( d->lastop & Private::Sign ) { |
1078 | return SigningResult( d->ctx, Error( d->lasterr ) ); |
1079 | } else { |
1080 | return SigningResult(); |
1081 | } |
1082 | } |
1083 | |
1084 | static gpgme_encrypt_flags_t encryptflags2encryptflags( Context::EncryptionFlags flags ) { |
1085 | unsigned int result = 0; |
1086 | if ( flags & Context::AlwaysTrust ) { |
1087 | result |= GPGME_ENCRYPT_ALWAYS_TRUST; |
1088 | } |
1089 | #ifdef HAVE_GPGME_ENCRYPT_NO_ENCRYPT_TO |
1090 | if ( flags & Context::NoEncryptTo ) { |
1091 | result |= GPGME_ENCRYPT_NO_ENCRYPT_TO; |
1092 | } |
1093 | #endif |
1094 | return static_cast<gpgme_encrypt_flags_t>( result ); |
1095 | } |
1096 | |
1097 | EncryptionResult Context::encrypt( const std::vector<Key> & recipients, const Data & plainText, Data & cipherText, EncryptionFlags flags ) { |
1098 | d->lastop = Private::Encrypt; |
1099 | #ifndef HAVE_GPGME_ENCRYPT_NO_ENCRYPT_TO |
1100 | if ( flags & NoEncryptTo ) { |
1101 | return EncryptionResult( Error( d->lasterr = make_error( GPG_ERR_NOT_IMPLEMENTED ) ) ); |
1102 | } |
1103 | #endif |
1104 | const Data::Private * const pdp = plainText.impl(); |
1105 | Data::Private * const cdp = cipherText.impl(); |
1106 | gpgme_key_t * const keys = new gpgme_key_t[ recipients.size() + 1 ]; |
1107 | gpgme_key_t * keys_it = keys; |
1108 | for ( std::vector<Key>::const_iterator it = recipients.begin() ; it != recipients.end() ; ++it ) { |
1109 | if ( it->impl() ) { |
1110 | *keys_it++ = it->impl(); |
1111 | } |
1112 | } |
1113 | *keys_it++ = 0; |
1114 | d->lasterr = gpgme_op_encrypt( d->ctx, keys, encryptflags2encryptflags( flags ), |
1115 | pdp ? pdp->data : 0, cdp ? cdp->data : 0 ); |
1116 | delete[] keys; |
1117 | return EncryptionResult( d->ctx, Error( d->lasterr ) ); |
1118 | } |
1119 | |
1120 | Error Context::encryptSymmetrically( const Data & plainText, Data & cipherText ) { |
1121 | d->lastop = Private::Encrypt; |
1122 | const Data::Private * const pdp = plainText.impl(); |
1123 | Data::Private * const cdp = cipherText.impl(); |
1124 | return Error( d->lasterr = gpgme_op_encrypt( d->ctx, 0, (gpgme_encrypt_flags_t)0, |
1125 | pdp ? pdp->data : 0, cdp ? cdp->data : 0 ) ); |
1126 | } |
1127 | |
1128 | Error Context::startEncryption( const std::vector<Key> & recipients, const Data & plainText, Data & cipherText, EncryptionFlags flags ) { |
1129 | d->lastop = Private::Encrypt; |
1130 | #ifndef HAVE_GPGME_ENCRYPT_NO_ENCRYPT_TO |
1131 | if ( flags & NoEncryptTo ) { |
1132 | return Error( d->lasterr = make_error( GPG_ERR_NOT_IMPLEMENTED ) ); |
1133 | } |
1134 | #endif |
1135 | const Data::Private * const pdp = plainText.impl(); |
1136 | Data::Private * const cdp = cipherText.impl(); |
1137 | gpgme_key_t * const keys = new gpgme_key_t[ recipients.size() + 1 ]; |
1138 | gpgme_key_t * keys_it = keys; |
1139 | for ( std::vector<Key>::const_iterator it = recipients.begin() ; it != recipients.end() ; ++it ) { |
1140 | if ( it->impl() ) { |
1141 | *keys_it++ = it->impl(); |
1142 | } |
1143 | } |
1144 | *keys_it++ = 0; |
1145 | d->lasterr = gpgme_op_encrypt_start( d->ctx, keys, encryptflags2encryptflags( flags ), |
1146 | pdp ? pdp->data : 0, cdp ? cdp->data : 0 ); |
1147 | delete[] keys; |
1148 | return Error( d->lasterr ); |
1149 | } |
1150 | |
1151 | EncryptionResult Context::encryptionResult() const { |
1152 | if ( d->lastop & Private::Encrypt ) { |
1153 | return EncryptionResult( d->ctx, Error( d->lasterr ) ); |
1154 | } else { |
1155 | return EncryptionResult(); |
1156 | } |
1157 | } |
1158 | |
1159 | std::pair<SigningResult,EncryptionResult> Context::signAndEncrypt( const std::vector<Key> & recipients, const Data & plainText, Data & cipherText, EncryptionFlags flags ) { |
1160 | d->lastop = Private::SignAndEncrypt; |
1161 | const Data::Private * const pdp = plainText.impl(); |
1162 | Data::Private * const cdp = cipherText.impl(); |
1163 | gpgme_key_t * const keys = new gpgme_key_t[ recipients.size() + 1 ]; |
1164 | gpgme_key_t * keys_it = keys; |
1165 | for ( std::vector<Key>::const_iterator it = recipients.begin() ; it != recipients.end() ; ++it ) { |
1166 | if ( it->impl() ) { |
1167 | *keys_it++ = it->impl(); |
1168 | } |
1169 | } |
1170 | *keys_it++ = 0; |
1171 | d->lasterr = gpgme_op_encrypt_sign( d->ctx, keys, encryptflags2encryptflags( flags ), |
1172 | pdp ? pdp->data : 0, cdp ? cdp->data : 0 ); |
1173 | delete[] keys; |
1174 | return std::make_pair( SigningResult( d->ctx, Error( d->lasterr ) ), |
1175 | EncryptionResult( d->ctx, Error( d->lasterr ) ) ); |
1176 | } |
1177 | |
1178 | Error Context::startCombinedSigningAndEncryption( const std::vector<Key> & recipients, const Data & plainText, Data & cipherText, EncryptionFlags flags ) { |
1179 | d->lastop = Private::SignAndEncrypt; |
1180 | const Data::Private * const pdp = plainText.impl(); |
1181 | Data::Private * const cdp = cipherText.impl(); |
1182 | gpgme_key_t * const keys = new gpgme_key_t[ recipients.size() + 1 ]; |
1183 | gpgme_key_t * keys_it = keys; |
1184 | for ( std::vector<Key>::const_iterator it = recipients.begin() ; it != recipients.end() ; ++it ) { |
1185 | if ( it->impl() ) { |
1186 | *keys_it++ = it->impl(); |
1187 | } |
1188 | } |
1189 | *keys_it++ = 0; |
1190 | d->lasterr = gpgme_op_encrypt_sign_start( d->ctx, keys, encryptflags2encryptflags( flags ), |
1191 | pdp ? pdp->data : 0, cdp ? cdp->data : 0 ); |
1192 | delete[] keys; |
1193 | return Error( d->lasterr ); |
1194 | } |
1195 | |
1196 | Error Context::createVFS(const char* containerFile, const std::vector< Key >& recipients) { |
1197 | d->lastop = Private::CreateVFS; |
1198 | #ifdef HAVE_GPGME_G13_VFS |
1199 | gpgme_key_t * const keys = new gpgme_key_t[ recipients.size() + 1 ]; |
1200 | gpgme_key_t * keys_it = keys; |
1201 | for ( std::vector<Key>::const_iterator it = recipients.begin() ; it != recipients.end() ; ++it ) { |
1202 | if ( it->impl() ) { |
1203 | *keys_it++ = it->impl(); |
1204 | } |
1205 | } |
1206 | *keys_it++ = 0; |
1207 | |
1208 | gpgme_error_t op_err; |
1209 | d->lasterr = gpgme_op_vfs_create( d->ctx, keys, containerFile, 0, &op_err ); |
1210 | delete[] keys; |
1211 | Error error( d->lasterr ); |
1212 | if ( error ) { |
1213 | return error; |
1214 | } |
1215 | return Error( d->lasterr = op_err ); |
1216 | #else |
1217 | Q_UNUSED( containerFile ); |
1218 | Q_UNUSED( recipients ); |
1219 | return Error( d->lasterr = make_error( GPG_ERR_NOT_SUPPORTED ) ); |
1220 | #endif |
1221 | } |
1222 | |
1223 | VfsMountResult Context::mountVFS(const char* containerFile, const char* mountDir) { |
1224 | d->lastop = Private::MountVFS; |
1225 | #ifdef HAVE_GPGME_G13_VFS |
1226 | gpgme_error_t op_err; |
1227 | d->lasterr = gpgme_op_vfs_mount( d->ctx, containerFile, mountDir, 0, &op_err ); |
1228 | return VfsMountResult( d->ctx, Error( d->lasterr ), Error( op_err ) ); |
1229 | #else |
1230 | Q_UNUSED( containerFile ); |
1231 | Q_UNUSED( mountDir ); |
1232 | return VfsMountResult( d->ctx, Error( d->lasterr = make_error( GPG_ERR_NOT_SUPPORTED ) ), Error() ); |
1233 | #endif |
1234 | } |
1235 | |
1236 | Error Context::cancelPendingOperation() { |
1237 | #ifdef HAVE_GPGME_CANCEL_ASYNC |
1238 | return Error( gpgme_cancel_async( d->ctx ) ); |
1239 | #else |
1240 | return Error( gpgme_cancel( d->ctx ) ); |
1241 | #endif |
1242 | } |
1243 | |
1244 | bool Context::poll() { |
1245 | gpgme_error_t e = GPG_ERR_NO_ERROR; |
1246 | const bool finished = gpgme_wait( d->ctx, &e, 0 ); |
1247 | if ( finished ) { |
1248 | d->lasterr = e; |
1249 | } |
1250 | return finished; |
1251 | } |
1252 | |
1253 | Error Context::wait() { |
1254 | gpgme_error_t e = GPG_ERR_NO_ERROR; |
1255 | gpgme_wait( d->ctx, &e, 1 ); |
1256 | return Error( d->lasterr = e ); |
1257 | } |
1258 | |
1259 | Error Context::lastError() const { |
1260 | return Error( d->lasterr ); |
1261 | } |
1262 | |
1263 | std::ostream & operator<<( std::ostream & os, Protocol proto ) { |
1264 | os << "GpgME::Protocol(" ; |
1265 | switch ( proto ) { |
1266 | case OpenPGP: |
1267 | os << "OpenPGP" ; |
1268 | break; |
1269 | case CMS: |
1270 | os << "CMS" ; |
1271 | break; |
1272 | default: |
1273 | case UnknownProtocol: |
1274 | os << "UnknownProtocol" ; |
1275 | break; |
1276 | } |
1277 | return os << ')'; |
1278 | } |
1279 | |
1280 | std::ostream & operator<<( std::ostream & os, Engine eng ) { |
1281 | os << "GpgME::Engine(" ; |
1282 | switch ( eng ) { |
1283 | case GpgEngine: |
1284 | os << "GpgEngine" ; |
1285 | break; |
1286 | case GpgSMEngine: |
1287 | os << "GpgSMEngine" ; |
1288 | break; |
1289 | case GpgConfEngine: |
1290 | os << "GpgConfEngine" ; |
1291 | break; |
1292 | case AssuanEngine: |
1293 | os << "AssuanEngine" ; |
1294 | break; |
1295 | default: |
1296 | case UnknownEngine: |
1297 | os << "UnknownEngine" ; |
1298 | break; |
1299 | } |
1300 | return os << ')'; |
1301 | } |
1302 | |
1303 | std::ostream & operator<<( std::ostream & os, Context::CertificateInclusion incl ) { |
1304 | os << "GpgME::Context::CertificateInclusion(" << static_cast<int>( incl ); |
1305 | switch ( incl ) { |
1306 | case Context::DefaultCertificates: |
1307 | os << "(DefaultCertificates)" ; |
1308 | break; |
1309 | case Context::AllCertificatesExceptRoot: |
1310 | os << "(AllCertificatesExceptRoot)" ; |
1311 | break; |
1312 | case Context::AllCertificates: |
1313 | os << "(AllCertificates)" ; |
1314 | break; |
1315 | case Context::NoCertificates: |
1316 | os << "(NoCertificates)" ; |
1317 | break; |
1318 | case Context::OnlySenderCertificate: |
1319 | os << "(OnlySenderCertificate)" ; |
1320 | break; |
1321 | } |
1322 | return os << ')'; |
1323 | } |
1324 | |
1325 | std::ostream & operator<<( std::ostream & os, KeyListMode mode ) { |
1326 | os << "GpgME::KeyListMode(" ; |
1327 | #define CHECK( x ) if ( !(mode & (x)) ) {} else do { os << #x " "; } while (0) |
1328 | CHECK( Local ); |
1329 | CHECK( Extern ); |
1330 | CHECK( Signatures ); |
1331 | CHECK( Validate ); |
1332 | CHECK( Ephemeral ); |
1333 | #undef CHECK |
1334 | return os << ')'; |
1335 | } |
1336 | |
1337 | std::ostream & operator<<( std::ostream & os, SignatureMode mode ) { |
1338 | os << "GpgME::SignatureMode(" ; |
1339 | switch ( mode ) { |
1340 | #define CHECK( x ) case x: os << #x; break |
1341 | CHECK( NormalSignatureMode ); |
1342 | CHECK( Detached ); |
1343 | CHECK( Clearsigned ); |
1344 | #undef CHECK |
1345 | default: |
1346 | os << "???" "(" << static_cast<int>( mode ) << ')'; |
1347 | break; |
1348 | } |
1349 | return os << ')'; |
1350 | } |
1351 | |
1352 | std::ostream & operator<<( std::ostream & os, Context::EncryptionFlags flags ) { |
1353 | os << "GpgME::Context::EncryptionFlags(" ; |
1354 | #define CHECK( x ) if ( !(flags & (Context::x)) ) {} else do { os << #x " "; } while (0) |
1355 | CHECK( AlwaysTrust ); |
1356 | #undef CHECK |
1357 | return os << ')'; |
1358 | } |
1359 | |
1360 | std::ostream & operator<<( std::ostream & os, Context::AuditLogFlags flags ) { |
1361 | os << "GpgME::Context::AuditLogFlags(" ; |
1362 | #define CHECK( x ) if ( !(flags & (Context::x)) ) {} else do { os << #x " "; } while (0) |
1363 | CHECK( HtmlAuditLog ); |
1364 | CHECK( AuditLogWithHelp ); |
1365 | #undef CHECK |
1366 | return os << ')'; |
1367 | } |
1368 | |
1369 | } // namespace GpgME |
1370 | |
1371 | GpgME::Error GpgME::setDefaultLocale( int cat, const char * val ) { |
1372 | return Error( gpgme_set_locale( 0, cat, val ) ); |
1373 | } |
1374 | |
1375 | GpgME::EngineInfo GpgME::engineInfo( GpgME::Protocol proto ) { |
1376 | gpgme_engine_info_t ei = 0; |
1377 | if ( gpgme_get_engine_info( &ei ) ) { |
1378 | return EngineInfo(); |
1379 | } |
1380 | |
1381 | const gpgme_protocol_t p = proto == CMS ? GPGME_PROTOCOL_CMS : GPGME_PROTOCOL_OpenPGP ; |
1382 | |
1383 | for ( gpgme_engine_info_t i = ei ; i ; i = i->next ) { |
1384 | if ( i->protocol == p ) { |
1385 | return EngineInfo( i ); |
1386 | } |
1387 | } |
1388 | |
1389 | return EngineInfo(); |
1390 | } |
1391 | |
1392 | GpgME::Error GpgME::checkEngine( GpgME::Protocol proto ) { |
1393 | const gpgme_protocol_t p = proto == CMS ? GPGME_PROTOCOL_CMS : GPGME_PROTOCOL_OpenPGP ; |
1394 | |
1395 | return Error( gpgme_engine_check_version( p ) ); |
1396 | } |
1397 | |
1398 | static gpgme_protocol_t UNKNOWN_PROTOCOL = static_cast<gpgme_protocol_t>( 255 ); |
1399 | |
1400 | static gpgme_protocol_t engine2protocol( const GpgME::Engine engine ) { |
1401 | switch ( engine ) { |
1402 | case GpgME::GpgEngine: return GPGME_PROTOCOL_OpenPGP; |
1403 | case GpgME::GpgSMEngine: return GPGME_PROTOCOL_CMS; |
1404 | case GpgME::GpgConfEngine: |
1405 | #ifdef HAVE_GPGME_PROTOCOL_GPGCONF |
1406 | return GPGME_PROTOCOL_GPGCONF; |
1407 | #else |
1408 | break; |
1409 | #endif |
1410 | case GpgME::AssuanEngine: |
1411 | #ifdef HAVE_GPGME_ASSUAN_ENGINE |
1412 | return GPGME_PROTOCOL_ASSUAN; |
1413 | #else |
1414 | break; |
1415 | #endif |
1416 | case GpgME::G13Engine: |
1417 | #ifdef HAVE_GPGME_G13_VFS |
1418 | return GPGME_PROTOCOL_G13; |
1419 | #else |
1420 | break; |
1421 | #endif |
1422 | case GpgME::UnknownEngine: |
1423 | ; |
1424 | } |
1425 | return UNKNOWN_PROTOCOL; |
1426 | } |
1427 | |
1428 | GpgME::EngineInfo GpgME::engineInfo( GpgME::Engine engine ) { |
1429 | gpgme_engine_info_t ei = 0; |
1430 | if ( gpgme_get_engine_info( &ei ) ) { |
1431 | return EngineInfo(); |
1432 | } |
1433 | |
1434 | const gpgme_protocol_t p = engine2protocol( engine ); |
1435 | |
1436 | for ( gpgme_engine_info_t i = ei ; i ; i = i->next ) { |
1437 | if ( i->protocol == p ) { |
1438 | return EngineInfo( i ); |
1439 | } |
1440 | } |
1441 | |
1442 | return EngineInfo(); |
1443 | } |
1444 | |
1445 | GpgME::Error GpgME::checkEngine( GpgME::Engine engine ) { |
1446 | const gpgme_protocol_t p = engine2protocol( engine ); |
1447 | |
1448 | return Error( gpgme_engine_check_version( p ) ); |
1449 | } |
1450 | |
1451 | static const unsigned long supported_features = 0 |
1452 | | GpgME::ValidatingKeylistModeFeature |
1453 | | GpgME::CancelOperationFeature |
1454 | | GpgME::WrongKeyUsageFeature |
1455 | #ifdef HAVE_GPGME_INCLUDE_CERTS_DEFAULT |
1456 | | GpgME::DefaultCertificateInclusionFeature |
1457 | #endif |
1458 | #ifdef HAVE_GPGME_CTX_GETSET_ENGINE_INFO |
1459 | | GpgME::GetSetEngineInfoFeature |
1460 | #endif |
1461 | #ifdef HAVE_GPGME_SIG_NOTATION_CLEARADDGET |
1462 | | GpgME::ClearAddGetSignatureNotationsFeature |
1463 | #endif |
1464 | #ifdef HAVE_GPGME_DATA_SET_FILE_NAME |
1465 | | GpgME::SetDataFileNameFeeature |
1466 | #endif |
1467 | #ifdef HAVE_GPGME_KEYLIST_MODE_SIG_NOTATIONS |
1468 | | GpgME::SignatureNotationsKeylistModeFeature |
1469 | #endif |
1470 | #ifdef HAVE_GPGME_KEY_SIG_NOTATIONS |
1471 | | GpgME::KeySignatureNotationsFeature |
1472 | #endif |
1473 | #ifdef HAVE_GPGME_KEY_T_IS_QUALIFIED |
1474 | | GpgME::KeyIsQualifiedFeature |
1475 | #endif |
1476 | #ifdef HAVE_GPGME_SIG_NOTATION_CRITICAL |
1477 | | GpgME::SignatureNotationsCriticalFlagFeature |
1478 | #endif |
1479 | #ifdef HAVE_GPGME_SIG_NOTATION_FLAGS_T |
1480 | | GpgME::SignatureNotationsFlagsFeature |
1481 | #endif |
1482 | #ifdef HAVE_GPGME_SIG_NOTATION_HUMAN_READABLE |
1483 | | GpgME::SignatureNotationsHumanReadableFlagFeature |
1484 | #endif |
1485 | #ifdef HAVE_GPGME_SUBKEY_T_IS_QUALIFIED |
1486 | | GpgME::SubkeyIsQualifiedFeature |
1487 | #endif |
1488 | #ifdef HAVE_GPGME_ENGINE_INFO_T_HOME_DIR |
1489 | | GpgME::EngineInfoHomeDirFeature |
1490 | #endif |
1491 | #ifdef HAVE_GPGME_DECRYPT_RESULT_T_FILE_NAME |
1492 | | GpgME::DecryptionResultFileNameFeature |
1493 | #endif |
1494 | #ifdef HAVE_GPGME_DECRYPT_RESULT_T_RECIPIENTS |
1495 | | GpgME::DecryptionResultRecipientsFeature |
1496 | #endif |
1497 | #ifdef HAVE_GPGME_VERIFY_RESULT_T_FILE_NAME |
1498 | | GpgME::VerificationResultFileNameFeature |
1499 | #endif |
1500 | #ifdef HAVE_GPGME_SIGNATURE_T_PKA_FIELDS |
1501 | | GpgME::SignaturePkaFieldsFeature |
1502 | #endif |
1503 | #ifdef HAVE_GPGME_SIGNATURE_T_ALGORITHM_FIELDS |
1504 | | GpgME::SignatureAlgorithmFieldsFeature |
1505 | #endif |
1506 | #ifdef HAVE_GPGME_GET_FDPTR |
1507 | | GpgME::FdPointerFeature |
1508 | #endif |
1509 | #ifdef HAVE_GPGME_OP_GETAUDITLOG |
1510 | | GpgME::AuditLogFeature |
1511 | #endif |
1512 | #ifdef HAVE_GPGME_PROTOCOL_GPGCONF |
1513 | | GpgME::GpgConfEngineFeature |
1514 | #endif |
1515 | #ifdef HAVE_GPGME_CANCEL_ASYNC |
1516 | | GpgME::CancelOperationAsyncFeature |
1517 | #endif |
1518 | #ifdef HAVE_GPGME_ENCRYPT_NO_ENCRYPT_TO |
1519 | | GpgME::NoEncryptToEncryptionFlagFeature |
1520 | #endif |
1521 | #ifdef HAVE_GPGME_SUBKEY_T_IS_CARDKEY |
1522 | | GpgME::CardKeyFeature |
1523 | #endif |
1524 | #ifdef HAVE_GPGME_ASSUAN_ENGINE |
1525 | | GpgME::AssuanEngineFeature |
1526 | #endif |
1527 | #ifdef HAVE_GPGME_KEYLIST_MODE_EPHEMERAL |
1528 | | GpgME::EphemeralKeylistModeFeature |
1529 | #endif |
1530 | #ifdef HAVE_GPGME_OP_IMPORT_KEYS |
1531 | | GpgME::ImportFromKeyserverFeature |
1532 | #endif |
1533 | #ifdef HAVE_GPGME_G13_VFS |
1534 | | GpgME::G13VFSFeature |
1535 | #endif |
1536 | #ifdef HAVE_GPGME_OP_PASSWD |
1537 | | GpgME::PasswdFeature |
1538 | #endif |
1539 | ; |
1540 | |
1541 | static const unsigned long supported_features2 = 0 |
1542 | ; |
1543 | |
1544 | bool GpgME::hasFeature( unsigned long features ) { |
1545 | return features == ( features & supported_features ); |
1546 | } |
1547 | |
1548 | bool GpgME::hasFeature( unsigned long features, unsigned long features2 ) { |
1549 | return features == ( features & supported_features ) |
1550 | && features2 == ( features2 & supported_features2 ) |
1551 | ; |
1552 | } |
1553 | |