2016-06-06 12:08:59 +00:00
|
|
|
# Copyright (C) 2016 g10 Code GmbH
|
2015-05-05 17:09:44 +00:00
|
|
|
# Copyright (C) 2004,2008 Igor Belyi <belyi@users.sourceforge.net>
|
|
|
|
# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
|
|
|
|
#
|
|
|
|
# This library is free software; you can redistribute it and/or
|
|
|
|
# modify it under the terms of the GNU Lesser General Public
|
|
|
|
# License as published by the Free Software Foundation; either
|
|
|
|
# version 2.1 of the License, or (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This library is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
# Lesser General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU Lesser General Public
|
|
|
|
# License along with this library; if not, write to the Free Software
|
|
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
|
2016-06-06 12:08:59 +00:00
|
|
|
"""Core functionality
|
|
|
|
|
|
|
|
Core functionality of GPGME wrapped in a object-oriented fashion.
|
|
|
|
Provides the 'Context' class for performing cryptographic operations,
|
|
|
|
and the 'Data' class describing buffers of data.
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2016-05-27 10:25:59 +00:00
|
|
|
import weakref
|
2015-05-05 17:09:44 +00:00
|
|
|
from . import pygpgme
|
2016-05-17 12:14:25 +00:00
|
|
|
from .errors import errorcheck, GPGMEError
|
2015-05-05 17:09:44 +00:00
|
|
|
from . import errors
|
2016-05-24 14:45:39 +00:00
|
|
|
|
|
|
|
class GpgmeWrapper(object):
|
2016-06-06 12:08:59 +00:00
|
|
|
"""Base wrapper class
|
|
|
|
|
|
|
|
Not to be instantiated directly.
|
|
|
|
|
|
|
|
"""
|
2016-05-24 14:45:39 +00:00
|
|
|
|
|
|
|
def __init__(self, wrapped):
|
|
|
|
self._callback_excinfo = None
|
|
|
|
self.wrapped = wrapped
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return '<instance of %s.%s with GPG object at %s>' % \
|
|
|
|
(__name__, self.__class__.__name__,
|
|
|
|
self.wrapped)
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return repr(self)
|
|
|
|
|
|
|
|
def __hash__(self):
|
|
|
|
return hash(repr(self.wrapped))
|
|
|
|
|
|
|
|
def __eq__(self, other):
|
|
|
|
if other == None:
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
return repr(self.wrapped) == repr(other.wrapped)
|
|
|
|
|
|
|
|
def _getctype(self):
|
|
|
|
"""Must be implemented by child classes.
|
|
|
|
|
|
|
|
Must return the name of the c type."""
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
def _getnameprepend(self):
|
|
|
|
"""Must be implemented by child classes.
|
|
|
|
|
|
|
|
Must return the prefix of all c functions mapped to methods of
|
|
|
|
this class."""
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
def _errorcheck(self, name):
|
|
|
|
"""Must be implemented by child classes.
|
|
|
|
|
|
|
|
This function must return a trueish value for all c functions
|
|
|
|
returning gpgme_error_t."""
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
def __getattr__(self, key):
|
2016-06-06 12:08:59 +00:00
|
|
|
"""On-the-fly generation of wrapper methods."""
|
2016-05-24 14:45:39 +00:00
|
|
|
if key[0] == '_' or self._getnameprepend() == None:
|
|
|
|
return None
|
|
|
|
name = self._getnameprepend() + key
|
|
|
|
func = getattr(pygpgme, name)
|
|
|
|
|
|
|
|
if self._errorcheck(name):
|
|
|
|
def _funcwrap(slf, *args, **kwargs):
|
|
|
|
result = func(slf.wrapped, *args, **kwargs)
|
|
|
|
if slf._callback_excinfo:
|
|
|
|
pygpgme.pygpgme_raise_callback_exception(slf)
|
|
|
|
return errorcheck(result, "Invocation of " + name)
|
|
|
|
else:
|
|
|
|
def _funcwrap(slf, *args, **kwargs):
|
|
|
|
result = func(slf.wrapped, *args, **kwargs)
|
|
|
|
if slf._callback_excinfo:
|
|
|
|
pygpgme.pygpgme_raise_callback_exception(slf)
|
|
|
|
return result
|
|
|
|
|
|
|
|
_funcwrap.__doc__ = getattr(func, "__doc__")
|
|
|
|
|
|
|
|
# Monkey-patch the class.
|
|
|
|
setattr(self.__class__, key, _funcwrap)
|
|
|
|
|
|
|
|
# Bind the method to 'self'.
|
|
|
|
def wrapper(*args, **kwargs):
|
|
|
|
return _funcwrap(self, *args, **kwargs)
|
|
|
|
_funcwrap.__doc__ = getattr(func, "__doc__")
|
|
|
|
|
|
|
|
return wrapper
|
2015-05-05 17:09:44 +00:00
|
|
|
|
|
|
|
class Context(GpgmeWrapper):
|
2016-06-06 12:08:59 +00:00
|
|
|
"""Context for cryptographic operations
|
|
|
|
|
|
|
|
All cryptographic operations in GPGME are performed within a
|
|
|
|
context, which contains the internal state of the operation as
|
|
|
|
well as configuration parameters. By using several contexts you
|
|
|
|
can run several cryptographic operations in parallel, with
|
|
|
|
different configuration.
|
2015-05-05 17:09:44 +00:00
|
|
|
|
2016-06-06 12:08:59 +00:00
|
|
|
Access to a context must be synchronized.
|
2015-05-05 17:09:44 +00:00
|
|
|
|
2016-06-06 12:08:59 +00:00
|
|
|
"""
|
2015-05-05 17:09:44 +00:00
|
|
|
|
|
|
|
def _getctype(self):
|
|
|
|
return 'gpgme_ctx_t'
|
2016-05-10 11:30:30 +00:00
|
|
|
|
2015-05-05 17:09:44 +00:00
|
|
|
def _getnameprepend(self):
|
|
|
|
return 'gpgme_'
|
|
|
|
|
|
|
|
def _errorcheck(self, name):
|
|
|
|
"""This function should list all functions returning gpgme_error_t"""
|
|
|
|
if (name.startswith('gpgme_op_') and \
|
|
|
|
not name.endswith('_result')) or \
|
|
|
|
name == 'gpgme_signers_add' or \
|
|
|
|
name == 'gpgme_set_locale' or \
|
|
|
|
name == 'gpgme_set_keylist_mode' or \
|
|
|
|
name == 'gpgme_set_protocol':
|
|
|
|
return 1
|
|
|
|
return 0
|
|
|
|
|
|
|
|
def __init__(self, wrapped=None):
|
|
|
|
if wrapped:
|
|
|
|
self.own = False
|
|
|
|
else:
|
|
|
|
tmp = pygpgme.new_gpgme_ctx_t_p()
|
|
|
|
errorcheck(pygpgme.gpgme_new(tmp))
|
2016-05-19 09:03:27 +00:00
|
|
|
wrapped = pygpgme.gpgme_ctx_t_p_value(tmp)
|
2015-05-05 17:09:44 +00:00
|
|
|
pygpgme.delete_gpgme_ctx_t_p(tmp)
|
|
|
|
self.own = True
|
2016-05-19 09:03:27 +00:00
|
|
|
super().__init__(wrapped)
|
2015-05-05 17:09:44 +00:00
|
|
|
self.last_passcb = None
|
|
|
|
self.last_progresscb = None
|
2016-05-24 13:14:53 +00:00
|
|
|
self.last_statuscb = None
|
2015-05-05 17:09:44 +00:00
|
|
|
|
|
|
|
def __del__(self):
|
2016-05-12 09:53:43 +00:00
|
|
|
if not pygpgme:
|
|
|
|
# At interpreter shutdown, pygpgme is set to NONE.
|
|
|
|
return
|
|
|
|
|
2015-05-05 17:09:44 +00:00
|
|
|
self._free_passcb()
|
|
|
|
self._free_progresscb()
|
2016-05-27 10:25:59 +00:00
|
|
|
self._free_statuscb()
|
2016-06-07 17:31:10 +00:00
|
|
|
if self.own and self.wrapped and pygpgme.gpgme_release:
|
2015-05-05 17:09:44 +00:00
|
|
|
pygpgme.gpgme_release(self.wrapped)
|
2016-06-07 17:31:10 +00:00
|
|
|
self.wrapped = None
|
|
|
|
|
|
|
|
# Implement the context manager protocol.
|
|
|
|
def __enter__(self):
|
|
|
|
return self
|
|
|
|
def __exit__(self, type, value, tb):
|
|
|
|
self.__del__()
|
2015-05-05 17:09:44 +00:00
|
|
|
|
|
|
|
def _free_passcb(self):
|
|
|
|
if self.last_passcb != None:
|
2016-05-12 09:53:43 +00:00
|
|
|
if pygpgme.pygpgme_clear_generic_cb:
|
|
|
|
pygpgme.pygpgme_clear_generic_cb(self.last_passcb)
|
|
|
|
if pygpgme.delete_PyObject_p_p:
|
|
|
|
pygpgme.delete_PyObject_p_p(self.last_passcb)
|
2015-05-05 17:09:44 +00:00
|
|
|
self.last_passcb = None
|
|
|
|
|
|
|
|
def _free_progresscb(self):
|
|
|
|
if self.last_progresscb != None:
|
2016-05-12 09:53:43 +00:00
|
|
|
if pygpgme.pygpgme_clear_generic_cb:
|
|
|
|
pygpgme.pygpgme_clear_generic_cb(self.last_progresscb)
|
|
|
|
if pygpgme.delete_PyObject_p_p:
|
|
|
|
pygpgme.delete_PyObject_p_p(self.last_progresscb)
|
2015-05-05 17:09:44 +00:00
|
|
|
self.last_progresscb = None
|
|
|
|
|
2016-05-24 13:14:53 +00:00
|
|
|
def _free_statuscb(self):
|
|
|
|
if self.last_statuscb != None:
|
|
|
|
if pygpgme.pygpgme_clear_generic_cb:
|
|
|
|
pygpgme.pygpgme_clear_generic_cb(self.last_statuscb)
|
|
|
|
if pygpgme.delete_PyObject_p_p:
|
|
|
|
pygpgme.delete_PyObject_p_p(self.last_statuscb)
|
|
|
|
self.last_statuscb = None
|
|
|
|
|
2015-05-05 17:09:44 +00:00
|
|
|
def op_keylist_all(self, *args, **kwargs):
|
|
|
|
self.op_keylist_start(*args, **kwargs)
|
|
|
|
key = self.op_keylist_next()
|
|
|
|
while key:
|
|
|
|
yield key
|
|
|
|
key = self.op_keylist_next()
|
|
|
|
|
|
|
|
def op_keylist_next(self):
|
|
|
|
"""Returns the next key in the list created
|
|
|
|
by a call to op_keylist_start(). The object returned
|
|
|
|
is of type Key."""
|
|
|
|
ptr = pygpgme.new_gpgme_key_t_p()
|
|
|
|
try:
|
|
|
|
errorcheck(pygpgme.gpgme_op_keylist_next(self.wrapped, ptr))
|
|
|
|
key = pygpgme.gpgme_key_t_p_value(ptr)
|
|
|
|
except errors.GPGMEError as excp:
|
|
|
|
key = None
|
|
|
|
if excp.getcode() != errors.EOF:
|
|
|
|
raise excp
|
|
|
|
pygpgme.delete_gpgme_key_t_p(ptr)
|
|
|
|
if key:
|
|
|
|
key.__del__ = lambda self: pygpgme.gpgme_key_unref(self)
|
|
|
|
return key
|
2016-05-10 11:30:30 +00:00
|
|
|
|
2015-05-05 17:09:44 +00:00
|
|
|
def get_key(self, fpr, secret):
|
|
|
|
"""Return the key corresponding to the fingerprint 'fpr'"""
|
|
|
|
ptr = pygpgme.new_gpgme_key_t_p()
|
|
|
|
errorcheck(pygpgme.gpgme_get_key(self.wrapped, fpr, ptr, secret))
|
|
|
|
key = pygpgme.gpgme_key_t_p_value(ptr)
|
|
|
|
pygpgme.delete_gpgme_key_t_p(ptr)
|
|
|
|
if key:
|
|
|
|
key.__del__ = lambda self: pygpgme.gpgme_key_unref(self)
|
|
|
|
return key
|
|
|
|
|
|
|
|
def op_trustlist_all(self, *args, **kwargs):
|
|
|
|
self.op_trustlist_start(*args, **kwargs)
|
|
|
|
trust = self.ctx.op_trustlist_next()
|
|
|
|
while trust:
|
|
|
|
yield trust
|
|
|
|
trust = self.ctx.op_trustlist_next()
|
|
|
|
|
|
|
|
def op_trustlist_next(self):
|
|
|
|
"""Returns the next trust item in the list created
|
|
|
|
by a call to op_trustlist_start(). The object returned
|
|
|
|
is of type TrustItem."""
|
|
|
|
ptr = pygpgme.new_gpgme_trust_item_t_p()
|
|
|
|
try:
|
|
|
|
errorcheck(pygpgme.gpgme_op_trustlist_next(self.wrapped, ptr))
|
|
|
|
trust = pygpgme.gpgme_trust_item_t_p_value(ptr)
|
|
|
|
except errors.GPGMEError as excp:
|
|
|
|
trust = None
|
|
|
|
if excp.getcode() != errors.EOF:
|
|
|
|
raise
|
|
|
|
pygpgme.delete_gpgme_trust_item_t_p(ptr)
|
|
|
|
return trust
|
|
|
|
|
|
|
|
def set_passphrase_cb(self, func, hook=None):
|
|
|
|
"""Sets the passphrase callback to the function specified by func.
|
|
|
|
|
|
|
|
When the system needs a passphrase, it will call func with three args:
|
|
|
|
hint, a string describing the key it needs the passphrase for;
|
|
|
|
desc, a string describing the passphrase it needs;
|
|
|
|
prev_bad, a boolean equal True if this is a call made after
|
|
|
|
unsuccessful previous attempt.
|
2016-05-10 11:30:30 +00:00
|
|
|
|
2015-05-05 17:09:44 +00:00
|
|
|
If hook has a value other than None it will be passed into the func
|
|
|
|
as a forth argument.
|
|
|
|
|
|
|
|
Please see the GPGME manual for more information.
|
|
|
|
"""
|
|
|
|
self._free_passcb()
|
|
|
|
if func == None:
|
|
|
|
hookdata = None
|
|
|
|
else:
|
|
|
|
self.last_passcb = pygpgme.new_PyObject_p_p()
|
|
|
|
if hook == None:
|
2016-05-27 10:25:59 +00:00
|
|
|
hookdata = (weakref.ref(self), func)
|
2015-05-05 17:09:44 +00:00
|
|
|
else:
|
2016-05-27 10:25:59 +00:00
|
|
|
hookdata = (weakref.ref(self), func, hook)
|
2015-05-05 17:09:44 +00:00
|
|
|
pygpgme.pygpgme_set_passphrase_cb(self.wrapped, hookdata, self.last_passcb)
|
|
|
|
|
|
|
|
def set_progress_cb(self, func, hook=None):
|
2016-05-24 13:12:40 +00:00
|
|
|
"""Sets the progress meter callback to the function specified by FUNC.
|
|
|
|
If FUNC is None, the callback will be cleared.
|
2015-05-05 17:09:44 +00:00
|
|
|
|
2016-05-24 13:12:40 +00:00
|
|
|
This function will be called to provide an interactive update
|
|
|
|
of the system's progress. The function will be called with
|
|
|
|
three arguments, type, total, and current. If HOOK is not
|
|
|
|
None, it will be supplied as fourth argument.
|
2015-05-05 17:09:44 +00:00
|
|
|
|
2016-05-24 13:12:40 +00:00
|
|
|
Please see the GPGME manual for more information.
|
|
|
|
|
|
|
|
"""
|
2015-05-05 17:09:44 +00:00
|
|
|
self._free_progresscb()
|
|
|
|
if func == None:
|
|
|
|
hookdata = None
|
|
|
|
else:
|
|
|
|
self.last_progresscb = pygpgme.new_PyObject_p_p()
|
|
|
|
if hook == None:
|
2016-05-27 10:25:59 +00:00
|
|
|
hookdata = (weakref.ref(self), func)
|
2015-05-05 17:09:44 +00:00
|
|
|
else:
|
2016-05-27 10:25:59 +00:00
|
|
|
hookdata = (weakref.ref(self), func, hook)
|
2015-05-05 17:09:44 +00:00
|
|
|
pygpgme.pygpgme_set_progress_cb(self.wrapped, hookdata, self.last_progresscb)
|
|
|
|
|
2016-05-24 13:14:53 +00:00
|
|
|
def set_status_cb(self, func, hook=None):
|
|
|
|
"""Sets the status callback to the function specified by FUNC. If
|
|
|
|
FUNC is None, the callback will be cleared.
|
|
|
|
|
|
|
|
The function will be called with two arguments, keyword and
|
|
|
|
args. If HOOK is not None, it will be supplied as third
|
|
|
|
argument.
|
|
|
|
|
|
|
|
Please see the GPGME manual for more information.
|
|
|
|
|
|
|
|
"""
|
|
|
|
self._free_statuscb()
|
|
|
|
if func == None:
|
|
|
|
hookdata = None
|
|
|
|
else:
|
|
|
|
self.last_statuscb = pygpgme.new_PyObject_p_p()
|
|
|
|
if hook == None:
|
2016-05-27 10:25:59 +00:00
|
|
|
hookdata = (weakref.ref(self), func)
|
2016-05-24 13:14:53 +00:00
|
|
|
else:
|
2016-05-27 10:25:59 +00:00
|
|
|
hookdata = (weakref.ref(self), func, hook)
|
2016-05-24 13:14:53 +00:00
|
|
|
pygpgme.pygpgme_set_status_cb(self.wrapped, hookdata,
|
|
|
|
self.last_statuscb)
|
|
|
|
|
2015-05-05 17:09:44 +00:00
|
|
|
def get_engine_info(self):
|
|
|
|
"""Returns this context specific engine info"""
|
|
|
|
return pygpgme.gpgme_ctx_get_engine_info(self.wrapped)
|
|
|
|
|
|
|
|
def set_engine_info(self, proto, file_name, home_dir=None):
|
|
|
|
"""Changes the configuration of the crypto engine implementing the
|
|
|
|
protocol 'proto' for the context. 'file_name' is the file name of
|
|
|
|
the executable program implementing this protocol. 'home_dir' is the
|
|
|
|
directory name of the configuration directory (engine's default is
|
|
|
|
used if omitted)."""
|
|
|
|
errorcheck(pygpgme.gpgme_ctx_set_engine_info(self.wrapped, proto, file_name, home_dir))
|
|
|
|
|
|
|
|
def wait(self, hang):
|
2016-05-24 15:57:10 +00:00
|
|
|
"""Wait for asynchronous call to finish. Wait forever if hang is True.
|
|
|
|
Raises an exception on errors.
|
2015-05-05 17:09:44 +00:00
|
|
|
|
2016-05-24 15:57:10 +00:00
|
|
|
Please read the GPGME manual for more information.
|
2015-05-05 17:09:44 +00:00
|
|
|
|
2016-05-24 15:57:10 +00:00
|
|
|
"""
|
2015-05-05 17:09:44 +00:00
|
|
|
ptr = pygpgme.new_gpgme_error_t_p()
|
2016-05-24 15:57:10 +00:00
|
|
|
pygpgme.gpgme_wait(self.wrapped, ptr, hang)
|
2015-05-05 17:09:44 +00:00
|
|
|
status = pygpgme.gpgme_error_t_p_value(ptr)
|
|
|
|
pygpgme.delete_gpgme_error_t_p(ptr)
|
2016-05-24 15:57:10 +00:00
|
|
|
errorcheck(status)
|
2015-05-05 17:09:44 +00:00
|
|
|
|
|
|
|
def op_edit(self, key, func, fnc_value, out):
|
|
|
|
"""Start key editing using supplied callback function"""
|
|
|
|
if key == None:
|
|
|
|
raise ValueError("op_edit: First argument cannot be None")
|
2016-05-24 10:29:32 +00:00
|
|
|
if fnc_value:
|
2016-05-27 10:25:59 +00:00
|
|
|
opaquedata = (weakref.ref(self), func, fnc_value)
|
2016-05-24 10:29:32 +00:00
|
|
|
else:
|
2016-05-27 10:25:59 +00:00
|
|
|
opaquedata = (weakref.ref(self), func)
|
2016-05-24 10:29:32 +00:00
|
|
|
|
|
|
|
result = pygpgme.gpgme_op_edit(self.wrapped, key, opaquedata, out)
|
|
|
|
if self._callback_excinfo:
|
|
|
|
pygpgme.pygpgme_raise_callback_exception(self)
|
|
|
|
errorcheck(result)
|
2016-05-10 11:30:30 +00:00
|
|
|
|
2015-05-05 17:09:44 +00:00
|
|
|
class Data(GpgmeWrapper):
|
2016-06-06 12:08:59 +00:00
|
|
|
"""Data buffer
|
2015-05-05 17:09:44 +00:00
|
|
|
|
2016-06-06 12:08:59 +00:00
|
|
|
A lot of data has to be exchanged between the user and the crypto
|
|
|
|
engine, like plaintext messages, ciphertext, signatures and
|
|
|
|
information about the keys. The technical details about
|
|
|
|
exchanging the data information are completely abstracted by
|
|
|
|
GPGME. The user provides and receives the data via `gpgme_data_t'
|
|
|
|
objects, regardless of the communication protocol between GPGME
|
|
|
|
and the crypto engine in use.
|
2015-05-05 17:09:44 +00:00
|
|
|
|
2016-06-06 12:08:59 +00:00
|
|
|
This Data class is the implementation of the GpgmeData objects.
|
2015-05-05 17:09:44 +00:00
|
|
|
|
2016-06-06 12:08:59 +00:00
|
|
|
Please see the information about __init__ for instantiation.
|
|
|
|
|
|
|
|
"""
|
2015-05-05 17:09:44 +00:00
|
|
|
|
|
|
|
def _getctype(self):
|
|
|
|
return 'gpgme_data_t'
|
2016-05-10 11:30:30 +00:00
|
|
|
|
2015-05-05 17:09:44 +00:00
|
|
|
def _getnameprepend(self):
|
|
|
|
return 'gpgme_data_'
|
|
|
|
|
|
|
|
def _errorcheck(self, name):
|
|
|
|
"""This function should list all functions returning gpgme_error_t"""
|
2016-05-27 13:58:23 +00:00
|
|
|
return name not in {
|
|
|
|
'gpgme_data_release_and_get_mem',
|
|
|
|
'gpgme_data_get_encoding',
|
|
|
|
'gpgme_data_seek',
|
|
|
|
'gpgme_data_get_file_name',
|
|
|
|
}
|
2016-05-10 11:30:30 +00:00
|
|
|
|
2016-05-25 10:47:28 +00:00
|
|
|
def __init__(self, string=None, file=None, offset=None,
|
|
|
|
length=None, cbs=None, copy=True):
|
2015-05-05 17:09:44 +00:00
|
|
|
"""Initialize a new gpgme_data_t object.
|
|
|
|
|
|
|
|
If no args are specified, make it an empty object.
|
|
|
|
|
|
|
|
If string alone is specified, initialize it with the data
|
|
|
|
contained there.
|
|
|
|
|
|
|
|
If file, offset, and length are all specified, file must
|
|
|
|
be either a filename or a file-like object, and the object
|
|
|
|
will be initialized by reading the specified chunk from the file.
|
|
|
|
|
|
|
|
If cbs is specified, it MUST be a tuple of the form:
|
|
|
|
|
2016-05-27 12:04:28 +00:00
|
|
|
(read_cb, write_cb, seek_cb, release_cb[, hook])
|
2015-05-05 17:09:44 +00:00
|
|
|
|
2016-06-06 12:08:59 +00:00
|
|
|
where the first four items are functions implementing reading,
|
|
|
|
writing, seeking the data, and releasing any resources once
|
|
|
|
the data object is deallocated. The functions must match the
|
|
|
|
following prototypes:
|
|
|
|
|
|
|
|
def read(amount, hook=None):
|
|
|
|
return <a b"bytes" object>
|
|
|
|
|
|
|
|
def write(data, hook=None):
|
|
|
|
return <the number of bytes written>
|
|
|
|
|
|
|
|
def seek(offset, whence, hook=None):
|
|
|
|
return <the new file position>
|
|
|
|
|
|
|
|
def release(hook=None):
|
|
|
|
<return value and exceptions are ignored>
|
|
|
|
|
|
|
|
The functions may be bound methods. In that case, you can
|
|
|
|
simply use the 'self' reference instead of using a hook.
|
2015-05-05 17:09:44 +00:00
|
|
|
|
|
|
|
If file is specified without any other arguments, then
|
|
|
|
it must be a filename, and the object will be initialized from
|
|
|
|
that file.
|
|
|
|
|
2016-06-06 12:08:59 +00:00
|
|
|
"""
|
2016-05-19 09:03:27 +00:00
|
|
|
super().__init__(None)
|
2016-05-27 12:04:28 +00:00
|
|
|
self.data_cbs = None
|
2015-05-05 17:09:44 +00:00
|
|
|
|
|
|
|
if cbs != None:
|
|
|
|
self.new_from_cbs(*cbs)
|
|
|
|
elif string != None:
|
2016-05-25 10:47:28 +00:00
|
|
|
self.new_from_mem(string, copy)
|
2015-05-05 17:09:44 +00:00
|
|
|
elif file != None and offset != None and length != None:
|
|
|
|
self.new_from_filepart(file, offset, length)
|
|
|
|
elif file != None:
|
|
|
|
if type(file) == type("x"):
|
2016-05-25 10:47:28 +00:00
|
|
|
self.new_from_file(file, copy)
|
2015-05-05 17:09:44 +00:00
|
|
|
else:
|
|
|
|
self.new_from_fd(file)
|
|
|
|
else:
|
|
|
|
self.new()
|
|
|
|
|
|
|
|
def __del__(self):
|
2016-05-12 09:53:43 +00:00
|
|
|
if not pygpgme:
|
|
|
|
# At interpreter shutdown, pygpgme is set to NONE.
|
|
|
|
return
|
|
|
|
|
|
|
|
if self.wrapped != None and pygpgme.gpgme_data_release:
|
2015-05-05 17:09:44 +00:00
|
|
|
pygpgme.gpgme_data_release(self.wrapped)
|
2016-05-27 12:04:28 +00:00
|
|
|
if self._callback_excinfo:
|
|
|
|
pygpgme.pygpgme_raise_callback_exception(self)
|
2016-06-07 17:31:10 +00:00
|
|
|
self.wrapped = None
|
2016-05-27 12:04:28 +00:00
|
|
|
self._free_datacbs()
|
2015-05-05 17:09:44 +00:00
|
|
|
|
2016-06-07 17:31:10 +00:00
|
|
|
# Implement the context manager protocol.
|
|
|
|
def __enter__(self):
|
|
|
|
return self
|
|
|
|
def __exit__(self, type, value, tb):
|
|
|
|
self.__del__()
|
|
|
|
|
2016-05-27 12:04:28 +00:00
|
|
|
def _free_datacbs(self):
|
|
|
|
if self.data_cbs != None:
|
2016-05-12 09:53:43 +00:00
|
|
|
if pygpgme.pygpgme_clear_generic_cb:
|
2016-05-27 12:04:28 +00:00
|
|
|
pygpgme.pygpgme_clear_generic_cb(self.data_cbs)
|
2016-05-12 09:53:43 +00:00
|
|
|
if pygpgme.delete_PyObject_p_p:
|
2016-05-27 12:04:28 +00:00
|
|
|
pygpgme.delete_PyObject_p_p(self.data_cbs)
|
|
|
|
self.data_cbs = None
|
2015-05-05 17:09:44 +00:00
|
|
|
|
|
|
|
def new(self):
|
|
|
|
tmp = pygpgme.new_gpgme_data_t_p()
|
|
|
|
errorcheck(pygpgme.gpgme_data_new(tmp))
|
|
|
|
self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
|
|
|
|
pygpgme.delete_gpgme_data_t_p(tmp)
|
|
|
|
|
2016-05-25 10:47:28 +00:00
|
|
|
def new_from_mem(self, string, copy=True):
|
2015-05-05 17:09:44 +00:00
|
|
|
tmp = pygpgme.new_gpgme_data_t_p()
|
|
|
|
errorcheck(pygpgme.gpgme_data_new_from_mem(tmp,string,len(string),copy))
|
|
|
|
self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
|
|
|
|
pygpgme.delete_gpgme_data_t_p(tmp)
|
|
|
|
|
2016-05-25 10:47:28 +00:00
|
|
|
def new_from_file(self, filename, copy=True):
|
2015-05-05 17:09:44 +00:00
|
|
|
tmp = pygpgme.new_gpgme_data_t_p()
|
2016-05-25 10:47:28 +00:00
|
|
|
try:
|
|
|
|
errorcheck(pygpgme.gpgme_data_new_from_file(tmp, filename, copy))
|
|
|
|
except errors.GPGMEError as e:
|
|
|
|
if e.getcode() == errors.INV_VALUE and not copy:
|
|
|
|
raise ValueError("delayed reads are not yet supported")
|
|
|
|
else:
|
|
|
|
raise e
|
2015-05-05 17:09:44 +00:00
|
|
|
self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
|
|
|
|
pygpgme.delete_gpgme_data_t_p(tmp)
|
|
|
|
|
2016-05-27 12:04:28 +00:00
|
|
|
def new_from_cbs(self, read_cb, write_cb, seek_cb, release_cb, hook=None):
|
|
|
|
assert self.data_cbs == None
|
|
|
|
self.data_cbs = pygpgme.new_PyObject_p_p()
|
2015-05-05 17:09:44 +00:00
|
|
|
tmp = pygpgme.new_gpgme_data_t_p()
|
2016-05-27 12:04:28 +00:00
|
|
|
if hook != None:
|
|
|
|
hookdata = (weakref.ref(self),
|
|
|
|
read_cb, write_cb, seek_cb, release_cb, hook)
|
|
|
|
else:
|
|
|
|
hookdata = (weakref.ref(self),
|
|
|
|
read_cb, write_cb, seek_cb, release_cb)
|
|
|
|
errorcheck(
|
|
|
|
pygpgme.pygpgme_data_new_from_cbs(tmp, hookdata, self.data_cbs))
|
2015-05-05 17:09:44 +00:00
|
|
|
self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
|
|
|
|
pygpgme.delete_gpgme_data_t_p(tmp)
|
|
|
|
|
|
|
|
def new_from_filepart(self, file, offset, length):
|
|
|
|
"""This wraps the GPGME gpgme_data_new_from_filepart() function.
|
|
|
|
The argument "file" may be:
|
|
|
|
|
2016-06-06 12:08:59 +00:00
|
|
|
* a string specifying a file name, or
|
|
|
|
* a file-like object supporting the fileno() and the mode attribute.
|
|
|
|
|
|
|
|
"""
|
2015-05-05 17:09:44 +00:00
|
|
|
|
|
|
|
tmp = pygpgme.new_gpgme_data_t_p()
|
|
|
|
filename = None
|
|
|
|
fp = None
|
|
|
|
|
|
|
|
if type(file) == type("x"):
|
|
|
|
filename = file
|
|
|
|
else:
|
|
|
|
fp = pygpgme.fdopen(file.fileno(), file.mode)
|
|
|
|
if fp == None:
|
|
|
|
raise ValueError("Failed to open file from %s arg %s" % \
|
|
|
|
(str(type(file)), str(file)))
|
|
|
|
|
|
|
|
errorcheck(pygpgme.gpgme_data_new_from_filepart(tmp, filename, fp,
|
|
|
|
offset, length))
|
|
|
|
self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
|
|
|
|
pygpgme.delete_gpgme_data_t_p(tmp)
|
|
|
|
|
|
|
|
def new_from_fd(self, file):
|
2016-05-25 10:47:28 +00:00
|
|
|
"""This wraps the GPGME gpgme_data_new_from_fd() function. The
|
|
|
|
argument "file" must be a file-like object, supporting the
|
|
|
|
fileno() method.
|
2016-05-10 11:30:30 +00:00
|
|
|
|
2016-05-25 10:47:28 +00:00
|
|
|
"""
|
2015-05-05 17:09:44 +00:00
|
|
|
tmp = pygpgme.new_gpgme_data_t_p()
|
2016-05-25 10:47:28 +00:00
|
|
|
errorcheck(pygpgme.gpgme_data_new_from_fd(tmp, file.fileno()))
|
2015-05-05 17:09:44 +00:00
|
|
|
self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
|
|
|
|
pygpgme.delete_gpgme_data_t_p(tmp)
|
|
|
|
|
|
|
|
def new_from_stream(self, file):
|
|
|
|
"""This wrap around gpgme_data_new_from_stream is an alias for
|
|
|
|
new_from_fd() method since in python there's not difference
|
|
|
|
between file stream and file descriptor"""
|
|
|
|
self.new_from_fd(file)
|
2016-05-10 11:30:30 +00:00
|
|
|
|
2015-05-05 17:09:44 +00:00
|
|
|
def write(self, buffer):
|
2016-05-12 15:44:54 +00:00
|
|
|
"""Write buffer given as string or bytes.
|
|
|
|
|
|
|
|
If a string is given, it is implicitly encoded using UTF-8."""
|
2016-05-12 16:00:16 +00:00
|
|
|
written = pygpgme.gpgme_data_write(self.wrapped, buffer)
|
|
|
|
if written < 0:
|
2016-05-27 12:04:28 +00:00
|
|
|
if self._callback_excinfo:
|
|
|
|
pygpgme.pygpgme_raise_callback_exception(self)
|
|
|
|
else:
|
|
|
|
raise GPGMEError.fromSyserror()
|
2016-05-12 16:00:16 +00:00
|
|
|
return written
|
2015-05-05 17:09:44 +00:00
|
|
|
|
|
|
|
def read(self, size = -1):
|
2016-05-12 09:21:58 +00:00
|
|
|
"""Read at most size bytes, returned as bytes.
|
2016-05-10 11:30:30 +00:00
|
|
|
|
2015-05-05 17:09:44 +00:00
|
|
|
If the size argument is negative or omitted, read until EOF is reached.
|
|
|
|
|
|
|
|
Returns the data read, or the empty string if there was no data
|
|
|
|
to read before EOF was reached."""
|
2016-05-10 11:30:30 +00:00
|
|
|
|
2015-05-05 17:09:44 +00:00
|
|
|
if size == 0:
|
|
|
|
return ''
|
|
|
|
|
|
|
|
if size > 0:
|
2016-05-27 12:04:28 +00:00
|
|
|
try:
|
|
|
|
result = pygpgme.gpgme_data_read(self.wrapped, size)
|
|
|
|
except:
|
|
|
|
if self._callback_excinfo:
|
|
|
|
pygpgme.pygpgme_raise_callback_exception(self)
|
|
|
|
else:
|
|
|
|
raise
|
|
|
|
return result
|
2015-05-05 17:09:44 +00:00
|
|
|
else:
|
2016-05-12 09:21:58 +00:00
|
|
|
chunks = []
|
2016-05-27 12:04:28 +00:00
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
result = pygpgme.gpgme_data_read(self.wrapped, 4096)
|
|
|
|
except:
|
|
|
|
if self._callback_excinfo:
|
|
|
|
pygpgme.pygpgme_raise_callback_exception(self)
|
|
|
|
else:
|
|
|
|
raise
|
2015-05-05 17:09:44 +00:00
|
|
|
if len(result) == 0:
|
|
|
|
break
|
2016-05-12 09:21:58 +00:00
|
|
|
chunks.append(result)
|
|
|
|
return b''.join(chunks)
|
2015-05-05 17:09:44 +00:00
|
|
|
|
|
|
|
def pubkey_algo_name(algo):
|
|
|
|
return pygpgme.gpgme_pubkey_algo_name(algo)
|
|
|
|
|
|
|
|
def hash_algo_name(algo):
|
|
|
|
return pygpgme.gpgme_hash_algo_name(algo)
|
|
|
|
|
|
|
|
def get_protocol_name(proto):
|
|
|
|
return pygpgme.gpgme_get_protocol_name(proto)
|
|
|
|
|
|
|
|
def check_version(version=None):
|
|
|
|
return pygpgme.gpgme_check_version(version)
|
|
|
|
|
2016-06-02 13:18:40 +00:00
|
|
|
# check_version also makes sure that several subsystems are properly
|
|
|
|
# initialized, and it must be run at least once before invoking any
|
|
|
|
# other function. We do it here so that the user does not have to do
|
|
|
|
# it unless she really wants to check for a certain version.
|
|
|
|
check_version()
|
|
|
|
|
2015-05-05 17:09:44 +00:00
|
|
|
def engine_check_version (proto):
|
|
|
|
try:
|
|
|
|
errorcheck(pygpgme.gpgme_engine_check_version(proto))
|
|
|
|
return True
|
|
|
|
except errors.GPGMEError:
|
|
|
|
return False
|
|
|
|
|
|
|
|
def get_engine_info():
|
|
|
|
ptr = pygpgme.new_gpgme_engine_info_t_p()
|
|
|
|
try:
|
|
|
|
errorcheck(pygpgme.gpgme_get_engine_info(ptr))
|
|
|
|
info = pygpgme.gpgme_engine_info_t_p_value(ptr)
|
|
|
|
except errors.GPGMEError:
|
|
|
|
info = None
|
|
|
|
pygpgme.delete_gpgme_engine_info_t_p(ptr)
|
|
|
|
return info
|
|
|
|
|
|
|
|
def set_engine_info(proto, file_name, home_dir=None):
|
|
|
|
"""Changes the default configuration of the crypto engine implementing
|
|
|
|
the protocol 'proto'. 'file_name' is the file name of
|
|
|
|
the executable program implementing this protocol. 'home_dir' is the
|
|
|
|
directory name of the configuration directory (engine's default is
|
|
|
|
used if omitted)."""
|
|
|
|
errorcheck(pygpgme.gpgme_set_engine_info(proto, file_name, home_dir))
|
|
|
|
|
|
|
|
def set_locale(category, value):
|
|
|
|
"""Sets the default locale used by contexts"""
|
|
|
|
errorcheck(pygpgme.gpgme_set_locale(None, category, value))
|
|
|
|
|
|
|
|
def wait(hang):
|
|
|
|
"""Wait for asynchronous call on any Context to finish.
|
|
|
|
Wait forever if hang is True.
|
2016-05-10 11:30:30 +00:00
|
|
|
|
2015-05-05 17:09:44 +00:00
|
|
|
For finished anynch calls it returns a tuple (status, context):
|
|
|
|
status - status return by asnynchronous call.
|
|
|
|
context - context which caused this call to return.
|
2016-05-10 11:30:30 +00:00
|
|
|
|
2015-05-05 17:09:44 +00:00
|
|
|
Please read the GPGME manual of more information."""
|
|
|
|
ptr = pygpgme.new_gpgme_error_t_p()
|
|
|
|
context = pygpgme.gpgme_wait(None, ptr, hang)
|
|
|
|
status = pygpgme.gpgme_error_t_p_value(ptr)
|
|
|
|
pygpgme.delete_gpgme_error_t_p(ptr)
|
|
|
|
if context == None:
|
|
|
|
errorcheck(status)
|
|
|
|
else:
|
|
|
|
context = Context(context)
|
|
|
|
return (status, context)
|