diff options
Diffstat (limited to 'lang/python/pyme')
21 files changed, 1097 insertions, 0 deletions
diff --git a/lang/python/pyme/__init__.py b/lang/python/pyme/__init__.py new file mode 100644 index 00000000..7716e51c --- /dev/null +++ b/lang/python/pyme/__init__.py @@ -0,0 +1,139 @@ +# $Id$ +""" +Pyme: GPGME Interface for Python +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 + +Welcome to PyME, the GPGME Interface for Python. "Pyme", when prounced, +rhymes with "Pine". + +The latest release of this package may be obtained from +http://pyme.sourceforge.net +Previous releases of this package can be obtained from +http://quux.org/devel/pyme/ + +FEATURES +-------- + + * Feature-rich, full implementation of the GPGME library. Supports + all GPGME features except interactive editing (coming soon). + Callback functions may be written in pure Python. + + * 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. + +GENERAL OVERVIEW +---------------- + +For those of you familiar with GPGME, you will be right at home here. + +Pyme 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 pyme.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. + +QUICK START SAMPLE PROGRAM +-------------------------- +This program is not for serious encryption, but for example purposes only! + +import sys +import os +from pyme import core, constants + +# Set up our input and output buffers. + +plain = core.Data('This is my message.') +cipher = core.Data() + +# Initialize our context. + +c = core.Context() +c.set_armor(1) + +# Set up the recipients. + +sys.stdout.write("Enter name of your recipient: ") +sys.stdout.flush() +name = sys.stdin.readline().strip() +c.op_keylist_start(name, 0) +r = c.op_keylist_next() + +# Do the encryption. + +c.op_encrypt([r], 1, plain, cipher) +cipher.seek(0, os.SEEK_SET) +sys.stdout.buffer.write(cipher.read()) + +Note that although there is no explicit error checking done here, the +Python GPGME library is automatically doing error-checking, and will +raise an exception if there is any problem. + +This program is in the Pyme distribution as examples/simple.py. The examples +directory contains more advanced samples as well. + +FOR MORE INFORMATION +-------------------- +PYME homepage: http://pyme.sourceforge.net +GPGME documentation: http://pyme.sourceforge.net/doc/gpgme/index.html +GPGME homepage: http://www.gnupg.org/gpgme.html + +Base classes: pyme.core (START HERE!) +Error classes: pyme.errors +Constants: pyme.constants +Version information: pyme.version +Utilities: pyme.util + +Base classes are documented at pyme.core. +Classes of pyme.util usually are not instantiated by users +directly but return by methods of base classes. + +""" + +__all__ = ['core', 'errors', 'constants', 'util', 'callbacks', 'version'] diff --git a/lang/python/pyme/callbacks.py b/lang/python/pyme/callbacks.py new file mode 100644 index 00000000..3a507b99 --- /dev/null +++ b/lang/python/pyme/callbacks.py @@ -0,0 +1,47 @@ +# $Id$ +# 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 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/pyme/constants/__init__.py b/lang/python/pyme/constants/__init__.py new file mode 100644 index 00000000..b557da87 --- /dev/null +++ b/lang/python/pyme/constants/__init__.py @@ -0,0 +1,7 @@ +# $Id$ + +from pyme import util +util.process_constants('GPGME_', globals()) + +__all__ = ['data', 'event', 'import', 'keylist', 'md', 'pk', + 'protocol', 'sig', 'sigsum', 'status', 'validity'] diff --git a/lang/python/pyme/constants/data/__init__.py b/lang/python/pyme/constants/data/__init__.py new file mode 100644 index 00000000..f172e0c7 --- /dev/null +++ b/lang/python/pyme/constants/data/__init__.py @@ -0,0 +1,4 @@ +# $Id$ + +from . import encoding +__all__ = ['encoding'] diff --git a/lang/python/pyme/constants/data/encoding.py b/lang/python/pyme/constants/data/encoding.py new file mode 100644 index 00000000..d1485ad4 --- /dev/null +++ b/lang/python/pyme/constants/data/encoding.py @@ -0,0 +1,20 @@ +# $Id$ +# 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 pyme import util +util.process_constants('GPGME_DATA_ENCODING_', globals()) diff --git a/lang/python/pyme/constants/event.py b/lang/python/pyme/constants/event.py new file mode 100644 index 00000000..1a4fac6e --- /dev/null +++ b/lang/python/pyme/constants/event.py @@ -0,0 +1,20 @@ +# $Id$ +# 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 pyme import util +util.process_constants('GPGME_EVENT_', globals()) diff --git a/lang/python/pyme/constants/import.py b/lang/python/pyme/constants/import.py new file mode 100644 index 00000000..628177d8 --- /dev/null +++ b/lang/python/pyme/constants/import.py @@ -0,0 +1,20 @@ +# $Id$ +# 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 pyme import util +util.process_constants('GPGME_IMPORT_', globals()) diff --git a/lang/python/pyme/constants/keylist/__init__.py b/lang/python/pyme/constants/keylist/__init__.py new file mode 100644 index 00000000..2f2152a5 --- /dev/null +++ b/lang/python/pyme/constants/keylist/__init__.py @@ -0,0 +1,4 @@ +# $Id$ + +from . import mode +__all__ = ['mode'] diff --git a/lang/python/pyme/constants/keylist/mode.py b/lang/python/pyme/constants/keylist/mode.py new file mode 100644 index 00000000..137ce17a --- /dev/null +++ b/lang/python/pyme/constants/keylist/mode.py @@ -0,0 +1,20 @@ +# $Id$ +# 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 pyme import util +util.process_constants('GPGME_KEYLIST_MODE_', globals()) diff --git a/lang/python/pyme/constants/md.py b/lang/python/pyme/constants/md.py new file mode 100644 index 00000000..2db01a52 --- /dev/null +++ b/lang/python/pyme/constants/md.py @@ -0,0 +1,20 @@ +# $Id$ +# 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 pyme import util +util.process_constants('GPGME_MD_', globals()) diff --git a/lang/python/pyme/constants/pk.py b/lang/python/pyme/constants/pk.py new file mode 100644 index 00000000..5f39235a --- /dev/null +++ b/lang/python/pyme/constants/pk.py @@ -0,0 +1,20 @@ +# $Id$ +# 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 pyme import util +util.process_constants('GPGME_PK_', globals()) diff --git a/lang/python/pyme/constants/protocol.py b/lang/python/pyme/constants/protocol.py new file mode 100644 index 00000000..3d3c790a --- /dev/null +++ b/lang/python/pyme/constants/protocol.py @@ -0,0 +1,20 @@ +# $Id$ +# 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 pyme import util +util.process_constants('GPGME_PROTOCOL_', globals()) diff --git a/lang/python/pyme/constants/sig/__init__.py b/lang/python/pyme/constants/sig/__init__.py new file mode 100644 index 00000000..2f2152a5 --- /dev/null +++ b/lang/python/pyme/constants/sig/__init__.py @@ -0,0 +1,4 @@ +# $Id$ + +from . import mode +__all__ = ['mode'] diff --git a/lang/python/pyme/constants/sig/mode.py b/lang/python/pyme/constants/sig/mode.py new file mode 100644 index 00000000..fa090ab9 --- /dev/null +++ b/lang/python/pyme/constants/sig/mode.py @@ -0,0 +1,20 @@ +# $Id$ +# 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 pyme import util +util.process_constants('GPGME_SIG_MODE_', globals()) diff --git a/lang/python/pyme/constants/sigsum.py b/lang/python/pyme/constants/sigsum.py new file mode 100644 index 00000000..7be40ae6 --- /dev/null +++ b/lang/python/pyme/constants/sigsum.py @@ -0,0 +1,20 @@ +# $Id$ +# 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 pyme import util +util.process_constants('GPGME_SIGSUM_', globals()) diff --git a/lang/python/pyme/constants/status.py b/lang/python/pyme/constants/status.py new file mode 100644 index 00000000..60c0c90d --- /dev/null +++ b/lang/python/pyme/constants/status.py @@ -0,0 +1,20 @@ +# $Id$ +# 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 pyme import util +util.process_constants('GPGME_STATUS_', globals()) diff --git a/lang/python/pyme/constants/validity.py b/lang/python/pyme/constants/validity.py new file mode 100644 index 00000000..9590b27a --- /dev/null +++ b/lang/python/pyme/constants/validity.py @@ -0,0 +1,20 @@ +# $Id$ +# 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 pyme import util +util.process_constants('GPGME_VALIDITY_', globals()) diff --git a/lang/python/pyme/core.py b/lang/python/pyme/core.py new file mode 100644 index 00000000..2a0ca072 --- /dev/null +++ b/lang/python/pyme/core.py @@ -0,0 +1,482 @@ +# $Id$ +# 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 + +# import generators for portability with python2.2 + + +from . import pygpgme +from .errors import errorcheck, GPGMEError +from . import errors +from .util import GpgmeWrapper + +class Context(GpgmeWrapper): + """From the GPGME C documentation: + + * 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. + + Thus, this is the place that you will usually start.""" + + def _getctype(self): + return 'gpgme_ctx_t' + + def _getnameprepend(self): + return 'gpgme_' + + def _errorcheck(self, name): + """This function should list all functions returning gpgme_error_t""" + if (name.startswith('gpgme_op_') and \ + not name.endswith('_result')) or \ + name == 'gpgme_signers_add' or \ + name == 'gpgme_set_locale' or \ + name == 'gpgme_set_keylist_mode' or \ + name == 'gpgme_set_protocol': + return 1 + return 0 + + def __init__(self, wrapped=None): + if wrapped: + self.wrapped = wrapped + self.own = False + else: + tmp = pygpgme.new_gpgme_ctx_t_p() + errorcheck(pygpgme.gpgme_new(tmp)) + self.wrapped = pygpgme.gpgme_ctx_t_p_value(tmp) + pygpgme.delete_gpgme_ctx_t_p(tmp) + self.own = True + self.last_passcb = None + self.last_progresscb = None + + def __del__(self): + if not pygpgme: + # At interpreter shutdown, pygpgme is set to NONE. + return + + self._free_passcb() + self._free_progresscb() + if self.own and pygpgme.gpgme_release: + pygpgme.gpgme_release(self.wrapped) + + def _free_passcb(self): + if self.last_passcb != None: + if pygpgme.pygpgme_clear_generic_cb: + pygpgme.pygpgme_clear_generic_cb(self.last_passcb) + if pygpgme.delete_PyObject_p_p: + pygpgme.delete_PyObject_p_p(self.last_passcb) + self.last_passcb = None + + def _free_progresscb(self): + if self.last_progresscb != None: + if pygpgme.pygpgme_clear_generic_cb: + pygpgme.pygpgme_clear_generic_cb(self.last_progresscb) + if pygpgme.delete_PyObject_p_p: + pygpgme.delete_PyObject_p_p(self.last_progresscb) + self.last_progresscb = None + + def op_keylist_all(self, *args, **kwargs): + self.op_keylist_start(*args, **kwargs) + key = self.op_keylist_next() + while key: + yield key + key = self.op_keylist_next() + + def op_keylist_next(self): + """Returns the next key in the list created + by a call to op_keylist_start(). The object returned + is of type Key.""" + ptr = pygpgme.new_gpgme_key_t_p() + try: + errorcheck(pygpgme.gpgme_op_keylist_next(self.wrapped, ptr)) + key = pygpgme.gpgme_key_t_p_value(ptr) + except errors.GPGMEError as excp: + key = None + if excp.getcode() != errors.EOF: + raise excp + pygpgme.delete_gpgme_key_t_p(ptr) + if key: + key.__del__ = lambda self: pygpgme.gpgme_key_unref(self) + return key + + def get_key(self, fpr, secret): + """Return the key corresponding to the fingerprint 'fpr'""" + ptr = pygpgme.new_gpgme_key_t_p() + errorcheck(pygpgme.gpgme_get_key(self.wrapped, fpr, ptr, secret)) + key = pygpgme.gpgme_key_t_p_value(ptr) + pygpgme.delete_gpgme_key_t_p(ptr) + if key: + key.__del__ = lambda self: pygpgme.gpgme_key_unref(self) + return key + + def op_trustlist_all(self, *args, **kwargs): + self.op_trustlist_start(*args, **kwargs) + trust = self.ctx.op_trustlist_next() + while trust: + yield trust + trust = self.ctx.op_trustlist_next() + + def op_trustlist_next(self): + """Returns the next trust item in the list created + by a call to op_trustlist_start(). The object returned + is of type TrustItem.""" + ptr = pygpgme.new_gpgme_trust_item_t_p() + try: + errorcheck(pygpgme.gpgme_op_trustlist_next(self.wrapped, ptr)) + trust = pygpgme.gpgme_trust_item_t_p_value(ptr) + except errors.GPGMEError as excp: + trust = None + if excp.getcode() != errors.EOF: + raise + pygpgme.delete_gpgme_trust_item_t_p(ptr) + return trust + + def set_passphrase_cb(self, func, hook=None): + """Sets the passphrase callback to the function specified by func. + + When the system needs a passphrase, it will call func with three args: + hint, a string describing the key it needs the passphrase for; + desc, a string describing the passphrase it needs; + prev_bad, a boolean equal True if this is a call made after + unsuccessful previous attempt. + + If hook has a value other than None it will be passed into the func + as a forth argument. + + Please see the GPGME manual for more information. + """ + self._free_passcb() + if func == None: + hookdata = None + else: + self.last_passcb = pygpgme.new_PyObject_p_p() + if hook == None: + hookdata = func + else: + hookdata = (func, hook) + pygpgme.pygpgme_set_passphrase_cb(self.wrapped, hookdata, self.last_passcb) + + def set_progress_cb(self, func, hook=None): + """Sets the progress meter callback to the function specified by + + This function will be called to provide an interactive update of + the system's progress. + + Please see the GPGME manual for more information.""" + self._free_progresscb() + if func == None: + hookdata = None + else: + self.last_progresscb = pygpgme.new_PyObject_p_p() + if hook == None: + hookdata = func + else: + hookdata = (func, hook) + pygpgme.pygpgme_set_progress_cb(self.wrapped, hookdata, self.last_progresscb) + + def get_engine_info(self): + """Returns this context specific engine info""" + return pygpgme.gpgme_ctx_get_engine_info(self.wrapped) + + def set_engine_info(self, proto, file_name, home_dir=None): + """Changes the configuration of the crypto engine implementing the + protocol 'proto' for the context. 'file_name' is the file name of + the executable program implementing this protocol. 'home_dir' is the + directory name of the configuration directory (engine's default is + used if omitted).""" + errorcheck(pygpgme.gpgme_ctx_set_engine_info(self.wrapped, proto, file_name, home_dir)) + + def wait(self, hang): + """Wait for asynchronous call to finish. Wait forever if hang is True + + Return: + On an async call completion its return status. + On timeout - None. + + Please read the GPGME manual for more information.""" + ptr = pygpgme.new_gpgme_error_t_p() + context = pygpgme.gpgme_wait(self.wrapped, ptr, hang) + status = pygpgme.gpgme_error_t_p_value(ptr) + pygpgme.delete_gpgme_error_t_p(ptr) + + if context == None: + errorcheck(status) + return None + else: + return status + + def op_edit(self, key, func, fnc_value, out): + """Start key editing using supplied callback function""" + if key == None: + raise ValueError("op_edit: First argument cannot be None") + opaquedata = (func, fnc_value) + errorcheck(pygpgme.gpgme_op_edit(self.wrapped, key, opaquedata, out)) + +class Data(GpgmeWrapper): + """From the GPGME C manual: + +* 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.""" + + def _getctype(self): + return 'gpgme_data_t' + + def _getnameprepend(self): + return 'gpgme_data_' + + def _errorcheck(self, name): + """This function should list all functions returning gpgme_error_t""" + if name == 'gpgme_data_release_and_get_mem' or \ + name == 'gpgme_data_get_encoding' or \ + name == 'gpgme_data_seek': + return 0 + return 1 + + def __init__(self, string = None, file = None, offset = None, + length = None, cbs = None): + """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 func is a callback function taking two arguments (count, + hook) and returning a string of read data, or None on EOF. + This will supply the read() method for the system. + + If file is specified without any other arguments, then + it must be a filename, and the object will be initialized from + that file. + + Any other use will result in undefined or erroneous behavior.""" + self.wrapped = None + self.last_readcb = None + + if cbs != None: + self.new_from_cbs(*cbs) + elif string != None: + self.new_from_mem(string) + elif file != None and offset != None and length != None: + self.new_from_filepart(file, offset, length) + elif file != None: + if type(file) == type("x"): + self.new_from_file(file) + else: + self.new_from_fd(file) + else: + self.new() + + def __del__(self): + if not pygpgme: + # At interpreter shutdown, pygpgme is set to NONE. + return + + if self.wrapped != None and pygpgme.gpgme_data_release: + pygpgme.gpgme_data_release(self.wrapped) + self._free_readcb() + + def _free_readcb(self): + if self.last_readcb != None: + if pygpgme.pygpgme_clear_generic_cb: + pygpgme.pygpgme_clear_generic_cb(self.last_readcb) + if pygpgme.delete_PyObject_p_p: + pygpgme.delete_PyObject_p_p(self.last_readcb) + self.last_readcb = None + + def new(self): + tmp = pygpgme.new_gpgme_data_t_p() + errorcheck(pygpgme.gpgme_data_new(tmp)) + self.wrapped = pygpgme.gpgme_data_t_p_value(tmp) + pygpgme.delete_gpgme_data_t_p(tmp) + + def new_from_mem(self, string, copy = 1): + tmp = pygpgme.new_gpgme_data_t_p() + errorcheck(pygpgme.gpgme_data_new_from_mem(tmp,string,len(string),copy)) + self.wrapped = pygpgme.gpgme_data_t_p_value(tmp) + pygpgme.delete_gpgme_data_t_p(tmp) + + def new_from_file(self, filename, copy = 1): + tmp = pygpgme.new_gpgme_data_t_p() + errorcheck(pygpgme.gpgme_data_new_from_file(tmp, filename, copy)) + self.wrapped = pygpgme.gpgme_data_t_p_value(tmp) + pygpgme.delete_gpgme_data_t_p(tmp) + + def new_from_cbs(self, funcs, hook): + """Argument funcs must be a 4 element tuple with callbacks: + (read_cb, write_cb, seek_cb, release_cb)""" + tmp = pygpgme.new_gpgme_data_t_p() + self._free_readcb() + self.last_readcb = pygpgme.new_PyObject_p_p() + hookdata = (funcs, hook) + pygpgme.pygpgme_data_new_from_cbs(tmp, hookdata, self.last_readcb) + self.wrapped = pygpgme.gpgme_data_t_p_value(tmp) + pygpgme.delete_gpgme_data_t_p(tmp) + + def new_from_filepart(self, file, offset, length): + """This wraps the GPGME gpgme_data_new_from_filepart() function. + The argument "file" may be: + + 1. a string specifying a file name, or + 3. a a file-like object. supporting the fileno() call and the mode + attribute.""" + + tmp = pygpgme.new_gpgme_data_t_p() + filename = None + fp = None + + if type(file) == type("x"): + filename = file + else: + fp = pygpgme.fdopen(file.fileno(), file.mode) + if fp == None: + raise ValueError("Failed to open file from %s arg %s" % \ + (str(type(file)), str(file))) + + errorcheck(pygpgme.gpgme_data_new_from_filepart(tmp, filename, fp, + offset, length)) + self.wrapped = pygpgme.gpgme_data_t_p_value(tmp) + pygpgme.delete_gpgme_data_t_p(tmp) + + def new_from_fd(self, file): + """This wraps the GPGME gpgme_data_new_from_fd() function. + The argument "file" may be a file-like object, supporting the fileno() + call and the mode attribute.""" + + tmp = pygpgme.new_gpgme_data_t_p() + fp = pygpgme.fdopen(file.fileno(), file.mode) + if fp == None: + raise ValueError("Failed to open file from %s arg %s" % \ + (str(type(file)), str(file))) + errorcheck(pygpgme.gpgme_data_new_from_fd(tmp, fp)) + self.wrapped = pygpgme.gpgme_data_t_p_value(tmp) + pygpgme.delete_gpgme_data_t_p(tmp) + + def new_from_stream(self, file): + """This wrap around gpgme_data_new_from_stream is an alias for + new_from_fd() method since in python there's not difference + between file stream and file descriptor""" + self.new_from_fd(file) + + def write(self, buffer): + """Write buffer given as string or bytes. + + If a string is given, it is implicitly encoded using UTF-8.""" + written = pygpgme.gpgme_data_write(self.wrapped, buffer) + if written < 0: + 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: + return pygpgme.gpgme_data_read(self.wrapped, size) + else: + chunks = [] + while 1: + result = pygpgme.gpgme_data_read(self.wrapped, 4096) + if len(result) == 0: + break + chunks.append(result) + return b''.join(chunks) + +def pubkey_algo_name(algo): + return pygpgme.gpgme_pubkey_algo_name(algo) + +def hash_algo_name(algo): + return pygpgme.gpgme_hash_algo_name(algo) + +def get_protocol_name(proto): + return pygpgme.gpgme_get_protocol_name(proto) + +def check_version(version=None): + return pygpgme.gpgme_check_version(version) + +def engine_check_version (proto): + try: + errorcheck(pygpgme.gpgme_engine_check_version(proto)) + return True + except errors.GPGMEError: + return False + +def get_engine_info(): + ptr = pygpgme.new_gpgme_engine_info_t_p() + try: + errorcheck(pygpgme.gpgme_get_engine_info(ptr)) + info = pygpgme.gpgme_engine_info_t_p_value(ptr) + except errors.GPGMEError: + info = None + pygpgme.delete_gpgme_engine_info_t_p(ptr) + return info + +def set_engine_info(proto, file_name, home_dir=None): + """Changes the default configuration of the crypto engine implementing + the protocol 'proto'. 'file_name' is the file name of + the executable program implementing this protocol. 'home_dir' is the + directory name of the configuration directory (engine's default is + used if omitted).""" + errorcheck(pygpgme.gpgme_set_engine_info(proto, file_name, home_dir)) + +def set_locale(category, value): + """Sets the default locale used by contexts""" + errorcheck(pygpgme.gpgme_set_locale(None, category, value)) + +def wait(hang): + """Wait for asynchronous call on any Context to finish. + Wait forever if hang is True. + + 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 = pygpgme.new_gpgme_error_t_p() + context = pygpgme.gpgme_wait(None, ptr, hang) + status = pygpgme.gpgme_error_t_p_value(ptr) + pygpgme.delete_gpgme_error_t_p(ptr) + if context == None: + errorcheck(status) + else: + context = Context(context) + return (status, context) diff --git a/lang/python/pyme/errors.py b/lang/python/pyme/errors.py new file mode 100644 index 00000000..5c8f9223 --- /dev/null +++ b/lang/python/pyme/errors.py @@ -0,0 +1,50 @@ +# $Id$ +# 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 . import pygpgme + +class GPGMEError(Exception): + def __init__(self, error = None, message = None): + self.error = error + self.message = message + + @classmethod + def fromSyserror(cls): + return cls(pygpgme.gpgme_err_code_from_syserror()) + + def getstring(self): + message = "%s: %s" % (pygpgme.gpgme_strsource(self.error), + pygpgme.gpgme_strerror(self.error)) + if self.message != None: + message = "%s: %s" % (self.message, message) + return message + + def getcode(self): + return pygpgme.gpgme_err_code(self.error) + + def getsource(self): + return pygpgme.gpgme_err_source(self.error) + + def __str__(self): + return "%s (%d,%d)"%(self.getstring(), self.getsource(), self.getcode()) + +EOF = getattr(pygpgme, "EOF") + +def errorcheck(retval, extradata = None): + if retval: + raise GPGMEError(retval, extradata) diff --git a/lang/python/pyme/util.py b/lang/python/pyme/util.py new file mode 100644 index 00000000..3c34c79e --- /dev/null +++ b/lang/python/pyme/util.py @@ -0,0 +1,97 @@ +# $Id$ +# 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 . import pygpgme +from .errors import errorcheck + +def process_constants(starttext, dict): + """Called by the constant libraries to load up the appropriate constants + from the C library.""" + index = len(starttext) + for identifier in dir(pygpgme): + if not identifier.startswith(starttext): + continue + name = identifier[index:] + dict[name] = getattr(pygpgme, identifier) + +class GpgmeWrapper(object): + """Base class all Pyme wrappers for GPGME functionality. Not to be + instantiated directly.""" + def __repr__(self): + return '<instance of %s.%s with GPG object at %s>' % \ + (__name__, self.__class__.__name__, + self.wrapped) + + def __str__(self): + return repr(self) + + def __hash__(self): + return hash(repr(self.wrapped)) + + def __eq__(self, other): + if other == None: + return False + else: + return repr(self.wrapped) == repr(other.wrapped) + + def _getctype(self): + """Must be implemented by child classes. + + Must return the name of the c type.""" + raise NotImplementedError() + + def _getnameprepend(self): + """Must be implemented by child classes. + + Must return the prefix of all c functions mapped to methods of + this class.""" + raise NotImplementedError() + + def _errorcheck(self, name): + """Must be implemented by child classes. + + This function must return a trueish value for all c functions + returning gpgme_error_t.""" + raise NotImplementedError() + + def __getattr__(self, key): + """On-the-fly function generation.""" + if key[0] == '_' or self._getnameprepend() == None: + return None + name = self._getnameprepend() + key + func = getattr(pygpgme, name) + + if self._errorcheck(name): + def _funcwrap(slf, *args, **kwargs): + return errorcheck(func(slf.wrapped, *args, **kwargs), + "Invocation of " + name) + else: + def _funcwrap(slf, *args, **kwargs): + return func(slf.wrapped, *args, **kwargs) + + _funcwrap.__doc__ = getattr(func, "__doc__") + + # Monkey-patch the class. + setattr(self.__class__, key, _funcwrap) + + # Bind the method to 'self'. + def wrapper(*args, **kwargs): + return _funcwrap(self, *args, **kwargs) + _funcwrap.__doc__ = getattr(func, "__doc__") + + return wrapper diff --git a/lang/python/pyme/version.py b/lang/python/pyme/version.py new file mode 100644 index 00000000..3dd8d3ac --- /dev/null +++ b/lang/python/pyme/version.py @@ -0,0 +1,43 @@ +# $Id$ + +productname = 'pyme' +versionstr = "0.9.1" +revno = int('$Rev: 281 $'[6:-2]) +revstr = "Rev %d" % revno +datestr = '$Date$' + +versionlist = versionstr.split(".") +major = versionlist[0] +minor = versionlist[1] +patch = versionlist[2] +copyright = "Copyright (C) 2015 Ben McGinnes, 2014-2015 Martin Albrecht, 2004-2008 Igor Belyi, 2002 John Goerzen" +author = "Ben McGinnes" +author_email = "[email protected]" +description = "Python 3 support for GPGME GnuPG cryptography library" +bigcopyright = """%(productname)s %(versionstr)s (%(revstr)s) +%(copyright)s <%(author_email)s>""" % locals() + +banner = bigcopyright + """ +This software comes with ABSOLUTELY NO WARRANTY; see the file +COPYING for details. This is free software, and you are welcome +to distribute it under the conditions laid out in COPYING.""" + +homepage = "https://gnupg.org" +license = """Copyright (C) 2015 Ben McGinnes <[email protected]> +Copyright (C) 2014, 2015 Martin Albrecht <[email protected]> +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""" |