f5cc69c3f0d032af8e387a3f3813e2913e844945
[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 pygpgme
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(pygpgme,
97                            "{}get_{}".format(self._cprefix, key))
98         set_func = getattr(pygpgme,
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(pygpgme, name)
124
125         if self._errorcheck(name):
126             def _funcwrap(slf, *args):
127                 result = func(slf.wrapped, *args)
128                 if slf._callback_excinfo:
129                     pygpgme.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                     pygpgme.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 = pygpgme.new_gpgme_ctx_t_p()
190             errorcheck(pygpgme.gpgme_new(tmp))
191             wrapped = pygpgme.gpgme_ctx_t_p_value(tmp)
192             pygpgme.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 assuan_transact(self, command,
472                         data_cb=None, inquire_cb=None, status_cb=None):
473         """Issue a raw assuan command
474
475         This function can be used to issue a raw assuan command to the
476         engine.
477
478         If command is a string or bytes, it will be used as-is.  If it
479         is an iterable of strings, it will be properly escaped and
480         joined into an well-formed assuan command.
481
482         Keyword arguments:
483         data_cb         -- a callback receiving data lines
484         inquire_cb      -- a callback providing more information
485         status_cb       -- a callback receiving status lines
486
487         Returns:
488         result          -- the result of command as GPGMEError
489
490         Raises:
491         GPGMEError      -- as signaled by the underlying library
492
493         """
494
495         if isinstance(command, (str, bytes)):
496             cmd = command
497         else:
498             cmd = " ".join(util.percent_escape(f) for f in command)
499
500         errptr = pygpgme.new_gpgme_error_t_p()
501
502         err = pygpgme.gpgme_op_assuan_transact_ext(
503             self.wrapped,
504             cmd,
505             (weakref.ref(self), data_cb) if data_cb else None,
506             (weakref.ref(self), inquire_cb) if inquire_cb else None,
507             (weakref.ref(self), status_cb) if status_cb else None,
508             errptr)
509
510         if self._callback_excinfo:
511             pygpgme.pyme_raise_callback_exception(self)
512
513         errorcheck(err)
514
515         status = pygpgme.gpgme_error_t_p_value(errptr)
516         pygpgme.delete_gpgme_error_t_p(errptr)
517
518         return GPGMEError(status) if status != 0 else None
519
520     @property
521     def signers(self):
522         """Keys used for signing"""
523         return [self.signers_enum(i) for i in range(self.signers_count())]
524     @signers.setter
525     def signers(self, signers):
526         old = self.signers
527         self.signers_clear()
528         try:
529             for key in signers:
530                 self.signers_add(key)
531         except:
532             self.signers = old
533             raise
534
535     @property
536     def pinentry_mode(self):
537         """Pinentry mode"""
538         return self.get_pinentry_mode()
539     @pinentry_mode.setter
540     def pinentry_mode(self, value):
541         self.set_pinentry_mode(value)
542
543     @property
544     def protocol(self):
545         """Protocol to use"""
546         return self.get_protocol()
547     @protocol.setter
548     def protocol(self, value):
549         self.set_protocol(value)
550
551     _ctype = 'gpgme_ctx_t'
552     _cprefix = 'gpgme_'
553
554     def _errorcheck(self, name):
555         """This function should list all functions returning gpgme_error_t"""
556         if (name.startswith('gpgme_op_') and \
557             not name.endswith('_result')) or \
558             name == 'gpgme_signers_add' or \
559             name == 'gpgme_set_locale' or \
560             name == 'gpgme_set_keylist_mode' or \
561             name == 'gpgme_set_protocol':
562             return 1
563         return 0
564
565     _boolean_properties = {'armor', 'textmode', 'offline'}
566
567     def __del__(self):
568         if not pygpgme:
569             # At interpreter shutdown, pygpgme is set to NONE.
570             return
571
572         self._free_passcb()
573         self._free_progresscb()
574         self._free_statuscb()
575         if self.own and self.wrapped and pygpgme.gpgme_release:
576             pygpgme.gpgme_release(self.wrapped)
577             self.wrapped = None
578
579     # Implement the context manager protocol.
580     def __enter__(self):
581         return self
582     def __exit__(self, type, value, tb):
583         self.__del__()
584
585     def op_keylist_all(self, *args, **kwargs):
586         self.op_keylist_start(*args, **kwargs)
587         key = self.op_keylist_next()
588         while key:
589             yield key
590             key = self.op_keylist_next()
591         self.op_keylist_end()
592
593     def op_keylist_next(self):
594         """Returns the next key in the list created
595         by a call to op_keylist_start().  The object returned
596         is of type Key."""
597         ptr = pygpgme.new_gpgme_key_t_p()
598         try:
599             errorcheck(pygpgme.gpgme_op_keylist_next(self.wrapped, ptr))
600             key = pygpgme.gpgme_key_t_p_value(ptr)
601         except errors.GPGMEError as excp:
602             key = None
603             if excp.getcode() != errors.EOF:
604                 raise excp
605         pygpgme.delete_gpgme_key_t_p(ptr)
606         if key:
607             key.__del__ = lambda self: pygpgme.gpgme_key_unref(self)
608             return key
609
610     def get_key(self, fpr, secret):
611         """Return the key corresponding to the fingerprint 'fpr'"""
612         ptr = pygpgme.new_gpgme_key_t_p()
613         errorcheck(pygpgme.gpgme_get_key(self.wrapped, fpr, ptr, secret))
614         key = pygpgme.gpgme_key_t_p_value(ptr)
615         pygpgme.delete_gpgme_key_t_p(ptr)
616         if key:
617             key.__del__ = lambda self: pygpgme.gpgme_key_unref(self)
618             return key
619
620     def op_trustlist_all(self, *args, **kwargs):
621         self.op_trustlist_start(*args, **kwargs)
622         trust = self.op_trustlist_next()
623         while trust:
624             yield trust
625             trust = self.op_trustlist_next()
626         self.op_trustlist_end()
627
628     def op_trustlist_next(self):
629         """Returns the next trust item in the list created
630         by a call to op_trustlist_start().  The object returned
631         is of type TrustItem."""
632         ptr = pygpgme.new_gpgme_trust_item_t_p()
633         try:
634             errorcheck(pygpgme.gpgme_op_trustlist_next(self.wrapped, ptr))
635             trust = pygpgme.gpgme_trust_item_t_p_value(ptr)
636         except errors.GPGMEError as excp:
637             trust = None
638             if excp.getcode() != errors.EOF:
639                 raise
640         pygpgme.delete_gpgme_trust_item_t_p(ptr)
641         return trust
642
643     def set_passphrase_cb(self, func, hook=None):
644         """Sets the passphrase callback to the function specified by func.
645
646         When the system needs a passphrase, it will call func with three args:
647         hint, a string describing the key it needs the passphrase for;
648         desc, a string describing the passphrase it needs;
649         prev_bad, a boolean equal True if this is a call made after
650         unsuccessful previous attempt.
651
652         If hook has a value other than None it will be passed into the func
653         as a forth argument.
654
655         Please see the GPGME manual for more information.
656         """
657         if func == None:
658             hookdata = None
659         else:
660             if hook == None:
661                 hookdata = (weakref.ref(self), func)
662             else:
663                 hookdata = (weakref.ref(self), func, hook)
664         pygpgme.pyme_set_passphrase_cb(self, hookdata)
665
666     def _free_passcb(self):
667         if pygpgme.pyme_set_passphrase_cb:
668             self.set_passphrase_cb(None)
669
670     def set_progress_cb(self, func, hook=None):
671         """Sets the progress meter callback to the function specified by FUNC.
672         If FUNC is None, the callback will be cleared.
673
674         This function will be called to provide an interactive update
675         of the system's progress.  The function will be called with
676         three arguments, type, total, and current.  If HOOK is not
677         None, it will be supplied as fourth argument.
678
679         Please see the GPGME manual for more information.
680
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         pygpgme.pyme_set_progress_cb(self, hookdata)
690
691     def _free_progresscb(self):
692         if pygpgme.pyme_set_progress_cb:
693             self.set_progress_cb(None)
694
695     def set_status_cb(self, func, hook=None):
696         """Sets the status callback to the function specified by FUNC.  If
697         FUNC is None, the callback will be cleared.
698
699         The function will be called with two arguments, keyword and
700         args.  If HOOK is not None, it will be supplied as third
701         argument.
702
703         Please see the GPGME manual for more information.
704
705         """
706         if func == None:
707             hookdata = None
708         else:
709             if hook == None:
710                 hookdata = (weakref.ref(self), func)
711             else:
712                 hookdata = (weakref.ref(self), func, hook)
713         pygpgme.pyme_set_status_cb(self, hookdata)
714
715     def _free_statuscb(self):
716         if pygpgme.pyme_set_status_cb:
717             self.set_status_cb(None)
718
719     @property
720     def engine_info(self):
721         """Configuration of the engine currently in use"""
722         p = self.protocol
723         infos = [i for i in self.get_engine_info() if i.protocol == p]
724         assert len(infos) == 1
725         return infos[0]
726
727     def get_engine_info(self):
728         """Get engine configuration
729
730         Returns information about all configured and installed
731         engines.
732
733         Returns:
734         infos           -- a list of engine infos
735
736         """
737         return pygpgme.gpgme_ctx_get_engine_info(self.wrapped)
738
739     def set_engine_info(self, proto, file_name=None, home_dir=None):
740         """Change engine configuration
741
742         Changes the configuration of the crypto engine implementing
743         the protocol 'proto' for the context.
744
745         Keyword arguments:
746         file_name       -- engine program file name (unchanged if None)
747         home_dir        -- configuration directory (unchanged if None)
748
749         """
750         errorcheck(pygpgme.gpgme_ctx_set_engine_info(
751             self.wrapped, proto, file_name, home_dir))
752
753     def wait(self, hang):
754         """Wait for asynchronous call to finish. Wait forever if hang is True.
755         Raises an exception on errors.
756
757         Please read the GPGME manual for more information.
758
759         """
760         ptr = pygpgme.new_gpgme_error_t_p()
761         pygpgme.gpgme_wait(self.wrapped, ptr, hang)
762         status = pygpgme.gpgme_error_t_p_value(ptr)
763         pygpgme.delete_gpgme_error_t_p(ptr)
764         errorcheck(status)
765
766     def op_edit(self, key, func, fnc_value, out):
767         """Start key editing using supplied callback function"""
768         if key == None:
769             raise ValueError("op_edit: First argument cannot be None")
770         if fnc_value:
771             opaquedata = (weakref.ref(self), func, fnc_value)
772         else:
773             opaquedata = (weakref.ref(self), func)
774
775         result = pygpgme.gpgme_op_edit(self.wrapped, key, opaquedata, out)
776         if self._callback_excinfo:
777             pygpgme.pyme_raise_callback_exception(self)
778         errorcheck(result)
779
780 class Data(GpgmeWrapper):
781     """Data buffer
782
783     A lot of data has to be exchanged between the user and the crypto
784     engine, like plaintext messages, ciphertext, signatures and
785     information about the keys.  The technical details about
786     exchanging the data information are completely abstracted by
787     GPGME.  The user provides and receives the data via `gpgme_data_t'
788     objects, regardless of the communication protocol between GPGME
789     and the crypto engine in use.
790
791     This Data class is the implementation of the GpgmeData objects.
792
793     Please see the information about __init__ for instantiation.
794
795     """
796
797     _ctype = 'gpgme_data_t'
798     _cprefix = 'gpgme_data_'
799
800     def _errorcheck(self, name):
801         """This function should list all functions returning gpgme_error_t"""
802         return name not in {
803             'gpgme_data_release_and_get_mem',
804             'gpgme_data_get_encoding',
805             'gpgme_data_seek',
806             'gpgme_data_get_file_name',
807         }
808
809     def __init__(self, string=None, file=None, offset=None,
810                  length=None, cbs=None, copy=True):
811         """Initialize a new gpgme_data_t object.
812
813         If no args are specified, make it an empty object.
814
815         If string alone is specified, initialize it with the data
816         contained there.
817
818         If file, offset, and length are all specified, file must
819         be either a filename or a file-like object, and the object
820         will be initialized by reading the specified chunk from the file.
821
822         If cbs is specified, it MUST be a tuple of the form:
823
824         (read_cb, write_cb, seek_cb, release_cb[, hook])
825
826         where the first four items are functions implementing reading,
827         writing, seeking the data, and releasing any resources once
828         the data object is deallocated.  The functions must match the
829         following prototypes:
830
831             def read(amount, hook=None):
832                 return <a b"bytes" object>
833
834             def write(data, hook=None):
835                 return <the number of bytes written>
836
837             def seek(offset, whence, hook=None):
838                 return <the new file position>
839
840             def release(hook=None):
841                 <return value and exceptions are ignored>
842
843         The functions may be bound methods.  In that case, you can
844         simply use the 'self' reference instead of using a hook.
845
846         If file is specified without any other arguments, then
847         it must be a filename, and the object will be initialized from
848         that file.
849
850         """
851         super().__init__(None)
852         self.data_cbs = None
853
854         if cbs != None:
855             self.new_from_cbs(*cbs)
856         elif string != None:
857             self.new_from_mem(string, copy)
858         elif file != None and offset != None and length != None:
859             self.new_from_filepart(file, offset, length)
860         elif file != None:
861             if type(file) == type("x"):
862                 self.new_from_file(file, copy)
863             else:
864                 self.new_from_fd(file)
865         else:
866             self.new()
867
868     def __del__(self):
869         if not pygpgme:
870             # At interpreter shutdown, pygpgme is set to NONE.
871             return
872
873         if self.wrapped != None and pygpgme.gpgme_data_release:
874             pygpgme.gpgme_data_release(self.wrapped)
875             if self._callback_excinfo:
876                 pygpgme.pyme_raise_callback_exception(self)
877             self.wrapped = None
878         self._free_datacbs()
879
880     # Implement the context manager protocol.
881     def __enter__(self):
882         return self
883     def __exit__(self, type, value, tb):
884         self.__del__()
885
886     def _free_datacbs(self):
887         self._data_cbs = None
888
889     def new(self):
890         tmp = pygpgme.new_gpgme_data_t_p()
891         errorcheck(pygpgme.gpgme_data_new(tmp))
892         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
893         pygpgme.delete_gpgme_data_t_p(tmp)
894
895     def new_from_mem(self, string, copy=True):
896         tmp = pygpgme.new_gpgme_data_t_p()
897         errorcheck(pygpgme.gpgme_data_new_from_mem(tmp,string,len(string),copy))
898         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
899         pygpgme.delete_gpgme_data_t_p(tmp)
900
901     def new_from_file(self, filename, copy=True):
902         tmp = pygpgme.new_gpgme_data_t_p()
903         try:
904             errorcheck(pygpgme.gpgme_data_new_from_file(tmp, filename, copy))
905         except errors.GPGMEError as e:
906             if e.getcode() == errors.INV_VALUE and not copy:
907                 raise ValueError("delayed reads are not yet supported")
908             else:
909                 raise e
910         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
911         pygpgme.delete_gpgme_data_t_p(tmp)
912
913     def new_from_cbs(self, read_cb, write_cb, seek_cb, release_cb, hook=None):
914         tmp = pygpgme.new_gpgme_data_t_p()
915         if hook != None:
916             hookdata = (weakref.ref(self),
917                         read_cb, write_cb, seek_cb, release_cb, hook)
918         else:
919             hookdata = (weakref.ref(self),
920                         read_cb, write_cb, seek_cb, release_cb)
921         pygpgme.pyme_data_new_from_cbs(self, hookdata, tmp)
922         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
923         pygpgme.delete_gpgme_data_t_p(tmp)
924
925     def new_from_filepart(self, file, offset, length):
926         """This wraps the GPGME gpgme_data_new_from_filepart() function.
927         The argument "file" may be:
928
929         * a string specifying a file name, or
930         * a file-like object supporting the fileno() and the mode attribute.
931
932         """
933
934         tmp = pygpgme.new_gpgme_data_t_p()
935         filename = None
936         fp = None
937
938         if type(file) == type("x"):
939             filename = file
940         else:
941             fp = pygpgme.fdopen(file.fileno(), file.mode)
942             if fp == None:
943                 raise ValueError("Failed to open file from %s arg %s" % \
944                       (str(type(file)), str(file)))
945
946         errorcheck(pygpgme.gpgme_data_new_from_filepart(tmp, filename, fp,
947                                                       offset, length))
948         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
949         pygpgme.delete_gpgme_data_t_p(tmp)
950
951     def new_from_fd(self, file):
952         """This wraps the GPGME gpgme_data_new_from_fd() function.  The
953         argument "file" must be a file-like object, supporting the
954         fileno() method.
955
956         """
957         tmp = pygpgme.new_gpgme_data_t_p()
958         errorcheck(pygpgme.gpgme_data_new_from_fd(tmp, file.fileno()))
959         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
960         pygpgme.delete_gpgme_data_t_p(tmp)
961
962     def new_from_stream(self, file):
963         """This wrap around gpgme_data_new_from_stream is an alias for
964         new_from_fd() method since in python there's not difference
965         between file stream and file descriptor"""
966         self.new_from_fd(file)
967
968     def write(self, buffer):
969         """Write buffer given as string or bytes.
970
971         If a string is given, it is implicitly encoded using UTF-8."""
972         written = pygpgme.gpgme_data_write(self.wrapped, buffer)
973         if written < 0:
974             if self._callback_excinfo:
975                 pygpgme.pyme_raise_callback_exception(self)
976             else:
977                 raise GPGMEError.fromSyserror()
978         return written
979
980     def read(self, size = -1):
981         """Read at most size bytes, returned as bytes.
982
983         If the size argument is negative or omitted, read until EOF is reached.
984
985         Returns the data read, or the empty string if there was no data
986         to read before EOF was reached."""
987
988         if size == 0:
989             return ''
990
991         if size > 0:
992             try:
993                 result = pygpgme.gpgme_data_read(self.wrapped, size)
994             except:
995                 if self._callback_excinfo:
996                     pygpgme.pyme_raise_callback_exception(self)
997                 else:
998                     raise
999             return result
1000         else:
1001             chunks = []
1002             while True:
1003                 try:
1004                     result = pygpgme.gpgme_data_read(self.wrapped, 4096)
1005                 except:
1006                     if self._callback_excinfo:
1007                         pygpgme.pyme_raise_callback_exception(self)
1008                     else:
1009                         raise
1010                 if len(result) == 0:
1011                     break
1012                 chunks.append(result)
1013             return b''.join(chunks)
1014
1015 def pubkey_algo_name(algo):
1016     return pygpgme.gpgme_pubkey_algo_name(algo)
1017
1018 def hash_algo_name(algo):
1019     return pygpgme.gpgme_hash_algo_name(algo)
1020
1021 def get_protocol_name(proto):
1022     return pygpgme.gpgme_get_protocol_name(proto)
1023
1024 def check_version(version=None):
1025     return pygpgme.gpgme_check_version(version)
1026
1027 # check_version also makes sure that several subsystems are properly
1028 # initialized, and it must be run at least once before invoking any
1029 # other function.  We do it here so that the user does not have to do
1030 # it unless she really wants to check for a certain version.
1031 check_version()
1032
1033 def engine_check_version (proto):
1034     try:
1035         errorcheck(pygpgme.gpgme_engine_check_version(proto))
1036         return True
1037     except errors.GPGMEError:
1038         return False
1039
1040 def get_engine_info():
1041     ptr = pygpgme.new_gpgme_engine_info_t_p()
1042     try:
1043         errorcheck(pygpgme.gpgme_get_engine_info(ptr))
1044         info = pygpgme.gpgme_engine_info_t_p_value(ptr)
1045     except errors.GPGMEError:
1046         info = None
1047     pygpgme.delete_gpgme_engine_info_t_p(ptr)
1048     return info
1049
1050 def set_engine_info(proto, file_name, home_dir=None):
1051     """Changes the default configuration of the crypto engine implementing
1052     the protocol 'proto'. 'file_name' is the file name of
1053     the executable program implementing this protocol. 'home_dir' is the
1054     directory name of the configuration directory (engine's default is
1055     used if omitted)."""
1056     errorcheck(pygpgme.gpgme_set_engine_info(proto, file_name, home_dir))
1057
1058 def set_locale(category, value):
1059     """Sets the default locale used by contexts"""
1060     errorcheck(pygpgme.gpgme_set_locale(None, category, value))
1061
1062 def wait(hang):
1063     """Wait for asynchronous call on any Context  to finish.
1064     Wait forever if hang is True.
1065
1066     For finished anynch calls it returns a tuple (status, context):
1067         status  - status return by asnynchronous call.
1068         context - context which caused this call to return.
1069
1070     Please read the GPGME manual of more information."""
1071     ptr = pygpgme.new_gpgme_error_t_p()
1072     context = pygpgme.gpgme_wait(None, ptr, hang)
1073     status = pygpgme.gpgme_error_t_p_value(ptr)
1074     pygpgme.delete_gpgme_error_t_p(ptr)
1075     if context == None:
1076         errorcheck(status)
1077     else:
1078         context = Context(context)
1079     return (status, context)