diff --git a/lang/python/gpgme.i b/lang/python/gpgme.i index 9cc2022c..c6ddbb40 100644 --- a/lang/python/gpgme.i +++ b/lang/python/gpgme.i @@ -283,10 +283,11 @@ // Make types containing 'next' field to be lists %ignore next; -%typemap(out) gpgme_sig_notation_t, gpgme_engine_info_t, gpgme_subkey_t, gpgme_key_sig_t, - gpgme_user_id_t, gpgme_invalid_key_t, gpgme_recipient_t, gpgme_new_signature_t, - gpgme_signature_t, gpgme_import_status_t, gpgme_conf_arg_t, gpgme_conf_opt_t, - gpgme_conf_comp_t { +%typemap(out) gpgme_sig_notation_t, gpgme_engine_info_t, gpgme_subkey_t, + gpgme_key_sig_t, gpgme_user_id_t, gpgme_invalid_key_t, + gpgme_recipient_t, gpgme_new_signature_t, gpgme_signature_t, + gpgme_import_status_t, gpgme_conf_arg_t, gpgme_conf_opt_t, + gpgme_conf_comp_t, gpgme_tofu_info_t { int i; int size = 0; $1_ltype curr; @@ -300,6 +301,75 @@ } } + + +/* Wrap the fragile result objects into robust Python ones. */ +%typemap(out) gpgme_encrypt_result_t { + PyObject *fragile; + fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor, + %newpointer_flags); + $result = pygpgme_wrap_fragile_result(fragile, "EncryptResult"); + Py_DECREF(fragile); +} + +%typemap(out) gpgme_decrypt_result_t { + PyObject *fragile; + fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor, + %newpointer_flags); + $result = pygpgme_wrap_fragile_result(fragile, "DecryptResult"); + Py_DECREF(fragile); +} + +%typemap(out) gpgme_sign_result_t { + PyObject *fragile; + fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor, + %newpointer_flags); + $result = pygpgme_wrap_fragile_result(fragile, "SignResult"); + Py_DECREF(fragile); +} + +%typemap(out) gpgme_verify_result_t { + PyObject *fragile; + fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor, + %newpointer_flags); + $result = pygpgme_wrap_fragile_result(fragile, "VerifyResult"); + Py_DECREF(fragile); +} + +%typemap(out) gpgme_import_result_t { + PyObject *fragile; + fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor, + %newpointer_flags); + $result = pygpgme_wrap_fragile_result(fragile, "ImportResult"); + Py_DECREF(fragile); +} + +%typemap(out) gpgme_genkey_result_t { + PyObject *fragile; + fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor, + %newpointer_flags); + $result = pygpgme_wrap_fragile_result(fragile, "GenkeyResult"); + Py_DECREF(fragile); +} + +%typemap(out) gpgme_keylist_result_t { + PyObject *fragile; + fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor, + %newpointer_flags); + $result = pygpgme_wrap_fragile_result(fragile, "KeylistResult"); + Py_DECREF(fragile); +} + +%typemap(out) gpgme_vfs_mount_result_t { + PyObject *fragile; + fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor, + %newpointer_flags); + $result = pygpgme_wrap_fragile_result(fragile, "VFSMountResult"); + Py_DECREF(fragile); +} + + + // Include mapper for edit callbacks %typemap(in) (gpgme_edit_cb_t fnc, void *fnc_value) { if (! PyTuple_Check($input)) diff --git a/lang/python/helpers.c b/lang/python/helpers.c index 6de2b8de..1b661466 100644 --- a/lang/python/helpers.c +++ b/lang/python/helpers.c @@ -272,6 +272,38 @@ object_to_gpgme_data_t(PyObject *input, int argnum, gpgme_data_t *wrapper, +PyObject * +pygpgme_wrap_fragile_result(PyObject *fragile, const char *classname) +{ + static PyObject *results; + PyObject *class; + PyObject *replacement; + + if (results == NULL) + { + PyObject *from_list = PyList_New(0); + if (from_list == NULL) + return NULL; + + results = PyImport_ImportModuleLevel("results", PyEval_GetGlobals(), + PyEval_GetLocals(), from_list, 1); + Py_DECREF(from_list); + + if (results == NULL) + return NULL; + } + + class = PyMapping_GetItemString(PyModule_GetDict(results), classname); + if (class == NULL) + return NULL; + + replacement = PyObject_CallFunctionObjArgs(class, fragile, NULL); + Py_DECREF(class); + return replacement; +} + + + /* Callback support. */ static gpgme_error_t pyPassphraseCb(void *hook, const char *uid_hint, diff --git a/lang/python/helpers.h b/lang/python/helpers.h index 15642903..beb2682f 100644 --- a/lang/python/helpers.h +++ b/lang/python/helpers.h @@ -34,6 +34,8 @@ PyObject *object_to_gpgme_data_t(PyObject *input, int argnum, gpgme_data_t *wrapper, PyObject **bytesio, Py_buffer *view); +PyObject *pygpgme_wrap_fragile_result(PyObject *fragile, const char *classname); + PyObject *pygpgme_raise_callback_exception(PyObject *self); PyObject *pygpgme_set_passphrase_cb(PyObject *self, PyObject *cb); diff --git a/lang/python/pyme/results.py b/lang/python/pyme/results.py new file mode 100644 index 00000000..e6e89689 --- /dev/null +++ b/lang/python/pyme/results.py @@ -0,0 +1,116 @@ +# Robust result objects +# +# Copyright (C) 2016 g10 Code GmbH +# +# This file is part of GPGME. +# +# GPGME 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. +# +# GPGME 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 program; if not, see . + +"""Robust result objects + +Results returned by the underlying library are fragile, i.e. they are +only valid until the next operation is performed in the context. + +We cannot arbitrarily constrain the lifetime of Python objects, we +therefore create deep copies of the results. + +""" + +class Result(object): + """Result object + + Describes the result of an operation. + + """ + + """Convert to types""" + _type = {} + + """Map functions over list attributes""" + _map = {} + + """Automatically copy unless blacklisted""" + _blacklist = { + 'acquire', 'append', 'disown', 'next', 'own', 'this', 'thisown', + } + def __init__(self, fragile): + for key, func in self._type.items(): + setattr(self, key, func(getattr(fragile, key))) + + for key, func in self._map.items(): + setattr(self, key, list(map(func, getattr(fragile, key)))) + + for key, func in self._map.items(): + setattr(self, key, list(map(func, getattr(fragile, key)))) + + for key in dir(fragile): + if key.startswith('_') or key in self._blacklist: + continue + if hasattr(self, key): + continue + + setattr(self, key, getattr(fragile, key)) + + def __str__(self): + return '<{} {}>'.format( + self.__class__.__name__, + ', '.join('{}: {}'.format(k, getattr(self, k)) + for k in dir(self) if not k.startswith('_'))) + +class InvalidKey(Result): + pass + +class EncryptResult(Result): + _map = dict(invalid_recipients=InvalidKey) + +class Recipient(Result): + pass + +class DecryptResult(Result): + _type = dict(wrong_key_usage=bool) + _map = dict(recipients=Recipient) + +class NewSignature(Result): + pass + +class SignResult(Result): + _map = dict(invalid_signers=InvalidKey, signatures=NewSignature) + +class Notation(Result): + pass + +class TofuInfo(Result): + pass + +class Signature(Result): + _type = dict(wrong_key_usage=bool, chain_model=bool) + _map = dict(notations=Notation, tofu=TofuInfo) + +class VerifyResult(Result): + _map = dict(signatures=Signature) + +class ImportStatus(Result): + pass + +class ImportResult(Result): + _map = dict(imports=ImportStatus) + +class GenkeyResult(Result): + _type = dict(primary=bool, sub=bool) + +class KeylistResult(Result): + _type = dict(truncated=bool) + +class VFSMountResult(Result): + pass