e5ccf7cd15290039fe23529958e7b477a61da975
[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 weakref
29 from . import pygpgme
30 from .errors import errorcheck, GPGMEError
31 from . import constants
32 from . import errors
33
34 class GpgmeWrapper(object):
35     """Base wrapper class
36
37     Not to be instantiated directly.
38
39     """
40
41     def __init__(self, wrapped):
42         self._callback_excinfo = None
43         self.wrapped = wrapped
44
45     def __repr__(self):
46         return '<{}/{!r}>'.format(super().__repr__(), self.wrapped)
47
48     def __str__(self):
49         acc = ['{}.{}'.format(__name__, self.__class__.__name__)]
50         flags = [f for f in self._boolean_properties if getattr(self, f)]
51         if flags:
52             acc.append('({})'.format(' '.join(flags)))
53
54         return '<{}>'.format(' '.join(acc))
55
56     def __hash__(self):
57         return hash(repr(self.wrapped))
58
59     def __eq__(self, other):
60         if other == None:
61             return False
62         else:
63             return repr(self.wrapped) == repr(other.wrapped)
64
65     @property
66     def _ctype(self):
67         """The name of the c type wrapped by this class
68
69         Must be set by child classes.
70
71         """
72         raise NotImplementedError()
73
74     @property
75     def _cprefix(self):
76         """The common prefix of c functions wrapped by this class
77
78         Must be set by child classes.
79
80         """
81         raise NotImplementedError()
82
83     def _errorcheck(self, name):
84         """Must be implemented by child classes.
85
86         This function must return a trueish value for all c functions
87         returning gpgme_error_t."""
88         raise NotImplementedError()
89
90     """The set of all boolean properties"""
91     _boolean_properties = set()
92
93     def __wrap_boolean_property(self, key, do_set=False, value=None):
94         get_func = getattr(pygpgme,
95                            "{}get_{}".format(self._cprefix, key))
96         set_func = getattr(pygpgme,
97                            "{}set_{}".format(self._cprefix, key))
98         def get(slf):
99             return bool(get_func(slf.wrapped))
100         def set_(slf, value):
101             set_func(slf.wrapped, bool(value))
102
103         p = property(get, set_, doc="{} flag".format(key))
104         setattr(self.__class__, key, p)
105
106         if do_set:
107             set_(self, bool(value))
108         else:
109             return get(self)
110
111     _munge_docstring = re.compile(r'gpgme_([^(]*)\(([^,]*), (.*\) -> .*)')
112     def __getattr__(self, key):
113         """On-the-fly generation of wrapper methods and properties"""
114         if key[0] == '_' or self._cprefix == None:
115             return None
116
117         if key in self._boolean_properties:
118             return self.__wrap_boolean_property(key)
119
120         name = self._cprefix + key
121         func = getattr(pygpgme, name)
122
123         if self._errorcheck(name):
124             def _funcwrap(slf, *args):
125                 result = func(slf.wrapped, *args)
126                 if slf._callback_excinfo:
127                     pygpgme.pygpgme_raise_callback_exception(slf)
128                 return errorcheck(result, "Invocation of " + name)
129         else:
130             def _funcwrap(slf, *args):
131                 result = func(slf.wrapped, *args)
132                 if slf._callback_excinfo:
133                     pygpgme.pygpgme_raise_callback_exception(slf)
134                 return result
135
136         doc = self._munge_docstring.sub(r'\2.\1(\3', getattr(func, "__doc__"))
137         _funcwrap.__doc__ = doc
138
139         # Monkey-patch the class.
140         setattr(self.__class__, key, _funcwrap)
141
142         # Bind the method to 'self'.
143         def wrapper(*args):
144             return _funcwrap(self, *args)
145         wrapper.__doc__ = doc
146
147         return wrapper
148
149     def __setattr__(self, key, value):
150         """On-the-fly generation of properties"""
151         if key in self._boolean_properties:
152             self.__wrap_boolean_property(key, True, value)
153         else:
154             super().__setattr__(key, value)
155
156 class Context(GpgmeWrapper):
157     """Context for cryptographic operations
158
159     All cryptographic operations in GPGME are performed within a
160     context, which contains the internal state of the operation as
161     well as configuration parameters.  By using several contexts you
162     can run several cryptographic operations in parallel, with
163     different configuration.
164
165     Access to a context must be synchronized.
166
167     """
168
169     @property
170     def signers(self):
171         """Keys used for signing"""
172         return [self.signers_enum(i) for i in range(self.signers_count())]
173     @signers.setter
174     def signers(self, signers):
175         old = self.signers
176         self.signers_clear()
177         try:
178             for key in signers:
179                 self.signers_add(key)
180         except:
181             self.signers = old
182             raise
183
184     @property
185     def pinentry_mode(self):
186         """Pinentry mode"""
187         return self.get_pinentry_mode()
188     @pinentry_mode.setter
189     def pinentry_mode(self, value):
190         self.set_pinentry_mode(value)
191
192     _ctype = 'gpgme_ctx_t'
193     _cprefix = 'gpgme_'
194
195     def _errorcheck(self, name):
196         """This function should list all functions returning gpgme_error_t"""
197         if (name.startswith('gpgme_op_') and \
198             not name.endswith('_result')) or \
199             name == 'gpgme_signers_add' or \
200             name == 'gpgme_set_locale' or \
201             name == 'gpgme_set_keylist_mode' or \
202             name == 'gpgme_set_protocol':
203             return 1
204         return 0
205
206     _boolean_properties = {'armor', 'textmode', 'offline'}
207     def __init__(self, armor=False, textmode=False, offline=False,
208                  signers=[], pinentry_mode=constants.PINENTRY_MODE_DEFAULT,
209                  wrapped=None):
210         """Construct a context object
211
212         Keyword arguments:
213         armor           -- enable ASCII armoring (default False)
214         textmode        -- enable canonical text mode (default False)
215         offline         -- do not contact external key sources (default False)
216         signers         -- list of keys used for signing (default [])
217         pinentry_mode   -- pinentry mode (default PINENTRY_MODE_DEFAULT)
218         """
219         if wrapped:
220             self.own = False
221         else:
222             tmp = pygpgme.new_gpgme_ctx_t_p()
223             errorcheck(pygpgme.gpgme_new(tmp))
224             wrapped = pygpgme.gpgme_ctx_t_p_value(tmp)
225             pygpgme.delete_gpgme_ctx_t_p(tmp)
226             self.own = True
227         super().__init__(wrapped)
228         self.armor = armor
229         self.textmode = textmode
230         self.offline = offline
231         self.signers = signers
232         self.pinentry_mode = pinentry_mode
233
234     def __del__(self):
235         if not pygpgme:
236             # At interpreter shutdown, pygpgme is set to NONE.
237             return
238
239         self._free_passcb()
240         self._free_progresscb()
241         self._free_statuscb()
242         if self.own and self.wrapped and pygpgme.gpgme_release:
243             pygpgme.gpgme_release(self.wrapped)
244             self.wrapped = None
245
246     # Implement the context manager protocol.
247     def __enter__(self):
248         return self
249     def __exit__(self, type, value, tb):
250         self.__del__()
251
252     def op_keylist_all(self, *args, **kwargs):
253         self.op_keylist_start(*args, **kwargs)
254         key = self.op_keylist_next()
255         while key:
256             yield key
257             key = self.op_keylist_next()
258         self.op_keylist_end()
259
260     def op_keylist_next(self):
261         """Returns the next key in the list created
262         by a call to op_keylist_start().  The object returned
263         is of type Key."""
264         ptr = pygpgme.new_gpgme_key_t_p()
265         try:
266             errorcheck(pygpgme.gpgme_op_keylist_next(self.wrapped, ptr))
267             key = pygpgme.gpgme_key_t_p_value(ptr)
268         except errors.GPGMEError as excp:
269             key = None
270             if excp.getcode() != errors.EOF:
271                 raise excp
272         pygpgme.delete_gpgme_key_t_p(ptr)
273         if key:
274             key.__del__ = lambda self: pygpgme.gpgme_key_unref(self)
275             return key
276
277     def get_key(self, fpr, secret):
278         """Return the key corresponding to the fingerprint 'fpr'"""
279         ptr = pygpgme.new_gpgme_key_t_p()
280         errorcheck(pygpgme.gpgme_get_key(self.wrapped, fpr, ptr, secret))
281         key = pygpgme.gpgme_key_t_p_value(ptr)
282         pygpgme.delete_gpgme_key_t_p(ptr)
283         if key:
284             key.__del__ = lambda self: pygpgme.gpgme_key_unref(self)
285             return key
286
287     def op_trustlist_all(self, *args, **kwargs):
288         self.op_trustlist_start(*args, **kwargs)
289         trust = self.op_trustlist_next()
290         while trust:
291             yield trust
292             trust = self.op_trustlist_next()
293         self.op_trustlist_end()
294
295     def op_trustlist_next(self):
296         """Returns the next trust item in the list created
297         by a call to op_trustlist_start().  The object returned
298         is of type TrustItem."""
299         ptr = pygpgme.new_gpgme_trust_item_t_p()
300         try:
301             errorcheck(pygpgme.gpgme_op_trustlist_next(self.wrapped, ptr))
302             trust = pygpgme.gpgme_trust_item_t_p_value(ptr)
303         except errors.GPGMEError as excp:
304             trust = None
305             if excp.getcode() != errors.EOF:
306                 raise
307         pygpgme.delete_gpgme_trust_item_t_p(ptr)
308         return trust
309
310     def set_passphrase_cb(self, func, hook=None):
311         """Sets the passphrase callback to the function specified by func.
312
313         When the system needs a passphrase, it will call func with three args:
314         hint, a string describing the key it needs the passphrase for;
315         desc, a string describing the passphrase it needs;
316         prev_bad, a boolean equal True if this is a call made after
317         unsuccessful previous attempt.
318
319         If hook has a value other than None it will be passed into the func
320         as a forth argument.
321
322         Please see the GPGME manual for more information.
323         """
324         if func == None:
325             hookdata = None
326         else:
327             if hook == None:
328                 hookdata = (weakref.ref(self), func)
329             else:
330                 hookdata = (weakref.ref(self), func, hook)
331         pygpgme.pygpgme_set_passphrase_cb(self, hookdata)
332
333     def _free_passcb(self):
334         if pygpgme.pygpgme_set_passphrase_cb:
335             self.set_passphrase_cb(None)
336
337     def set_progress_cb(self, func, hook=None):
338         """Sets the progress meter callback to the function specified by FUNC.
339         If FUNC is None, the callback will be cleared.
340
341         This function will be called to provide an interactive update
342         of the system's progress.  The function will be called with
343         three arguments, type, total, and current.  If HOOK is not
344         None, it will be supplied as fourth argument.
345
346         Please see the GPGME manual for more information.
347
348         """
349         if func == None:
350             hookdata = None
351         else:
352             if hook == None:
353                 hookdata = (weakref.ref(self), func)
354             else:
355                 hookdata = (weakref.ref(self), func, hook)
356         pygpgme.pygpgme_set_progress_cb(self, hookdata)
357
358     def _free_progresscb(self):
359         if pygpgme.pygpgme_set_progress_cb:
360             self.set_progress_cb(None)
361
362     def set_status_cb(self, func, hook=None):
363         """Sets the status callback to the function specified by FUNC.  If
364         FUNC is None, the callback will be cleared.
365
366         The function will be called with two arguments, keyword and
367         args.  If HOOK is not None, it will be supplied as third
368         argument.
369
370         Please see the GPGME manual for more information.
371
372         """
373         if func == None:
374             hookdata = None
375         else:
376             if hook == None:
377                 hookdata = (weakref.ref(self), func)
378             else:
379                 hookdata = (weakref.ref(self), func, hook)
380         pygpgme.pygpgme_set_status_cb(self, hookdata)
381
382     def _free_statuscb(self):
383         if pygpgme.pygpgme_set_status_cb:
384             self.set_status_cb(None)
385
386     def get_engine_info(self):
387         """Returns this context specific engine info"""
388         return pygpgme.gpgme_ctx_get_engine_info(self.wrapped)
389
390     def set_engine_info(self, proto, file_name, home_dir=None):
391         """Changes the configuration of the crypto engine implementing the
392     protocol 'proto' for the context. 'file_name' is the file name of
393     the executable program implementing this protocol. 'home_dir' is the
394     directory name of the configuration directory (engine's default is
395     used if omitted)."""
396         errorcheck(pygpgme.gpgme_ctx_set_engine_info(self.wrapped, proto, file_name, home_dir))
397
398     def wait(self, hang):
399         """Wait for asynchronous call to finish. Wait forever if hang is True.
400         Raises an exception on errors.
401
402         Please read the GPGME manual for more information.
403
404         """
405         ptr = pygpgme.new_gpgme_error_t_p()
406         pygpgme.gpgme_wait(self.wrapped, ptr, hang)
407         status = pygpgme.gpgme_error_t_p_value(ptr)
408         pygpgme.delete_gpgme_error_t_p(ptr)
409         errorcheck(status)
410
411     def op_edit(self, key, func, fnc_value, out):
412         """Start key editing using supplied callback function"""
413         if key == None:
414             raise ValueError("op_edit: First argument cannot be None")
415         if fnc_value:
416             opaquedata = (weakref.ref(self), func, fnc_value)
417         else:
418             opaquedata = (weakref.ref(self), func)
419
420         result = pygpgme.gpgme_op_edit(self.wrapped, key, opaquedata, out)
421         if self._callback_excinfo:
422             pygpgme.pygpgme_raise_callback_exception(self)
423         errorcheck(result)
424
425 class Data(GpgmeWrapper):
426     """Data buffer
427
428     A lot of data has to be exchanged between the user and the crypto
429     engine, like plaintext messages, ciphertext, signatures and
430     information about the keys.  The technical details about
431     exchanging the data information are completely abstracted by
432     GPGME.  The user provides and receives the data via `gpgme_data_t'
433     objects, regardless of the communication protocol between GPGME
434     and the crypto engine in use.
435
436     This Data class is the implementation of the GpgmeData objects.
437
438     Please see the information about __init__ for instantiation.
439
440     """
441
442     _ctype = 'gpgme_data_t'
443     _cprefix = 'gpgme_data_'
444
445     def _errorcheck(self, name):
446         """This function should list all functions returning gpgme_error_t"""
447         return name not in {
448             'gpgme_data_release_and_get_mem',
449             'gpgme_data_get_encoding',
450             'gpgme_data_seek',
451             'gpgme_data_get_file_name',
452         }
453
454     def __init__(self, string=None, file=None, offset=None,
455                  length=None, cbs=None, copy=True):
456         """Initialize a new gpgme_data_t object.
457
458         If no args are specified, make it an empty object.
459
460         If string alone is specified, initialize it with the data
461         contained there.
462
463         If file, offset, and length are all specified, file must
464         be either a filename or a file-like object, and the object
465         will be initialized by reading the specified chunk from the file.
466
467         If cbs is specified, it MUST be a tuple of the form:
468
469         (read_cb, write_cb, seek_cb, release_cb[, hook])
470
471         where the first four items are functions implementing reading,
472         writing, seeking the data, and releasing any resources once
473         the data object is deallocated.  The functions must match the
474         following prototypes:
475
476             def read(amount, hook=None):
477                 return <a b"bytes" object>
478
479             def write(data, hook=None):
480                 return <the number of bytes written>
481
482             def seek(offset, whence, hook=None):
483                 return <the new file position>
484
485             def release(hook=None):
486                 <return value and exceptions are ignored>
487
488         The functions may be bound methods.  In that case, you can
489         simply use the 'self' reference instead of using a hook.
490
491         If file is specified without any other arguments, then
492         it must be a filename, and the object will be initialized from
493         that file.
494
495         """
496         super().__init__(None)
497         self.data_cbs = None
498
499         if cbs != None:
500             self.new_from_cbs(*cbs)
501         elif string != None:
502             self.new_from_mem(string, copy)
503         elif file != None and offset != None and length != None:
504             self.new_from_filepart(file, offset, length)
505         elif file != None:
506             if type(file) == type("x"):
507                 self.new_from_file(file, copy)
508             else:
509                 self.new_from_fd(file)
510         else:
511             self.new()
512
513     def __del__(self):
514         if not pygpgme:
515             # At interpreter shutdown, pygpgme is set to NONE.
516             return
517
518         if self.wrapped != None and pygpgme.gpgme_data_release:
519             pygpgme.gpgme_data_release(self.wrapped)
520             if self._callback_excinfo:
521                 pygpgme.pygpgme_raise_callback_exception(self)
522             self.wrapped = None
523         self._free_datacbs()
524
525     # Implement the context manager protocol.
526     def __enter__(self):
527         return self
528     def __exit__(self, type, value, tb):
529         self.__del__()
530
531     def _free_datacbs(self):
532         self._data_cbs = None
533
534     def new(self):
535         tmp = pygpgme.new_gpgme_data_t_p()
536         errorcheck(pygpgme.gpgme_data_new(tmp))
537         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
538         pygpgme.delete_gpgme_data_t_p(tmp)
539
540     def new_from_mem(self, string, copy=True):
541         tmp = pygpgme.new_gpgme_data_t_p()
542         errorcheck(pygpgme.gpgme_data_new_from_mem(tmp,string,len(string),copy))
543         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
544         pygpgme.delete_gpgme_data_t_p(tmp)
545
546     def new_from_file(self, filename, copy=True):
547         tmp = pygpgme.new_gpgme_data_t_p()
548         try:
549             errorcheck(pygpgme.gpgme_data_new_from_file(tmp, filename, copy))
550         except errors.GPGMEError as e:
551             if e.getcode() == errors.INV_VALUE and not copy:
552                 raise ValueError("delayed reads are not yet supported")
553             else:
554                 raise e
555         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
556         pygpgme.delete_gpgme_data_t_p(tmp)
557
558     def new_from_cbs(self, read_cb, write_cb, seek_cb, release_cb, hook=None):
559         tmp = pygpgme.new_gpgme_data_t_p()
560         if hook != None:
561             hookdata = (weakref.ref(self),
562                         read_cb, write_cb, seek_cb, release_cb, hook)
563         else:
564             hookdata = (weakref.ref(self),
565                         read_cb, write_cb, seek_cb, release_cb)
566         pygpgme.pygpgme_data_new_from_cbs(self, hookdata, tmp)
567         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
568         pygpgme.delete_gpgme_data_t_p(tmp)
569
570     def new_from_filepart(self, file, offset, length):
571         """This wraps the GPGME gpgme_data_new_from_filepart() function.
572         The argument "file" may be:
573
574         * a string specifying a file name, or
575         * a file-like object supporting the fileno() and the mode attribute.
576
577         """
578
579         tmp = pygpgme.new_gpgme_data_t_p()
580         filename = None
581         fp = None
582
583         if type(file) == type("x"):
584             filename = file
585         else:
586             fp = pygpgme.fdopen(file.fileno(), file.mode)
587             if fp == None:
588                 raise ValueError("Failed to open file from %s arg %s" % \
589                       (str(type(file)), str(file)))
590
591         errorcheck(pygpgme.gpgme_data_new_from_filepart(tmp, filename, fp,
592                                                       offset, length))
593         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
594         pygpgme.delete_gpgme_data_t_p(tmp)
595
596     def new_from_fd(self, file):
597         """This wraps the GPGME gpgme_data_new_from_fd() function.  The
598         argument "file" must be a file-like object, supporting the
599         fileno() method.
600
601         """
602         tmp = pygpgme.new_gpgme_data_t_p()
603         errorcheck(pygpgme.gpgme_data_new_from_fd(tmp, file.fileno()))
604         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
605         pygpgme.delete_gpgme_data_t_p(tmp)
606
607     def new_from_stream(self, file):
608         """This wrap around gpgme_data_new_from_stream is an alias for
609         new_from_fd() method since in python there's not difference
610         between file stream and file descriptor"""
611         self.new_from_fd(file)
612
613     def write(self, buffer):
614         """Write buffer given as string or bytes.
615
616         If a string is given, it is implicitly encoded using UTF-8."""
617         written = pygpgme.gpgme_data_write(self.wrapped, buffer)
618         if written < 0:
619             if self._callback_excinfo:
620                 pygpgme.pygpgme_raise_callback_exception(self)
621             else:
622                 raise GPGMEError.fromSyserror()
623         return written
624
625     def read(self, size = -1):
626         """Read at most size bytes, returned as bytes.
627
628         If the size argument is negative or omitted, read until EOF is reached.
629
630         Returns the data read, or the empty string if there was no data
631         to read before EOF was reached."""
632
633         if size == 0:
634             return ''
635
636         if size > 0:
637             try:
638                 result = pygpgme.gpgme_data_read(self.wrapped, size)
639             except:
640                 if self._callback_excinfo:
641                     pygpgme.pygpgme_raise_callback_exception(self)
642                 else:
643                     raise
644             return result
645         else:
646             chunks = []
647             while True:
648                 try:
649                     result = pygpgme.gpgme_data_read(self.wrapped, 4096)
650                 except:
651                     if self._callback_excinfo:
652                         pygpgme.pygpgme_raise_callback_exception(self)
653                     else:
654                         raise
655                 if len(result) == 0:
656                     break
657                 chunks.append(result)
658             return b''.join(chunks)
659
660 def pubkey_algo_name(algo):
661     return pygpgme.gpgme_pubkey_algo_name(algo)
662
663 def hash_algo_name(algo):
664     return pygpgme.gpgme_hash_algo_name(algo)
665
666 def get_protocol_name(proto):
667     return pygpgme.gpgme_get_protocol_name(proto)
668
669 def check_version(version=None):
670     return pygpgme.gpgme_check_version(version)
671
672 # check_version also makes sure that several subsystems are properly
673 # initialized, and it must be run at least once before invoking any
674 # other function.  We do it here so that the user does not have to do
675 # it unless she really wants to check for a certain version.
676 check_version()
677
678 def engine_check_version (proto):
679     try:
680         errorcheck(pygpgme.gpgme_engine_check_version(proto))
681         return True
682     except errors.GPGMEError:
683         return False
684
685 def get_engine_info():
686     ptr = pygpgme.new_gpgme_engine_info_t_p()
687     try:
688         errorcheck(pygpgme.gpgme_get_engine_info(ptr))
689         info = pygpgme.gpgme_engine_info_t_p_value(ptr)
690     except errors.GPGMEError:
691         info = None
692     pygpgme.delete_gpgme_engine_info_t_p(ptr)
693     return info
694
695 def set_engine_info(proto, file_name, home_dir=None):
696     """Changes the default configuration of the crypto engine implementing
697     the protocol 'proto'. 'file_name' is the file name of
698     the executable program implementing this protocol. 'home_dir' is the
699     directory name of the configuration directory (engine's default is
700     used if omitted)."""
701     errorcheck(pygpgme.gpgme_set_engine_info(proto, file_name, home_dir))
702
703 def set_locale(category, value):
704     """Sets the default locale used by contexts"""
705     errorcheck(pygpgme.gpgme_set_locale(None, category, value))
706
707 def wait(hang):
708     """Wait for asynchronous call on any Context  to finish.
709     Wait forever if hang is True.
710
711     For finished anynch calls it returns a tuple (status, context):
712         status  - status return by asnynchronous call.
713         context - context which caused this call to return.
714
715     Please read the GPGME manual of more information."""
716     ptr = pygpgme.new_gpgme_error_t_p()
717     context = pygpgme.gpgme_wait(None, ptr, hang)
718     status = pygpgme.gpgme_error_t_p_value(ptr)
719     pygpgme.delete_gpgme_error_t_p(ptr)
720     if context == None:
721         errorcheck(status)
722     else:
723         context = Context(context)
724     return (status, context)