diff options
| author | Alon Bar-Lev <[email protected]> | 2017-04-05 16:47:08 +0000 | 
|---|---|---|
| committer | Justus Winter <[email protected]> | 2017-04-07 10:12:20 +0000 | 
| commit | 49195c487e6c923f7137f092b982e7d833d98de6 (patch) | |
| tree | 5be39ddc018fcba7771e0c77ca0ae607e7435d66 /lang/python/src | |
| parent | python: Fix vpath builds, fix distcheck. (diff) | |
| download | gpgme-49195c487e6c923f7137f092b982e7d833d98de6.tar.gz gpgme-49195c487e6c923f7137f092b982e7d833d98de6.zip | |
python: simplify build, some fixups
* lang/python/gpg/version.py.in: Rename to lang/python/version.py.in.
configure.ac: Generate version.py.in in lang/python.
* lang/python/MANIFEST.in: Include version.py explicitly.
* lang/python/gpg: Rename to 'src'.
* lang/python/Makefile.am: Do not copy source files, do not use absolute
directories, support lib64 in uninstall, clean also dist directory, use
symlink for gpg src.
* lang/python/setup.py.in: Use builddir, copy sources into builddir,
copy version.py into module.
--
Simplify build to symlink the gpg sources into builddir instead of
copying. This requires handling of version.py as generated file.
In addition apply some cleanups: Drop the absolution pathes, clean the
dist directory as well, support lib64 for sitelib at uninstall.
Signed-off-by: Alon Bar-Lev <[email protected]>
Diffstat (limited to 'lang/python/src')
26 files changed, 2597 insertions, 0 deletions
| diff --git a/lang/python/src/__init__.py b/lang/python/src/__init__.py new file mode 100644 index 00000000..385b17e3 --- /dev/null +++ b/lang/python/src/__init__.py @@ -0,0 +1,121 @@ +# Copyright (C) 2016 g10 Code GmbH +# Copyright (C) 2004 Igor Belyi <[email protected]> +# Copyright (C) 2002 John Goerzen <[email protected]> +# +# 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 + +"""gpg: GnuPG Interface for Python (GPGME bindings) + +Welcome to gpg, the GnuPG Interface for Python. + +The latest release of this package may be obtained from +https://www.gnupg.org + +FEATURES +-------- + + * Feature-rich, full implementation of the GPGME library.  Supports +   all GPGME features.  Callback functions may be written in pure +   Python.  Exceptions raised in callbacks are properly propagated. + + * Ability to sign, encrypt, decrypt, and verify data. + + * Ability to list keys, export and import keys, and manage the keyring. + + * Fully object-oriented with convenient classes and modules. + +QUICK EXAMPLE +------------- + +    >>> import gpg +    >>> with gpg.Context() as c: +    >>> with gpg.Context() as c: +    ...     cipher, _, _ = c.encrypt("Hello world :)".encode(), +    ...                              passphrase="abc") +    ...     c.decrypt(cipher, passphrase="abc") +    ... +    (b'Hello world :)', +     <gpg.results.DecryptResult object at 0x7f5ab8121080>, +     <gpg.results.VerifyResult object at 0x7f5ab81219b0>) + +GENERAL OVERVIEW +---------------- + +For those of you familiar with GPGME, you will be right at home here. + +The python gpg module is, for the most part, a direct interface to the C GPGME +library.  However, it is re-packaged in a more Pythonic way -- +object-oriented with classes and modules.  Take a look at the classes +defined here -- they correspond directly to certain object types in GPGME +for C.  For instance, the following C code: + +gpgme_ctx_t context; +gpgme_new(&context); +... +gpgme_op_encrypt(context, recp, 1, plain, cipher); + +Translates into the following Python code: + +context = core.Context() +... +context.op_encrypt(recp, 1, plain, cipher) + +The Python module automatically does error-checking and raises Python +exception gpg.errors.GPGMEError when GPGME signals an error. getcode() +and getsource() of this exception return code and source of the error. + +IMPORTANT NOTE +-------------- +This documentation only covers a small subset of available GPGME functions and +methods.  Please consult the documentation for the C library +for comprehensive coverage. + +This library uses Python's reflection to automatically detect the methods +that are available for each class, and as such, most of those methods +do not appear explicitly anywhere. You can use dir() python built-in command +on an object to see what methods and fields it has but their meaning can +be found only in GPGME documentation. + +FOR MORE INFORMATION +-------------------- +GnuPG homepage: https://www.gnupg.org/ +GPGME documentation: https://www.gnupg.org/documentation/manuals/gpgme/ + +""" + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +from . import core +from . import errors +from . import constants +from . import util +from . import callbacks +from . import version +from .core import Context +from .core import Data + +# Interface hygiene. + +# Drop the low-level gpgme that creeps in for some reason. +gpgme = None +del gpgme + +# This is a white-list of symbols.  Any other will alert pyflakes. +_ = [Context, Data, core, errors, constants, util, callbacks, version] +del _ + +__all__ = ["Context", "Data", +           "core", "errors", "constants", "util", "callbacks", "version"] diff --git a/lang/python/src/callbacks.py b/lang/python/src/callbacks.py new file mode 100644 index 00000000..b25a9a74 --- /dev/null +++ b/lang/python/src/callbacks.py @@ -0,0 +1,49 @@ +# Copyright (C) 2004 Igor Belyi <[email protected]> +# Copyright (C) 2002 John Goerzen <[email protected]> +# +#    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 + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +from getpass import getpass + +def passphrase_stdin(hint, desc, prev_bad, hook=None): +    """This is a sample callback that will read a passphrase from +    the terminal.  The hook here, if present, will be used to describe +    why the passphrase is needed.""" +    why = '' +    if hook != None: +        why = ' ' + hook +    if prev_bad: +        why += ' (again)' +    print("Please supply %s' password%s:" % (hint, why)) +    return getpass() + +def progress_stdout(what, type, current, total, hook=None): +    print("PROGRESS UPDATE: what = %s, type = %d, current = %d, total = %d" %\ +          (what, type, current, total)) + +def readcb_fh(count, hook): +    """A callback for data.  hook should be a Python file-like object.""" +    if count: +        # Should return '' on EOF +        return hook.read(count) +    else: +        # Wants to rewind. +        if not hasattr(hook, 'seek'): +            return None +        hook.seek(0, 0) +        return None diff --git a/lang/python/src/constants/__init__.py b/lang/python/src/constants/__init__.py new file mode 100644 index 00000000..484ffd29 --- /dev/null +++ b/lang/python/src/constants/__init__.py @@ -0,0 +1,142 @@ +# Constants. +# +# 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 <http://www.gnu.org/licenses/>. + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +from gpg import util +util.process_constants('GPGME_', globals()) +del util + +# For convenience, we import the modules here. +from . import data, keylist, sig, tofu # The subdirs. +from . import create, event, keysign, md, pk, protocol, sigsum, status, validity + +# A complication arises because 'import' is a reserved keyword. +# Import it as 'Import' instead. +globals()['Import'] = getattr(__import__('', globals(), locals(), +                                         [str('import')], 1), "import") + +__all__ = ['data', 'event', 'import', 'keysign', 'keylist', 'md', 'pk', +           'protocol', 'sig', 'sigsum', 'status', 'tofu', 'validity', 'create'] + +# GPGME 1.7 replaced gpgme_op_edit with gpgme_op_interact.  We +# implement gpg.Context.op_edit using gpgme_op_interact, so the +# callbacks will be called with string keywords instead of numeric +# status messages.  Code that is using these constants will continue +# to work. + +STATUS_ABORT = "ABORT" +STATUS_ALREADY_SIGNED = "ALREADY_SIGNED" +STATUS_ATTRIBUTE = "ATTRIBUTE" +STATUS_BACKUP_KEY_CREATED = "BACKUP_KEY_CREATED" +STATUS_BAD_PASSPHRASE = "BAD_PASSPHRASE" +STATUS_BADARMOR = "BADARMOR" +STATUS_BADMDC = "BADMDC" +STATUS_BADSIG = "BADSIG" +STATUS_BEGIN_DECRYPTION = "BEGIN_DECRYPTION" +STATUS_BEGIN_ENCRYPTION = "BEGIN_ENCRYPTION" +STATUS_BEGIN_SIGNING = "BEGIN_SIGNING" +STATUS_BEGIN_STREAM = "BEGIN_STREAM" +STATUS_CARDCTRL = "CARDCTRL" +STATUS_DECRYPTION_FAILED = "DECRYPTION_FAILED" +STATUS_DECRYPTION_INFO = "DECRYPTION_INFO" +STATUS_DECRYPTION_OKAY = "DECRYPTION_OKAY" +STATUS_DELETE_PROBLEM = "DELETE_PROBLEM" +STATUS_ENC_TO = "ENC_TO" +STATUS_END_DECRYPTION = "END_DECRYPTION" +STATUS_END_ENCRYPTION = "END_ENCRYPTION" +STATUS_END_STREAM = "END_STREAM" +STATUS_ENTER = "ENTER" +STATUS_ERRMDC = "ERRMDC" +STATUS_ERROR = "ERROR" +STATUS_ERRSIG = "ERRSIG" +STATUS_EXPKEYSIG = "EXPKEYSIG" +STATUS_EXPSIG = "EXPSIG" +STATUS_FAILURE = "FAILURE" +STATUS_FILE_DONE = "FILE_DONE" +STATUS_FILE_ERROR = "FILE_ERROR" +STATUS_FILE_START = "FILE_START" +STATUS_GET_BOOL = "GET_BOOL" +STATUS_GET_HIDDEN = "GET_HIDDEN" +STATUS_GET_LINE = "GET_LINE" +STATUS_GOOD_PASSPHRASE = "GOOD_PASSPHRASE" +STATUS_GOODMDC = "GOODMDC" +STATUS_GOODSIG = "GOODSIG" +STATUS_GOT_IT = "GOT_IT" +STATUS_IMPORT_OK = "IMPORT_OK" +STATUS_IMPORT_PROBLEM = "IMPORT_PROBLEM" +STATUS_IMPORT_RES = "IMPORT_RES" +STATUS_IMPORTED = "IMPORTED" +STATUS_INQUIRE_MAXLEN = "INQUIRE_MAXLEN" +STATUS_INV_RECP = "INV_RECP" +STATUS_INV_SGNR = "INV_SGNR" +STATUS_KEY_CONSIDERED = "KEY_CONSIDERED" +STATUS_KEY_CREATED = "KEY_CREATED" +STATUS_KEY_NOT_CREATED = "KEY_NOT_CREATED" +STATUS_KEYEXPIRED = "KEYEXPIRED" +STATUS_KEYREVOKED = "KEYREVOKED" +STATUS_LEAVE = "LEAVE" +STATUS_MISSING_PASSPHRASE = "MISSING_PASSPHRASE" +STATUS_MOUNTPOINT = "MOUNTPOINT" +STATUS_NEED_PASSPHRASE = "NEED_PASSPHRASE" +STATUS_NEED_PASSPHRASE_PIN = "NEED_PASSPHRASE_PIN" +STATUS_NEED_PASSPHRASE_SYM = "NEED_PASSPHRASE_SYM" +STATUS_NEWSIG = "NEWSIG" +STATUS_NO_PUBKEY = "NO_PUBKEY" +STATUS_NO_RECP = "NO_RECP" +STATUS_NO_SECKEY = "NO_SECKEY" +STATUS_NO_SGNR = "NO_SGNR" +STATUS_NODATA = "NODATA" +STATUS_NOTATION_DATA = "NOTATION_DATA" +STATUS_NOTATION_FLAGS = "NOTATION_FLAGS" +STATUS_NOTATION_NAME = "NOTATION_NAME" +STATUS_PINENTRY_LAUNCHED = "PINENTRY_LAUNCHED" +STATUS_PKA_TRUST_BAD = "PKA_TRUST_BAD" +STATUS_PKA_TRUST_GOOD = "PKA_TRUST_GOOD" +STATUS_PLAINTEXT = "PLAINTEXT" +STATUS_PLAINTEXT_LENGTH = "PLAINTEXT_LENGTH" +STATUS_POLICY_URL = "POLICY_URL" +STATUS_PROGRESS = "PROGRESS" +STATUS_REVKEYSIG = "REVKEYSIG" +STATUS_RSA_OR_IDEA = "RSA_OR_IDEA" +STATUS_SC_OP_FAILURE = "SC_OP_FAILURE" +STATUS_SC_OP_SUCCESS = "SC_OP_SUCCESS" +STATUS_SESSION_KEY = "SESSION_KEY" +STATUS_SHM_GET = "SHM_GET" +STATUS_SHM_GET_BOOL = "SHM_GET_BOOL" +STATUS_SHM_GET_HIDDEN = "SHM_GET_HIDDEN" +STATUS_SHM_INFO = "SHM_INFO" +STATUS_SIG_CREATED = "SIG_CREATED" +STATUS_SIG_ID = "SIG_ID" +STATUS_SIG_SUBPACKET = "SIG_SUBPACKET" +STATUS_SIGEXPIRED = "SIGEXPIRED" +STATUS_SUCCESS = "SUCCESS" +STATUS_TOFU_STATS = "TOFU_STATS" +STATUS_TOFU_STATS_LONG = "TOFU_STATS_LONG" +STATUS_TOFU_USER = "TOFU_USER" +STATUS_TRUNCATED = "TRUNCATED" +STATUS_TRUST_FULLY = "TRUST_FULLY" +STATUS_TRUST_MARGINAL = "TRUST_MARGINAL" +STATUS_TRUST_NEVER = "TRUST_NEVER" +STATUS_TRUST_ULTIMATE = "TRUST_ULTIMATE" +STATUS_TRUST_UNDEFINED = "TRUST_UNDEFINED" +STATUS_UNEXPECTED = "UNEXPECTED" +STATUS_USERID_HINT = "USERID_HINT" +STATUS_VALIDSIG = "VALIDSIG" diff --git a/lang/python/src/constants/create.py b/lang/python/src/constants/create.py new file mode 100644 index 00000000..132e96d4 --- /dev/null +++ b/lang/python/src/constants/create.py @@ -0,0 +1,25 @@ +# Flags for key creation +# +# Copyright (C) 2017 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 <http://www.gnu.org/licenses/>. + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +from gpg import util +util.process_constants('GPGME_CREATE_', globals()) +del util diff --git a/lang/python/src/constants/data/__init__.py b/lang/python/src/constants/data/__init__.py new file mode 100644 index 00000000..8274ab91 --- /dev/null +++ b/lang/python/src/constants/data/__init__.py @@ -0,0 +1,6 @@ + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +from . import encoding +__all__ = ['encoding'] diff --git a/lang/python/src/constants/data/encoding.py b/lang/python/src/constants/data/encoding.py new file mode 100644 index 00000000..e76a22ee --- /dev/null +++ b/lang/python/src/constants/data/encoding.py @@ -0,0 +1,23 @@ +# Copyright (C) 2004 Igor Belyi <[email protected]> +# Copyright (C) 2002 John Goerzen <[email protected]> +# +#    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 + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +from gpg import util +util.process_constants('GPGME_DATA_ENCODING_', globals()) +del util diff --git a/lang/python/src/constants/event.py b/lang/python/src/constants/event.py new file mode 100644 index 00000000..1b14d1d1 --- /dev/null +++ b/lang/python/src/constants/event.py @@ -0,0 +1,23 @@ +# Copyright (C) 2004 Igor Belyi <[email protected]> +# Copyright (C) 2002 John Goerzen <[email protected]> +# +#    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 + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +from gpg import util +util.process_constants('GPGME_EVENT_', globals()) +del util diff --git a/lang/python/src/constants/import.py b/lang/python/src/constants/import.py new file mode 100644 index 00000000..47c296cb --- /dev/null +++ b/lang/python/src/constants/import.py @@ -0,0 +1,23 @@ +# Copyright (C) 2004 Igor Belyi <[email protected]> +# Copyright (C) 2002 John Goerzen <[email protected]> +# +#    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 + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +from gpg import util +util.process_constants('GPGME_IMPORT_', globals()) +del util diff --git a/lang/python/src/constants/keylist/__init__.py b/lang/python/src/constants/keylist/__init__.py new file mode 100644 index 00000000..2ce0edfd --- /dev/null +++ b/lang/python/src/constants/keylist/__init__.py @@ -0,0 +1,6 @@ + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +from . import mode +__all__ = ['mode'] diff --git a/lang/python/src/constants/keylist/mode.py b/lang/python/src/constants/keylist/mode.py new file mode 100644 index 00000000..39e1819d --- /dev/null +++ b/lang/python/src/constants/keylist/mode.py @@ -0,0 +1,23 @@ +# Copyright (C) 2004 Igor Belyi <[email protected]> +# Copyright (C) 2002 John Goerzen <[email protected]> +# +#    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 + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +from gpg import util +util.process_constants('GPGME_KEYLIST_MODE_', globals()) +del util diff --git a/lang/python/src/constants/keysign.py b/lang/python/src/constants/keysign.py new file mode 100644 index 00000000..fccdbc42 --- /dev/null +++ b/lang/python/src/constants/keysign.py @@ -0,0 +1,25 @@ +# Flags for key signing +# +# Copyright (C) 2017 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 <http://www.gnu.org/licenses/>. + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +from gpg import util +util.process_constants('GPGME_KEYSIGN_', globals()) +del util diff --git a/lang/python/src/constants/md.py b/lang/python/src/constants/md.py new file mode 100644 index 00000000..f3e8bbdb --- /dev/null +++ b/lang/python/src/constants/md.py @@ -0,0 +1,23 @@ +# Copyright (C) 2004 Igor Belyi <[email protected]> +# Copyright (C) 2002 John Goerzen <[email protected]> +# +#    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 + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +from gpg import util +util.process_constants('GPGME_MD_', globals()) +del util diff --git a/lang/python/src/constants/pk.py b/lang/python/src/constants/pk.py new file mode 100644 index 00000000..6bf2a215 --- /dev/null +++ b/lang/python/src/constants/pk.py @@ -0,0 +1,23 @@ +# Copyright (C) 2004 Igor Belyi <[email protected]> +# Copyright (C) 2002 John Goerzen <[email protected]> +# +#    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 + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +from gpg import util +util.process_constants('GPGME_PK_', globals()) +del util diff --git a/lang/python/src/constants/protocol.py b/lang/python/src/constants/protocol.py new file mode 100644 index 00000000..d086bbde --- /dev/null +++ b/lang/python/src/constants/protocol.py @@ -0,0 +1,23 @@ +# Copyright (C) 2004 Igor Belyi <[email protected]> +# Copyright (C) 2002 John Goerzen <[email protected]> +# +#    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 + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +from gpg import util +util.process_constants('GPGME_PROTOCOL_', globals()) +del util diff --git a/lang/python/src/constants/sig/__init__.py b/lang/python/src/constants/sig/__init__.py new file mode 100644 index 00000000..39d4e6e1 --- /dev/null +++ b/lang/python/src/constants/sig/__init__.py @@ -0,0 +1,6 @@ + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +from . import mode, notation +__all__ = ['mode', 'notation'] diff --git a/lang/python/src/constants/sig/mode.py b/lang/python/src/constants/sig/mode.py new file mode 100644 index 00000000..0f4f0efc --- /dev/null +++ b/lang/python/src/constants/sig/mode.py @@ -0,0 +1,23 @@ +# Copyright (C) 2004 Igor Belyi <[email protected]> +# Copyright (C) 2002 John Goerzen <[email protected]> +# +#    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 + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +from gpg import util +util.process_constants('GPGME_SIG_MODE_', globals()) +del util diff --git a/lang/python/src/constants/sig/notation.py b/lang/python/src/constants/sig/notation.py new file mode 100644 index 00000000..9a79e014 --- /dev/null +++ b/lang/python/src/constants/sig/notation.py @@ -0,0 +1,25 @@ +# Constants for signature notation data. +# +# 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 <http://www.gnu.org/licenses/>. + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +from gpg import util +util.process_constants('GPGME_SIG_NOTATION_', globals()) +del util diff --git a/lang/python/src/constants/sigsum.py b/lang/python/src/constants/sigsum.py new file mode 100644 index 00000000..09ef9d78 --- /dev/null +++ b/lang/python/src/constants/sigsum.py @@ -0,0 +1,23 @@ +# Copyright (C) 2004 Igor Belyi <[email protected]> +# Copyright (C) 2002 John Goerzen <[email protected]> +# +#    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 + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +from gpg import util +util.process_constants('GPGME_SIGSUM_', globals()) +del util diff --git a/lang/python/src/constants/status.py b/lang/python/src/constants/status.py new file mode 100644 index 00000000..a0ad073e --- /dev/null +++ b/lang/python/src/constants/status.py @@ -0,0 +1,124 @@ +# Copyright (C) 2004 Igor Belyi <[email protected]> +# Copyright (C) 2002 John Goerzen <[email protected]> +# +#    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 + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +# GPGME 1.7 replaced gpgme_op_edit with gpgme_op_interact.  We +# implement gpg.Context.op_edit using gpgme_op_interact, so the +# callbacks will be called with string keywords instead of numeric +# status messages.  Code that is using these constants will continue +# to work. + +ABORT = "ABORT" +ALREADY_SIGNED = "ALREADY_SIGNED" +ATTRIBUTE = "ATTRIBUTE" +BACKUP_KEY_CREATED = "BACKUP_KEY_CREATED" +BAD_PASSPHRASE = "BAD_PASSPHRASE" +BADARMOR = "BADARMOR" +BADMDC = "BADMDC" +BADSIG = "BADSIG" +BEGIN_DECRYPTION = "BEGIN_DECRYPTION" +BEGIN_ENCRYPTION = "BEGIN_ENCRYPTION" +BEGIN_SIGNING = "BEGIN_SIGNING" +BEGIN_STREAM = "BEGIN_STREAM" +CARDCTRL = "CARDCTRL" +DECRYPTION_FAILED = "DECRYPTION_FAILED" +DECRYPTION_INFO = "DECRYPTION_INFO" +DECRYPTION_OKAY = "DECRYPTION_OKAY" +DELETE_PROBLEM = "DELETE_PROBLEM" +ENC_TO = "ENC_TO" +END_DECRYPTION = "END_DECRYPTION" +END_ENCRYPTION = "END_ENCRYPTION" +END_STREAM = "END_STREAM" +ENTER = "ENTER" +ERRMDC = "ERRMDC" +ERROR = "ERROR" +ERRSIG = "ERRSIG" +EXPKEYSIG = "EXPKEYSIG" +EXPSIG = "EXPSIG" +FAILURE = "FAILURE" +FILE_DONE = "FILE_DONE" +FILE_ERROR = "FILE_ERROR" +FILE_START = "FILE_START" +GET_BOOL = "GET_BOOL" +GET_HIDDEN = "GET_HIDDEN" +GET_LINE = "GET_LINE" +GOOD_PASSPHRASE = "GOOD_PASSPHRASE" +GOODMDC = "GOODMDC" +GOODSIG = "GOODSIG" +GOT_IT = "GOT_IT" +IMPORT_OK = "IMPORT_OK" +IMPORT_PROBLEM = "IMPORT_PROBLEM" +IMPORT_RES = "IMPORT_RES" +IMPORTED = "IMPORTED" +INQUIRE_MAXLEN = "INQUIRE_MAXLEN" +INV_RECP = "INV_RECP" +INV_SGNR = "INV_SGNR" +KEY_CONSIDERED = "KEY_CONSIDERED" +KEY_CREATED = "KEY_CREATED" +KEY_NOT_CREATED = "KEY_NOT_CREATED" +KEYEXPIRED = "KEYEXPIRED" +KEYREVOKED = "KEYREVOKED" +LEAVE = "LEAVE" +MISSING_PASSPHRASE = "MISSING_PASSPHRASE" +MOUNTPOINT = "MOUNTPOINT" +NEED_PASSPHRASE = "NEED_PASSPHRASE" +NEED_PASSPHRASE_PIN = "NEED_PASSPHRASE_PIN" +NEED_PASSPHRASE_SYM = "NEED_PASSPHRASE_SYM" +NEWSIG = "NEWSIG" +NO_PUBKEY = "NO_PUBKEY" +NO_RECP = "NO_RECP" +NO_SECKEY = "NO_SECKEY" +NO_SGNR = "NO_SGNR" +NODATA = "NODATA" +NOTATION_DATA = "NOTATION_DATA" +NOTATION_FLAGS = "NOTATION_FLAGS" +NOTATION_NAME = "NOTATION_NAME" +PINENTRY_LAUNCHED = "PINENTRY_LAUNCHED" +PKA_TRUST_BAD = "PKA_TRUST_BAD" +PKA_TRUST_GOOD = "PKA_TRUST_GOOD" +PLAINTEXT = "PLAINTEXT" +PLAINTEXT_LENGTH = "PLAINTEXT_LENGTH" +POLICY_URL = "POLICY_URL" +PROGRESS = "PROGRESS" +REVKEYSIG = "REVKEYSIG" +RSA_OR_IDEA = "RSA_OR_IDEA" +SC_OP_FAILURE = "SC_OP_FAILURE" +SC_OP_SUCCESS = "SC_OP_SUCCESS" +SESSION_KEY = "SESSION_KEY" +SHM_GET = "SHM_GET" +SHM_GET_BOOL = "SHM_GET_BOOL" +SHM_GET_HIDDEN = "SHM_GET_HIDDEN" +SHM_INFO = "SHM_INFO" +SIG_CREATED = "SIG_CREATED" +SIG_ID = "SIG_ID" +SIG_SUBPACKET = "SIG_SUBPACKET" +SIGEXPIRED = "SIGEXPIRED" +SUCCESS = "SUCCESS" +TOFU_STATS = "TOFU_STATS" +TOFU_STATS_LONG = "TOFU_STATS_LONG" +TOFU_USER = "TOFU_USER" +TRUNCATED = "TRUNCATED" +TRUST_FULLY = "TRUST_FULLY" +TRUST_MARGINAL = "TRUST_MARGINAL" +TRUST_NEVER = "TRUST_NEVER" +TRUST_ULTIMATE = "TRUST_ULTIMATE" +TRUST_UNDEFINED = "TRUST_UNDEFINED" +UNEXPECTED = "UNEXPECTED" +USERID_HINT = "USERID_HINT" +VALIDSIG = "VALIDSIG" diff --git a/lang/python/src/constants/tofu/__init__.py b/lang/python/src/constants/tofu/__init__.py new file mode 100644 index 00000000..819a58bb --- /dev/null +++ b/lang/python/src/constants/tofu/__init__.py @@ -0,0 +1,24 @@ +# TOFU +# +# Copyright (C) 2017 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 <http://www.gnu.org/licenses/>. + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +from . import policy +__all__ = ['policy'] diff --git a/lang/python/src/constants/tofu/policy.py b/lang/python/src/constants/tofu/policy.py new file mode 100644 index 00000000..5a61f067 --- /dev/null +++ b/lang/python/src/constants/tofu/policy.py @@ -0,0 +1,25 @@ +# TOFU policies +# +# Copyright (C) 2017 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 <http://www.gnu.org/licenses/>. + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +from gpg import util +util.process_constants('GPGME_TOFU_POLICY_', globals()) +del util diff --git a/lang/python/src/constants/validity.py b/lang/python/src/constants/validity.py new file mode 100644 index 00000000..d3c53458 --- /dev/null +++ b/lang/python/src/constants/validity.py @@ -0,0 +1,23 @@ +# Copyright (C) 2004 Igor Belyi <[email protected]> +# Copyright (C) 2002 John Goerzen <[email protected]> +# +#    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 + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +from gpg import util +util.process_constants('GPGME_VALIDITY_', globals()) +del util diff --git a/lang/python/src/core.py b/lang/python/src/core.py new file mode 100644 index 00000000..632f4ca5 --- /dev/null +++ b/lang/python/src/core.py @@ -0,0 +1,1490 @@ +# Copyright (C) 2016-2017 g10 Code GmbH +# Copyright (C) 2004,2008 Igor Belyi <[email protected]> +# Copyright (C) 2002 John Goerzen <[email protected]> +# +#    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 + +"""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. + +""" + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +import re +import os +import warnings +import weakref +from . import gpgme +from .errors import errorcheck, GPGMEError +from . import constants +from . import errors +from . import util + +class GpgmeWrapper(object): +    """Base wrapper class + +    Not to be instantiated directly. + +    """ + +    def __init__(self, wrapped): +        self._callback_excinfo = None +        self.wrapped = wrapped + +    def __repr__(self): +        return '<{}/{!r}>'.format(super(GpgmeWrapper, self).__repr__(), +                                  self.wrapped) + +    def __str__(self): +        acc = ['{}.{}'.format(__name__, self.__class__.__name__)] +        flags = [f for f in self._boolean_properties if getattr(self, f)] +        if flags: +            acc.append('({})'.format(' '.join(flags))) + +        return '<{}>'.format(' '.join(acc)) + +    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) + +    @property +    def _ctype(self): +        """The name of the c type wrapped by this class + +        Must be set by child classes. + +        """ +        raise NotImplementedError() + +    @property +    def _cprefix(self): +        """The common prefix of c functions wrapped by this class + +        Must be set by child classes. + +        """ +        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() + +    """The set of all boolean properties""" +    _boolean_properties = set() + +    def __wrap_boolean_property(self, key, do_set=False, value=None): +        get_func = getattr(gpgme, +                           "{}get_{}".format(self._cprefix, key)) +        set_func = getattr(gpgme, +                           "{}set_{}".format(self._cprefix, key)) +        def get(slf): +            return bool(get_func(slf.wrapped)) +        def set_(slf, value): +            set_func(slf.wrapped, bool(value)) + +        p = property(get, set_, doc="{} flag".format(key)) +        setattr(self.__class__, key, p) + +        if do_set: +            set_(self, bool(value)) +        else: +            return get(self) + +    _munge_docstring = re.compile(r'gpgme_([^(]*)\(([^,]*), (.*\) -> .*)') +    def __getattr__(self, key): +        """On-the-fly generation of wrapper methods and properties""" +        if key[0] == '_' or self._cprefix == None: +            return None + +        if key in self._boolean_properties: +            return self.__wrap_boolean_property(key) + +        name = self._cprefix + key +        func = getattr(gpgme, name) + +        if self._errorcheck(name): +            def _funcwrap(slf, *args): +                result = func(slf.wrapped, *args) +                if slf._callback_excinfo: +                    gpgme.gpg_raise_callback_exception(slf) +                return errorcheck(result, "Invocation of " + name) +        else: +            def _funcwrap(slf, *args): +                result = func(slf.wrapped, *args) +                if slf._callback_excinfo: +                    gpgme.gpg_raise_callback_exception(slf) +                return result + +        doc = self._munge_docstring.sub(r'\2.\1(\3', getattr(func, "__doc__")) +        _funcwrap.__doc__ = doc + +        # Monkey-patch the class. +        setattr(self.__class__, key, _funcwrap) + +        # Bind the method to 'self'. +        def wrapper(*args): +            return _funcwrap(self, *args) +        wrapper.__doc__ = doc + +        return wrapper + +    def __setattr__(self, key, value): +        """On-the-fly generation of properties""" +        if key in self._boolean_properties: +            self.__wrap_boolean_property(key, True, value) +        else: +            super(GpgmeWrapper, self).__setattr__(key, value) + +class Context(GpgmeWrapper): +    """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. + +    Access to a context must be synchronized. + +    """ + +    def __init__(self, armor=False, textmode=False, offline=False, +                 signers=[], pinentry_mode=constants.PINENTRY_MODE_DEFAULT, +                 protocol=constants.PROTOCOL_OpenPGP, +                 wrapped=None, home_dir=None): +        """Construct a context object + +        Keyword arguments: +        armor		-- enable ASCII armoring (default False) +        textmode	-- enable canonical text mode (default False) +        offline		-- do not contact external key sources (default False) +        signers		-- list of keys used for signing (default []) +        pinentry_mode	-- pinentry mode (default PINENTRY_MODE_DEFAULT) +        protocol	-- protocol to use (default PROTOCOL_OpenPGP) +        home_dir        -- state directory (default is the engine default) + +        """ +        if wrapped: +            self.own = False +        else: +            tmp = gpgme.new_gpgme_ctx_t_p() +            errorcheck(gpgme.gpgme_new(tmp)) +            wrapped = gpgme.gpgme_ctx_t_p_value(tmp) +            gpgme.delete_gpgme_ctx_t_p(tmp) +            self.own = True +        super(Context, self).__init__(wrapped) +        self.armor = armor +        self.textmode = textmode +        self.offline = offline +        self.signers = signers +        self.pinentry_mode = pinentry_mode +        self.protocol = protocol +        self.home_dir = home_dir + +    def __repr__(self): +        return ( +            "Context(armor={0.armor}, " +            "textmode={0.textmode}, offline={0.offline}, " +            "signers={0.signers}, pinentry_mode={0.pinentry_mode}, " +            "protocol={0.protocol}, home_dir={0.home_dir}" +            ")").format(self) + +    def encrypt(self, plaintext, recipients=[], sign=True, sink=None, +                passphrase=None, always_trust=False, add_encrypt_to=False, +                prepare=False, expect_sign=False, compress=True): +        """Encrypt data + +        Encrypt the given plaintext for the given recipients.  If the +        list of recipients is empty, the data is encrypted +        symmetrically with a passphrase. + +        The passphrase can be given as parameter, using a callback +        registered at the context, or out-of-band via pinentry. + +        Keyword arguments: +        recipients	-- list of keys to encrypt to +        sign		-- sign plaintext (default True) +        sink		-- write result to sink instead of returning it +        passphrase	-- for symmetric encryption +        always_trust	-- always trust the keys (default False) +        add_encrypt_to	-- encrypt to configured additional keys (default False) +        prepare		-- (ui) prepare for encryption (default False) +        expect_sign	-- (ui) prepare for signing (default False) +        compress	-- compress plaintext (default True) + +        Returns: +        ciphertext	-- the encrypted data (or None if sink is given) +        result		-- additional information about the encryption +        sign_result	-- additional information about the signature(s) + +        Raises: +        InvalidRecipients -- if encryption using a particular key failed +        InvalidSigners	-- if signing using a particular key failed +        GPGMEError	-- as signaled by the underlying library + +        """ +        ciphertext = sink if sink else Data() +        flags = 0 +        flags |= always_trust * constants.ENCRYPT_ALWAYS_TRUST +        flags |= (not add_encrypt_to) * constants.ENCRYPT_NO_ENCRYPT_TO +        flags |= prepare * constants.ENCRYPT_PREPARE +        flags |= expect_sign * constants.ENCRYPT_EXPECT_SIGN +        flags |= (not compress) * constants.ENCRYPT_NO_COMPRESS + +        if passphrase != None: +            old_pinentry_mode = self.pinentry_mode +            old_passphrase_cb = getattr(self, '_passphrase_cb', None) +            self.pinentry_mode = constants.PINENTRY_MODE_LOOPBACK +            def passphrase_cb(hint, desc, prev_bad, hook=None): +                return passphrase +            self.set_passphrase_cb(passphrase_cb) + +        try: +            if sign: +                self.op_encrypt_sign(recipients, flags, plaintext, ciphertext) +            else: +                self.op_encrypt(recipients, flags, plaintext, ciphertext) +        except errors.GPGMEError as e: +            if e.getcode() == errors.UNUSABLE_PUBKEY: +                result = self.op_encrypt_result() +                if result.invalid_recipients: +                    raise errors.InvalidRecipients(result.invalid_recipients) +            if e.getcode() == errors.UNUSABLE_SECKEY: +                sig_result = self.op_sign_result() +                if sig_result.invalid_signers: +                    raise errors.InvalidSigners(sig_result.invalid_signers) +            raise +        finally: +            if passphrase != None: +                self.pinentry_mode = old_pinentry_mode +                if old_passphrase_cb: +                    self.set_passphrase_cb(*old_passphrase_cb[1:]) + +        result = self.op_encrypt_result() +        assert not result.invalid_recipients +        sig_result = self.op_sign_result() if sign else None +        assert not sig_result or not sig_result.invalid_signers + +        cipherbytes = None +        if not sink: +            ciphertext.seek(0, os.SEEK_SET) +            cipherbytes = ciphertext.read() +        return cipherbytes, result, sig_result + +    def decrypt(self, ciphertext, sink=None, passphrase=None, verify=True): +        """Decrypt data + +        Decrypt the given ciphertext and verify any signatures.  If +        VERIFY is an iterable of keys, the ciphertext must be signed +        by all those keys, otherwise an error is raised. + +        If the ciphertext is symmetrically encrypted using a +        passphrase, that passphrase can be given as parameter, using a +        callback registered at the context, or out-of-band via +        pinentry. + +        Keyword arguments: +        sink		-- write result to sink instead of returning it +        passphrase	-- for symmetric decryption +        verify		-- check signatures (default True) + +        Returns: +        plaintext	-- the decrypted data (or None if sink is given) +        result		-- additional information about the decryption +        verify_result	-- additional information about the signature(s) + +        Raises: +        UnsupportedAlgorithm -- if an unsupported algorithm was used +        BadSignatures	-- if a bad signature is encountered +        MissingSignatures -- if expected signatures are missing or bad +        GPGMEError	-- as signaled by the underlying library + +        """ +        plaintext = sink if sink else Data() + +        if passphrase != None: +            old_pinentry_mode = self.pinentry_mode +            old_passphrase_cb = getattr(self, '_passphrase_cb', None) +            self.pinentry_mode = constants.PINENTRY_MODE_LOOPBACK +            def passphrase_cb(hint, desc, prev_bad, hook=None): +                return passphrase +            self.set_passphrase_cb(passphrase_cb) + +        try: +            if verify: +                self.op_decrypt_verify(ciphertext, plaintext) +            else: +                self.op_decrypt(ciphertext, plaintext) +        finally: +            if passphrase != None: +                self.pinentry_mode = old_pinentry_mode +                if old_passphrase_cb: +                    self.set_passphrase_cb(*old_passphrase_cb[1:]) + +        result = self.op_decrypt_result() +        verify_result = self.op_verify_result() if verify else None +        if result.unsupported_algorithm: +            raise errors.UnsupportedAlgorithm(result.unsupported_algorithm) + +        if verify: +            if any(s.status != errors.NO_ERROR +                   for s in verify_result.signatures): +                raise errors.BadSignatures(verify_result) + +        if verify and verify != True: +            missing = list() +            for key in verify: +                ok = False +                for subkey in key.subkeys: +                    for sig in verify_result.signatures: +                        if sig.summary & constants.SIGSUM_VALID == 0: +                            continue +                        if subkey.can_sign and subkey.fpr == sig.fpr: +                            ok = True +                            break +                    if ok: +                        break +                if not ok: +                    missing.append(key) +            if missing: +                raise errors.MissingSignatures(verify_result, missing) + +        plainbytes = None +        if not sink: +            plaintext.seek(0, os.SEEK_SET) +            plainbytes = plaintext.read() +        return plainbytes, result, verify_result + +    def sign(self, data, sink=None, mode=constants.SIG_MODE_NORMAL): +        """Sign data + +        Sign the given data with either the configured default local +        key, or the 'signers' keys of this context. + +        Keyword arguments: +        mode		-- signature mode (default: normal, see below) +        sink		-- write result to sink instead of returning it + +        Returns: +        either +          signed_data	-- encoded data and signature (normal mode) +          signature	-- only the signature data (detached mode) +          cleartext	-- data and signature as text (cleartext mode) +            (or None if sink is given) +        result		-- additional information about the signature(s) + +        Raises: +        InvalidSigners	-- if signing using a particular key failed +        GPGMEError	-- as signaled by the underlying library + +        """ +        signeddata = sink if sink else Data() + +        try: +            self.op_sign(data, signeddata, mode) +        except errors.GPGMEError as e: +            if e.getcode() == errors.UNUSABLE_SECKEY: +                result = self.op_sign_result() +                if result.invalid_signers: +                    raise errors.InvalidSigners(result.invalid_signers) +            raise + +        result = self.op_sign_result() +        assert not result.invalid_signers + +        signedbytes = None +        if not sink: +            signeddata.seek(0, os.SEEK_SET) +            signedbytes = signeddata.read() +        return signedbytes, result + +    def verify(self, signed_data, signature=None, sink=None, verify=[]): +        """Verify signatures + +        Verify signatures over data.  If VERIFY is an iterable of +        keys, the ciphertext must be signed by all those keys, +        otherwise an error is raised. + +        Keyword arguments: +        signature	-- detached signature data +        sink		-- write result to sink instead of returning it + +        Returns: +        data		-- the plain data +            (or None if sink is given, or we verified a detached signature) +        result		-- additional information about the signature(s) + +        Raises: +        BadSignatures	-- if a bad signature is encountered +        MissingSignatures -- if expected signatures are missing or bad +        GPGMEError	-- as signaled by the underlying library + +        """ +        if signature: +            # Detached signature, we don't return the plain text. +            data = None +        else: +            data = sink if sink else Data() + +        if signature: +            self.op_verify(signature, signed_data, None) +        else: +            self.op_verify(signed_data, None, data) + +        result = self.op_verify_result() +        if any(s.status != errors.NO_ERROR for s in result.signatures): +            raise errors.BadSignatures(result) + +        missing = list() +        for key in verify: +            ok = False +            for subkey in key.subkeys: +                for sig in result.signatures: +                    if sig.summary & constants.SIGSUM_VALID == 0: +                        continue +                    if subkey.can_sign and subkey.fpr == sig.fpr: +                        ok = True +                        break +                if ok: +                    break +            if not ok: +                missing.append(key) +        if missing: +            raise errors.MissingSignatures(result, missing) + +        plainbytes = None +        if data and not sink: +            data.seek(0, os.SEEK_SET) +            plainbytes = data.read() +        return plainbytes, result + +    def keylist(self, pattern=None, secret=False, +                mode=constants.keylist.mode.LOCAL, +                source=None): +        """List keys + +        Keyword arguments: +        pattern	-- return keys matching pattern (default: all keys) +        secret	-- return only secret keys (default: False) +        mode    -- keylist mode (default: list local keys) +        source  -- read keys from source instead from the keyring +                       (all other options are ignored in this case) + +        Returns: +                -- an iterator returning key objects + +        Raises: +        GPGMEError	-- as signaled by the underlying library +        """ +        if not source: +            self.set_keylist_mode(mode) +            self.op_keylist_start(pattern, secret) +        else: +            # Automatic wrapping of SOURCE is not possible here, +            # because the object must not be deallocated until the +            # iteration over the results ends. +            if not isinstance(source, Data): +                source = Data(file=source) +            self.op_keylist_from_data_start(source, 0) + +        key = self.op_keylist_next() +        while key: +            yield key +            key = self.op_keylist_next() +        self.op_keylist_end() + +    def create_key(self, userid, algorithm=None, expires_in=0, expires=True, +                   sign=False, encrypt=False, certify=False, authenticate=False, +                   passphrase=None, force=False): +        """Create a primary key + +        Create a primary key for the user id USERID. + +        ALGORITHM may be used to specify the public key encryption +        algorithm for the new key.  By default, a reasonable default +        is chosen.  You may use "future-default" to select an +        algorithm that will be the default in a future implementation +        of the engine.  ALGORITHM may be a string like "rsa", or +        "rsa2048" to explicitly request an algorithm and a key size. + +        EXPIRES_IN specifies the expiration time of the key in number +        of seconds since the keys creation.  By default, a reasonable +        expiration time is chosen.  If you want to create a key that +        does not expire, use the keyword argument EXPIRES. + +        SIGN, ENCRYPT, CERTIFY, and AUTHENTICATE can be used to +        request the capabilities of the new key.  If you don't request +        any, a reasonable set of capabilities is selected, and in case +        of OpenPGP, a subkey with a reasonable set of capabilities is +        created. + +        If PASSPHRASE is None (the default), then the key will not be +        protected with a passphrase.  If PASSPHRASE is a string, it +        will be used to protect the key.  If PASSPHRASE is True, the +        passphrase must be supplied using a passphrase callback or +        out-of-band with a pinentry. + +        Keyword arguments: +        algorithm    -- public key algorithm, see above (default: reasonable) +        expires_in   -- expiration time in seconds (default: reasonable) +        expires      -- whether or not the key should expire (default: True) +        sign         -- request the signing capability (see above) +        encrypt      -- request the encryption capability (see above) +        certify      -- request the certification capability (see above) +        authenticate -- request the authentication capability (see above) +        passphrase   -- protect the key with a passphrase (default: no passphrase) +        force        -- force key creation even if a key with the same userid exists +                                                          (default: False) + +        Returns: +                     -- an object describing the result of the key creation + +        Raises: +        GPGMEError   -- as signaled by the underlying library + +        """ +        if util.is_a_string(passphrase): +            old_pinentry_mode = self.pinentry_mode +            old_passphrase_cb = getattr(self, '_passphrase_cb', None) +            self.pinentry_mode = constants.PINENTRY_MODE_LOOPBACK +            def passphrase_cb(hint, desc, prev_bad, hook=None): +                return passphrase +            self.set_passphrase_cb(passphrase_cb) + +        try: +            self.op_createkey(userid, algorithm, +                              0, # reserved +                              expires_in, +                              None, # extrakey +                              ((constants.create.SIGN if sign else 0) +                               | (constants.create.ENCR if encrypt else 0) +                               | (constants.create.CERT if certify else 0) +                               | (constants.create.AUTH if authenticate else 0) +                               | (constants.create.NOPASSWD if passphrase == None else 0) +                               | (0 if expires else constants.create.NOEXPIRE) +                               | (constants.create.FORCE if force else 0))) +        finally: +            if util.is_a_string(passphrase): +                self.pinentry_mode = old_pinentry_mode +                if old_passphrase_cb: +                    self.set_passphrase_cb(*old_passphrase_cb[1:]) + +        return self.op_genkey_result() + +    def create_subkey(self, key, algorithm=None, expires_in=0, expires=True, +                      sign=False, encrypt=False, authenticate=False, passphrase=None): +        """Create a subkey + +        Create a subkey for the given KEY.  As subkeys are a concept +        of OpenPGP, calling this is only valid for the OpenPGP +        protocol. + +        ALGORITHM may be used to specify the public key encryption +        algorithm for the new subkey.  By default, a reasonable +        default is chosen.  You may use "future-default" to select an +        algorithm that will be the default in a future implementation +        of the engine.  ALGORITHM may be a string like "rsa", or +        "rsa2048" to explicitly request an algorithm and a key size. + +        EXPIRES_IN specifies the expiration time of the subkey in +        number of seconds since the subkeys creation.  By default, a +        reasonable expiration time is chosen.  If you want to create a +        subkey that does not expire, use the keyword argument EXPIRES. + +        SIGN, ENCRYPT, and AUTHENTICATE can be used to request the +        capabilities of the new subkey.  If you don't request any, an +        encryption subkey is generated. + +        If PASSPHRASE is None (the default), then the subkey will not +        be protected with a passphrase.  If PASSPHRASE is a string, it +        will be used to protect the subkey.  If PASSPHRASE is True, +        the passphrase must be supplied using a passphrase callback or +        out-of-band with a pinentry. + +        Keyword arguments: +        algorithm    -- public key algorithm, see above (default: reasonable) +        expires_in   -- expiration time in seconds (default: reasonable) +        expires      -- whether or not the subkey should expire (default: True) +        sign         -- request the signing capability (see above) +        encrypt      -- request the encryption capability (see above) +        authenticate -- request the authentication capability (see above) +        passphrase   -- protect the subkey with a passphrase (default: no passphrase) + +        Returns: +                     -- an object describing the result of the subkey creation + +        Raises: +        GPGMEError   -- as signaled by the underlying library + +        """ +        if util.is_a_string(passphrase): +            old_pinentry_mode = self.pinentry_mode +            old_passphrase_cb = getattr(self, '_passphrase_cb', None) +            self.pinentry_mode = constants.PINENTRY_MODE_LOOPBACK +            def passphrase_cb(hint, desc, prev_bad, hook=None): +                return passphrase +            self.set_passphrase_cb(passphrase_cb) + +        try: +            self.op_createsubkey(key, algorithm, +                                 0, # reserved +                                 expires_in, +                                 ((constants.create.SIGN if sign else 0) +                                  | (constants.create.ENCR if encrypt else 0) +                                  | (constants.create.AUTH if authenticate else 0) +                                  | (constants.create.NOPASSWD +                                     if passphrase == None else 0) +                                  | (0 if expires else constants.create.NOEXPIRE))) +        finally: +            if util.is_a_string(passphrase): +                self.pinentry_mode = old_pinentry_mode +                if old_passphrase_cb: +                    self.set_passphrase_cb(*old_passphrase_cb[1:]) + +        return self.op_genkey_result() + +    def key_add_uid(self, key, uid): +        """Add a UID + +        Add the uid UID to the given KEY.  Calling this function is +        only valid for the OpenPGP protocol. + +        Raises: +        GPGMEError   -- as signaled by the underlying library + +        """ +        self.op_adduid(key, uid, 0) + +    def key_revoke_uid(self, key, uid): +        """Revoke a UID + +        Revoke the uid UID from the given KEY.  Calling this function +        is only valid for the OpenPGP protocol. + +        Raises: +        GPGMEError   -- as signaled by the underlying library + +        """ +        self.op_revuid(key, uid, 0) + +    def key_sign(self, key, uids=None, expires_in=False, local=False): +        """Sign a key + +        Sign a key with the current set of signing keys.  Calling this +        function is only valid for the OpenPGP protocol. + +        If UIDS is None (the default), then all UIDs are signed.  If +        it is a string, then only the matching UID is signed.  If it +        is a list of strings, then all matching UIDs are signed.  Note +        that a case-sensitive exact string comparison is done. + +        EXPIRES_IN specifies the expiration time of the signature in +        seconds.  If EXPIRES_IN is False, the signature does not +        expire. + +        Keyword arguments: +        uids         -- user ids to sign, see above (default: sign all) +        expires_in   -- validity period of the signature in seconds +                                               (default: do not expire) +        local        -- create a local, non-exportable signature +                                               (default: False) + +        Raises: +        GPGMEError   -- as signaled by the underlying library + +        """ +        flags = 0 +        if uids == None or util.is_a_string(uids): +            pass#through unchanged +        else: +            flags |= constants.keysign.LFSEP +            uids = "\n".join(uids) + +        if not expires_in: +            flags |= constants.keysign.NOEXPIRE + +        if local: +            flags |= constants.keysign.LOCAL + +        self.op_keysign(key, uids, expires_in, flags) + +    def key_tofu_policy(self, key, policy): +        """Set a keys' TOFU policy + +        Set the TOFU policy associated with KEY to POLICY.  Calling +        this function is only valid for the OpenPGP protocol. + +        Raises: +        GPGMEError   -- as signaled by the underlying library + +        """ +        self.op_tofu_policy(key, policy) + +    def assuan_transact(self, command, +                        data_cb=None, inquire_cb=None, status_cb=None): +        """Issue a raw assuan command + +        This function can be used to issue a raw assuan command to the +        engine. + +        If command is a string or bytes, it will be used as-is.  If it +        is an iterable of strings, it will be properly escaped and +        joined into an well-formed assuan command. + +        Keyword arguments: +        data_cb		-- a callback receiving data lines +        inquire_cb	-- a callback providing more information +        status_cb	-- a callback receiving status lines + +        Returns: +        result		-- the result of command as GPGMEError + +        Raises: +        GPGMEError	-- as signaled by the underlying library + +        """ + +        if util.is_a_string(command) or isinstance(command, bytes): +            cmd = command +        else: +            cmd = " ".join(util.percent_escape(f) for f in command) + +        errptr = gpgme.new_gpgme_error_t_p() + +        err = gpgme.gpgme_op_assuan_transact_ext( +            self.wrapped, +            cmd, +            (weakref.ref(self), data_cb) if data_cb else None, +            (weakref.ref(self), inquire_cb) if inquire_cb else None, +            (weakref.ref(self), status_cb) if status_cb else None, +            errptr) + +        if self._callback_excinfo: +            gpgme.gpg_raise_callback_exception(self) + +        errorcheck(err) + +        status = gpgme.gpgme_error_t_p_value(errptr) +        gpgme.delete_gpgme_error_t_p(errptr) + +        return GPGMEError(status) if status != 0 else None + +    def interact(self, key, func, sink=None, flags=0, fnc_value=None): +        """Interact with the engine + +        This method can be used to edit keys and cards interactively. +        KEY is the key to edit, FUNC is called repeatedly with two +        unicode arguments, 'keyword' and 'args'.  See the GPGME manual +        for details. + +        Keyword arguments: +        sink		-- if given, additional output is written here +        flags		-- use constants.INTERACT_CARD to edit a card + +        Raises: +        GPGMEError	-- as signaled by the underlying library + +        """ +        if key == None: +            raise ValueError("First argument cannot be None") + +        if sink == None: +            sink = Data() + +        if fnc_value: +            opaquedata = (weakref.ref(self), func, fnc_value) +        else: +            opaquedata = (weakref.ref(self), func) + +        result = gpgme.gpgme_op_interact(self.wrapped, key, flags, +                                         opaquedata, sink) +        if self._callback_excinfo: +            gpgme.gpg_raise_callback_exception(self) +        errorcheck(result) + +    @property +    def signers(self): +        """Keys used for signing""" +        return [self.signers_enum(i) for i in range(self.signers_count())] +    @signers.setter +    def signers(self, signers): +        old = self.signers +        self.signers_clear() +        try: +            for key in signers: +                self.signers_add(key) +        except: +            self.signers = old +            raise + +    @property +    def pinentry_mode(self): +        """Pinentry mode""" +        return self.get_pinentry_mode() +    @pinentry_mode.setter +    def pinentry_mode(self, value): +        self.set_pinentry_mode(value) + +    @property +    def protocol(self): +        """Protocol to use""" +        return self.get_protocol() +    @protocol.setter +    def protocol(self, value): +        errorcheck(gpgme.gpgme_engine_check_version(value)) +        self.set_protocol(value) + +    @property +    def home_dir(self): +        """Engine's home directory""" +        return self.engine_info.home_dir +    @home_dir.setter +    def home_dir(self, value): +        self.set_engine_info(self.protocol, home_dir=value) + +    _ctype = 'gpgme_ctx_t' +    _cprefix = 'gpgme_' + +    def _errorcheck(self, name): +        """This function should list all functions returning gpgme_error_t""" +        # The list of functions is created using: +        # +        # $ grep '^gpgme_error_t ' obj/lang/python/python3.5-gpg/gpgme.h \ +        #   | grep -v _op_ | awk "/\(gpgme_ctx/ { printf (\"'%s',\\n\", \$2) } " +        return ((name.startswith('gpgme_op_') +                 and not name.endswith('_result')) +                or name in { +                    'gpgme_new', +                    'gpgme_set_ctx_flag', +                    'gpgme_set_protocol', +                    'gpgme_set_sub_protocol', +                    'gpgme_set_keylist_mode', +                    'gpgme_set_pinentry_mode', +                    'gpgme_set_locale', +                    'gpgme_ctx_set_engine_info', +                    'gpgme_signers_add', +                    'gpgme_sig_notation_add', +                    'gpgme_set_sender', +                    'gpgme_cancel', +                    'gpgme_cancel_async', +                    'gpgme_get_key', +                }) + +    _boolean_properties = {'armor', 'textmode', 'offline'} + +    def __del__(self): +        if not gpgme: +            # At interpreter shutdown, gpgme is set to NONE. +            return + +        self._free_passcb() +        self._free_progresscb() +        self._free_statuscb() +        if self.own and self.wrapped and gpgme.gpgme_release: +            gpgme.gpgme_release(self.wrapped) +            self.wrapped = None + +    # Implement the context manager protocol. +    def __enter__(self): +        return self +    def __exit__(self, type, value, tb): +        self.__del__() + +    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() +        self.op_keylist_end() + +    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 = gpgme.new_gpgme_key_t_p() +        try: +            errorcheck(gpgme.gpgme_op_keylist_next(self.wrapped, ptr)) +            key = gpgme.gpgme_key_t_p_value(ptr) +        except errors.GPGMEError as excp: +            key = None +            if excp.getcode() != errors.EOF: +                raise excp +        gpgme.delete_gpgme_key_t_p(ptr) +        if key: +            key.__del__ = lambda self: gpgme.gpgme_key_unref(self) +            return key + +    def get_key(self, fpr, secret=False): +        """Get a key given a fingerprint + +        Keyword arguments: +        secret		-- to request a secret key + +        Returns: +                        -- the matching key + +        Raises: +        KeyError	-- if the key was not found +        GPGMEError	-- as signaled by the underlying library + +        """ +        ptr = gpgme.new_gpgme_key_t_p() + +        try: +            errorcheck(gpgme.gpgme_get_key(self.wrapped, fpr, ptr, secret)) +        except errors.GPGMEError as e: +            if e.getcode() == errors.EOF: +                raise errors.KeyNotFound(fpr) +            raise e + +        key = gpgme.gpgme_key_t_p_value(ptr) +        gpgme.delete_gpgme_key_t_p(ptr) +        assert key +        key.__del__ = lambda self: gpgme.gpgme_key_unref(self) +        return key + +    def op_trustlist_all(self, *args, **kwargs): +        self.op_trustlist_start(*args, **kwargs) +        trust = self.op_trustlist_next() +        while trust: +            yield trust +            trust = self.op_trustlist_next() +        self.op_trustlist_end() + +    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 = gpgme.new_gpgme_trust_item_t_p() +        try: +            errorcheck(gpgme.gpgme_op_trustlist_next(self.wrapped, ptr)) +            trust = gpgme.gpgme_trust_item_t_p_value(ptr) +        except errors.GPGMEError as excp: +            trust = None +            if excp.getcode() != errors.EOF: +                raise +        gpgme.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. + +        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. +        """ +        if func == None: +            hookdata = None +        else: +            if hook == None: +                hookdata = (weakref.ref(self), func) +            else: +                hookdata = (weakref.ref(self), func, hook) +        gpgme.gpg_set_passphrase_cb(self, hookdata) + +    def _free_passcb(self): +        if gpgme.gpg_set_passphrase_cb: +            self.set_passphrase_cb(None) + +    def set_progress_cb(self, func, hook=None): +        """Sets the progress meter callback to the function specified by FUNC. +        If FUNC is None, the callback will be cleared. + +        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. + +        Please see the GPGME manual for more information. + +        """ +        if func == None: +            hookdata = None +        else: +            if hook == None: +                hookdata = (weakref.ref(self), func) +            else: +                hookdata = (weakref.ref(self), func, hook) +        gpgme.gpg_set_progress_cb(self, hookdata) + +    def _free_progresscb(self): +        if gpgme.gpg_set_progress_cb: +            self.set_progress_cb(None) + +    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. + +        """ +        if func == None: +            hookdata = None +        else: +            if hook == None: +                hookdata = (weakref.ref(self), func) +            else: +                hookdata = (weakref.ref(self), func, hook) +        gpgme.gpg_set_status_cb(self, hookdata) + +    def _free_statuscb(self): +        if gpgme.gpg_set_status_cb: +            self.set_status_cb(None) + +    @property +    def engine_info(self): +        """Configuration of the engine currently in use""" +        p = self.protocol +        infos = [i for i in self.get_engine_info() if i.protocol == p] +        assert len(infos) == 1 +        return infos[0] + +    def get_engine_info(self): +        """Get engine configuration + +        Returns information about all configured and installed +        engines. + +        Returns: +        infos		-- a list of engine infos + +        """ +        return gpgme.gpgme_ctx_get_engine_info(self.wrapped) + +    def set_engine_info(self, proto, file_name=None, home_dir=None): +        """Change engine configuration + +        Changes the configuration of the crypto engine implementing +        the protocol 'proto' for the context. + +        Keyword arguments: +        file_name	-- engine program file name (unchanged if None) +        home_dir	-- configuration directory (unchanged if None) + +        """ +        self.ctx_set_engine_info(proto, file_name, home_dir) + +    def wait(self, hang): +        """Wait for asynchronous call to finish. Wait forever if hang is True. +        Raises an exception on errors. + +        Please read the GPGME manual for more information. + +        """ +        ptr = gpgme.new_gpgme_error_t_p() +        gpgme.gpgme_wait(self.wrapped, ptr, hang) +        status = gpgme.gpgme_error_t_p_value(ptr) +        gpgme.delete_gpgme_error_t_p(ptr) +        errorcheck(status) + +    def op_edit(self, key, func, fnc_value, out): +        """Start key editing using supplied callback function + +        Note: This interface is deprecated and will be removed with +        GPGME 1.8.  Please use .interact instead.  Furthermore, we +        implement this using gpgme_op_interact, so callbacks will get +        called with string keywords instead of numeric status +        messages.  Code that is using constants.STATUS_X or +        constants.status.X will continue to work, whereas code using +        magic numbers will break as a result. + +        """ +        warnings.warn("Call to deprecated method op_edit.", +                      category=DeprecationWarning) +        return self.interact(key, func, sink=out, fnc_value=fnc_value) + + +class Data(GpgmeWrapper): +    """Data buffer + +    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. + +    This Data class is the implementation of the GpgmeData objects. + +    Please see the information about __init__ for instantiation. + +    """ + +    _ctype = 'gpgme_data_t' +    _cprefix = 'gpgme_data_' + +    def _errorcheck(self, name): +        """This function should list all functions returning gpgme_error_t""" +        # This list is compiled using +        # +        # $ grep -v '^gpgme_error_t ' obj/lang/python/python3.5-gpg/gpgme.h \ +        #   | awk "/\(gpgme_data_t/ { printf (\"'%s',\\n\", \$2) } " | sed "s/'\\*/'/" +        return name not in { +            'gpgme_data_read', +            'gpgme_data_write', +            'gpgme_data_seek', +            'gpgme_data_release', +            'gpgme_data_release_and_get_mem', +            'gpgme_data_get_encoding', +            'gpgme_data_get_file_name', +            'gpgme_data_identify', +        } + +    def __init__(self, string=None, file=None, offset=None, +                 length=None, cbs=None, copy=True): +        """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: + +        (read_cb, write_cb, seek_cb, release_cb[, hook]) + +        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. + +        If file is specified without any other arguments, then +        it must be a filename, and the object will be initialized from +        that file. + +        """ +        super(Data, self).__init__(None) +        self.data_cbs = None + +        if cbs != None: +            self.new_from_cbs(*cbs) +        elif string != None: +            self.new_from_mem(string, copy) +        elif file != None and offset != None and length != None: +            self.new_from_filepart(file, offset, length) +        elif file != None: +            if util.is_a_string(file): +                self.new_from_file(file, copy) +            else: +                self.new_from_fd(file) +        else: +            self.new() + +    def __del__(self): +        if not gpgme: +            # At interpreter shutdown, gpgme is set to NONE. +            return + +        if self.wrapped != None and gpgme.gpgme_data_release: +            gpgme.gpgme_data_release(self.wrapped) +            if self._callback_excinfo: +                gpgme.gpg_raise_callback_exception(self) +            self.wrapped = None +        self._free_datacbs() + +    # Implement the context manager protocol. +    def __enter__(self): +        return self +    def __exit__(self, type, value, tb): +        self.__del__() + +    def _free_datacbs(self): +        self._data_cbs = None + +    def new(self): +        tmp = gpgme.new_gpgme_data_t_p() +        errorcheck(gpgme.gpgme_data_new(tmp)) +        self.wrapped = gpgme.gpgme_data_t_p_value(tmp) +        gpgme.delete_gpgme_data_t_p(tmp) + +    def new_from_mem(self, string, copy=True): +        tmp = gpgme.new_gpgme_data_t_p() +        errorcheck(gpgme.gpgme_data_new_from_mem(tmp,string,len(string),copy)) +        self.wrapped = gpgme.gpgme_data_t_p_value(tmp) +        gpgme.delete_gpgme_data_t_p(tmp) + +    def new_from_file(self, filename, copy=True): +        tmp = gpgme.new_gpgme_data_t_p() +        try: +            errorcheck(gpgme.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 +        self.wrapped = gpgme.gpgme_data_t_p_value(tmp) +        gpgme.delete_gpgme_data_t_p(tmp) + +    def new_from_cbs(self, read_cb, write_cb, seek_cb, release_cb, hook=None): +        tmp = gpgme.new_gpgme_data_t_p() +        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) +        gpgme.gpg_data_new_from_cbs(self, hookdata, tmp) +        self.wrapped = gpgme.gpgme_data_t_p_value(tmp) +        gpgme.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: + +        * a string specifying a file name, or +        * a file-like object supporting the fileno() and the mode attribute. + +        """ + +        tmp = gpgme.new_gpgme_data_t_p() +        filename = None +        fp = None + +        if util.is_a_string(file): +            filename = file +        else: +            fp = gpgme.fdopen(file.fileno(), file.mode) +            if fp == None: +                raise ValueError("Failed to open file from %s arg %s" % \ +                      (str(type(file)), str(file))) + +        errorcheck(gpgme.gpgme_data_new_from_filepart(tmp, filename, fp, +                                                      offset, length)) +        self.wrapped = gpgme.gpgme_data_t_p_value(tmp) +        gpgme.delete_gpgme_data_t_p(tmp) + +    def new_from_fd(self, file): +        """This wraps the GPGME gpgme_data_new_from_fd() function.  The +        argument "file" must be a file-like object, supporting the +        fileno() method. + +        """ +        tmp = gpgme.new_gpgme_data_t_p() +        errorcheck(gpgme.gpgme_data_new_from_fd(tmp, file.fileno())) +        self.wrapped = gpgme.gpgme_data_t_p_value(tmp) +        gpgme.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) + +    def write(self, buffer): +        """Write buffer given as string or bytes. + +        If a string is given, it is implicitly encoded using UTF-8.""" +        written = gpgme.gpgme_data_write(self.wrapped, buffer) +        if written < 0: +            if self._callback_excinfo: +                gpgme.gpg_raise_callback_exception(self) +            else: +                raise GPGMEError.fromSyserror() +        return written + +    def read(self, size = -1): +        """Read at most size bytes, returned as bytes. + +        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.""" + +        if size == 0: +            return '' + +        if size > 0: +            try: +                result = gpgme.gpgme_data_read(self.wrapped, size) +            except: +                if self._callback_excinfo: +                    gpgme.gpg_raise_callback_exception(self) +                else: +                    raise +            return result +        else: +            chunks = [] +            while True: +                try: +                    result = gpgme.gpgme_data_read(self.wrapped, 4096) +                except: +                    if self._callback_excinfo: +                        gpgme.gpg_raise_callback_exception(self) +                    else: +                        raise +                if len(result) == 0: +                    break +                chunks.append(result) +            return b''.join(chunks) + +def pubkey_algo_string(subkey): +    """Return short algorithm string + +    Return a public key algorithm string (e.g. "rsa2048") for a given +    SUBKEY. + +    Returns: +    algo      - a string + +    """ +    return gpgme.gpgme_pubkey_algo_string(subkey) + +def pubkey_algo_name(algo): +    """Return name of public key algorithm + +    Return the name of the public key algorithm for a given numeric +    algorithm id ALGO (cf. RFC4880). + +    Returns: +    algo      - a string + +    """ +    return gpgme.gpgme_pubkey_algo_name(algo) + +def hash_algo_name(algo): +    """Return name of hash algorithm + +    Return the name of the hash algorithm for a given numeric +    algorithm id ALGO (cf. RFC4880). + +    Returns: +    algo      - a string + +    """ +    return gpgme.gpgme_hash_algo_name(algo) + +def get_protocol_name(proto): +    """Get protocol description + +    Get the string describing protocol PROTO. + +    Returns: +    proto     - a string + +    """ +    return gpgme.gpgme_get_protocol_name(proto) + +def addrspec_from_uid(uid): +    """Return the address spec + +    Return the addr-spec (cf. RFC2822 section 4.3) from a user id UID. + +    Returns: +    addr_spec - a string + +    """ +    return gpgme.gpgme_addrspec_from_uid(uid) + +def check_version(version=None): +    return gpgme.gpgme_check_version(version) + +# 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() + +def engine_check_version (proto): +    try: +        errorcheck(gpgme.gpgme_engine_check_version(proto)) +        return True +    except errors.GPGMEError: +        return False + +def get_engine_info(): +    ptr = gpgme.new_gpgme_engine_info_t_p() +    try: +        errorcheck(gpgme.gpgme_get_engine_info(ptr)) +        info = gpgme.gpgme_engine_info_t_p_value(ptr) +    except errors.GPGMEError: +        info = None +    gpgme.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(gpgme.gpgme_set_engine_info(proto, file_name, home_dir)) + +def set_locale(category, value): +    """Sets the default locale used by contexts""" +    errorcheck(gpgme.gpgme_set_locale(None, category, value)) + +def wait(hang): +    """Wait for asynchronous call on any Context  to finish. +    Wait forever if hang is True. + +    For finished anynch calls it returns a tuple (status, context): +        status  - status return by asnynchronous call. +        context - context which caused this call to return. + +    Please read the GPGME manual of more information.""" +    ptr = gpgme.new_gpgme_error_t_p() +    context = gpgme.gpgme_wait(None, ptr, hang) +    status = gpgme.gpgme_error_t_p_value(ptr) +    gpgme.delete_gpgme_error_t_p(ptr) +    if context == None: +        errorcheck(status) +    else: +        context = Context(context) +    return (status, context) diff --git a/lang/python/src/errors.py b/lang/python/src/errors.py new file mode 100644 index 00000000..1ce139e8 --- /dev/null +++ b/lang/python/src/errors.py @@ -0,0 +1,128 @@ +# Copyright (C) 2004 Igor Belyi <[email protected]> +# Copyright (C) 2002 John Goerzen <[email protected]> +# +#    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 + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +from . import gpgme +from . import util + +# To appease static analysis tools, we define some constants here. +# They are overwritten with the proper values by process_constants. +NO_ERROR = None +EOF = None + +util.process_constants('GPG_ERR_', globals()) +del util + +class GpgError(Exception): +    pass + +class GPGMEError(GpgError): +    def __init__(self, error = None, message = None): +        self.error = error +        self.message = message + +    @classmethod +    def fromSyserror(cls): +        return cls(gpgme.gpgme_err_code_from_syserror()) + +    def getstring(self): +        message = "%s: %s" % (gpgme.gpgme_strsource(self.error), +                              gpgme.gpgme_strerror(self.error)) +        if self.message != None: +            message = "%s: %s" % (self.message, message) +        return message + +    def getcode(self): +        return gpgme.gpgme_err_code(self.error) + +    def getsource(self): +        return gpgme.gpgme_err_source(self.error) + +    def __str__(self): +        return self.getstring() + +def errorcheck(retval, extradata = None): +    if retval: +        raise GPGMEError(retval, extradata) + +class KeyNotFound(GPGMEError, KeyError): +    """Raised if a key was not found + +    GPGME indicates this condition with EOF, which is not very +    idiomatic.  We raise this error that is both a GPGMEError +    indicating EOF, and a KeyError. + +    """ +    def __init__(self, keystr): +        self.keystr = keystr +        GPGMEError.__init__(self, EOF) +    def __str__(self): +        return self.keystr + +# These errors are raised in the idiomatic interface code. + +class EncryptionError(GpgError): +    pass + +class InvalidRecipients(EncryptionError): +    def __init__(self, recipients): +        self.recipients = recipients +    def __str__(self): +        return ", ".join("{}: {}".format(r.fpr, +                                         gpgme.gpgme_strerror(r.reason)) +                         for r in self.recipients) + +class DeryptionError(GpgError): +    pass + +class UnsupportedAlgorithm(DeryptionError): +    def __init__(self, algorithm): +        self.algorithm = algorithm +    def __str__(self): +        return self.algorithm + +class SigningError(GpgError): +    pass + +class InvalidSigners(SigningError): +    def __init__(self, signers): +        self.signers = signers +    def __str__(self): +        return ", ".join("{}: {}".format(s.fpr, +                                         gpgme.gpgme_strerror(s.reason)) +                         for s in self.signers) + +class VerificationError(GpgError): +    pass + +class BadSignatures(VerificationError): +    def __init__(self, result): +        self.result = result +    def __str__(self): +        return ", ".join("{}: {}".format(s.fpr, +                                         gpgme.gpgme_strerror(s.status)) +                         for s in self.result.signatures +                         if s.status != NO_ERROR) + +class MissingSignatures(VerificationError): +    def __init__(self, result, missing): +        self.result = result +        self.missing = missing +    def __str__(self): +        return ", ".join(k.subkeys[0].fpr for k in self.missing) diff --git a/lang/python/src/results.py b/lang/python/src/results.py new file mode 100644 index 00000000..46ebeec6 --- /dev/null +++ b/lang/python/src/results.py @@ -0,0 +1,118 @@ +# 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 <http://www.gnu.org/licenses/>. + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +"""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(): +            if hasattr(fragile, key): +                setattr(self, key, func(getattr(fragile, key))) + +        for key, func in self._map.items(): +            if hasattr(fragile, key): +                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 __repr__(self): +        return '{}({})'.format( +            self.__class__.__name__, +            ', '.join('{}={!r}'.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 Signature(Result): +    _type = dict(wrong_key_usage=bool, chain_model=bool) +    _map = dict(notations=Notation) + +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 + +class EngineInfo(Result): +    pass diff --git a/lang/python/src/util.py b/lang/python/src/util.py new file mode 100644 index 00000000..e4fca4c1 --- /dev/null +++ b/lang/python/src/util.py @@ -0,0 +1,53 @@ +# Copyright (C) 2016 g10 Code GmbH +# Copyright (C) 2004,2008 Igor Belyi <[email protected]> +# Copyright (C) 2002 John Goerzen <[email protected]> +# +#    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 + +from __future__ import absolute_import, print_function, unicode_literals +del absolute_import, print_function, unicode_literals + +import sys + +def process_constants(prefix, scope): +    """Called by the constant modules to load up the constants from the C +    library starting with PREFIX.  Matching constants will be inserted +    into SCOPE with PREFIX stripped from the names.  Returns the names +    of inserted constants. + +    """ +    from . import gpgme +    index = len(prefix) +    constants = {identifier[index:]: getattr(gpgme, identifier) +                 for identifier in dir(gpgme) +                 if identifier.startswith(prefix)} +    scope.update(constants) +    return list(constants.keys()) + +def percent_escape(s): +    return ''.join( +        '%{0:2x}'.format(ord(c)) +        if c == '+' or c == '"' or c == '%' or ord(c) <= 0x20 else c +        for c in s) + +# Python2/3 compatibility +if sys.version_info[0] == 3: +    # Python3 +    def is_a_string(x): +        return isinstance(x, str) +else: +    # Python2 +    def is_a_string(x): +        return isinstance(x, basestring) | 
