f9df6e84c1425e581f38ab3b84fdd51b414b3961
[gpgme.git] / lang / python / pyme / core.py
1 # Copyright (C) 2016 g10 Code GmbH
2 # Copyright (C) 2004,2008 Igor Belyi <belyi@users.sourceforge.net>
3 # Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
4 #
5 #    This library is free software; you can redistribute it and/or
6 #    modify it under the terms of the GNU Lesser General Public
7 #    License as published by the Free Software Foundation; either
8 #    version 2.1 of the License, or (at your option) any later version.
9 #
10 #    This library is distributed in the hope that it will be useful,
11 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 #    Lesser General Public License for more details.
14 #
15 #    You should have received a copy of the GNU Lesser General Public
16 #    License along with this library; if not, write to the Free Software
17 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
18
19 """Core functionality
20
21 Core functionality of GPGME wrapped in a object-oriented fashion.
22 Provides the 'Context' class for performing cryptographic operations,
23 and the 'Data' class describing buffers of data.
24
25 """
26
27 import re
28 import os
29 import weakref
30 from . import gpgme
31 from .errors import errorcheck, GPGMEError
32 from . import constants
33 from . import errors
34 from . import util
35
36 class GpgmeWrapper(object):
37     """Base wrapper class
38
39     Not to be instantiated directly.
40
41     """
42
43     def __init__(self, wrapped):
44         self._callback_excinfo = None
45         self.wrapped = wrapped
46
47     def __repr__(self):
48         return '<{}/{!r}>'.format(super().__repr__(), self.wrapped)
49
50     def __str__(self):
51         acc = ['{}.{}'.format(__name__, self.__class__.__name__)]
52         flags = [f for f in self._boolean_properties if getattr(self, f)]
53         if flags:
54             acc.append('({})'.format(' '.join(flags)))
55
56         return '<{}>'.format(' '.join(acc))
57
58     def __hash__(self):
59         return hash(repr(self.wrapped))
60
61     def __eq__(self, other):
62         if other == None:
63             return False
64         else:
65             return repr(self.wrapped) == repr(other.wrapped)
66
67     @property
68     def _ctype(self):
69         """The name of the c type wrapped by this class
70
71         Must be set by child classes.
72
73         """
74         raise NotImplementedError()
75
76     @property
77     def _cprefix(self):
78         """The common prefix of c functions wrapped by this class
79
80         Must be set by child classes.
81
82         """
83         raise NotImplementedError()
84
85     def _errorcheck(self, name):
86         """Must be implemented by child classes.
87
88         This function must return a trueish value for all c functions
89         returning gpgme_error_t."""
90         raise NotImplementedError()
91
92     """The set of all boolean properties"""
93     _boolean_properties = set()
94
95     def __wrap_boolean_property(self, key, do_set=False, value=None):
96         get_func = getattr(gpgme,
97                            "{}get_{}".format(self._cprefix, key))
98         set_func = getattr(gpgme,
99                            "{}set_{}".format(self._cprefix, key))
100         def get(slf):
101             return bool(get_func(slf.wrapped))
102         def set_(slf, value):
103             set_func(slf.wrapped, bool(value))
104
105         p = property(get, set_, doc="{} flag".format(key))
106         setattr(self.__class__, key, p)
107
108         if do_set:
109             set_(self, bool(value))
110         else:
111             return get(self)
112
113     _munge_docstring = re.compile(r'gpgme_([^(]*)\(([^,]*), (.*\) -> .*)')
114     def __getattr__(self, key):
115         """On-the-fly generation of wrapper methods and properties"""
116         if key[0] == '_' or self._cprefix == None:
117             return None
118
119         if key in self._boolean_properties:
120             return self.__wrap_boolean_property(key)
121
122         name = self._cprefix + key
123         func = getattr(gpgme, name)
124
125         if self._errorcheck(name):
126             def _funcwrap(slf, *args):
127                 result = func(slf.wrapped, *args)
128                 if slf._callback_excinfo:
129                     gpgme.pyme_raise_callback_exception(slf)
130                 return errorcheck(result, "Invocation of " + name)
131         else:
132             def _funcwrap(slf, *args):
133                 result = func(slf.wrapped, *args)
134                 if slf._callback_excinfo:
135                     gpgme.pyme_raise_callback_exception(slf)
136                 return result
137
138         doc = self._munge_docstring.sub(r'\2.\1(\3', getattr(func, "__doc__"))
139         _funcwrap.__doc__ = doc
140
141         # Monkey-patch the class.
142         setattr(self.__class__, key, _funcwrap)
143
144         # Bind the method to 'self'.
145         def wrapper(*args):
146             return _funcwrap(self, *args)
147         wrapper.__doc__ = doc
148
149         return wrapper
150
151     def __setattr__(self, key, value):
152         """On-the-fly generation of properties"""
153         if key in self._boolean_properties:
154             self.__wrap_boolean_property(key, True, value)
155         else:
156             super().__setattr__(key, value)
157
158 class Context(GpgmeWrapper):
159     """Context for cryptographic operations
160
161     All cryptographic operations in GPGME are performed within a
162     context, which contains the internal state of the operation as
163     well as configuration parameters.  By using several contexts you
164     can run several cryptographic operations in parallel, with
165     different configuration.
166
167     Access to a context must be synchronized.
168
169     """
170
171     def __init__(self, armor=False, textmode=False, offline=False,
172                  signers=[], pinentry_mode=constants.PINENTRY_MODE_DEFAULT,
173                  protocol=constants.PROTOCOL_OpenPGP,
174                  wrapped=None):
175         """Construct a context object
176
177         Keyword arguments:
178         armor           -- enable ASCII armoring (default False)
179         textmode        -- enable canonical text mode (default False)
180         offline         -- do not contact external key sources (default False)
181         signers         -- list of keys used for signing (default [])
182         pinentry_mode   -- pinentry mode (default PINENTRY_MODE_DEFAULT)
183         protocol        -- protocol to use (default PROTOCOL_OpenPGP)
184
185         """
186         if wrapped:
187             self.own = False
188         else:
189             tmp = gpgme.new_gpgme_ctx_t_p()
190             errorcheck(gpgme.gpgme_new(tmp))
191             wrapped = gpgme.gpgme_ctx_t_p_value(tmp)
192             gpgme.delete_gpgme_ctx_t_p(tmp)
193             self.own = True
194         super().__init__(wrapped)
195         self.armor = armor
196         self.textmode = textmode
197         self.offline = offline
198         self.signers = signers
199         self.pinentry_mode = pinentry_mode
200         self.protocol = protocol
201
202     def encrypt(self, plaintext, recipients=[], sign=True, sink=None,
203                 passphrase=None, always_trust=False, add_encrypt_to=False,
204                 prepare=False, expect_sign=False, compress=True):
205         """Encrypt data
206
207         Encrypt the given plaintext for the given recipients.  If the
208         list of recipients is empty, the data is encrypted
209         symmetrically with a passphrase.
210
211         The passphrase can be given as parameter, using a callback
212         registered at the context, or out-of-band via pinentry.
213
214         Keyword arguments:
215         recipients      -- list of keys to encrypt to
216         sign            -- sign plaintext (default True)
217         sink            -- write result to sink instead of returning it
218         passphrase      -- for symmetric encryption
219         always_trust    -- always trust the keys (default False)
220         add_encrypt_to  -- encrypt to configured additional keys (default False)
221         prepare         -- (ui) prepare for encryption (default False)
222         expect_sign     -- (ui) prepare for signing (default False)
223         compress        -- compress plaintext (default True)
224
225         Returns:
226         ciphertext      -- the encrypted data (or None if sink is given)
227         result          -- additional information about the encryption
228         sign_result     -- additional information about the signature(s)
229
230         Raises:
231         InvalidRecipients -- if encryption using a particular key failed
232         InvalidSigners  -- if signing using a particular key failed
233         GPGMEError      -- as signaled by the underlying library
234
235         """
236         ciphertext = sink if sink else Data()
237         flags = 0
238         flags |= always_trust * constants.ENCRYPT_ALWAYS_TRUST
239         flags |= (not add_encrypt_to) * constants.ENCRYPT_NO_ENCRYPT_TO
240         flags |= prepare * constants.ENCRYPT_PREPARE
241         flags |= expect_sign * constants.ENCRYPT_EXPECT_SIGN
242         flags |= (not compress) * constants.ENCRYPT_NO_COMPRESS
243
244         if passphrase != None:
245             old_pinentry_mode = self.pinentry_mode
246             old_passphrase_cb = getattr(self, '_passphrase_cb', None)
247             self.pinentry_mode = constants.PINENTRY_MODE_LOOPBACK
248             def passphrase_cb(hint, desc, prev_bad, hook=None):
249                 return passphrase
250             self.set_passphrase_cb(passphrase_cb)
251
252         try:
253             if sign:
254                 self.op_encrypt_sign(recipients, flags, plaintext, ciphertext)
255             else:
256                 self.op_encrypt(recipients, flags, plaintext, ciphertext)
257         except errors.GPGMEError as e:
258             if e.getcode() == errors.UNUSABLE_PUBKEY:
259                 result = self.op_encrypt_result()
260                 if result.invalid_recipients:
261                     raise errors.InvalidRecipients(result.invalid_recipients)
262             if e.getcode() == errors.UNUSABLE_SECKEY:
263                 sig_result = self.op_sign_result()
264                 if sig_result.invalid_signers:
265                     raise errors.InvalidSigners(sig_result.invalid_signers)
266             raise
267         finally:
268             if passphrase != None:
269                 self.pinentry_mode = old_pinentry_mode
270                 if old_passphrase_cb:
271                     self.set_passphrase_cb(*old_passphrase_cb[1:])
272
273         result = self.op_encrypt_result()
274         assert not result.invalid_recipients
275         sig_result = self.op_sign_result() if sign else None
276         assert not sig_result or not sig_result.invalid_signers
277
278         cipherbytes = None
279         if not sink:
280             ciphertext.seek(0, os.SEEK_SET)
281             cipherbytes = ciphertext.read()
282         return cipherbytes, result, sig_result
283
284     def decrypt(self, ciphertext, sink=None, passphrase=None, verify=True):
285         """Decrypt data
286
287         Decrypt the given ciphertext and verify any signatures.  If
288         VERIFY is an iterable of keys, the ciphertext must be signed
289         by all those keys, otherwise an error is raised.
290
291         If the ciphertext is symmetrically encrypted using a
292         passphrase, that passphrase can be given as parameter, using a
293         callback registered at the context, or out-of-band via
294         pinentry.
295
296         Keyword arguments:
297         sink            -- write result to sink instead of returning it
298         passphrase      -- for symmetric decryption
299         verify          -- check signatures (default True)
300
301         Returns:
302         plaintext       -- the decrypted data (or None if sink is given)
303         result          -- additional information about the decryption
304         verify_result   -- additional information about the signature(s)
305
306         Raises:
307         UnsupportedAlgorithm -- if an unsupported algorithm was used
308         BadSignatures   -- if a bad signature is encountered
309         MissingSignatures -- if expected signatures are missing or bad
310         GPGMEError      -- as signaled by the underlying library
311
312         """
313         plaintext = sink if sink else Data()
314
315         if passphrase != None:
316             old_pinentry_mode = self.pinentry_mode
317             old_passphrase_cb = getattr(self, '_passphrase_cb', None)
318             self.pinentry_mode = constants.PINENTRY_MODE_LOOPBACK
319             def passphrase_cb(hint, desc, prev_bad, hook=None):
320                 return passphrase
321             self.set_passphrase_cb(passphrase_cb)
322
323         try:
324             if verify:
325                 self.op_decrypt_verify(ciphertext, plaintext)
326             else:
327                 self.op_decrypt(ciphertext, plaintext)
328         finally:
329             if passphrase != None:
330                 self.pinentry_mode = old_pinentry_mode
331                 if old_passphrase_cb:
332                     self.set_passphrase_cb(*old_passphrase_cb[1:])
333
334         result = self.op_decrypt_result()
335         verify_result = self.op_verify_result() if verify else None
336         if result.unsupported_algorithm:
337             raise errors.UnsupportedAlgorithm(result.unsupported_algorithm)
338
339         if verify:
340             if any(s.status != errors.NO_ERROR
341                    for s in verify_result.signatures):
342                 raise errors.BadSignatures(verify_result)
343
344         if verify and verify != True:
345             missing = list()
346             for key in verify:
347                 ok = False
348                 for subkey in key.subkeys:
349                     for sig in verify_result.signatures:
350                         if sig.summary & constants.SIGSUM_VALID == 0:
351                             continue
352                         if subkey.can_sign and subkey.fpr == sig.fpr:
353                             ok = True
354                             break
355                     if ok:
356                         break
357                 if not ok:
358                     missing.append(key)
359             if missing:
360                 raise errors.MissingSignatures(verify_result, missing)
361
362         plainbytes = None
363         if not sink:
364             plaintext.seek(0, os.SEEK_SET)
365             plainbytes = plaintext.read()
366         return plainbytes, result, verify_result
367
368     def sign(self, data, sink=None, mode=constants.SIG_MODE_NORMAL):
369         """Sign data
370
371         Sign the given data with either the configured default local
372         key, or the 'signers' keys of this context.
373
374         Keyword arguments:
375         mode            -- signature mode (default: normal, see below)
376         sink            -- write result to sink instead of returning it
377
378         Returns:
379         either
380           signed_data   -- encoded data and signature (normal mode)
381           signature     -- only the signature data (detached mode)
382           cleartext     -- data and signature as text (cleartext mode)
383             (or None if sink is given)
384         result          -- additional information about the signature(s)
385
386         Raises:
387         InvalidSigners  -- if signing using a particular key failed
388         GPGMEError      -- as signaled by the underlying library
389
390         """
391         signeddata = sink if sink else Data()
392
393         try:
394             self.op_sign(data, signeddata, mode)
395         except errors.GPGMEError as e:
396             if e.getcode() == errors.UNUSABLE_SECKEY:
397                 result = self.op_sign_result()
398                 if result.invalid_signers:
399                     raise errors.InvalidSigners(result.invalid_signers)
400             raise
401
402         result = self.op_sign_result()
403         assert not result.invalid_signers
404
405         signedbytes = None
406         if not sink:
407             signeddata.seek(0, os.SEEK_SET)
408             signedbytes = signeddata.read()
409         return signedbytes, result
410
411     def verify(self, signed_data, signature=None, sink=None, verify=[]):
412         """Verify signatures
413
414         Verify signatures over data.  If VERIFY is an iterable of
415         keys, the ciphertext must be signed by all those keys,
416         otherwise an error is raised.
417
418         Keyword arguments:
419         signature       -- detached signature data
420         sink            -- write result to sink instead of returning it
421
422         Returns:
423         data            -- the plain data
424             (or None if sink is given, or we verified a detached signature)
425         result          -- additional information about the signature(s)
426
427         Raises:
428         BadSignatures   -- if a bad signature is encountered
429         MissingSignatures -- if expected signatures are missing or bad
430         GPGMEError      -- as signaled by the underlying library
431
432         """
433         if signature:
434             # Detached signature, we don't return the plain text.
435             data = None
436         else:
437             data = sink if sink else Data()
438
439         if signature:
440             self.op_verify(signature, signed_data, None)
441         else:
442             self.op_verify(signed_data, None, data)
443
444         result = self.op_verify_result()
445         if any(s.status != errors.NO_ERROR for s in result.signatures):
446             raise errors.BadSignatures(result)
447
448         missing = list()
449         for key in verify:
450             ok = False
451             for subkey in key.subkeys:
452                 for sig in result.signatures:
453                     if sig.summary & constants.SIGSUM_VALID == 0:
454                         continue
455                     if subkey.can_sign and subkey.fpr == sig.fpr:
456                         ok = True
457                         break
458                 if ok:
459                     break
460             if not ok:
461                 missing.append(key)
462         if missing:
463             raise errors.MissingSignatures(result, missing)
464
465         plainbytes = None
466         if data and not sink:
467             data.seek(0, os.SEEK_SET)
468             plainbytes = data.read()
469         return plainbytes, result
470
471     def keylist(self, pattern=None, secret=False):
472         """List keys
473
474         Keyword arguments:
475         pattern -- return keys matching pattern (default: all keys)
476         secret  -- return only secret keys
477
478         Returns:
479                 -- an iterator returning key objects
480
481         Raises:
482         GPGMEError      -- as signaled by the underlying library
483         """
484         return self.op_keylist_all(pattern, secret)
485
486     def assuan_transact(self, command,
487                         data_cb=None, inquire_cb=None, status_cb=None):
488         """Issue a raw assuan command
489
490         This function can be used to issue a raw assuan command to the
491         engine.
492
493         If command is a string or bytes, it will be used as-is.  If it
494         is an iterable of strings, it will be properly escaped and
495         joined into an well-formed assuan command.
496
497         Keyword arguments:
498         data_cb         -- a callback receiving data lines
499         inquire_cb      -- a callback providing more information
500         status_cb       -- a callback receiving status lines
501
502         Returns:
503         result          -- the result of command as GPGMEError
504
505         Raises:
506         GPGMEError      -- as signaled by the underlying library
507
508         """
509
510         if isinstance(command, (str, bytes)):
511             cmd = command
512         else:
513             cmd = " ".join(util.percent_escape(f) for f in command)
514
515         errptr = gpgme.new_gpgme_error_t_p()
516
517         err = gpgme.gpgme_op_assuan_transact_ext(
518             self.wrapped,
519             cmd,
520             (weakref.ref(self), data_cb) if data_cb else None,
521             (weakref.ref(self), inquire_cb) if inquire_cb else None,
522             (weakref.ref(self), status_cb) if status_cb else None,
523             errptr)
524
525         if self._callback_excinfo:
526             gpgme.pyme_raise_callback_exception(self)
527
528         errorcheck(err)
529
530         status = gpgme.gpgme_error_t_p_value(errptr)
531         gpgme.delete_gpgme_error_t_p(errptr)
532
533         return GPGMEError(status) if status != 0 else None
534
535     @property
536     def signers(self):
537         """Keys used for signing"""
538         return [self.signers_enum(i) for i in range(self.signers_count())]
539     @signers.setter
540     def signers(self, signers):
541         old = self.signers
542         self.signers_clear()
543         try:
544             for key in signers:
545                 self.signers_add(key)
546         except:
547             self.signers = old
548             raise
549
550     @property
551     def pinentry_mode(self):
552         """Pinentry mode"""
553         return self.get_pinentry_mode()
554     @pinentry_mode.setter
555     def pinentry_mode(self, value):
556         self.set_pinentry_mode(value)
557
558     @property
559     def protocol(self):
560         """Protocol to use"""
561         return self.get_protocol()
562     @protocol.setter
563     def protocol(self, value):
564         errorcheck(gpgme.gpgme_engine_check_version(value))
565         self.set_protocol(value)
566
567     _ctype = 'gpgme_ctx_t'
568     _cprefix = 'gpgme_'
569
570     def _errorcheck(self, name):
571         """This function should list all functions returning gpgme_error_t"""
572         return ((name.startswith('gpgme_op_')
573                  and not name.endswith('_result'))
574                 or name in {
575                     'gpgme_set_ctx_flag',
576                     'gpgme_set_protocol',
577                     'gpgme_set_sub_protocol',
578                     'gpgme_set_keylist_mode',
579                     'gpgme_set_pinentry_mode',
580                     'gpgme_set_locale',
581                     'gpgme_set_engine_info',
582                     'gpgme_signers_add',
583                     'gpgme_get_sig_key',
584                     'gpgme_sig_notation_add',
585                     'gpgme_cancel',
586                     'gpgme_cancel_async',
587                     'gpgme_cancel_get_key',
588                 })
589
590     _boolean_properties = {'armor', 'textmode', 'offline'}
591
592     def __del__(self):
593         if not gpgme:
594             # At interpreter shutdown, gpgme is set to NONE.
595             return
596
597         self._free_passcb()
598         self._free_progresscb()
599         self._free_statuscb()
600         if self.own and self.wrapped and gpgme.gpgme_release:
601             gpgme.gpgme_release(self.wrapped)
602             self.wrapped = None
603
604     # Implement the context manager protocol.
605     def __enter__(self):
606         return self
607     def __exit__(self, type, value, tb):
608         self.__del__()
609
610     def op_keylist_all(self, *args, **kwargs):
611         self.op_keylist_start(*args, **kwargs)
612         key = self.op_keylist_next()
613         while key:
614             yield key
615             key = self.op_keylist_next()
616         self.op_keylist_end()
617
618     def op_keylist_next(self):
619         """Returns the next key in the list created
620         by a call to op_keylist_start().  The object returned
621         is of type Key."""
622         ptr = gpgme.new_gpgme_key_t_p()
623         try:
624             errorcheck(gpgme.gpgme_op_keylist_next(self.wrapped, ptr))
625             key = gpgme.gpgme_key_t_p_value(ptr)
626         except errors.GPGMEError as excp:
627             key = None
628             if excp.getcode() != errors.EOF:
629                 raise excp
630         gpgme.delete_gpgme_key_t_p(ptr)
631         if key:
632             key.__del__ = lambda self: gpgme.gpgme_key_unref(self)
633             return key
634
635     def get_key(self, fpr, secret):
636         """Return the key corresponding to the fingerprint 'fpr'"""
637         ptr = gpgme.new_gpgme_key_t_p()
638         errorcheck(gpgme.gpgme_get_key(self.wrapped, fpr, ptr, secret))
639         key = gpgme.gpgme_key_t_p_value(ptr)
640         gpgme.delete_gpgme_key_t_p(ptr)
641         if key:
642             key.__del__ = lambda self: gpgme.gpgme_key_unref(self)
643             return key
644
645     def op_trustlist_all(self, *args, **kwargs):
646         self.op_trustlist_start(*args, **kwargs)
647         trust = self.op_trustlist_next()
648         while trust:
649             yield trust
650             trust = self.op_trustlist_next()
651         self.op_trustlist_end()
652
653     def op_trustlist_next(self):
654         """Returns the next trust item in the list created
655         by a call to op_trustlist_start().  The object returned
656         is of type TrustItem."""
657         ptr = gpgme.new_gpgme_trust_item_t_p()
658         try:
659             errorcheck(gpgme.gpgme_op_trustlist_next(self.wrapped, ptr))
660             trust = gpgme.gpgme_trust_item_t_p_value(ptr)
661         except errors.GPGMEError as excp:
662             trust = None
663             if excp.getcode() != errors.EOF:
664                 raise
665         gpgme.delete_gpgme_trust_item_t_p(ptr)
666         return trust
667
668     def set_passphrase_cb(self, func, hook=None):
669         """Sets the passphrase callback to the function specified by func.
670
671         When the system needs a passphrase, it will call func with three args:
672         hint, a string describing the key it needs the passphrase for;
673         desc, a string describing the passphrase it needs;
674         prev_bad, a boolean equal True if this is a call made after
675         unsuccessful previous attempt.
676
677         If hook has a value other than None it will be passed into the func
678         as a forth argument.
679
680         Please see the GPGME manual for more information.
681         """
682         if func == None:
683             hookdata = None
684         else:
685             if hook == None:
686                 hookdata = (weakref.ref(self), func)
687             else:
688                 hookdata = (weakref.ref(self), func, hook)
689         gpgme.pyme_set_passphrase_cb(self, hookdata)
690
691     def _free_passcb(self):
692         if gpgme.pyme_set_passphrase_cb:
693             self.set_passphrase_cb(None)
694
695     def set_progress_cb(self, func, hook=None):
696         """Sets the progress meter callback to the function specified by FUNC.
697         If FUNC is None, the callback will be cleared.
698
699         This function will be called to provide an interactive update
700         of the system's progress.  The function will be called with
701         three arguments, type, total, and current.  If HOOK is not
702         None, it will be supplied as fourth argument.
703
704         Please see the GPGME manual for more information.
705
706         """
707         if func == None:
708             hookdata = None
709         else:
710             if hook == None:
711                 hookdata = (weakref.ref(self), func)
712             else:
713                 hookdata = (weakref.ref(self), func, hook)
714         gpgme.pyme_set_progress_cb(self, hookdata)
715
716     def _free_progresscb(self):
717         if gpgme.pyme_set_progress_cb:
718             self.set_progress_cb(None)
719
720     def set_status_cb(self, func, hook=None):
721         """Sets the status callback to the function specified by FUNC.  If
722         FUNC is None, the callback will be cleared.
723
724         The function will be called with two arguments, keyword and
725         args.  If HOOK is not None, it will be supplied as third
726         argument.
727
728         Please see the GPGME manual for more information.
729
730         """
731         if func == None:
732             hookdata = None
733         else:
734             if hook == None:
735                 hookdata = (weakref.ref(self), func)
736             else:
737                 hookdata = (weakref.ref(self), func, hook)
738         gpgme.pyme_set_status_cb(self, hookdata)
739
740     def _free_statuscb(self):
741         if gpgme.pyme_set_status_cb:
742             self.set_status_cb(None)
743
744     @property
745     def engine_info(self):
746         """Configuration of the engine currently in use"""
747         p = self.protocol
748         infos = [i for i in self.get_engine_info() if i.protocol == p]
749         assert len(infos) == 1
750         return infos[0]
751
752     def get_engine_info(self):
753         """Get engine configuration
754
755         Returns information about all configured and installed
756         engines.
757
758         Returns:
759         infos           -- a list of engine infos
760
761         """
762         return gpgme.gpgme_ctx_get_engine_info(self.wrapped)
763
764     def set_engine_info(self, proto, file_name=None, home_dir=None):
765         """Change engine configuration
766
767         Changes the configuration of the crypto engine implementing
768         the protocol 'proto' for the context.
769
770         Keyword arguments:
771         file_name       -- engine program file name (unchanged if None)
772         home_dir        -- configuration directory (unchanged if None)
773
774         """
775         errorcheck(gpgme.gpgme_ctx_set_engine_info(
776             self.wrapped, proto, file_name, home_dir))
777
778     def wait(self, hang):
779         """Wait for asynchronous call to finish. Wait forever if hang is True.
780         Raises an exception on errors.
781
782         Please read the GPGME manual for more information.
783
784         """
785         ptr = gpgme.new_gpgme_error_t_p()
786         gpgme.gpgme_wait(self.wrapped, ptr, hang)
787         status = gpgme.gpgme_error_t_p_value(ptr)
788         gpgme.delete_gpgme_error_t_p(ptr)
789         errorcheck(status)
790
791     def op_edit(self, key, func, fnc_value, out):
792         """Start key editing using supplied callback function"""
793         if key == None:
794             raise ValueError("op_edit: First argument cannot be None")
795         if fnc_value:
796             opaquedata = (weakref.ref(self), func, fnc_value)
797         else:
798             opaquedata = (weakref.ref(self), func)
799
800         result = gpgme.gpgme_op_edit(self.wrapped, key, opaquedata, out)
801         if self._callback_excinfo:
802             gpgme.pyme_raise_callback_exception(self)
803         errorcheck(result)
804
805 class Data(GpgmeWrapper):
806     """Data buffer
807
808     A lot of data has to be exchanged between the user and the crypto
809     engine, like plaintext messages, ciphertext, signatures and
810     information about the keys.  The technical details about
811     exchanging the data information are completely abstracted by
812     GPGME.  The user provides and receives the data via `gpgme_data_t'
813     objects, regardless of the communication protocol between GPGME
814     and the crypto engine in use.
815
816     This Data class is the implementation of the GpgmeData objects.
817
818     Please see the information about __init__ for instantiation.
819
820     """
821
822     _ctype = 'gpgme_data_t'
823     _cprefix = 'gpgme_data_'
824
825     def _errorcheck(self, name):
826         """This function should list all functions returning gpgme_error_t"""
827         return name not in {
828             'gpgme_data_release_and_get_mem',
829             'gpgme_data_get_encoding',
830             'gpgme_data_seek',
831             'gpgme_data_get_file_name',
832         }
833
834     def __init__(self, string=None, file=None, offset=None,
835                  length=None, cbs=None, copy=True):
836         """Initialize a new gpgme_data_t object.
837
838         If no args are specified, make it an empty object.
839
840         If string alone is specified, initialize it with the data
841         contained there.
842
843         If file, offset, and length are all specified, file must
844         be either a filename or a file-like object, and the object
845         will be initialized by reading the specified chunk from the file.
846
847         If cbs is specified, it MUST be a tuple of the form:
848
849         (read_cb, write_cb, seek_cb, release_cb[, hook])
850
851         where the first four items are functions implementing reading,
852         writing, seeking the data, and releasing any resources once
853         the data object is deallocated.  The functions must match the
854         following prototypes:
855
856             def read(amount, hook=None):
857                 return <a b"bytes" object>
858
859             def write(data, hook=None):
860                 return <the number of bytes written>
861
862             def seek(offset, whence, hook=None):
863                 return <the new file position>
864
865             def release(hook=None):
866                 <return value and exceptions are ignored>
867
868         The functions may be bound methods.  In that case, you can
869         simply use the 'self' reference instead of using a hook.
870
871         If file is specified without any other arguments, then
872         it must be a filename, and the object will be initialized from
873         that file.
874
875         """
876         super().__init__(None)
877         self.data_cbs = None
878
879         if cbs != None:
880             self.new_from_cbs(*cbs)
881         elif string != None:
882             self.new_from_mem(string, copy)
883         elif file != None and offset != None and length != None:
884             self.new_from_filepart(file, offset, length)
885         elif file != None:
886             if type(file) == type("x"):
887                 self.new_from_file(file, copy)
888             else:
889                 self.new_from_fd(file)
890         else:
891             self.new()
892
893     def __del__(self):
894         if not gpgme:
895             # At interpreter shutdown, gpgme is set to NONE.
896             return
897
898         if self.wrapped != None and gpgme.gpgme_data_release:
899             gpgme.gpgme_data_release(self.wrapped)
900             if self._callback_excinfo:
901                 gpgme.pyme_raise_callback_exception(self)
902             self.wrapped = None
903         self._free_datacbs()
904
905     # Implement the context manager protocol.
906     def __enter__(self):
907         return self
908     def __exit__(self, type, value, tb):
909         self.__del__()
910
911     def _free_datacbs(self):
912         self._data_cbs = None
913
914     def new(self):
915         tmp = gpgme.new_gpgme_data_t_p()
916         errorcheck(gpgme.gpgme_data_new(tmp))
917         self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
918         gpgme.delete_gpgme_data_t_p(tmp)
919
920     def new_from_mem(self, string, copy=True):
921         tmp = gpgme.new_gpgme_data_t_p()
922         errorcheck(gpgme.gpgme_data_new_from_mem(tmp,string,len(string),copy))
923         self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
924         gpgme.delete_gpgme_data_t_p(tmp)
925
926     def new_from_file(self, filename, copy=True):
927         tmp = gpgme.new_gpgme_data_t_p()
928         try:
929             errorcheck(gpgme.gpgme_data_new_from_file(tmp, filename, copy))
930         except errors.GPGMEError as e:
931             if e.getcode() == errors.INV_VALUE and not copy:
932                 raise ValueError("delayed reads are not yet supported")
933             else:
934                 raise e
935         self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
936         gpgme.delete_gpgme_data_t_p(tmp)
937
938     def new_from_cbs(self, read_cb, write_cb, seek_cb, release_cb, hook=None):
939         tmp = gpgme.new_gpgme_data_t_p()
940         if hook != None:
941             hookdata = (weakref.ref(self),
942                         read_cb, write_cb, seek_cb, release_cb, hook)
943         else:
944             hookdata = (weakref.ref(self),
945                         read_cb, write_cb, seek_cb, release_cb)
946         gpgme.pyme_data_new_from_cbs(self, hookdata, tmp)
947         self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
948         gpgme.delete_gpgme_data_t_p(tmp)
949
950     def new_from_filepart(self, file, offset, length):
951         """This wraps the GPGME gpgme_data_new_from_filepart() function.
952         The argument "file" may be:
953
954         * a string specifying a file name, or
955         * a file-like object supporting the fileno() and the mode attribute.
956
957         """
958
959         tmp = gpgme.new_gpgme_data_t_p()
960         filename = None
961         fp = None
962
963         if type(file) == type("x"):
964             filename = file
965         else:
966             fp = gpgme.fdopen(file.fileno(), file.mode)
967             if fp == None:
968                 raise ValueError("Failed to open file from %s arg %s" % \
969                       (str(type(file)), str(file)))
970
971         errorcheck(gpgme.gpgme_data_new_from_filepart(tmp, filename, fp,
972                                                       offset, length))
973         self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
974         gpgme.delete_gpgme_data_t_p(tmp)
975
976     def new_from_fd(self, file):
977         """This wraps the GPGME gpgme_data_new_from_fd() function.  The
978         argument "file" must be a file-like object, supporting the
979         fileno() method.
980
981         """
982         tmp = gpgme.new_gpgme_data_t_p()
983         errorcheck(gpgme.gpgme_data_new_from_fd(tmp, file.fileno()))
984         self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
985         gpgme.delete_gpgme_data_t_p(tmp)
986
987     def new_from_stream(self, file):
988         """This wrap around gpgme_data_new_from_stream is an alias for
989         new_from_fd() method since in python there's not difference
990         between file stream and file descriptor"""
991         self.new_from_fd(file)
992
993     def write(self, buffer):
994         """Write buffer given as string or bytes.
995
996         If a string is given, it is implicitly encoded using UTF-8."""
997         written = gpgme.gpgme_data_write(self.wrapped, buffer)
998         if written < 0:
999             if self._callback_excinfo:
1000                 gpgme.pyme_raise_callback_exception(self)
1001             else:
1002                 raise GPGMEError.fromSyserror()
1003         return written
1004
1005     def read(self, size = -1):
1006         """Read at most size bytes, returned as bytes.
1007
1008         If the size argument is negative or omitted, read until EOF is reached.
1009
1010         Returns the data read, or the empty string if there was no data
1011         to read before EOF was reached."""
1012
1013         if size == 0:
1014             return ''
1015
1016         if size > 0:
1017             try:
1018                 result = gpgme.gpgme_data_read(self.wrapped, size)
1019             except:
1020                 if self._callback_excinfo:
1021                     gpgme.pyme_raise_callback_exception(self)
1022                 else:
1023                     raise
1024             return result
1025         else:
1026             chunks = []
1027             while True:
1028                 try:
1029                     result = gpgme.gpgme_data_read(self.wrapped, 4096)
1030                 except:
1031                     if self._callback_excinfo:
1032                         gpgme.pyme_raise_callback_exception(self)
1033                     else:
1034                         raise
1035                 if len(result) == 0:
1036                     break
1037                 chunks.append(result)
1038             return b''.join(chunks)
1039
1040 def pubkey_algo_name(algo):
1041     return gpgme.gpgme_pubkey_algo_name(algo)
1042
1043 def hash_algo_name(algo):
1044     return gpgme.gpgme_hash_algo_name(algo)
1045
1046 def get_protocol_name(proto):
1047     return gpgme.gpgme_get_protocol_name(proto)
1048
1049 def check_version(version=None):
1050     return gpgme.gpgme_check_version(version)
1051
1052 # check_version also makes sure that several subsystems are properly
1053 # initialized, and it must be run at least once before invoking any
1054 # other function.  We do it here so that the user does not have to do
1055 # it unless she really wants to check for a certain version.
1056 check_version()
1057
1058 def engine_check_version (proto):
1059     try:
1060         errorcheck(gpgme.gpgme_engine_check_version(proto))
1061         return True
1062     except errors.GPGMEError:
1063         return False
1064
1065 def get_engine_info():
1066     ptr = gpgme.new_gpgme_engine_info_t_p()
1067     try:
1068         errorcheck(gpgme.gpgme_get_engine_info(ptr))
1069         info = gpgme.gpgme_engine_info_t_p_value(ptr)
1070     except errors.GPGMEError:
1071         info = None
1072     gpgme.delete_gpgme_engine_info_t_p(ptr)
1073     return info
1074
1075 def set_engine_info(proto, file_name, home_dir=None):
1076     """Changes the default configuration of the crypto engine implementing
1077     the protocol 'proto'. 'file_name' is the file name of
1078     the executable program implementing this protocol. 'home_dir' is the
1079     directory name of the configuration directory (engine's default is
1080     used if omitted)."""
1081     errorcheck(gpgme.gpgme_set_engine_info(proto, file_name, home_dir))
1082
1083 def set_locale(category, value):
1084     """Sets the default locale used by contexts"""
1085     errorcheck(gpgme.gpgme_set_locale(None, category, value))
1086
1087 def wait(hang):
1088     """Wait for asynchronous call on any Context  to finish.
1089     Wait forever if hang is True.
1090
1091     For finished anynch calls it returns a tuple (status, context):
1092         status  - status return by asnynchronous call.
1093         context - context which caused this call to return.
1094
1095     Please read the GPGME manual of more information."""
1096     ptr = gpgme.new_gpgme_error_t_p()
1097     context = gpgme.gpgme_wait(None, ptr, hang)
1098     status = gpgme.gpgme_error_t_p_value(ptr)
1099     gpgme.delete_gpgme_error_t_p(ptr)
1100     if context == None:
1101         errorcheck(status)
1102     else:
1103         context = Context(context)
1104     return (status, context)