Imported Upstream version 0.1 upstream upstream/0.1
authorAnas Nashif <anas.nashif@intel.com>
Tue, 19 Feb 2013 16:20:13 +0000 (08:20 -0800)
committerAnas Nashif <anas.nashif@intel.com>
Tue, 19 Feb 2013 16:20:13 +0000 (08:20 -0800)
40 files changed:
MANIFEST.in [new file with mode: 0644]
Makefile [new file with mode: 0644]
PKG-INFO [new file with mode: 0644]
README [new file with mode: 0644]
gpgme/__init__.py [new file with mode: 0644]
gpgme/editutil.py [new file with mode: 0644]
gpgme/tests/__init__.py [new file with mode: 0644]
gpgme/tests/keys/key1.pub [new file with mode: 0644]
gpgme/tests/keys/key1.sec [new file with mode: 0644]
gpgme/tests/keys/key2.pub [new file with mode: 0644]
gpgme/tests/keys/key2.sec [new file with mode: 0644]
gpgme/tests/keys/passphrase.pub [new file with mode: 0644]
gpgme/tests/keys/passphrase.sec [new file with mode: 0644]
gpgme/tests/keys/revoked.pub [new file with mode: 0644]
gpgme/tests/keys/signonly.pub [new file with mode: 0644]
gpgme/tests/keys/signonly.sec [new file with mode: 0644]
gpgme/tests/test_context.py [new file with mode: 0644]
gpgme/tests/test_delete.py [new file with mode: 0644]
gpgme/tests/test_editkey.py [new file with mode: 0644]
gpgme/tests/test_encrypt_decrypt.py [new file with mode: 0644]
gpgme/tests/test_export.py [new file with mode: 0644]
gpgme/tests/test_import.py [new file with mode: 0644]
gpgme/tests/test_keylist.py [new file with mode: 0644]
gpgme/tests/test_keys.py [new file with mode: 0644]
gpgme/tests/test_passphrase.py [new file with mode: 0644]
gpgme/tests/test_progress.py [new file with mode: 0644]
gpgme/tests/test_sign_verify.py [new file with mode: 0644]
gpgme/tests/util.py [new file with mode: 0644]
setup.py [new file with mode: 0755]
src/gpgme.c [new file with mode: 0644]
src/pygpgme-constants.c [new file with mode: 0644]
src/pygpgme-context.c [new file with mode: 0644]
src/pygpgme-data.c [new file with mode: 0644]
src/pygpgme-error.c [new file with mode: 0644]
src/pygpgme-import.c [new file with mode: 0644]
src/pygpgme-key.c [new file with mode: 0644]
src/pygpgme-keyiter.c [new file with mode: 0644]
src/pygpgme-signature.c [new file with mode: 0644]
src/pygpgme.h [new file with mode: 0644]
test_all.py [new file with mode: 0644]

diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644 (file)
index 0000000..255566b
--- /dev/null
@@ -0,0 +1,10 @@
+include Makefile
+include MANIFEST.in
+include test_all.py
+
+recursive-include gpgme *.py
+include gpgme/tests/keys/*.pub
+include gpgme/tests/keys/*.sec
+
+include src/*.c
+include src/*.h
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..4922048
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,15 @@
+PYTHON = python2.4
+
+build:
+       $(PYTHON) setup.py build_ext -i
+
+check: build
+       $(PYTHON) test_all.py -v
+
+clean:
+       $(PYTHON) setup.py clean
+
+dist: build
+       $(PYTHON) setup.py sdist --force-manifest
+
+.PHONY: build check clean dist
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644 (file)
index 0000000..e151bf2
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,22 @@
+Metadata-Version: 1.0
+Name: pygpgme
+Version: 0.1
+Summary: A Python module for working with OpenPGP messages
+Home-page: https://launchpad.net/products/pygpgme
+Author: James Henstridge
+Author-email: james@jamesh.id.au
+License: LGPL
+Description: 
+        PyGPGME is a Python module that lets you sign, verify, encrypt
+        and decrypt messages using the OpenPGP format.
+        
+        It is built on top of the GNU Privacy Guard and the GPGME
+        library.
+Platform: UNKNOWN
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
+Classifier: Operating System :: POSIX
+Classifier: Programming Language :: C
+Classifier: Programming Language :: Python
+Classifier: Topic :: Security :: Cryptography
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..8796112
--- /dev/null
+++ b/README
@@ -0,0 +1,34 @@
+A Python binding for the gpgme library, used to drive the gpg command
+line program.
+
+More information about gpgme can be found here:
+  http://www.gnupg.org/(en)/related_software/gpgme/index.html
+
+This binding stays fairly close to the C API with the following
+exceptions:
+
+ * Memory management is not exposed to the user
+
+ * Functions like gpgme_get_foo()/gpgme_set_foo() are converted to
+   attribute access on gpgme.Context objects.
+
+ * Functions that take gpgme_data_t arguments take arbitrary Python
+   file-like objects.  The read(), write(), seek() and tell() methods
+   may be used on the object.
+
+ * Non-zero gpgme_error_t return values are converted to gpgme.error
+   exceptions.
+
+ * Only the synchronous versions of functions have been wrapped.
+   However, the Python global interpreter lock is dropped, so should
+   play nicely in multi-threaded Python programs.
+
+ * Function pairs like gpgme_op_import()/gpgme_op_import_result() are
+   combined into single method calls.
+
+ * The Python version of gpgme_op_keylist() returns an iterator over
+   the matched keys, rather than requiring the user to use a special
+   iteration function.
+
+This library is licensed under the LGPL, the same license as the gpgme
+library.
diff --git a/gpgme/__init__.py b/gpgme/__init__.py
new file mode 100644 (file)
index 0000000..f26185b
--- /dev/null
@@ -0,0 +1,22 @@
+# pygpgme - a Python wrapper for the gpgme library
+# Copyright (C) 2006  James Henstridge
+#
+# 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+from _gpgme import *
+
+# create constants
+make_constants(globals())
+del make_constants
diff --git a/gpgme/editutil.py b/gpgme/editutil.py
new file mode 100644 (file)
index 0000000..bf6f77f
--- /dev/null
@@ -0,0 +1,188 @@
+# pygpgme - a Python wrapper for the gpgme library
+# Copyright (C) 2006  James Henstridge
+#
+# 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+"""Utilities related to editing keys.
+
+Currently only contains a utility function for editing the owner trust
+value of a key in a keyring.
+"""
+
+__metaclass__ = type
+
+__all__ = ['edit_trust']
+
+import os
+import StringIO
+import gpgme
+
+
+class _EditData:
+    """Simple base class to wrap 'edit key' interactions"""
+
+    STATE_START = 0
+    STATE_ERROR = -1
+
+    def __init__(self):
+        self.state = self.STATE_START
+        self.transitions = {}
+        # a default state transition to try and quit the edit on error
+        self.addTransition(self.STATE_ERROR,
+                           gpgme.STATUS_GET_LINE, 'keyedit.prompt',
+                           self.STATE_ERROR, 'quit\n')
+
+    def addTransition(self, state, status, args, newstate, data):
+        self.transitions[state, status, args] = newstate, data
+
+    def do_edit(self, ctx, key):
+        output = StringIO.StringIO()
+        ctx.edit(key, self.callback, output)
+
+    def callback(self, status, args, fd):
+        if status in (gpgme.STATUS_EOF,
+                      gpgme.STATUS_GOT_IT,
+                      gpgme.STATUS_NEED_PASSPHRASE,
+                      gpgme.STATUS_GOOD_PASSPHRASE,
+                      gpgme.STATUS_BAD_PASSPHRASE,
+                      gpgme.STATUS_USERID_HINT,
+                      gpgme.STATUS_SIGEXPIRED,
+                      gpgme.STATUS_KEYEXPIRED,
+                      gpgme.STATUS_PROGRESS,
+                      gpgme.STATUS_KEY_CREATED,
+                      gpgme.STATUS_ALREADY_SIGNED):
+            return
+
+        #print 'S: %s (%d)' % (args, status)
+
+        if (self.state, status, args) in self.transitions:
+            self.state, data = self.transitions[self.state, status, args]
+            #print 'C: %r' % data
+            if data is not None:
+                os.write(fd, data)
+        else:
+            self.state = STATE_ERROR
+            raise gpgme.error(gpgme.ERR_SOURCE_UNKNOWN, gpgme.ERR_GENERAL)
+
+
+class _EditTrust(_EditData):
+    # states
+    STATE_COMMAND = 1
+    STATE_VALUE   = 2
+    STATE_CONFIRM = 3
+    STATE_QUIT    = 4
+
+    def __init__(self, trust):
+        _EditData.__init__(self)
+
+        self.addTransition(self.STATE_START,
+                           gpgme.STATUS_GET_LINE, 'keyedit.prompt',
+                           self.STATE_COMMAND, 'trust\n')
+
+        self.addTransition(self.STATE_COMMAND,
+                           gpgme.STATUS_GET_LINE, 'edit_ownertrust.value',
+                           self.STATE_VALUE, '%d\n' % trust)
+
+        self.addTransition(self.STATE_VALUE,
+                           gpgme.STATUS_GET_LINE, 'keyedit.prompt',
+                           self.STATE_QUIT, 'quit\n')
+
+        self.addTransition(self.STATE_VALUE,
+                           gpgme.STATUS_GET_BOOL, 'edit_ownertrust.set_ultimate.okay',
+                           self.STATE_CONFIRM, 'Y\n')
+
+        self.addTransition(self.STATE_CONFIRM,
+                           gpgme.STATUS_GET_LINE, 'keyedit.prompt',
+                           self.STATE_QUIT, 'quit\n')
+
+        self.addTransition(self.STATE_QUIT,
+                           gpgme.STATUS_GET_BOOL, 'keyedit.save.okay',
+                           self.STATE_CONFIRM, 'Y\n')
+
+class _EditSign(_EditData):
+    # states
+    STATE_UID = 1
+    STATE_COMMAND = 2
+    STATE_QUIT = 3
+
+    def __init__(self, index, command, expire, check):
+        _EditData.__init__(self)
+
+        self.addTransition(self.STATE_START,
+                           gpgme.STATUS_GET_LINE, 'keyedit.prompt',
+                           self.STATE_UID, 'uid %d\n' % index)
+
+        self.addTransition(self.STATE_UID,
+                           gpgme.STATUS_GET_LINE, 'keyedit.prompt',
+                           self.STATE_COMMAND, '%s\n' % command)
+
+        self.addTransition(self.STATE_COMMAND,
+                           gpgme.STATUS_GET_BOOL, 'keyedit.sign_all.okay',
+                           self.STATE_COMMAND, 'Y\n')
+        self.addTransition(self.STATE_COMMAND,
+                           gpgme.STATUS_GET_LINE, 'sign_uid.expire',
+                           self.STATE_COMMAND, '%s\n' % (expire and 'Y' or 'N'))
+        self.addTransition(self.STATE_COMMAND,
+                           gpgme.STATUS_GET_LINE, 'sign_uid.class',
+                           self.STATE_COMMAND, '%d\n' % check)
+        self.addTransition(self.STATE_COMMAND,
+                           gpgme.STATUS_GET_BOOL, 'sign_uid.okay',
+                           self.STATE_COMMAND, 'Y\n')
+        self.addTransition(self.STATE_COMMAND,
+                           gpgme.STATUS_GET_LINE, 'keyedit.prompt',
+                           self.STATE_QUIT, 'quit\n')
+
+        self.addTransition(self.STATE_QUIT,
+                           gpgme.STATUS_GET_BOOL, 'keyedit.save.okay',
+                           self.STATE_COMMAND, 'Y\n')
+
+
+def edit_trust(ctx, key, trust):
+    if trust not in (gpgme.VALIDITY_UNDEFINED,
+                     gpgme.VALIDITY_NEVER,
+                     gpgme.VALIDITY_MARGINAL,
+                     gpgme.VALIDITY_FULL,
+                     gpgme.VALIDITY_ULTIMATE):
+        raise ValueError('Bad trust value %d' % trust)
+    statemachine = _EditTrust(trust)
+    statemachine.do_edit(ctx, key)
+
+def edit_sign(ctx, key, index=0, local=False, norevoke=False,
+              expire=True, check=0):
+    """Sign the given key.
+
+    index:    the index of the user ID to sign, starting at 1.  Sign all
+               user IDs if set to 0.
+    local:    make a local signature
+    norevoke: make a non-revokable signature
+    command:  the type of signature.  One of sign, lsign, tsign or nrsign.
+    expire:   whether the signature should expire with the key.
+    check:    Amount of checking performed.  One of:
+                 0 - no answer
+                 1 - no checking
+                 2 - casual checking
+                 3 - careful checking
+    """
+    if index < 0 or index > len(key.uids):
+        raise ValueError('user ID index out of range')
+    command = 'sign'
+    if local:
+        command = 'l%s' % command
+    if norevoke:
+        command = 'nr%s' % command
+    if check not in [0, 1, 2, 3]:
+        raise ValueError('check must be one of 0, 1, 2, 3')
+    statemachine = _EditSign(index, command, expire, check)
+    statemachine.do_edit(ctx, key)
diff --git a/gpgme/tests/__init__.py b/gpgme/tests/__init__.py
new file mode 100644 (file)
index 0000000..8c9399f
--- /dev/null
@@ -0,0 +1,44 @@
+# pygpgme - a Python wrapper for the gpgme library
+# Copyright (C) 2006  James Henstridge
+#
+# 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import unittest
+
+def test_suite():
+    import gpgme.tests.test_context
+    import gpgme.tests.test_keys
+    import gpgme.tests.test_keylist
+    import gpgme.tests.test_import
+    import gpgme.tests.test_export
+    import gpgme.tests.test_delete
+    import gpgme.tests.test_sign_verify
+    import gpgme.tests.test_encrypt_decrypt
+    import gpgme.tests.test_passphrase
+    import gpgme.tests.test_progress
+    import gpgme.tests.test_editkey
+    suite = unittest.TestSuite()
+    suite.addTest(gpgme.tests.test_context.test_suite())
+    suite.addTest(gpgme.tests.test_keys.test_suite())
+    suite.addTest(gpgme.tests.test_keylist.test_suite())
+    suite.addTest(gpgme.tests.test_import.test_suite())
+    suite.addTest(gpgme.tests.test_export.test_suite())
+    suite.addTest(gpgme.tests.test_delete.test_suite())
+    suite.addTest(gpgme.tests.test_sign_verify.test_suite())
+    suite.addTest(gpgme.tests.test_encrypt_decrypt.test_suite())
+    suite.addTest(gpgme.tests.test_passphrase.test_suite())
+    suite.addTest(gpgme.tests.test_progress.test_suite())
+    suite.addTest(gpgme.tests.test_editkey.test_suite())
+    return suite
diff --git a/gpgme/tests/keys/key1.pub b/gpgme/tests/keys/key1.pub
new file mode 100644 (file)
index 0000000..7da3774
--- /dev/null
@@ -0,0 +1,29 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+mQGiBEPN6eMRBAC3/jKHCnI9c9fATgGLV4x53wN6ZZoqzcgks4rR9BgrYLvPBzDX
+em74/m5uRMY90hGzJw8kb/rG8ZznJTU/ZE50qCmEEQuTyZar80LEb4ujbqTfZzGK
+v3blpSS+f5ON+2VATiL85roJ7BhpMnhUvS7nk/rSoKFYz+sdf/l3SR76ywCgg+OK
+ISC9UWqI9wlyhs7BpZib6SkEAJ7SzSDHNFI0Uvl5/+QYwy1N9IxCx6po7OJuBFK8
+9XBSbqRnnrlcCko/qTIuPxy9R9g+0JPhi2cNB++f3I/TPz73Xsg7JjkNnqD7aZS7
+MXRlzxESuu7Br+JVwzIERfXDLuA0pnMy+nt1mhuUFxc4dFEzD0DDsbFNWmo57rlr
+t2BZA/4naBrB6L763BMIzebjTCOJ5d0/4pbqlQrP2O+2BxKvkb7OA3ySgXlTpQsb
+Q1E5D+prEI6fTKeqytar9NR2FuOc61kEEmN4ACdEX5FWSYM0grAm68BfJ3Q2IHCV
+pVMF4ExSWlRb05k3TK1HDqjoj8uXMACmz5XAU+69q8ydh3B1LrQYS2V5IDEgPGtl
+eTFAZXhhbXBsZS5vcmc+iF4EExECAB4FAkPN6eMCGwMGCwkIBwMCAxUCAwMWAgEC
+HgECF4AACgkQRrtV8IhcZaSq1QCeObAJFNF3yGlgfV26ACIZyM84gXcAn1yM0T5/
+Qcwkbeu4J/e2PKw/uQ7QuQINBEPN6eoQCACMxxJidY6L1c5zHgU7vZ3yRvnp8tkr
+ShyNtwj6MCgyp9kHK8lgndsIpwXWxfXsSbTPT6wtdH15is7uitHjG1tKphmNgkTH
+KEE62ZMAP5XSfr0ntd9fMxGAqlKcsjdnkC4m/FQzJSFQRnZE9OsGMbACaCtEfc6t
+LU8e3th/tyFCT3nTkfHLYmfVeBWjbsZPoAVcDVAs094E05EmA7lg72PJwzgxyKRM
+4rO1DLgUf9zeXbukYA98SMAPLzf1X9/zY4CfMCicrFkFOkul/maXF7chfxrQqabn
+etu9XvAO/nxE5lJvtHu0hQotmK3WyAQidPP+fx3dPO2SLVi2bEt4Wy7HAAMFB/4n
+rQbjh1x5fjNyLu+UlQL8xpKf3NDreP7ncvUmXn516wNxzk8S5KEDrxAwevEeixxY
+2LPTE4FbbUhIEkRilz22YIWxrFaKywAkbomGFaHkj5miily2daS1bxYidkjb4mGU
+DFujMMIOqI1N3Ag4lNo9NP7XkESj6uqcBOS6pd735pFg0j3+9+HGT1e1dwynoMdS
+60oSnCMEaD2xod1T5We2vofgwj53w/4yug0h84dWYSKFfgOgycslBJ1Z2aJDp664
+/Ora5PpPjBw6pHklvmOiLWD0UEL3nv/B7UxU+UgD3fYZ63j5RKuvmzw9YdHtVUyH
+kRy7A70j1pHt1aDt7GqoiEkEGBECAAkFAkPN6eoCGwwACgkQRrtV8IhcZaR5XACf
+fc3iRAkf1hOGAx7Qi4yIqfVoG+4AoIAlsvKuGQ/9Q7fSSBsp1mWTLl4D
+=A4B6
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/gpgme/tests/keys/key1.sec b/gpgme/tests/keys/key1.sec
new file mode 100644 (file)
index 0000000..175efd4
--- /dev/null
@@ -0,0 +1,31 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+lQG7BEPN6eMRBAC3/jKHCnI9c9fATgGLV4x53wN6ZZoqzcgks4rR9BgrYLvPBzDX
+em74/m5uRMY90hGzJw8kb/rG8ZznJTU/ZE50qCmEEQuTyZar80LEb4ujbqTfZzGK
+v3blpSS+f5ON+2VATiL85roJ7BhpMnhUvS7nk/rSoKFYz+sdf/l3SR76ywCgg+OK
+ISC9UWqI9wlyhs7BpZib6SkEAJ7SzSDHNFI0Uvl5/+QYwy1N9IxCx6po7OJuBFK8
+9XBSbqRnnrlcCko/qTIuPxy9R9g+0JPhi2cNB++f3I/TPz73Xsg7JjkNnqD7aZS7
+MXRlzxESuu7Br+JVwzIERfXDLuA0pnMy+nt1mhuUFxc4dFEzD0DDsbFNWmo57rlr
+t2BZA/4naBrB6L763BMIzebjTCOJ5d0/4pbqlQrP2O+2BxKvkb7OA3ySgXlTpQsb
+Q1E5D+prEI6fTKeqytar9NR2FuOc61kEEmN4ACdEX5FWSYM0grAm68BfJ3Q2IHCV
+pVMF4ExSWlRb05k3TK1HDqjoj8uXMACmz5XAU+69q8ydh3B1LgAAni2eZ4K2MRJq
+oqJc5SsgIMHzLXw2CTi0GEtleSAxIDxrZXkxQGV4YW1wbGUub3JnPoheBBMRAgAe
+BQJDzenjAhsDBgsJCAcDAgMVAgMDFgIBAh4BAheAAAoJEEa7VfCIXGWkqtUAnjI7
+uB1911Iaf5Ab4xmriEfKbj+EAJ93FeHcuajBs6TEoLKLNetpCf8TCJ0CPQRDzenq
+EAgAjMcSYnWOi9XOcx4FO72d8kb56fLZK0ocjbcI+jAoMqfZByvJYJ3bCKcF1sX1
+7Em0z0+sLXR9eYrO7orR4xtbSqYZjYJExyhBOtmTAD+V0n69J7XfXzMRgKpSnLI3
+Z5AuJvxUMyUhUEZ2RPTrBjGwAmgrRH3OrS1PHt7Yf7chQk9505Hxy2Jn1XgVo27G
+T6AFXA1QLNPeBNORJgO5YO9jycM4McikTOKztQy4FH/c3l27pGAPfEjADy839V/f
+82OAnzAonKxZBTpLpf5mlxe3IX8a0Kmm53rbvV7wDv58ROZSb7R7tIUKLZit1sgE
+InTz/n8d3Tztki1YtmxLeFsuxwADBQf+J60G44dceX4zci7vlJUC/MaSn9zQ63j+
+53L1Jl5+desDcc5PEuShA68QMHrxHoscWNiz0xOBW21ISBJEYpc9tmCFsaxWissA
+JG6JhhWh5I+ZoopctnWktW8WInZI2+JhlAxbozDCDqiNTdwIOJTaPTT+15BEo+rq
+nATkuqXe9+aRYNI9/vfhxk9XtXcMp6DHUutKEpwjBGg9saHdU+Vntr6H4MI+d8P+
+MroNIfOHVmEihX4DoMnLJQSdWdmiQ6euuPzq2uT6T4wcOqR5Jb5joi1g9FBC957/
+we1MVPlIA932Get4+USrr5s8PWHR7VVMh5EcuwO9I9aR7dWg7exqqAABUwYkXGcj
+ydeyTvGPtJx23TAu6UCJ9ycRv1DEjZXgyz/0KJa8MuLdp6PKq/YYWYhJBBgRAgAJ
+BQJDzenqAhsMAAoJEEa7VfCIXGWkeVwAn1Z2OsXbZ32B+U/jvl5QG0NE66+cAJ4r
+jyoVKtrbw0t7+xoBOMft2cv6Xw==
+=kwYN
+-----END PGP PRIVATE KEY BLOCK-----
diff --git a/gpgme/tests/keys/key2.pub b/gpgme/tests/keys/key2.pub
new file mode 100644 (file)
index 0000000..d26510b
--- /dev/null
@@ -0,0 +1,53 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+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
+zw+9DaUWqQ==
+=guWc
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/gpgme/tests/keys/key2.sec b/gpgme/tests/keys/key2.sec
new file mode 100644 (file)
index 0000000..497fc58
--- /dev/null
@@ -0,0 +1,105 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+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
+FTstUwJS/LPkRekNydTPD70NpRap
+=e8Ot
+-----END PGP PRIVATE KEY BLOCK-----
diff --git a/gpgme/tests/keys/passphrase.pub b/gpgme/tests/keys/passphrase.pub
new file mode 100644 (file)
index 0000000..4b8def1
Binary files /dev/null and b/gpgme/tests/keys/passphrase.pub differ
diff --git a/gpgme/tests/keys/passphrase.sec b/gpgme/tests/keys/passphrase.sec
new file mode 100644 (file)
index 0000000..02ab5d6
Binary files /dev/null and b/gpgme/tests/keys/passphrase.sec differ
diff --git a/gpgme/tests/keys/revoked.pub b/gpgme/tests/keys/revoked.pub
new file mode 100644 (file)
index 0000000..5e6a1fc
--- /dev/null
@@ -0,0 +1,26 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+mQGiBEPN7RMRBACqAXL6t6ZVuyFkjqM8FH52eN06p+XFHG+PqxXYTnUUWyumLO5T
+Pjmpgf22QmHnSI6IaB13rC7v5gA2B4z5P6/j/LTV51d/T/0thFy/p7XINSBw+CES
+6RpST3AWdkmj+TvX4Kb4xP9klIbcC3cFexLKjWe+V78Nli05DpqR3V8rZwCguKwn
+MyaMS4smhncgSbdFZ/jSbN8D/iU7vlQ2r1tiH5G70WWyeV/+8Rz/gOEhlKo+Yfds
+x3t94qpWr+qn7u9615w9eX4WYyGmRXdxEzQqAyagqkXZBNyPNE1nA6/lCx/2hAlQ
+mPOSN2F7YII29RuRjNF3Cd1z/RuobP0yjmXyIYOo4Qf1/GKHWjp0Xu+qcCPDmnmD
+a6YHA/0Xwe2VSY9BQg74FI+L7C83R3Xm9qubgkWpyYwrpN/aSlVCywqG3VjYNAZ5
+RvXfkFW43r0dURAMz6I01X/LrkIWxvgz2pii7qfjs1pBGNM2kYK3Vwzcdp7Lg9uf
+/c5aljGBDpn7SF4jBenOPH51LYI8N9NIMioEmOyaD1jrBvmk7ohcBCARAgAcBQJD
+ze1GFR0DVGVzdCBvZiByZXZva2VkIGtleQAKCRAu9ljJh3VDaH0bAJ9K2nuztlmt
+b8tMy9+FApwomHC0kQCfSLAgQa9hZS1TyPtHXGAMoghNif20HVJldm9rZWQgPHJl
+dm9rZWRAZXhhbXBsZS5vcmc+iF4EExECAB4FAkPN7RMCGwMGCwkIBwMCAxUCAwMW
+AgECHgECF4AACgkQLvZYyYd1Q2jE8gCeP9IXvrznkFehQGBMf3gGeO57GJgAn2uL
+DkgH811s1LN9GKqKBGbCLH5CuQENBEPN7RcQBADoHXOivf3MqUR99JJTQtYo9b3f
+zQzZIgMtqmOwCFfe/jWA0VNf1svUemhXgiqi/gQUC2JLIJgIArhlxey5mU1gn7L/
+OMtwKfXD5zUrOKptTby8RIcySv20LR+vh6SmeYTI9ELdlwrG2efYDWtl2jBodnHf
+1u38PjzE2NhvqqjdNwADBQQAlIbPfQmFSnB8YRsLCq6w9Qc/kqwPcAgGbPsIaFsG
+qLc0HRk4/SivyH+i/TTUlpcQ3gXoZ/eUInO9R2IMmQzg+J4XAtx/llSJ0krO27Cy
+qs0QLdvtpaN9dvunhf7FGduHmJys8uPcxo6h/TvBsVUF1ZJCWZzCT7iMOjUZYSau
+99SISQQYEQIACQUCQ83tFwIbDAAKCRAu9ljJh3VDaOcpAKCFql4b4u7YEr3enZx5
+3c2YXJUubQCeP+/3+KFxmfr+NS5d08JjplMT96I=
+=+ttA
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/gpgme/tests/keys/signonly.pub b/gpgme/tests/keys/signonly.pub
new file mode 100644 (file)
index 0000000..f84c9c1
--- /dev/null
@@ -0,0 +1,66 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+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
+IiupsX+7alW4D8gI++6xtVTyanhtymAW
+=WN/+
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/gpgme/tests/keys/signonly.sec b/gpgme/tests/keys/signonly.sec
new file mode 100644 (file)
index 0000000..2e4fbfc
--- /dev/null
@@ -0,0 +1,69 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+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
+ewllGZCqbWy9TQ==
+=pPlO
+-----END PGP PRIVATE KEY BLOCK-----
diff --git a/gpgme/tests/test_context.py b/gpgme/tests/test_context.py
new file mode 100644 (file)
index 0000000..b2878e1
--- /dev/null
@@ -0,0 +1,127 @@
+# pygpgme - a Python wrapper for the gpgme library
+# Copyright (C) 2006  James Henstridge
+#
+# 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import unittest
+
+import gpgme
+from gpgme.tests.util import GpgHomeTestCase
+
+class ContextTestCase(GpgHomeTestCase):
+
+    def test_constructor(self):
+        ctx = gpgme.Context()
+
+    def test_protocol(self):
+        ctx = gpgme.Context()
+        # XXX: this should use symbolic constant names
+        self.assertEqual(ctx.protocol, gpgme.PROTOCOL_OpenPGP)
+        ctx.protocol = gpgme.PROTOCOL_CMS
+        self.assertEqual(ctx.protocol, gpgme.PROTOCOL_CMS)
+        ctx.protocol = gpgme.PROTOCOL_OpenPGP
+        self.assertEqual(ctx.protocol, gpgme.PROTOCOL_OpenPGP)
+
+        # check error on setting to invalid protocol value
+        def set_protocol(ctx, value):
+            ctx.protocol = value
+        self.assertRaises(gpgme.GpgmeError, set_protocol, ctx, 999)
+
+        def del_protocol(ctx):
+            del ctx.protocol
+        self.assertRaises(TypeError, del_protocol, ctx)
+
+    def test_armor(self):
+        ctx = gpgme.Context()
+        self.assertEqual(ctx.armor, False)
+        ctx.armor = True
+        self.assertEqual(ctx.armor, True)
+        ctx.armor = False
+        self.assertEqual(ctx.armor, False)
+
+        def del_armor(ctx):
+            del ctx.armor
+        self.assertRaises(TypeError, del_armor, ctx)
+
+    def test_textmode(self):
+        ctx = gpgme.Context()
+        self.assertEqual(ctx.textmode, False)
+        ctx.textmode = True
+        self.assertEqual(ctx.textmode, True)
+        ctx.textmode = False
+        self.assertEqual(ctx.textmode, False)
+
+        def del_textmode(ctx):
+            del ctx.textmode
+        self.assertRaises(TypeError, del_textmode, ctx)
+
+    def test_include_certs(self):
+        ctx = gpgme.Context()
+        self.assertEqual(ctx.include_certs, 1)
+        ctx.include_certs = 2
+        self.assertEqual(ctx.include_certs, 2)
+
+        def del_include_certs(ctx):
+            del ctx.include_certs
+        self.assertRaises(TypeError, del_include_certs, ctx)
+
+    def test_keylist_mode(self):
+        ctx = gpgme.Context()
+        self.assertEqual(ctx.keylist_mode, gpgme.KEYLIST_MODE_LOCAL)
+        ctx.keylist_mode = gpgme.KEYLIST_MODE_EXTERN
+        self.assertEqual(ctx.keylist_mode, gpgme.KEYLIST_MODE_EXTERN)
+        ctx.keylist_mode = gpgme.KEYLIST_MODE_LOCAL | gpgme.KEYLIST_MODE_EXTERN
+        self.assertEqual(ctx.keylist_mode,
+                         gpgme.KEYLIST_MODE_LOCAL | gpgme.KEYLIST_MODE_EXTERN)
+
+        # check error on setting to invalid keylist_mode value
+        def set_keylist_mode(ctx, value):
+            ctx.keylist_mode = value
+        self.assertRaises(gpgme.GpgmeError, set_keylist_mode, ctx, 128)
+
+        def del_keylist_mode(ctx):
+            del ctx.keylist_mode
+        self.assertRaises(TypeError, del_keylist_mode, ctx)
+
+    def test_passphrase_cb(self):
+        ctx = gpgme.Context()
+        def passphrase_cb(uid_hint, passphrase_info, prev_was_bad, fd):
+            pass
+        self.assertEqual(ctx.passphrase_cb, None)
+        ctx.passphrase_cb = passphrase_cb
+        self.assertEqual(ctx.passphrase_cb, passphrase_cb)
+        ctx.passphrase_cb = None
+        self.assertEqual(ctx.passphrase_cb, None)
+        ctx.passphrase_cb = passphrase_cb
+        del ctx.passphrase_cb
+        self.assertEqual(ctx.passphrase_cb, None)
+
+    def test_progress_cb(self):
+        ctx = gpgme.Context()
+        def progress_cb(what, type, current, total):
+            pass
+        self.assertEqual(ctx.progress_cb, None)
+        ctx.progress_cb = progress_cb
+        self.assertEqual(ctx.progress_cb, progress_cb)
+        ctx.progress_cb = None
+        self.assertEqual(ctx.progress_cb, None)
+        ctx.progress_cb = progress_cb
+        del ctx.progress_cb
+        self.assertEqual(ctx.progress_cb, None)
+
+
+def test_suite():
+    loader = unittest.TestLoader()
+    return loader.loadTestsFromName(__name__)
diff --git a/gpgme/tests/test_delete.py b/gpgme/tests/test_delete.py
new file mode 100644 (file)
index 0000000..b42eedc
--- /dev/null
@@ -0,0 +1,68 @@
+# pygpgme - a Python wrapper for the gpgme library
+# Copyright (C) 2006  James Henstridge
+#
+# 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import unittest
+import StringIO
+
+import gpgme
+from gpgme.tests.util import GpgHomeTestCase
+
+class DeleteTestCase(GpgHomeTestCase):
+
+    import_keys = ['key1.pub', 'key1.sec', 'key2.pub']
+
+    def test_delete_public_key(self):
+        ctx = gpgme.Context()
+        # key2
+        key = ctx.get_key('93C2240D6B8AA10AB28F701D2CF46B7FC97E6B0F')
+        ctx.delete(key)
+
+        # check that it is deleted
+        self.assertRaises(gpgme.GpgmeError, ctx.get_key,
+                          '93C2240D6B8AA10AB28F701D2CF46B7FC97E6B0F')
+
+    def test_delete_public_key_with_secret_key(self):
+        ctx = gpgme.Context()
+        # key1
+        key = ctx.get_key('E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        self.assertRaises(gpgme.GpgmeError, ctx.delete, key)
+
+    def test_delete_secret_key(self):
+        ctx = gpgme.Context()
+        # key1
+        key = ctx.get_key('E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        ctx.delete(key, True)
+
+    def test_delete_non_existant(self):
+        ctx = gpgme.Context()
+        # key2
+        key = ctx.get_key('93C2240D6B8AA10AB28F701D2CF46B7FC97E6B0F')
+        ctx.delete(key)
+
+        # delete it again
+        try:
+            ctx.delete(key)
+        except gpgme.GpgmeError, exc:
+            self.assertEqual(exc[0], gpgme.ERR_SOURCE_GPGME)
+            self.assertEqual(exc[1], gpgme.ERR_NO_PUBKEY)
+        else:
+            self.fail('gpgme.GpgmeError was not raised')
+
+
+def test_suite():
+    loader = unittest.TestLoader()
+    return loader.loadTestsFromName(__name__)
diff --git a/gpgme/tests/test_editkey.py b/gpgme/tests/test_editkey.py
new file mode 100644 (file)
index 0000000..fc626a4
--- /dev/null
@@ -0,0 +1,89 @@
+# pygpgme - a Python wrapper for the gpgme library
+# Copyright (C) 2006  James Henstridge
+#
+# 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import unittest
+import os
+import StringIO
+
+import gpgme
+import gpgme.editutil
+from gpgme.tests.util import GpgHomeTestCase
+
+class EditKeyTestCase(GpgHomeTestCase):
+
+    import_keys = ['key1.pub', 'key1.sec', 'key2.pub',
+                   'signonly.pub', 'signonly.sec']
+
+    def edit_quit_cb(self, status, args, fd):
+        if status in [gpgme.STATUS_EOF, gpgme.STATUS_GOT_IT]:
+            return
+        self.status = status
+        self.args = args
+        os.write(fd, 'quit\n')
+
+    def test_edit_quit(self):
+        ctx = gpgme.Context()
+        key = ctx.get_key('E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        output = StringIO.StringIO()
+
+        self.status = None
+        self.args = None
+        ctx.edit(key, self.edit_quit_cb, output)
+
+        self.assertEqual(self.status, gpgme.STATUS_GET_LINE)
+        self.assertEqual(self.args, 'keyedit.prompt')
+
+    def test_edit_ownertrust(self):
+        ctx = gpgme.Context()
+        key = ctx.get_key('93C2240D6B8AA10AB28F701D2CF46B7FC97E6B0F')
+        self.assertEqual(key.owner_trust, gpgme.VALIDITY_UNKNOWN)
+
+        # try setting each validity:
+        for trust in [gpgme.VALIDITY_NEVER,
+                      gpgme.VALIDITY_MARGINAL,
+                      gpgme.VALIDITY_FULL,
+                      gpgme.VALIDITY_ULTIMATE]:
+            gpgme.editutil.edit_trust(ctx, key, trust)
+            key = ctx.get_key('93C2240D6B8AA10AB28F701D2CF46B7FC97E6B0F')
+            self.assertEqual(key.owner_trust, trust)
+
+    def test_edit_sign(self):
+        ctx = gpgme.Context()
+        # we set the keylist mode so we can see signatures
+        ctx.keylist_mode = gpgme.KEYLIST_MODE_SIGS
+        ctx.signers = [ctx.get_key('15E7CE9BF1771A4ABC550B31F540A569CB935A42')]
+        key = ctx.get_key('E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+
+        # check that there are no signatures from 0xCB935A42
+        for uid in key.uids:
+            sigs = [sig for sig in uid.signatures
+                    if sig.keyid == 'F540A569CB935A42']
+            self.assertEqual(len(sigs), 0)
+
+        gpgme.editutil.edit_sign(ctx, key, check=0)
+        key = ctx.get_key('E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+
+        # check that there is a signature from 0xCB935A42 on each UID
+        for uid in key.uids:
+            sigs = [sig for sig in uid.signatures
+                    if sig.keyid == 'F540A569CB935A42']
+            self.assertEqual(len(sigs), 1)
+
+
+def test_suite():
+    loader = unittest.TestLoader()
+    return loader.loadTestsFromName(__name__)
diff --git a/gpgme/tests/test_encrypt_decrypt.py b/gpgme/tests/test_encrypt_decrypt.py
new file mode 100644 (file)
index 0000000..ff29f91
--- /dev/null
@@ -0,0 +1,171 @@
+# pygpgme - a Python wrapper for the gpgme library
+# Copyright (C) 2006  James Henstridge
+#
+# 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import unittest
+import StringIO
+from textwrap import dedent
+
+import gpgme
+from gpgme.tests.util import GpgHomeTestCase
+
+class EncryptDecryptTestCase(GpgHomeTestCase):
+
+    import_keys = ['key1.pub', 'key1.sec', 'key2.pub', 'key2.sec',
+                   'signonly.pub', 'signonly.sec']
+
+    def test_decrypt(self):
+        ciphertext = StringIO.StringIO(dedent('''
+            -----BEGIN PGP MESSAGE-----
+            Version: GnuPG v1.4.1 (GNU/Linux)
+
+            hQIMA6lSIdANy91kARAAtXjViihD6DVBAYGdVs0a2sMPGRXjIR5Tw1ONKx4MtKn+
+            pydR+/+0rBRGaQXe/mMODA8gqADQjz7PcTMWBa7ja969+K2nw7j5DUIMatonQVMf
+            vpc7ze5hovZ1jXYAgmmXdUzDmk8ZkpHaEc5mMMAHYKFn+mm37AFY5JUjg2Ae9k3H
+            29t+pW+n9ncn/QBImW3oVslZ8Fza1xOIWZTUrmvtU0vELdlIxy+d945bvD9EhmTK
+            zRrD5m8V1etWINO2tE1Xhd4lV1KxncHzWafXLB5BKprztTqFUXNPAfnucYIczDon
+            5VvkOz3WAtl/93o85hUKhbgGK0dvU3m+bj620ZUE5oDpPB4l1CiO5RqUFYtyN4LF
+            OSAceVOh7X764VLtpAzuOpNfTYgvzIFJqrFOZHlf3XJRdGdpJuxMe8BwhdLyDscx
+            pap4QxajOUSUAeS45x6ERA7xHO0QOwXZNzoxiOt9KRaoIhEacu70A9xRcGNJfZE2
+            3z/AEMKr2CK6ny9/S8UQEhNvn1/gYfSXakFjWjM6PUXJSnz8WGjpFKKITpex3WBz
+            m/X8bKgG3fT92zqJdYocrl4wgz4Dt3+KirnGG4gITxaEYpTT0y6l6NAO60De0oRh
+            yqk+ulj2pvAlA82Ph0vTGZ9uOQGbrfN+NhwsG0HMNq+vmjShS1jJbSfoEt1AAIPS
+            RwGMq7SDk/V7nhKqZcxeIWdtRIgFvEf0KljthkOZxT/GozNjQclak7Kt69K2qyvt
+            XUfidNAhuOmeZ4pF31qSFiAnKRbzjzHp
+            =1s5N
+            -----END PGP MESSAGE-----
+            '''))
+        plaintext = StringIO.StringIO()
+        ctx = gpgme.Context()
+        ctx.decrypt(ciphertext, plaintext)
+        self.assertEqual(plaintext.getvalue(), 'hello world\n')
+
+    def test_decrypt_verify(self):
+        ciphertext = StringIO.StringIO(dedent('''
+            -----BEGIN PGP MESSAGE-----
+            Version: GnuPG v1.4.1 (GNU/Linux)
+
+            hQIMA6lSIdANy91kAQ/9GGQxL/OWvxrTchSMmIhsvwONJNlFE5cMIC0xejY4eN+t
+            HtTg8V1fWXLRw7WY6FNFLeoR2hzqaZWw15lU55TmSJfJmK2mdUZu/IhSpCUFMEFW
+            ZQpxslKq7N+S8NZHgq1WG32Ar1auOEflBQUMhj7sRSAtkvU7fWrTwf4Q4mcIV68P
+            LiAAQoKxXNNVam9+EV/b3kx3bnJPKTn+ArpJf5Im+5XOGOeu9Ll0QUTbicgFhfpR
+            esR6dKI/Ji5FGIu01kYNrDjDeMcJuzI52kNNoT+GJ72R+Gp4bZk2ycd+eVo3eeUW
+            klO8K+7E5bd5ni+1H+ZWbVp9bn7Q++mFP6Mruv+v9Di5mvFXxMoFuB/8NzcilFVt
+            h5VOexW1OaZk2bMp9bXVja/N7Y1oAADhINk0feaKkwYVOBJU9kJtL2O1WQui85Q3
+            2dsL0YRJiR6mXesTezglZO44gsVAvCH8RUCtBnfEazfBg4jhcCHy6ooDgd0M4vcw
+            xG4U7IyDU5xyLi9QrTaSg5LzzwNFqb5k/lTemZw3ob3uwZinWewASLwn5N5OPVRs
+            gFT0eL0TfvDzHURsM/7QDvq9HX6JS7buyOlr5cZAsdSvm0FyE6YOkSvZR2jwp3vV
+            jfs7RHjq9V7jzPVVKHnWEDoJfchkT/3KyMRCIM/ukBk9MwTZTIJRhjTA2Xd4kWTS
+            kQEaU/OjumXPtw/T1pUH23nAkVssHsj8qgtxkFSmG/wrwNmfYx4tDhvgsHMJhar9
+            hqQKBMsGmLD6RNWKhF/LryNBKI2IRgJabKKYbbOsydom/hw8ZF4aWaZTcCBMoBB2
+            nhOi8WEIeWp93FGfHBa60nSBNGwgt24NmoFaXMjnCrJY/yK0L0MAajUC150OhtvG
+            OSk=
+            =fl3U
+            -----END PGP MESSAGE-----
+            '''))
+        plaintext = StringIO.StringIO()
+        ctx = gpgme.Context()
+        sigs = ctx.decrypt_verify(ciphertext, plaintext)
+        self.assertEqual(plaintext.getvalue(), 'hello world\n')
+        self.assertEqual(len(sigs), 1)
+        self.assertEqual(sigs[0].summary, 0)
+        self.assertEqual(sigs[0].fpr,
+                         'E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        self.assertEqual(sigs[0].status, None)
+        self.assertEqual(sigs[0].notations, [])
+        self.assertEqual(sigs[0].timestamp, 1138049495)
+        self.assertEqual(sigs[0].exp_timestamp, 0)
+        self.assertEqual(sigs[0].wrong_key_usage, False)
+        self.assertEqual(sigs[0].validity, gpgme.VALIDITY_UNKNOWN)
+        self.assertEqual(sigs[0].validity_reason, None)
+
+    def test_encrypt(self):
+        plaintext = StringIO.StringIO('Hello World\n')
+        ciphertext = StringIO.StringIO()
+        ctx = gpgme.Context()
+        recipient = ctx.get_key('93C2240D6B8AA10AB28F701D2CF46B7FC97E6B0F')
+        ctx.encrypt([recipient], gpgme.ENCRYPT_ALWAYS_TRUST,
+                    plaintext, ciphertext)
+
+        # rewind ciphertext buffer, and try to decrypt:
+        ciphertext.seek(0)
+        plaintext = StringIO.StringIO()
+        ctx.decrypt(ciphertext, plaintext)
+        self.assertEqual(plaintext.getvalue(), 'Hello World\n')
+
+    def test_encrypt_armor(self):
+        plaintext = StringIO.StringIO('Hello World\n')
+        ciphertext = StringIO.StringIO()
+        ctx = gpgme.Context()
+        ctx.armor = True
+        recipient = ctx.get_key('93C2240D6B8AA10AB28F701D2CF46B7FC97E6B0F')
+        ctx.encrypt([recipient], gpgme.ENCRYPT_ALWAYS_TRUST,
+                    plaintext, ciphertext)
+
+        # rewind ciphertext buffer, and try to decrypt:
+        ciphertext.seek(0)
+        plaintext = StringIO.StringIO()
+        ctx.decrypt(ciphertext, plaintext)
+        self.assertEqual(plaintext.getvalue(), 'Hello World\n')
+
+    def test_encrypt_sign(self):
+        plaintext = StringIO.StringIO('Hello World\n')
+        ciphertext = StringIO.StringIO()
+        ctx = gpgme.Context()
+        ctx.armor = True
+        signer = ctx.get_key('E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        recipient = ctx.get_key('93C2240D6B8AA10AB28F701D2CF46B7FC97E6B0F')
+        ctx.signers = [signer]
+        new_sigs = ctx.encrypt_sign([recipient], gpgme.ENCRYPT_ALWAYS_TRUST,
+                                    plaintext, ciphertext)
+
+        self.assertEqual(len(new_sigs), 1)
+        self.assertEqual(new_sigs[0].type, gpgme.SIG_MODE_NORMAL)
+        self.assertEqual(new_sigs[0].fpr,
+                        'E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+
+        # rewind ciphertext buffer, and try to decrypt:
+        ciphertext.seek(0)
+        plaintext = StringIO.StringIO()
+        sigs = ctx.decrypt_verify(ciphertext, plaintext)
+        self.assertEqual(plaintext.getvalue(), 'Hello World\n')
+        self.assertEqual(len(sigs), 1)
+        self.assertEqual(sigs[0].summary, 0)
+        self.assertEqual(sigs[0].fpr,
+                         'E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        self.assertEqual(sigs[0].status, None)
+        self.assertEqual(sigs[0].wrong_key_usage, False)
+        self.assertEqual(sigs[0].validity, gpgme.VALIDITY_UNKNOWN)
+        self.assertEqual(sigs[0].validity_reason, None)
+
+    def test_encrypt_to_signonly(self):
+        plaintext = StringIO.StringIO('Hello World\n')
+        ciphertext = StringIO.StringIO()
+        ctx = gpgme.Context()
+        recipient = ctx.get_key('15E7CE9BF1771A4ABC550B31F540A569CB935A42')
+        try:
+            ctx.encrypt([recipient], gpgme.ENCRYPT_ALWAYS_TRUST,
+                        plaintext, ciphertext)
+        except gpgme.GpgmeError, e:
+            self.assertEqual(e[0], gpgme.ERR_SOURCE_UNKNOWN)
+            self.assertEqual(e[1], gpgme.ERR_GENERAL)
+        else:
+            self.fail('gpgme.GpgmeError not raised')
+        
+
+def test_suite():
+    loader = unittest.TestLoader()
+    return loader.loadTestsFromName(__name__)
diff --git a/gpgme/tests/test_export.py b/gpgme/tests/test_export.py
new file mode 100644 (file)
index 0000000..b24afcb
--- /dev/null
@@ -0,0 +1,59 @@
+# pygpgme - a Python wrapper for the gpgme library
+# Copyright (C) 2006  James Henstridge
+#
+# 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import unittest
+import StringIO
+from textwrap import dedent
+
+import gpgme
+from gpgme.tests.util import GpgHomeTestCase
+
+class ExportTestCase(GpgHomeTestCase):
+
+    import_keys = ['signonly.pub', 'signonly.sec']
+
+    def test_export_by_fingerprint(self):
+        ctx = gpgme.Context()
+        ctx.armor = True
+        keydata = StringIO.StringIO()
+        ctx.export('15E7CE9BF1771A4ABC550B31F540A569CB935A42', keydata)
+
+        self.assertTrue(keydata.getvalue().startswith(
+            '-----BEGIN PGP PUBLIC KEY BLOCK-----\n'))
+        
+    def test_export_by_email(self):
+        ctx = gpgme.Context()
+        ctx.armor = True
+        keydata = StringIO.StringIO()
+        ctx.export('signonly@example.org', keydata)
+
+        self.assertTrue(keydata.getvalue().startswith(
+            '-----BEGIN PGP PUBLIC KEY BLOCK-----\n'))
+
+    def test_export_by_name(self):
+        ctx = gpgme.Context()
+        ctx.armor = True
+        keydata = StringIO.StringIO()
+        ctx.export('Sign Only', keydata)
+
+        self.assertTrue(keydata.getvalue().startswith(
+            '-----BEGIN PGP PUBLIC KEY BLOCK-----\n'))
+
+
+def test_suite():
+    loader = unittest.TestLoader()
+    return loader.loadTestsFromName(__name__)
diff --git a/gpgme/tests/test_import.py b/gpgme/tests/test_import.py
new file mode 100644 (file)
index 0000000..947b9cd
--- /dev/null
@@ -0,0 +1,169 @@
+# pygpgme - a Python wrapper for the gpgme library
+# Copyright (C) 2006  James Henstridge
+#
+# 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import unittest
+import StringIO
+
+import gpgme
+from gpgme.tests.util import GpgHomeTestCase
+
+class ImportTestCase(GpgHomeTestCase):
+
+    def test_import_file(self):
+        fp = self.keyfile('key1.pub')
+        ctx = gpgme.Context()
+        result = ctx.import_(fp)
+        self.assertEqual(result.considered, 1)
+        self.assertEqual(result.no_user_id, 0)
+        self.assertEqual(result.imported, 1)
+        self.assertEqual(result.imported_rsa, 0)
+        self.assertEqual(result.unchanged, 0)
+        self.assertEqual(result.new_user_ids, 0)
+        self.assertEqual(result.new_sub_keys, 0)
+        self.assertEqual(result.new_signatures, 0)
+        self.assertEqual(result.new_revocations, 0)
+        self.assertEqual(result.secret_read, 0)
+        self.assertEqual(result.secret_imported, 0)
+        self.assertEqual(result.secret_unchanged, 0)
+        self.assertEqual(result.skipped_new_keys, 0)
+        self.assertEqual(result.not_imported, 0)
+        self.assertEqual(len(result.imports), 1)
+        self.assertEqual(result.imports[0],
+                         ('E79A842DA34A1CA383F64A1546BB55F0885C65A4',
+                          None, gpgme.IMPORT_NEW))
+        # can we get the public key?
+        key = ctx.get_key('E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+
+    def test_import_secret_file(self):
+        fp = self.keyfile('key1.sec')
+        ctx = gpgme.Context()
+        result = ctx.import_(fp)
+        self.assertEqual(result.considered, 1)
+        self.assertEqual(result.no_user_id, 0)
+        self.assertEqual(result.imported, 1)
+        self.assertEqual(result.imported_rsa, 0)
+        self.assertEqual(result.unchanged, 0)
+        self.assertEqual(result.new_user_ids, 0)
+        self.assertEqual(result.new_sub_keys, 0)
+        self.assertEqual(result.new_signatures, 0)
+        self.assertEqual(result.new_revocations, 0)
+        self.assertEqual(result.secret_read, 1)
+        self.assertEqual(result.secret_imported, 1)
+        self.assertEqual(result.secret_unchanged, 0)
+        self.assertEqual(result.skipped_new_keys, 0)
+        self.assertEqual(result.not_imported, 0)
+        self.assertEqual(len(result.imports), 2)
+        self.assertEqual(result.imports[0],
+                         ('E79A842DA34A1CA383F64A1546BB55F0885C65A4',
+                          None, gpgme.IMPORT_NEW | gpgme.IMPORT_SECRET))
+        self.assertEqual(result.imports[1],
+                         ('E79A842DA34A1CA383F64A1546BB55F0885C65A4',
+                          None, gpgme.IMPORT_NEW))
+        # can we get the public key?
+        key = ctx.get_key('E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        # can we get the secret key?
+        key = ctx.get_key('E79A842DA34A1CA383F64A1546BB55F0885C65A4', True)
+
+    def test_import_stringio(self):
+        fp = StringIO.StringIO(self.keyfile('key1.pub').read())
+        ctx = gpgme.Context()
+        result = ctx.import_(fp)
+        self.assertEqual(len(result.imports), 1)
+        self.assertEqual(result.imports[0],
+                         ('E79A842DA34A1CA383F64A1546BB55F0885C65A4',
+                          None, gpgme.IMPORT_NEW))
+        # can we get the public key?
+        key = ctx.get_key('E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+
+    def test_import_concat(self):
+        keys = '\n'.join([self.keyfile('key1.pub').read(),
+                          self.keyfile('key1.sec').read(),
+                          self.keyfile('key2.pub').read()])
+        fp = StringIO.StringIO(keys)
+        ctx = gpgme.Context()
+        result = ctx.import_(fp)
+        self.assertEqual(result.considered, 3)
+        self.assertEqual(result.no_user_id, 0)
+        self.assertEqual(result.imported, 2)
+        self.assertEqual(result.imported_rsa, 1)
+        self.assertEqual(result.unchanged, 0)
+        self.assertEqual(result.new_user_ids, 0)
+        self.assertEqual(result.new_sub_keys, 0)
+        self.assertEqual(result.new_signatures, 1)
+        self.assertEqual(result.new_revocations, 0)
+        self.assertEqual(result.secret_read, 1)
+        self.assertEqual(result.secret_imported, 1)
+        self.assertEqual(result.secret_unchanged, 0)
+        self.assertEqual(result.skipped_new_keys, 0)
+        self.assertEqual(result.not_imported, 0)
+        self.assertEqual(len(result.imports), 4)
+        self.assertEqual(result.imports[0],
+                         ('E79A842DA34A1CA383F64A1546BB55F0885C65A4',
+                          None, gpgme.IMPORT_NEW))
+        self.assertEqual(result.imports[1],
+                         ('E79A842DA34A1CA383F64A1546BB55F0885C65A4',
+                          None, gpgme.IMPORT_NEW | gpgme.IMPORT_SECRET))
+        self.assertEqual(result.imports[2],
+                         ('E79A842DA34A1CA383F64A1546BB55F0885C65A4',
+                          None, gpgme.IMPORT_SIG))
+        self.assertEqual(result.imports[3],
+                         ('93C2240D6B8AA10AB28F701D2CF46B7FC97E6B0F',
+                          None, gpgme.IMPORT_NEW))
+        # can we get the public keys?
+        key = ctx.get_key('E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        key = ctx.get_key('93C2240D6B8AA10AB28F701D2CF46B7FC97E6B0F')
+        # can we get the secret key?
+        key = ctx.get_key('E79A842DA34A1CA383F64A1546BB55F0885C65A4', True)
+
+    def test_import_empty(self):
+        fp = StringIO.StringIO('')
+        ctx = gpgme.Context()
+        result = ctx.import_(fp)
+        self.assertEqual(result.considered, 0)
+        self.assertEqual(len(result.imports), 0)
+
+    def test_import_twice(self):
+        ctx = gpgme.Context()
+        fp = self.keyfile('key1.pub')
+        result = ctx.import_(fp)
+
+        fp = self.keyfile('key1.pub')
+        result = ctx.import_(fp)
+        
+        self.assertEqual(result.considered, 1)
+        self.assertEqual(result.no_user_id, 0)
+        self.assertEqual(result.imported, 0)
+        self.assertEqual(result.imported_rsa, 0)
+        self.assertEqual(result.unchanged, 1)
+        self.assertEqual(result.new_user_ids, 0)
+        self.assertEqual(result.new_sub_keys, 0)
+        self.assertEqual(result.new_signatures, 0)
+        self.assertEqual(result.new_revocations, 0)
+        self.assertEqual(result.secret_read, 0)
+        self.assertEqual(result.secret_imported, 0)
+        self.assertEqual(result.secret_unchanged, 0)
+        self.assertEqual(result.skipped_new_keys, 0)
+        self.assertEqual(result.not_imported, 0)
+        self.assertEqual(len(result.imports), 1)
+        self.assertEqual(result.imports[0],
+                         ('E79A842DA34A1CA383F64A1546BB55F0885C65A4', None, 0))
+        # can we get the public key?
+        key = ctx.get_key('E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+
+def test_suite():
+    loader = unittest.TestLoader()
+    return loader.loadTestsFromName(__name__)
diff --git a/gpgme/tests/test_keylist.py b/gpgme/tests/test_keylist.py
new file mode 100644 (file)
index 0000000..4ee5443
--- /dev/null
@@ -0,0 +1,70 @@
+# pygpgme - a Python wrapper for the gpgme library
+# Copyright (C) 2006  James Henstridge
+#
+# 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import unittest
+
+import gpgme
+from gpgme.tests.util import GpgHomeTestCase
+
+class KeylistTestCase(GpgHomeTestCase):
+
+    import_keys = ['key1.pub', 'key2.pub', 'revoked.pub', 'signonly.pub',
+                   'key1.sec']
+
+    def test_listall(self):
+        ctx = gpgme.Context()
+        keyids = set(key.subkeys[0].keyid for key in ctx.keylist())
+        self.assertTrue(keyids, set(['46BB55F0885C65A4',
+                                     '2CF46B7FC97E6B0F',
+                                     'F540A569CB935A42',
+                                     '2EF658C987754368']))
+
+    def test_list_by_email(self):
+        ctx = gpgme.Context()
+        keyids = set(key.subkeys[0].keyid
+                     for key in ctx.keylist('key1@example.org'))
+        self.assertTrue(keyids, set(['46BB55F0885C65A4']))
+        keyids = set(key.subkeys[0].keyid
+                     for key in ctx.keylist(['key1@example.org',
+                                             'signonly@example.com']))
+        self.assertTrue(keyids, set(['46BB55F0885C65A4', 'F540A569CB935A42']))
+
+    def test_list_by_name(self):
+        ctx = gpgme.Context()
+        keyids = set(key.subkeys[0].keyid
+                     for key in ctx.keylist('Key 1'))
+        self.assertTrue(keyids, set(['46BB55F0885C65A4']))
+
+    def test_list_by_email_substring(self):
+        ctx = gpgme.Context()
+        keyids = set(key.subkeys[0].keyid
+                     for key in ctx.keylist('@example.org'))
+        self.assertTrue(keyids, set(['46BB55F0885C65A4',
+                                     '2CF46B7FC97E6B0F',
+                                     'F540A569CB935A42',
+                                     '2EF658C987754368']))
+
+    def test_list_secret(self):
+        ctx = gpgme.Context()
+        keyids = set(key.subkeys[0].keyid
+                     for key in ctx.keylist(None, True))
+        self.assertTrue(keyids, set(['46BB55F0885C65A4']))
+
+
+def test_suite():
+    loader = unittest.TestLoader()
+    return loader.loadTestsFromName(__name__)
diff --git a/gpgme/tests/test_keys.py b/gpgme/tests/test_keys.py
new file mode 100644 (file)
index 0000000..ca7b0db
--- /dev/null
@@ -0,0 +1,247 @@
+# pygpgme - a Python wrapper for the gpgme library
+# Copyright (C) 2006  James Henstridge
+#
+# 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import unittest
+
+import gpgme
+from gpgme.tests.util import GpgHomeTestCase
+
+class KeyTestCase(GpgHomeTestCase):
+
+    import_keys = ['key1.pub', 'key2.pub', 'revoked.pub', 'signonly.pub']
+
+    def test_key1(self):
+        ctx = gpgme.Context()
+        key = ctx.get_key('E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        self.assertEqual(key.revoked, False)
+        self.assertEqual(key.expired, False)
+        self.assertEqual(key.invalid, False)
+        self.assertEqual(key.can_encrypt, True)
+        self.assertEqual(key.can_sign, True)
+        self.assertEqual(key.can_certify, True)
+        self.assertEqual(key.secret, False)
+        self.assertEqual(key.can_authenticate, False)
+        self.assertEqual(key.protocol, gpgme.PROTOCOL_OpenPGP)
+        self.assertEqual(len(key.subkeys), 2)
+        self.assertEqual(len(key.uids), 1)
+
+        self.assertEqual(key.subkeys[0].revoked, False)
+        self.assertEqual(key.subkeys[0].expired, False)
+        self.assertEqual(key.subkeys[0].disabled, False)
+        self.assertEqual(key.subkeys[0].invalid, False)
+        self.assertEqual(key.subkeys[0].can_encrypt, False)
+        self.assertEqual(key.subkeys[0].can_sign, True)
+        self.assertEqual(key.subkeys[0].can_certify, True)
+        self.assertEqual(key.subkeys[0].secret, False)
+        self.assertEqual(key.subkeys[0].can_authenticate, False)
+        self.assertEqual(key.subkeys[0].pubkey_algo, gpgme.PK_DSA)
+        self.assertEqual(key.subkeys[0].length, 1024)
+        self.assertEqual(key.subkeys[0].keyid, '46BB55F0885C65A4')
+        self.assertEqual(key.subkeys[0].fpr,
+                         'E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        self.assertEqual(key.subkeys[0].timestamp, 1137568227)
+        self.assertEqual(key.subkeys[0].expires, 0)
+
+        self.assertEqual(key.subkeys[1].revoked, False)
+        self.assertEqual(key.subkeys[1].expired, False)
+        self.assertEqual(key.subkeys[1].disabled, False)
+        self.assertEqual(key.subkeys[1].invalid, False)
+        self.assertEqual(key.subkeys[1].can_encrypt, True)
+        self.assertEqual(key.subkeys[1].can_sign, False)
+        self.assertEqual(key.subkeys[1].can_certify, False)
+        self.assertEqual(key.subkeys[1].secret, False)
+        self.assertEqual(key.subkeys[1].can_authenticate, False)
+        self.assertEqual(key.subkeys[1].pubkey_algo, gpgme.PK_ELG_E)
+        self.assertEqual(key.subkeys[1].length, 2048)
+        self.assertEqual(key.subkeys[1].keyid, '659A6AC69BC3B085')
+        self.assertEqual(key.subkeys[1].fpr, None)
+        self.assertEqual(key.subkeys[1].timestamp, 1137568234)
+        self.assertEqual(key.subkeys[1].expires, 0)
+
+        self.assertEqual(key.uids[0].revoked, False)
+        self.assertEqual(key.uids[0].invalid, False)
+        self.assertEqual(key.uids[0].validity, 0)
+        self.assertEqual(key.uids[0].uid, 'Key 1 <key1@example.org>')
+        self.assertEqual(key.uids[0].name, 'Key 1')
+        self.assertEqual(key.uids[0].email, 'key1@example.org')
+        self.assertEqual(key.uids[0].comment, '')
+
+    def test_key2(self):
+        ctx = gpgme.Context()
+        key = ctx.get_key('93C2240D6B8AA10AB28F701D2CF46B7FC97E6B0F')
+        self.assertEqual(key.revoked, False)
+        self.assertEqual(key.expired, False)
+        self.assertEqual(key.invalid, False)
+        self.assertEqual(key.can_encrypt, True)
+        self.assertEqual(key.can_sign, True)
+        self.assertEqual(key.can_certify, True)
+        self.assertEqual(key.secret, False)
+        self.assertEqual(key.can_authenticate, False)
+        self.assertEqual(key.protocol, gpgme.PROTOCOL_OpenPGP)
+        self.assertEqual(len(key.subkeys), 2)
+        self.assertEqual(len(key.uids), 1)
+
+        self.assertEqual(key.subkeys[0].revoked, False)
+        self.assertEqual(key.subkeys[0].expired, False)
+        self.assertEqual(key.subkeys[0].disabled, False)
+        self.assertEqual(key.subkeys[0].invalid, False)
+        self.assertEqual(key.subkeys[0].can_encrypt, False)
+        self.assertEqual(key.subkeys[0].can_sign, True)
+        self.assertEqual(key.subkeys[0].can_certify, True)
+        self.assertEqual(key.subkeys[0].secret, False)
+        self.assertEqual(key.subkeys[0].can_authenticate, False)
+        self.assertEqual(key.subkeys[0].pubkey_algo, gpgme.PK_RSA)
+        self.assertEqual(key.subkeys[0].length, 4096)
+        self.assertEqual(key.subkeys[0].keyid, '2CF46B7FC97E6B0F')
+        self.assertEqual(key.subkeys[0].fpr,
+                         '93C2240D6B8AA10AB28F701D2CF46B7FC97E6B0F')
+        self.assertEqual(key.subkeys[0].timestamp, 1137568343)
+        self.assertEqual(key.subkeys[0].expires, 0)
+
+        self.assertEqual(key.subkeys[1].revoked, False)
+        self.assertEqual(key.subkeys[1].expired, False)
+        self.assertEqual(key.subkeys[1].disabled, False)
+        self.assertEqual(key.subkeys[1].invalid, False)
+        self.assertEqual(key.subkeys[1].can_encrypt, True)
+        self.assertEqual(key.subkeys[1].can_sign, False)
+        self.assertEqual(key.subkeys[1].can_certify, False)
+        self.assertEqual(key.subkeys[1].secret, False)
+        self.assertEqual(key.subkeys[1].can_authenticate, False)
+        self.assertEqual(key.subkeys[1].pubkey_algo, gpgme.PK_RSA)
+        self.assertEqual(key.subkeys[1].length, 4096)
+        self.assertEqual(key.subkeys[1].keyid, 'A95221D00DCBDD64')
+        self.assertEqual(key.subkeys[1].fpr, None)
+        self.assertEqual(key.subkeys[1].timestamp, 1137568395)
+        self.assertEqual(key.subkeys[1].expires, 0)
+
+        self.assertEqual(key.uids[0].revoked, False)
+        self.assertEqual(key.uids[0].invalid, False)
+        self.assertEqual(key.uids[0].validity, 0)
+        self.assertEqual(key.uids[0].uid, 'Key 2 <key2@example.org>')
+        self.assertEqual(key.uids[0].name, 'Key 2')
+        self.assertEqual(key.uids[0].email, 'key2@example.org')
+        self.assertEqual(key.uids[0].comment, '')
+
+    def test_revoked(self):
+        ctx = gpgme.Context()
+        key = ctx.get_key('B6525A39EB81F88B4D2CFB3E2EF658C987754368')
+        self.assertEqual(key.revoked, True)
+        self.assertEqual(key.expired, False)
+        self.assertEqual(key.invalid, False)
+        self.assertEqual(key.can_encrypt, False)
+        self.assertEqual(key.can_sign, True)
+        self.assertEqual(key.can_certify, True)
+        self.assertEqual(key.secret, False)
+        self.assertEqual(key.can_authenticate, False)
+        self.assertEqual(key.protocol, gpgme.PROTOCOL_OpenPGP)
+        self.assertEqual(len(key.subkeys), 2)
+        self.assertEqual(len(key.uids), 1)
+
+        self.assertEqual(key.subkeys[0].revoked, True)
+        self.assertEqual(key.subkeys[0].expired, False)
+        self.assertEqual(key.subkeys[0].disabled, False)
+        self.assertEqual(key.subkeys[0].invalid, False)
+        self.assertEqual(key.subkeys[0].can_encrypt, False)
+        self.assertEqual(key.subkeys[0].can_sign, True)
+        self.assertEqual(key.subkeys[0].can_certify, True)
+        self.assertEqual(key.subkeys[0].secret, False)
+        self.assertEqual(key.subkeys[0].can_authenticate, False)
+        self.assertEqual(key.subkeys[0].pubkey_algo, gpgme.PK_DSA)
+        self.assertEqual(key.subkeys[0].length, 1024)
+        self.assertEqual(key.subkeys[0].keyid, '2EF658C987754368')
+        self.assertEqual(key.subkeys[0].fpr,
+                         'B6525A39EB81F88B4D2CFB3E2EF658C987754368')
+        self.assertEqual(key.subkeys[0].timestamp, 1137569043)
+        self.assertEqual(key.subkeys[0].expires, 0)
+
+        self.assertEqual(key.subkeys[1].revoked, True)
+        self.assertEqual(key.subkeys[1].expired, False)
+        self.assertEqual(key.subkeys[1].disabled, False)
+        self.assertEqual(key.subkeys[1].invalid, False)
+        self.assertEqual(key.subkeys[1].can_encrypt, True)
+        self.assertEqual(key.subkeys[1].can_sign, False)
+        self.assertEqual(key.subkeys[1].can_certify, False)
+        self.assertEqual(key.subkeys[1].secret, False)
+        self.assertEqual(key.subkeys[1].can_authenticate, False)
+        self.assertEqual(key.subkeys[1].pubkey_algo, gpgme.PK_ELG_E)
+        self.assertEqual(key.subkeys[1].length, 1024)
+        self.assertEqual(key.subkeys[1].keyid, 'E50B59CF50CE4D54')
+        self.assertEqual(key.subkeys[1].fpr, None)
+        self.assertEqual(key.subkeys[1].timestamp, 1137569047)
+        self.assertEqual(key.subkeys[1].expires, 0)
+
+        self.assertEqual(key.uids[0].revoked, True)
+        self.assertEqual(key.uids[0].invalid, False)
+        self.assertEqual(key.uids[0].validity, 0)
+        self.assertEqual(key.uids[0].uid, 'Revoked <revoked@example.org>')
+        self.assertEqual(key.uids[0].name, 'Revoked')
+        self.assertEqual(key.uids[0].email, 'revoked@example.org')
+        self.assertEqual(key.uids[0].comment, '')
+
+    def test_signonly(self):
+        ctx = gpgme.Context()
+        key = ctx.get_key('15E7CE9BF1771A4ABC550B31F540A569CB935A42')
+        self.assertEqual(key.revoked, False)
+        self.assertEqual(key.expired, False)
+        self.assertEqual(key.invalid, False)
+        self.assertEqual(key.can_encrypt, False)
+        self.assertEqual(key.can_sign, True)
+        self.assertEqual(key.can_certify, True)
+        self.assertEqual(key.secret, False)
+        self.assertEqual(key.can_authenticate, False)
+        self.assertEqual(key.protocol, gpgme.PROTOCOL_OpenPGP)
+        self.assertEqual(len(key.subkeys), 1)
+        self.assertEqual(len(key.uids), 2)
+
+        self.assertEqual(key.subkeys[0].revoked, False)
+        self.assertEqual(key.subkeys[0].expired, False)
+        self.assertEqual(key.subkeys[0].disabled, False)
+        self.assertEqual(key.subkeys[0].invalid, False)
+        self.assertEqual(key.subkeys[0].can_encrypt, False)
+        self.assertEqual(key.subkeys[0].can_sign, True)
+        self.assertEqual(key.subkeys[0].can_certify, True)
+        self.assertEqual(key.subkeys[0].secret, False)
+        self.assertEqual(key.subkeys[0].can_authenticate, False)
+        self.assertEqual(key.subkeys[0].pubkey_algo, gpgme.PK_RSA)
+        self.assertEqual(key.subkeys[0].length, 4096)
+        self.assertEqual(key.subkeys[0].keyid, 'F540A569CB935A42')
+        self.assertEqual(key.subkeys[0].fpr,
+                         '15E7CE9BF1771A4ABC550B31F540A569CB935A42')
+        self.assertEqual(key.subkeys[0].timestamp, 1137568835)
+        self.assertEqual(key.subkeys[0].expires, 0)
+
+        self.assertEqual(key.uids[0].revoked, False)
+        self.assertEqual(key.uids[0].invalid, False)
+        self.assertEqual(key.uids[0].validity, 0)
+        self.assertEqual(key.uids[0].uid, 'Sign Only <signonly@example.org>')
+        self.assertEqual(key.uids[0].name, 'Sign Only')
+        self.assertEqual(key.uids[0].email, 'signonly@example.org')
+        self.assertEqual(key.uids[0].comment, '')
+
+        self.assertEqual(key.uids[1].revoked, False)
+        self.assertEqual(key.uids[1].invalid, False)
+        self.assertEqual(key.uids[1].validity, 0)
+        self.assertEqual(key.uids[1].uid,
+                         'Sign Only (work address) <signonly@example.com>')
+        self.assertEqual(key.uids[1].name, 'Sign Only')
+        self.assertEqual(key.uids[1].email, 'signonly@example.com')
+        self.assertEqual(key.uids[1].comment, 'work address')
+
+
+def test_suite():
+    loader = unittest.TestLoader()
+    return loader.loadTestsFromName(__name__)
diff --git a/gpgme/tests/test_passphrase.py b/gpgme/tests/test_passphrase.py
new file mode 100644 (file)
index 0000000..82e47cb
--- /dev/null
@@ -0,0 +1,77 @@
+# pygpgme - a Python wrapper for the gpgme library
+# Copyright (C) 2006  James Henstridge
+#
+# 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import unittest
+import os
+import StringIO
+from textwrap import dedent
+
+import gpgme
+from gpgme.tests.util import GpgHomeTestCase
+
+class PassphraseTestCase(GpgHomeTestCase):
+
+    import_keys = ['passphrase.pub', 'passphrase.sec']
+
+    def test_sign_without_passphrase_cb(self):
+        ctx = gpgme.Context()
+        key = ctx.get_key('EFB052B4230BBBC51914BCBB54DCBBC8DBFB9EB3')
+        ctx.signers = [key]
+        plaintext = StringIO.StringIO('Hello World\n')
+        signature = StringIO.StringIO()
+
+        try:
+            new_sigs = ctx.sign(plaintext, signature, gpgme.SIG_MODE_CLEAR)
+        except gpgme.GpgmeError, e:
+            self.assertEqual(e[0], gpgme.ERR_SOURCE_GPGME)
+            self.assertEqual(e[1], gpgme.ERR_BAD_PASSPHRASE)
+        else:
+            self.fail('gpgme.GpgmeError not raised')
+
+    def passphrase_cb(self, uid_hint, passphrase_info, prev_was_bad, fd):
+        self.uid_hint = uid_hint
+        self.passphrase_info = passphrase_info
+        self.prev_was_bad = prev_was_bad
+        os.write(fd, 'test\n')
+
+    def test_sign_with_passphrase_cb(self):
+        ctx = gpgme.Context()
+        key = ctx.get_key('EFB052B4230BBBC51914BCBB54DCBBC8DBFB9EB3')
+        ctx.signers = [key]
+        ctx.passphrase_cb = self.passphrase_cb
+        plaintext = StringIO.StringIO('Hello World\n')
+        signature = StringIO.StringIO()
+
+        self.uid_hint = None
+        self.passphrase_info = None
+        self.prev_was_bad = None
+        new_sigs = ctx.sign(plaintext, signature, gpgme.SIG_MODE_CLEAR)
+
+        # ensure that passphrase_cb has been run, and the data it was passed
+        self.assertEqual(self.uid_hint,
+            '54DCBBC8DBFB9EB3 Passphrase (test) <passphrase@example.org>')
+        self.assertEqual(self.passphrase_info,
+            '54DCBBC8DBFB9EB3 54DCBBC8DBFB9EB3 17 0')
+        self.assertEqual(self.prev_was_bad, False)
+
+        self.assertEqual(new_sigs[0].type, gpgme.SIG_MODE_CLEAR)
+        self.assertEqual(new_sigs[0].fpr,
+                        'EFB052B4230BBBC51914BCBB54DCBBC8DBFB9EB3')
+
+def test_suite():
+    loader = unittest.TestLoader()
+    return loader.loadTestsFromName(__name__)
diff --git a/gpgme/tests/test_progress.py b/gpgme/tests/test_progress.py
new file mode 100644 (file)
index 0000000..c2ece5d
--- /dev/null
@@ -0,0 +1,53 @@
+# pygpgme - a Python wrapper for the gpgme library
+# Copyright (C) 2006  James Henstridge
+#
+# 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import unittest
+import os
+import StringIO
+from textwrap import dedent
+
+import gpgme
+from gpgme.tests.util import GpgHomeTestCase
+
+class ProgressTestCase(GpgHomeTestCase):
+
+    import_keys = ['key1.pub', 'key1.sec']
+
+    def progress_cb(self, what, type_, current, total):
+        self.progress_cb_called = True
+
+    def test_sign_with_progress_cb(self):
+        ctx = gpgme.Context()
+        key = ctx.get_key('E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        ctx.signers = [key]
+        ctx.progress_cb = self.progress_cb
+        plaintext = StringIO.StringIO('Hello World\n')
+        signature = StringIO.StringIO()
+
+        self.progress_cb_called = False
+        new_sigs = ctx.sign(plaintext, signature, gpgme.SIG_MODE_CLEAR)
+
+        # ensure that progress_cb has been run
+        self.assertEqual(self.progress_cb_called, True)
+
+        self.assertEqual(new_sigs[0].type, gpgme.SIG_MODE_CLEAR)
+        self.assertEqual(new_sigs[0].fpr,
+                        'E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+
+def test_suite():
+    loader = unittest.TestLoader()
+    return loader.loadTestsFromName(__name__)
diff --git a/gpgme/tests/test_sign_verify.py b/gpgme/tests/test_sign_verify.py
new file mode 100644 (file)
index 0000000..bb2d2e6
--- /dev/null
@@ -0,0 +1,384 @@
+# pygpgme - a Python wrapper for the gpgme library
+# Copyright (C) 2006  James Henstridge
+#
+# 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import unittest
+import StringIO
+from textwrap import dedent
+
+import gpgme
+from gpgme.tests.util import GpgHomeTestCase
+
+class SignVerifyTestCase(GpgHomeTestCase):
+
+    import_keys = ['key1.pub', 'key1.sec', 'key2.pub', 'key2.sec',
+                   'signonly.pub', 'signonly.sec']
+
+    def test_verify_normal(self):
+        signature = StringIO.StringIO(dedent('''
+            -----BEGIN PGP MESSAGE-----
+            Version: GnuPG v1.4.1 (GNU/Linux)
+
+            owGbwMvMwCTotjv0Q0dM6hLG00JJDM7nNx31SM3JyVcIzy/KSeHqsGdmBQvCVAky
+            pR9hmGfw0qo3bfpWZwun5euYAsUcVkyZMJlhfvkU6UBjD8WF9RfeND05zC/TK+H+
+            EQA=
+            =HCW0
+            -----END PGP MESSAGE-----
+            '''))
+        plaintext = StringIO.StringIO()
+        ctx = gpgme.Context()
+        sigs = ctx.verify(signature, None, plaintext)
+
+        self.assertEqual(plaintext.getvalue(), 'Hello World\n')
+        self.assertEqual(len(sigs), 1)
+        self.assertEqual(sigs[0].summary, 0)
+        self.assertEqual(sigs[0].fpr,
+                         'E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        self.assertEqual(sigs[0].status, None)
+        self.assertEqual(sigs[0].notations, [])
+        self.assertEqual(sigs[0].timestamp, 1137685189)
+        self.assertEqual(sigs[0].exp_timestamp, 0)
+        self.assertEqual(sigs[0].wrong_key_usage, False)
+        self.assertEqual(sigs[0].validity, gpgme.VALIDITY_UNKNOWN)
+        self.assertEqual(sigs[0].validity_reason, None)
+
+    def test_verify_detached(self):
+        signature = StringIO.StringIO(dedent('''
+            -----BEGIN PGP SIGNATURE-----
+            Version: GnuPG v1.4.1 (GNU/Linux)
+
+            iD8DBQBDz7ReRrtV8IhcZaQRAtuUAJwMiJeS5QPohToxA3+vp+z5c3jr1wCdHhGP
+            hhSTiguzgSYNwKSuV6SLGOM=
+            =dyZS
+            -----END PGP SIGNATURE-----
+            '''))
+        signed_text = StringIO.StringIO('Hello World\n')
+        ctx = gpgme.Context()
+        sigs = ctx.verify(signature, signed_text, None)
+
+        self.assertEqual(len(sigs), 1)
+        self.assertEqual(sigs[0].summary, 0)
+        self.assertEqual(sigs[0].fpr,
+                         'E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        self.assertEqual(sigs[0].status, None)
+        self.assertEqual(sigs[0].notations, [])
+        self.assertEqual(sigs[0].timestamp, 1137685598)
+        self.assertEqual(sigs[0].exp_timestamp, 0)
+        self.assertEqual(sigs[0].wrong_key_usage, False)
+        self.assertEqual(sigs[0].validity, gpgme.VALIDITY_UNKNOWN)
+        self.assertEqual(sigs[0].validity_reason, None)
+
+    def test_verify_clearsign(self):
+        signature = StringIO.StringIO(dedent('''
+            -----BEGIN PGP SIGNED MESSAGE-----
+            Hash: SHA1
+
+            Hello World
+            -----BEGIN PGP SIGNATURE-----
+            Version: GnuPG v1.4.1 (GNU/Linux)
+
+            iD8DBQFDz7DiRrtV8IhcZaQRAjuYAJ43/NhhNHx+gzGBUqtIK5LpENTCGgCfV3aO
+            ZTFlGRyKN26HccsC6ZWcPUQ=
+            =kZ2c
+            -----END PGP SIGNATURE-----
+            '''))
+        plaintext = StringIO.StringIO()
+        ctx = gpgme.Context()
+        sigs = ctx.verify(signature, None, plaintext)
+
+        self.assertEqual(plaintext.getvalue(), 'Hello World\n')
+        self.assertEqual(len(sigs), 1)
+        self.assertEqual(sigs[0].summary, 0)
+        self.assertEqual(sigs[0].fpr,
+                         'E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        self.assertEqual(sigs[0].status, None)
+        self.assertEqual(sigs[0].notations, [])
+        self.assertEqual(sigs[0].timestamp, 1137684706)
+        self.assertEqual(sigs[0].exp_timestamp, 0)
+        self.assertEqual(sigs[0].wrong_key_usage, False)
+        self.assertEqual(sigs[0].validity, gpgme.VALIDITY_UNKNOWN)
+        self.assertEqual(sigs[0].validity_reason, None)
+
+    def test_verify_multiple_sigs(self):
+        signature = StringIO.StringIO(dedent('''
+            -----BEGIN PGP SIGNED MESSAGE-----
+            Hash: SHA1
+
+            Hello World
+            -----BEGIN PGP SIGNATURE-----
+            Version: GnuPG v1.4.1 (GNU/Linux)
+
+            iD8DBQFDz7V9RrtV8IhcZaQRAia/AJ9eC/Q3pssWW9PWckQ3+1kbiIiEVQCfSeFv
+            7SlUCFJOs/sfl+EtaOafgQGJAhUDBQFDz7V9LPRrf8l+aw8BAia/EAClI1X/hL38
+            6NeOnMD6zXNm7r20Qkpp7PT63PqUa9dU1P+Ha2Uju5C2jBVYouDOpHnEsw3AqItl
+            M0y6xiBAbXbdv0K2OdX8/290g/uODQE/oRGu+YtIh8HcY9N1JmzYw6msRO1LD/Oo
+            xVqfyJiPx+Ol3juAuVqggBzQQmhQpZ7MfHcZSIWxYtRZNlCGYp2lUVae7fJlrJc8
+            DvTkGSkdqBRoDqy0rKcdXRuExXyq081m7bli2sMvImejmEsqyMcbZrkW69v+/BQD
+            Tki8tEkxINw1YHhcBDI0KAn3SuynY+i132oU2qJWQF3ZBRqEbD0IxfakPSZyhJKj
+            sxk38VHgA+5r/QKRs+4n3z09yFqNIWpnvVVZ2iMfKhHtKd1nNq6tOzHiQrmdSdyK
+            dwRaRm4Zt0hWT8v+CXX/RPK5xGL3FCZQs7VTO0ANHR7cIS+v3ChaHO6naQSBQMrW
+            7l69hTh009LFIKlYJ+7ZBS2pySkvHmEzJKl4Ko4UfOeD2xDsq5nHhi/AJ7TXtHCo
+            TLo8OwJvfiW6Fa9zzu6IkerhQlZrvbLOkmBpuyFo0UEuM/89bquaZ3GoEj3hePsZ
+            nD9LtsgsjkFV1jZQ4n/wM3jolo0aA4+ZEBCgw9XJUSZ67m+jvFNBvZtDqWnbQWxe
+            FsW3EQWNlQnwkn2lic51Cdp3w7yPH5CKfw==
+            =0A7N
+            -----END PGP SIGNATURE-----
+            '''))
+        plaintext = StringIO.StringIO()
+        ctx = gpgme.Context()
+        sigs = ctx.verify(signature, None, plaintext)
+
+        self.assertEqual(plaintext.getvalue(), 'Hello World\n')
+        self.assertEqual(len(sigs), 2)
+        self.assertEqual(sigs[0].summary, 0)
+        self.assertEqual(sigs[0].fpr,
+                         'E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        self.assertEqual(sigs[0].status, None)
+        self.assertEqual(sigs[0].notations, [])
+        self.assertEqual(sigs[0].timestamp, 1137685885)
+        self.assertEqual(sigs[0].exp_timestamp, 0)
+        self.assertEqual(sigs[0].wrong_key_usage, False)
+        self.assertEqual(sigs[0].validity, gpgme.VALIDITY_UNKNOWN)
+        self.assertEqual(sigs[0].validity_reason, None)
+
+        self.assertEqual(sigs[1].summary, 0)
+        self.assertEqual(sigs[1].fpr,
+                         '93C2240D6B8AA10AB28F701D2CF46B7FC97E6B0F')
+        self.assertEqual(sigs[1].status, None)
+        self.assertEqual(sigs[1].notations, [])
+        self.assertEqual(sigs[1].timestamp, 1137685885)
+        self.assertEqual(sigs[1].exp_timestamp, 0)
+        self.assertEqual(sigs[1].wrong_key_usage, False)
+        self.assertEqual(sigs[1].validity, gpgme.VALIDITY_UNKNOWN)
+        self.assertEqual(sigs[1].validity_reason, None)
+
+    def test_verify_multiple_clearsigned_sections(self):
+        signature = StringIO.StringIO(dedent('''
+            -----BEGIN PGP SIGNED MESSAGE-----
+            Hash: SHA1
+
+            Hello World
+            -----BEGIN PGP SIGNATURE-----
+            Version: GnuPG v1.4.1 (GNU/Linux)
+
+            iD8DBQFD1tutRrtV8IhcZaQRAgD4AJ9oqVSFt3UW1lUxhnNM9YXh2G09AQCdGxEe
+            zlgLsoU2R8b0RrGZAHb+Dzw=
+            =rTzD
+            -----END PGP SIGNATURE-----
+
+            -----BEGIN PGP SIGNED MESSAGE-----
+            Hash: SHA1
+
+            Hello World
+            -----BEGIN PGP SIGNATURE-----
+            Version: GnuPG v1.4.1 (GNU/Linux)
+
+            iQIVAwUBQ9bbrSz0a3/JfmsPAQIA+A/+I5mr+hlq+keZZqZGGYQ/U9VdYBJHX/mG
+            Rf3KQERuReimE/pYUDmeLAxys9KD5uBwtd4ajhCYalNIsPZB1W3jXxpQWe0A238v
+            zAQD//bpEO8i2LH17IrSd3plAMCvcfR8Fk/CTAERZbQ5/cPc1Wrzt6BI+fQvqwI2
+            aEyi6vYVeYqq7WXdlyfo5WztfL0igGGrPj5Aw0BblyfaQAxkgNeLUXT2wvBNoOGF
+            Nk+hNW4UQiUVAmZX5iHNKD+CGUX2WNCQYwYCETVGa84Ve0wxMbstBnT3eMBczAPD
+            6QESHmm0YtAM1rx1jLH2dehksXhCInvD6AHnmKNkcOk2C1tua5tr2hdbPRRkR4WY
+            9/pAkV0CWVH9xomzLIDAlOlRYE/jsY14qX9iLvahoegORN749pWacMAUfXA+BhOQ
+            6afPtM5tMwzz6Pc/pXTk2WQueWPXDXJ/CjFg6KqtcnXa3wMSi1LoyZosfkZXfIuz
+            wE3SZ2IQUMTQNXfjwyHj6DWPqKzjhlD8VdOvtHmle+eeRpT1xqb910Anfh/2WMY7
+            QxLCvwZcDKkGb8T8Rxc8ajjPUNrhHT6Tawk/msmNDZyWBZDNjXmh5Y/UzfaN77pf
+            47G49HVk0RLx1hsNLVPFpHWsOCrLhbm8CgVNPy/TLtJiDmCHvL3n0KNSnZv5u2oE
+            9mE1kR0aVOM=
+            =3Ryd
+            -----END PGP SIGNATURE-----
+            '''))
+        plaintext = StringIO.StringIO()
+        ctx = gpgme.Context()
+        sigs = ctx.verify(signature, None, plaintext)
+
+        self.assertEqual(plaintext.getvalue(),
+                         'Hello World\nHello World\n')
+        self.assertEqual(len(sigs), 2)
+        self.assertEqual(sigs[0].summary, 0)
+        self.assertEqual(sigs[0].fpr,
+                         'E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        self.assertEqual(sigs[0].status, None)
+        self.assertEqual(sigs[0].notations, [])
+        self.assertEqual(sigs[0].timestamp, 1138154413)
+        self.assertEqual(sigs[0].exp_timestamp, 0)
+        self.assertEqual(sigs[0].wrong_key_usage, False)
+        self.assertEqual(sigs[0].validity, gpgme.VALIDITY_UNKNOWN)
+        self.assertEqual(sigs[0].validity_reason, None)
+
+        self.assertEqual(sigs[1].summary, 0)
+        self.assertEqual(sigs[1].fpr,
+                         '93C2240D6B8AA10AB28F701D2CF46B7FC97E6B0F')
+        self.assertEqual(sigs[1].status, None)
+        self.assertEqual(sigs[1].notations, [])
+        self.assertEqual(sigs[1].timestamp, 1138154413)
+        self.assertEqual(sigs[1].exp_timestamp, 0)
+        self.assertEqual(sigs[1].wrong_key_usage, False)
+        self.assertEqual(sigs[1].validity, gpgme.VALIDITY_UNKNOWN)
+        self.assertEqual(sigs[1].validity_reason, None)
+
+    def test_verify_no_signature(self):
+        signature = StringIO.StringIO(dedent('''
+            -----BEGIN PGP SIGNED MESSAGE-----
+            Hash: SHA1
+
+            Hello World
+            -----BEGIN PGP SIGNATURE-----
+            -----END PGP SIGNATURE-----
+            '''))
+        plaintext = StringIO.StringIO()
+        ctx = gpgme.Context()
+        sigs = ctx.verify(signature, None, plaintext)
+
+        self.assertEqual(plaintext.getvalue(), '')
+        self.assertEqual(len(sigs), 0)
+
+    def test_verify_bad_signature(self):
+        signature = StringIO.StringIO(dedent('''
+            -----BEGIN PGP SIGNED MESSAGE-----
+            Hash: SHA1
+
+            Hello World
+            -----BEGIN PGP SIGNATURE-----
+            Version: GnuPG v1.4.1 (GNU/Linux)
+
+            iNhhNHx+gzGBUqtIK5LpENTCGgCfV3aO
+            -----END PGP SIGNATURE-----
+            '''))
+        plaintext = StringIO.StringIO()
+        ctx = gpgme.Context()
+        try:
+            ctx.verify(signature, None, plaintext)
+        except gpgme.GpgmeError, exc:
+            self.assertEqual(exc[0], gpgme.ERR_SOURCE_GPGME)
+            self.assertEqual(exc[1], gpgme.ERR_NO_DATA)
+        else:
+            self.fail('gpgme.GpgmeError not raised')
+
+    def test_sign_normal(self):
+        ctx = gpgme.Context()
+        ctx.armor = False
+        key = ctx.get_key('E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        ctx.signers = [key]
+        plaintext = StringIO.StringIO('Hello World\n')
+        signature = StringIO.StringIO()
+
+        new_sigs = ctx.sign(plaintext, signature, gpgme.SIG_MODE_NORMAL)
+        self.assertEqual(len(new_sigs), 1)
+        self.assertEqual(new_sigs[0].type, gpgme.SIG_MODE_NORMAL)
+        self.assertEqual(new_sigs[0].fpr,
+                        'E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+
+        # now verify the signature
+        signature.seek(0)
+        plaintext = StringIO.StringIO()
+        sigs = ctx.verify(signature, None, plaintext)
+        self.assertEqual(plaintext.getvalue(), 'Hello World\n')
+        self.assertEqual(len(sigs), 1)
+        self.assertEqual(sigs[0].summary, 0)
+        self.assertEqual(sigs[0].fpr,
+                         'E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        self.assertEqual(sigs[0].status, None)
+        self.assertEqual(sigs[0].wrong_key_usage, False)
+        self.assertEqual(sigs[0].validity, gpgme.VALIDITY_UNKNOWN)
+        self.assertEqual(sigs[0].validity_reason, None)
+
+    def test_sign_normal_armor(self):
+        ctx = gpgme.Context()
+        ctx.armor = True
+        key = ctx.get_key('E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        ctx.signers = [key]
+        plaintext = StringIO.StringIO('Hello World\n')
+        signature = StringIO.StringIO()
+
+        new_sigs = ctx.sign(plaintext, signature, gpgme.SIG_MODE_NORMAL)
+        self.assertEqual(len(new_sigs), 1)
+        self.assertEqual(new_sigs[0].type, gpgme.SIG_MODE_NORMAL)
+        self.assertEqual(new_sigs[0].fpr,
+                        'E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+
+        # now verify the signature
+        signature.seek(0)
+        plaintext = StringIO.StringIO()
+        sigs = ctx.verify(signature, None, plaintext)
+        self.assertEqual(plaintext.getvalue(), 'Hello World\n')
+        self.assertEqual(len(sigs), 1)
+        self.assertEqual(sigs[0].summary, 0)
+        self.assertEqual(sigs[0].fpr,
+                         'E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        self.assertEqual(sigs[0].status, None)
+        self.assertEqual(sigs[0].wrong_key_usage, False)
+        self.assertEqual(sigs[0].validity, gpgme.VALIDITY_UNKNOWN)
+        self.assertEqual(sigs[0].validity_reason, None)
+
+    def test_sign_detatch(self):
+        ctx = gpgme.Context()
+        ctx.armor = True
+        key = ctx.get_key('E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        ctx.signers = [key]
+        plaintext = StringIO.StringIO('Hello World\n')
+        signature = StringIO.StringIO()
+
+        new_sigs = ctx.sign(plaintext, signature, gpgme.SIG_MODE_DETACH)
+        self.assertEqual(len(new_sigs), 1)
+        self.assertEqual(new_sigs[0].type, gpgme.SIG_MODE_DETACH)
+        self.assertEqual(new_sigs[0].fpr,
+                        'E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+
+        # now verify the signature
+        signature.seek(0)
+        plaintext.seek(0)
+        sigs = ctx.verify(signature, plaintext, None)
+        self.assertEqual(len(sigs), 1)
+        self.assertEqual(sigs[0].summary, 0)
+        self.assertEqual(sigs[0].fpr,
+                         'E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        self.assertEqual(sigs[0].status, None)
+        self.assertEqual(sigs[0].wrong_key_usage, False)
+        self.assertEqual(sigs[0].validity, gpgme.VALIDITY_UNKNOWN)
+        self.assertEqual(sigs[0].validity_reason, None)
+
+    def test_sign_clearsign(self):
+        ctx = gpgme.Context()
+        ctx.armor = True
+        key = ctx.get_key('E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        ctx.signers = [key]
+        plaintext = StringIO.StringIO('Hello World\n')
+        signature = StringIO.StringIO()
+
+        new_sigs = ctx.sign(plaintext, signature, gpgme.SIG_MODE_CLEAR)
+        self.assertEqual(len(new_sigs), 1)
+        self.assertEqual(new_sigs[0].type, gpgme.SIG_MODE_CLEAR)
+        self.assertEqual(new_sigs[0].fpr,
+                        'E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+
+        # now verify the signature
+        signature.seek(0)
+        plaintext = StringIO.StringIO()
+        sigs = ctx.verify(signature, None, plaintext)
+        self.assertEqual(plaintext.getvalue(), 'Hello World\n')
+        self.assertEqual(len(sigs), 1)
+        self.assertEqual(sigs[0].summary, 0)
+        self.assertEqual(sigs[0].fpr,
+                         'E79A842DA34A1CA383F64A1546BB55F0885C65A4')
+        self.assertEqual(sigs[0].status, None)
+        self.assertEqual(sigs[0].wrong_key_usage, False)
+        self.assertEqual(sigs[0].validity, gpgme.VALIDITY_UNKNOWN)
+        self.assertEqual(sigs[0].validity_reason, None)
+
+def test_suite():
+    loader = unittest.TestLoader()
+    return loader.loadTestsFromName(__name__)
diff --git a/gpgme/tests/util.py b/gpgme/tests/util.py
new file mode 100644 (file)
index 0000000..3a3adea
--- /dev/null
@@ -0,0 +1,51 @@
+# pygpgme - a Python wrapper for the gpgme library
+# Copyright (C) 2006  James Henstridge
+#
+# 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import os
+import shutil
+import tempfile
+import unittest
+
+import gpgme
+
+__all__ = ['GpgHomeTestCase']
+
+keydir = os.path.join(os.path.dirname(__file__), 'keys')
+
+class GpgHomeTestCase(unittest.TestCase):
+
+    gpg_conf_contents = ''
+    import_keys = []
+
+    def keyfile(self, key):
+        return open(os.path.join(keydir, key), 'rb')
+
+    def setUp(self):
+        self._gpghome = tempfile.mkdtemp(prefix='tmp.gpghome')
+        os.environ['GNUPGHOME'] = self._gpghome
+        fp = open(os.path.join(self._gpghome, 'gpg.conf'), 'wb')
+        fp.write(self.gpg_conf_contents)
+        fp.close()
+
+        # import requested keys into the keyring
+        ctx = gpgme.Context()
+        for key in self.import_keys:
+            ctx.import_(self.keyfile(key))
+
+    def tearDown(self):
+        del os.environ['GNUPGHOME']
+        shutil.rmtree(self._gpghome, ignore_errors=True)
diff --git a/setup.py b/setup.py
new file mode 100755 (executable)
index 0000000..a8ef4ff
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+
+from distutils.core import setup, Extension
+
+gpgme = Extension(
+    'gpgme._gpgme',
+    ['src/gpgme.c',
+     'src/pygpgme-error.c',
+     'src/pygpgme-data.c',
+     'src/pygpgme-context.c',
+     'src/pygpgme-key.c',
+     'src/pygpgme-signature.c',
+     'src/pygpgme-import.c',
+     'src/pygpgme-keyiter.c',
+     'src/pygpgme-constants.c',
+     ],
+    libraries=['gpgme'])
+
+setup(name='pygpgme',
+      version='0.1',
+      author='James Henstridge',
+      author_email='james@jamesh.id.au',
+      description='A Python module for working with OpenPGP messages',
+      long_description='''
+          PyGPGME is a Python module that lets you sign, verify, encrypt
+          and decrypt messages using the OpenPGP format.
+
+          It is built on top of the GNU Privacy Guard and the GPGME
+          library.''',
+      license='LGPL',
+      classifiers=[
+          'Intended Audience :: Developers',
+          'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
+          'Operating System :: POSIX',
+          'Programming Language :: C',
+          'Programming Language :: Python',
+          'Topic :: Security :: Cryptography',
+          'Topic :: Software Development :: Libraries :: Python Modules'
+      ],
+      url='https://launchpad.net/products/pygpgme',
+      ext_modules=[gpgme],
+      packages=['gpgme', 'gpgme.tests'],
+      package_data={'gpgme.tests': ['keys/*.pub', 'keys/*.sec']})
diff --git a/src/gpgme.c b/src/gpgme.c
new file mode 100644 (file)
index 0000000..09a206f
--- /dev/null
@@ -0,0 +1,75 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+    pygpgme - a Python wrapper for the gpgme library
+    Copyright (C) 2006  James Henstridge
+
+    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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <Python.h>
+#include "pygpgme.h"
+
+static PyMethodDef pygpgme_functions[] = {
+    { "make_constants", (PyCFunction)pygpgme_make_constants, METH_VARARGS },
+    { NULL, NULL, 0 }
+};
+
+PyMODINIT_FUNC
+init_gpgme(void)
+{
+    PyObject *mod;
+
+    pygpgme_error = PyErr_NewException("gpgme.GpgmeError",
+                                       PyExc_RuntimeError, NULL);
+
+#define INIT_TYPE(type)                      \
+    if (!type.ob_type)                       \
+        type.ob_type = &PyType_Type;         \
+    if (!type.tp_alloc)                      \
+        type.tp_alloc = PyType_GenericAlloc; \
+    if (!type.tp_new)                        \
+        type.tp_new = PyType_GenericNew;     \
+    if (PyType_Ready(&type) < 0)             \
+        return
+
+#define ADD_TYPE(type)                \
+    Py_INCREF(&PyGpgme ## type ## _Type); \
+    PyModule_AddObject(mod, #type, (PyObject *)&PyGpgme ## type ## _Type)
+
+    INIT_TYPE(PyGpgmeContext_Type);
+    INIT_TYPE(PyGpgmeKey_Type);
+    INIT_TYPE(PyGpgmeSubkey_Type);
+    INIT_TYPE(PyGpgmeUserId_Type);
+    INIT_TYPE(PyGpgmeKeySig_Type);
+    INIT_TYPE(PyGpgmeNewSignature_Type);
+    INIT_TYPE(PyGpgmeSignature_Type);
+    INIT_TYPE(PyGpgmeImportResult_Type);
+    INIT_TYPE(PyGpgmeKeyIter_Type);
+
+    mod = Py_InitModule("gpgme._gpgme", pygpgme_functions);
+
+    ADD_TYPE(Context);
+    ADD_TYPE(Key);
+    ADD_TYPE(Subkey);
+    ADD_TYPE(UserId);
+    ADD_TYPE(KeySig);
+    ADD_TYPE(NewSignature);
+    ADD_TYPE(Signature);
+    ADD_TYPE(ImportResult);
+    ADD_TYPE(KeyIter);
+
+    Py_INCREF(pygpgme_error);
+    PyModule_AddObject(mod, "GpgmeError", pygpgme_error);
+}
diff --git a/src/pygpgme-constants.c b/src/pygpgme-constants.c
new file mode 100644 (file)
index 0000000..c8a2e9c
--- /dev/null
@@ -0,0 +1,566 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+    pygpgme - a Python wrapper for the gpgme library
+    Copyright (C) 2006  James Henstridge
+
+    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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#include "pygpgme.h"
+
+struct pygpgme_constant {
+  const char name[28];
+  long value;
+};
+
+#define CONST(name) { #name, GPGME_##name }
+
+static const struct pygpgme_constant constants[] = {
+  /* gpgme_data_encoding_t */
+  CONST(DATA_ENCODING_NONE),
+  CONST(DATA_ENCODING_BINARY),
+  CONST(DATA_ENCODING_BASE64),
+  CONST(DATA_ENCODING_ARMOR),
+
+  /* gpgme_pubkey_algo_t */
+  CONST(PK_RSA),
+  CONST(PK_RSA_E),
+  CONST(PK_RSA_S),
+  CONST(PK_ELG_E),
+  CONST(PK_DSA),
+  CONST(PK_ELG),
+
+  /* gpgme_hash_algo_t */
+  CONST(MD_NONE),
+  CONST(MD_MD5),
+  CONST(MD_SHA1),
+  CONST(MD_RMD160),
+  CONST(MD_MD2),
+  CONST(MD_TIGER),
+  CONST(MD_HAVAL),
+  CONST(MD_SHA256),
+  CONST(MD_SHA384),
+  CONST(MD_SHA512),
+  CONST(MD_MD4),
+  CONST(MD_CRC32),
+  CONST(MD_CRC32_RFC1510),
+  CONST(MD_CRC24_RFC2440),
+
+  /* gpgme_sig_mode_t */
+  CONST(SIG_MODE_NORMAL),
+  CONST(SIG_MODE_DETACH),
+  CONST(SIG_MODE_CLEAR),
+
+  /* gpgme_validity_t */
+  CONST(VALIDITY_UNKNOWN),
+  CONST(VALIDITY_UNDEFINED),
+  CONST(VALIDITY_NEVER),
+  CONST(VALIDITY_MARGINAL),
+  CONST(VALIDITY_FULL),
+  CONST(VALIDITY_ULTIMATE),
+
+  /* gpgme_protocol_t */
+  CONST(PROTOCOL_OpenPGP),
+  CONST(PROTOCOL_CMS),
+
+  /* gpgme_keylist_mode_t */
+  CONST(KEYLIST_MODE_LOCAL),
+  CONST(KEYLIST_MODE_EXTERN),
+  CONST(KEYLIST_MODE_SIGS),
+  CONST(KEYLIST_MODE_VALIDATE),
+
+  /* gpgme_status_code_t */
+  CONST(STATUS_EOF),
+  CONST(STATUS_ENTER),
+  CONST(STATUS_LEAVE),
+  CONST(STATUS_ABORT),
+  CONST(STATUS_GOODSIG),
+  CONST(STATUS_BADSIG),
+  CONST(STATUS_ERRSIG),
+  CONST(STATUS_BADARMOR),
+  CONST(STATUS_RSA_OR_IDEA),
+  CONST(STATUS_KEYEXPIRED),
+  CONST(STATUS_KEYREVOKED),
+  CONST(STATUS_TRUST_UNDEFINED),
+  CONST(STATUS_TRUST_NEVER),
+  CONST(STATUS_TRUST_MARGINAL),
+  CONST(STATUS_TRUST_FULLY),
+  CONST(STATUS_TRUST_ULTIMATE),
+  CONST(STATUS_SHM_INFO),
+  CONST(STATUS_SHM_GET),
+  CONST(STATUS_SHM_GET_BOOL),
+  CONST(STATUS_SHM_GET_HIDDEN),
+  CONST(STATUS_NEED_PASSPHRASE),
+  CONST(STATUS_VALIDSIG),
+  CONST(STATUS_SIG_ID),
+  CONST(STATUS_ENC_TO),
+  CONST(STATUS_NODATA),
+  CONST(STATUS_BAD_PASSPHRASE),
+  CONST(STATUS_NO_PUBKEY),
+  CONST(STATUS_NO_SECKEY),
+  CONST(STATUS_NEED_PASSPHRASE_SYM),
+  CONST(STATUS_DECRYPTION_FAILED),
+  CONST(STATUS_DECRYPTION_OKAY),
+  CONST(STATUS_MISSING_PASSPHRASE),
+  CONST(STATUS_GOOD_PASSPHRASE),
+  CONST(STATUS_GOODMDC),
+  CONST(STATUS_BADMDC),
+  CONST(STATUS_ERRMDC),
+  CONST(STATUS_IMPORTED),
+  CONST(STATUS_IMPORT_OK),
+  CONST(STATUS_IMPORT_PROBLEM),
+  CONST(STATUS_IMPORT_RES),
+  CONST(STATUS_FILE_START),
+  CONST(STATUS_FILE_DONE),
+  CONST(STATUS_FILE_ERROR),
+  CONST(STATUS_BEGIN_DECRYPTION),
+  CONST(STATUS_END_DECRYPTION),
+  CONST(STATUS_BEGIN_ENCRYPTION),
+  CONST(STATUS_END_ENCRYPTION),
+  CONST(STATUS_DELETE_PROBLEM),
+  CONST(STATUS_GET_BOOL),
+  CONST(STATUS_GET_LINE),
+  CONST(STATUS_GET_HIDDEN),
+  CONST(STATUS_GOT_IT),
+  CONST(STATUS_PROGRESS),
+  CONST(STATUS_SIG_CREATED),
+  CONST(STATUS_SESSION_KEY),
+  CONST(STATUS_NOTATION_NAME),
+  CONST(STATUS_NOTATION_DATA),
+  CONST(STATUS_POLICY_URL),
+  CONST(STATUS_BEGIN_STREAM),
+  CONST(STATUS_END_STREAM),
+  CONST(STATUS_KEY_CREATED),
+  CONST(STATUS_USERID_HINT),
+  CONST(STATUS_UNEXPECTED),
+  CONST(STATUS_INV_RECP),
+  CONST(STATUS_NO_RECP),
+  CONST(STATUS_ALREADY_SIGNED),
+  CONST(STATUS_SIGEXPIRED),
+  CONST(STATUS_EXPSIG),
+  CONST(STATUS_EXPKEYSIG),
+  CONST(STATUS_TRUNCATED),
+  CONST(STATUS_ERROR),
+  CONST(STATUS_NEWSIG),
+  CONST(STATUS_REVKEYSIG),
+
+  /* gpgme_encrypt_flags_t */
+  CONST(ENCRYPT_ALWAYS_TRUST),
+
+  /* gpgme_sigsum_t */
+  CONST(SIGSUM_VALID),
+  CONST(SIGSUM_GREEN),
+  CONST(SIGSUM_RED),
+  CONST(SIGSUM_KEY_REVOKED),
+  CONST(SIGSUM_KEY_EXPIRED),
+  CONST(SIGSUM_SIG_EXPIRED),
+  CONST(SIGSUM_KEY_MISSING),
+  CONST(SIGSUM_CRL_MISSING),
+  CONST(SIGSUM_CRL_TOO_OLD),
+  CONST(SIGSUM_BAD_POLICY),
+  CONST(SIGSUM_SYS_ERROR),
+
+  /* import status */
+  CONST(IMPORT_NEW),
+  CONST(IMPORT_UID),
+  CONST(IMPORT_SIG),
+  CONST(IMPORT_SUBKEY),
+  CONST(IMPORT_SECRET),
+
+  /* gpg-error.h constants */
+#undef CONST
+#define CONST(name) { #name, GPG_##name }
+  CONST(ERR_SOURCE_UNKNOWN),
+  CONST(ERR_SOURCE_GCRYPT),
+  CONST(ERR_SOURCE_GPG),
+  CONST(ERR_SOURCE_GPGSM),
+  CONST(ERR_SOURCE_GPGAGENT),
+  CONST(ERR_SOURCE_PINENTRY),
+  CONST(ERR_SOURCE_SCD),
+  CONST(ERR_SOURCE_GPGME),
+  CONST(ERR_SOURCE_KEYBOX),
+  CONST(ERR_SOURCE_KSBA),
+  CONST(ERR_SOURCE_DIRMNGR),
+  CONST(ERR_SOURCE_GSTI),
+  CONST(ERR_SOURCE_USER_1),
+  CONST(ERR_SOURCE_USER_2),
+  CONST(ERR_SOURCE_USER_3),
+  CONST(ERR_SOURCE_USER_4),
+
+  CONST(ERR_NO_ERROR),
+  CONST(ERR_GENERAL),
+  CONST(ERR_UNKNOWN_PACKET),
+  CONST(ERR_UNKNOWN_VERSION),
+  CONST(ERR_PUBKEY_ALGO),
+  CONST(ERR_DIGEST_ALGO),
+  CONST(ERR_BAD_PUBKEY),
+  CONST(ERR_BAD_SECKEY),
+  CONST(ERR_BAD_SIGNATURE),
+  CONST(ERR_NO_PUBKEY),
+  CONST(ERR_CHECKSUM),
+  CONST(ERR_BAD_PASSPHRASE),
+  CONST(ERR_CIPHER_ALGO),
+  CONST(ERR_KEYRING_OPEN),
+  CONST(ERR_INV_PACKET),
+  CONST(ERR_INV_ARMOR),
+  CONST(ERR_NO_USER_ID),
+  CONST(ERR_NO_SECKEY),
+  CONST(ERR_WRONG_SECKEY),
+  CONST(ERR_BAD_KEY),
+  CONST(ERR_COMPR_ALGO),
+  CONST(ERR_NO_PRIME),
+  CONST(ERR_NO_ENCODING_METHOD),
+  CONST(ERR_NO_ENCRYPTION_SCHEME),
+  CONST(ERR_NO_SIGNATURE_SCHEME),
+  CONST(ERR_INV_ATTR),
+  CONST(ERR_NO_VALUE),
+  CONST(ERR_NOT_FOUND),
+  CONST(ERR_VALUE_NOT_FOUND),
+  CONST(ERR_SYNTAX),
+  CONST(ERR_BAD_MPI),
+  CONST(ERR_INV_PASSPHRASE),
+  CONST(ERR_SIG_CLASS),
+  CONST(ERR_RESOURCE_LIMIT),
+  CONST(ERR_INV_KEYRING),
+  CONST(ERR_TRUSTDB),
+  CONST(ERR_BAD_CERT),
+  CONST(ERR_INV_USER_ID),
+  CONST(ERR_UNEXPECTED),
+  CONST(ERR_TIME_CONFLICT),
+  CONST(ERR_KEYSERVER),
+  CONST(ERR_WRONG_PUBKEY_ALGO),
+  CONST(ERR_TRIBUTE_TO_D_A),
+  CONST(ERR_WEAK_KEY),
+  CONST(ERR_INV_KEYLEN),
+  CONST(ERR_INV_ARG),
+  CONST(ERR_BAD_URI),
+  CONST(ERR_INV_URI),
+  CONST(ERR_NETWORK),
+  CONST(ERR_UNKNOWN_HOST),
+  CONST(ERR_SELFTEST_FAILED),
+  CONST(ERR_NOT_ENCRYPTED),
+  CONST(ERR_NOT_PROCESSED),
+  CONST(ERR_UNUSABLE_PUBKEY),
+  CONST(ERR_UNUSABLE_SECKEY),
+  CONST(ERR_INV_VALUE),
+  CONST(ERR_BAD_CERT_CHAIN),
+  CONST(ERR_MISSING_CERT),
+  CONST(ERR_NO_DATA),
+  CONST(ERR_BUG),
+  CONST(ERR_NOT_SUPPORTED),
+  CONST(ERR_INV_OP),
+  CONST(ERR_TIMEOUT),
+  CONST(ERR_INTERNAL),
+  CONST(ERR_EOF_GCRYPT),
+  CONST(ERR_INV_OBJ),
+  CONST(ERR_TOO_SHORT),
+  CONST(ERR_TOO_LARGE),
+  CONST(ERR_NO_OBJ),
+  CONST(ERR_NOT_IMPLEMENTED),
+  CONST(ERR_CONFLICT),
+  CONST(ERR_INV_CIPHER_MODE),
+  CONST(ERR_INV_FLAG),
+  CONST(ERR_INV_HANDLE),
+  CONST(ERR_TRUNCATED),
+  CONST(ERR_INCOMPLETE_LINE),
+  CONST(ERR_INV_RESPONSE),
+  CONST(ERR_NO_AGENT),
+  CONST(ERR_AGENT),
+  CONST(ERR_INV_DATA),
+  CONST(ERR_ASSUAN_SERVER_FAULT),
+  CONST(ERR_ASSUAN),
+  CONST(ERR_INV_SESSION_KEY),
+  CONST(ERR_INV_SEXP),
+  CONST(ERR_UNSUPPORTED_ALGORITHM),
+  CONST(ERR_NO_PIN_ENTRY),
+  CONST(ERR_PIN_ENTRY),
+  CONST(ERR_BAD_PIN),
+  CONST(ERR_INV_NAME),
+  CONST(ERR_BAD_DATA),
+  CONST(ERR_INV_PARAMETER),
+  CONST(ERR_WRONG_CARD),
+  CONST(ERR_NO_DIRMNGR),
+  CONST(ERR_DIRMNGR),
+  CONST(ERR_CERT_REVOKED),
+  CONST(ERR_NO_CRL_KNOWN),
+  CONST(ERR_CRL_TOO_OLD),
+  CONST(ERR_LINE_TOO_LONG),
+  CONST(ERR_NOT_TRUSTED),
+  CONST(ERR_CANCELED),
+  CONST(ERR_BAD_CA_CERT),
+  CONST(ERR_CERT_EXPIRED),
+  CONST(ERR_CERT_TOO_YOUNG),
+  CONST(ERR_UNSUPPORTED_CERT),
+  CONST(ERR_UNKNOWN_SEXP),
+  CONST(ERR_UNSUPPORTED_PROTECTION),
+  CONST(ERR_CORRUPTED_PROTECTION),
+  CONST(ERR_AMBIGUOUS_NAME),
+  CONST(ERR_CARD),
+  CONST(ERR_CARD_RESET),
+  CONST(ERR_CARD_REMOVED),
+  CONST(ERR_INV_CARD),
+  CONST(ERR_CARD_NOT_PRESENT),
+  CONST(ERR_NO_PKCS15_APP),
+  CONST(ERR_NOT_CONFIRMED),
+  CONST(ERR_CONFIGURATION),
+  CONST(ERR_NO_POLICY_MATCH),
+  CONST(ERR_INV_INDEX),
+  CONST(ERR_INV_ID),
+  CONST(ERR_NO_SCDAEMON),
+  CONST(ERR_SCDAEMON),
+  CONST(ERR_UNSUPPORTED_PROTOCOL),
+  CONST(ERR_BAD_PIN_METHOD),
+  CONST(ERR_CARD_NOT_INITIALIZED),
+  CONST(ERR_UNSUPPORTED_OPERATION),
+  CONST(ERR_WRONG_KEY_USAGE),
+  CONST(ERR_NOTHING_FOUND),
+  CONST(ERR_WRONG_BLOB_TYPE),
+  CONST(ERR_MISSING_VALUE),
+  CONST(ERR_HARDWARE),
+  CONST(ERR_PIN_BLOCKED),
+  CONST(ERR_USE_CONDITIONS),
+  CONST(ERR_PIN_NOT_SYNCED),
+  CONST(ERR_INV_CRL),
+  CONST(ERR_BAD_BER),
+  CONST(ERR_INV_BER),
+  CONST(ERR_ELEMENT_NOT_FOUND),
+  CONST(ERR_IDENTIFIER_NOT_FOUND),
+  CONST(ERR_INV_TAG),
+  CONST(ERR_INV_LENGTH),
+  CONST(ERR_INV_KEYINFO),
+  CONST(ERR_UNEXPECTED_TAG),
+  CONST(ERR_NOT_DER_ENCODED),
+  CONST(ERR_NO_CMS_OBJ),
+  CONST(ERR_INV_CMS_OBJ),
+  CONST(ERR_UNKNOWN_CMS_OBJ),
+  CONST(ERR_UNSUPPORTED_CMS_OBJ),
+  CONST(ERR_UNSUPPORTED_ENCODING),
+  CONST(ERR_UNSUPPORTED_CMS_VERSION),
+  CONST(ERR_UNKNOWN_ALGORITHM),
+  CONST(ERR_INV_ENGINE),
+  CONST(ERR_PUBKEY_NOT_TRUSTED),
+  CONST(ERR_DECRYPT_FAILED),
+  CONST(ERR_KEY_EXPIRED),
+  CONST(ERR_SIG_EXPIRED),
+  CONST(ERR_ENCODING_PROBLEM),
+  CONST(ERR_INV_STATE),
+  CONST(ERR_DUP_VALUE),
+  CONST(ERR_MISSING_ACTION),
+  CONST(ERR_MODULE_NOT_FOUND),
+  CONST(ERR_INV_OID_STRING),
+  CONST(ERR_INV_TIME),
+  CONST(ERR_INV_CRL_OBJ),
+  CONST(ERR_UNSUPPORTED_CRL_VERSION),
+  CONST(ERR_INV_CERT_OBJ),
+  CONST(ERR_UNKNOWN_NAME),
+  CONST(ERR_LOCALE_PROBLEM),
+  CONST(ERR_NOT_LOCKED),
+  CONST(ERR_PROTOCOL_VIOLATION),
+  CONST(ERR_INV_MAC),
+  CONST(ERR_INV_REQUEST),
+  CONST(ERR_BUFFER_TOO_SHORT),
+  CONST(ERR_SEXP_INV_LEN_SPEC),
+  CONST(ERR_SEXP_STRING_TOO_LONG),
+  CONST(ERR_SEXP_UNMATCHED_PAREN),
+  CONST(ERR_SEXP_NOT_CANONICAL),
+  CONST(ERR_SEXP_BAD_CHARACTER),
+  CONST(ERR_SEXP_BAD_QUOTATION),
+  CONST(ERR_SEXP_ZERO_PREFIX),
+  CONST(ERR_SEXP_NESTED_DH),
+  CONST(ERR_SEXP_UNMATCHED_DH),
+  CONST(ERR_SEXP_UNEXPECTED_PUNC),
+  CONST(ERR_SEXP_BAD_HEX_CHAR),
+  CONST(ERR_SEXP_ODD_HEX_NUMBERS),
+  CONST(ERR_SEXP_BAD_OCT_CHAR),
+  CONST(ERR_USER_1),
+  CONST(ERR_USER_2),
+  CONST(ERR_USER_3),
+  CONST(ERR_USER_4),
+  CONST(ERR_USER_5),
+  CONST(ERR_USER_6),
+  CONST(ERR_USER_7),
+  CONST(ERR_USER_8),
+  CONST(ERR_USER_9),
+  CONST(ERR_USER_10),
+  CONST(ERR_USER_11),
+  CONST(ERR_USER_12),
+  CONST(ERR_USER_13),
+  CONST(ERR_USER_14),
+  CONST(ERR_USER_15),
+  CONST(ERR_USER_16),
+  CONST(ERR_UNKNOWN_ERRNO),
+  CONST(ERR_EOF),
+  CONST(ERR_E2BIG),
+  CONST(ERR_EACCES),
+  CONST(ERR_EADDRINUSE),
+  CONST(ERR_EADDRNOTAVAIL),
+  CONST(ERR_EADV),
+  CONST(ERR_EAFNOSUPPORT),
+  CONST(ERR_EAGAIN),
+  CONST(ERR_EALREADY),
+  CONST(ERR_EAUTH),
+  CONST(ERR_EBACKGROUND),
+  CONST(ERR_EBADE),
+  CONST(ERR_EBADF),
+  CONST(ERR_EBADFD),
+  CONST(ERR_EBADMSG),
+  CONST(ERR_EBADR),
+  CONST(ERR_EBADRPC),
+  CONST(ERR_EBADRQC),
+  CONST(ERR_EBADSLT),
+  CONST(ERR_EBFONT),
+  CONST(ERR_EBUSY),
+  CONST(ERR_ECANCELED),
+  CONST(ERR_ECHILD),
+  CONST(ERR_ECHRNG),
+  CONST(ERR_ECOMM),
+  CONST(ERR_ECONNABORTED),
+  CONST(ERR_ECONNREFUSED),
+  CONST(ERR_ECONNRESET),
+  CONST(ERR_ED),
+  CONST(ERR_EDEADLK),
+  CONST(ERR_EDEADLOCK),
+  CONST(ERR_EDESTADDRREQ),
+  CONST(ERR_EDIED),
+  CONST(ERR_EDOM),
+  CONST(ERR_EDOTDOT),
+  CONST(ERR_EDQUOT),
+  CONST(ERR_EEXIST),
+  CONST(ERR_EFAULT),
+  CONST(ERR_EFBIG),
+  CONST(ERR_EFTYPE),
+  CONST(ERR_EGRATUITOUS),
+  CONST(ERR_EGREGIOUS),
+  CONST(ERR_EHOSTDOWN),
+  CONST(ERR_EHOSTUNREACH),
+  CONST(ERR_EIDRM),
+  CONST(ERR_EIEIO),
+  CONST(ERR_EILSEQ),
+  CONST(ERR_EINPROGRESS),
+  CONST(ERR_EINTR),
+  CONST(ERR_EINVAL),
+  CONST(ERR_EIO),
+  CONST(ERR_EISCONN),
+  CONST(ERR_EISDIR),
+  CONST(ERR_EISNAM),
+  CONST(ERR_EL2HLT),
+  CONST(ERR_EL2NSYNC),
+  CONST(ERR_EL3HLT),
+  CONST(ERR_EL3RST),
+  CONST(ERR_ELIBACC),
+  CONST(ERR_ELIBBAD),
+  CONST(ERR_ELIBEXEC),
+  CONST(ERR_ELIBMAX),
+  CONST(ERR_ELIBSCN),
+  CONST(ERR_ELNRNG),
+  CONST(ERR_ELOOP),
+  CONST(ERR_EMEDIUMTYPE),
+  CONST(ERR_EMFILE),
+  CONST(ERR_EMLINK),
+  CONST(ERR_EMSGSIZE),
+  CONST(ERR_EMULTIHOP),
+  CONST(ERR_ENAMETOOLONG),
+  CONST(ERR_ENAVAIL),
+  CONST(ERR_ENEEDAUTH),
+  CONST(ERR_ENETDOWN),
+  CONST(ERR_ENETRESET),
+  CONST(ERR_ENETUNREACH),
+  CONST(ERR_ENFILE),
+  CONST(ERR_ENOANO),
+  CONST(ERR_ENOBUFS),
+  CONST(ERR_ENOCSI),
+  CONST(ERR_ENODATA),
+  CONST(ERR_ENODEV),
+  CONST(ERR_ENOENT),
+  CONST(ERR_ENOEXEC),
+  CONST(ERR_ENOLCK),
+  CONST(ERR_ENOLINK),
+  CONST(ERR_ENOMEDIUM),
+  CONST(ERR_ENOMEM),
+  CONST(ERR_ENOMSG),
+  CONST(ERR_ENONET),
+  CONST(ERR_ENOPKG),
+  CONST(ERR_ENOPROTOOPT),
+  CONST(ERR_ENOSPC),
+  CONST(ERR_ENOSR),
+  CONST(ERR_ENOSTR),
+  CONST(ERR_ENOSYS),
+  CONST(ERR_ENOTBLK),
+  CONST(ERR_ENOTCONN),
+  CONST(ERR_ENOTDIR),
+  CONST(ERR_ENOTEMPTY),
+  CONST(ERR_ENOTNAM),
+  CONST(ERR_ENOTSOCK),
+  CONST(ERR_ENOTSUP),
+  CONST(ERR_ENOTTY),
+  CONST(ERR_ENOTUNIQ),
+  CONST(ERR_ENXIO),
+  CONST(ERR_EOPNOTSUPP),
+  CONST(ERR_EOVERFLOW),
+  CONST(ERR_EPERM),
+  CONST(ERR_EPFNOSUPPORT),
+  CONST(ERR_EPIPE),
+  CONST(ERR_EPROCLIM),
+  CONST(ERR_EPROCUNAVAIL),
+  CONST(ERR_EPROGMISMATCH),
+  CONST(ERR_EPROGUNAVAIL),
+  CONST(ERR_EPROTO),
+  CONST(ERR_EPROTONOSUPPORT),
+  CONST(ERR_EPROTOTYPE),
+  CONST(ERR_ERANGE),
+  CONST(ERR_EREMCHG),
+  CONST(ERR_EREMOTE),
+  CONST(ERR_EREMOTEIO),
+  CONST(ERR_ERESTART),
+  CONST(ERR_EROFS),
+  CONST(ERR_ERPCMISMATCH),
+  CONST(ERR_ESHUTDOWN),
+  CONST(ERR_ESOCKTNOSUPPORT),
+  CONST(ERR_ESPIPE),
+  CONST(ERR_ESRCH),
+  CONST(ERR_ESRMNT),
+  CONST(ERR_ESTALE),
+  CONST(ERR_ESTRPIPE),
+  CONST(ERR_ETIME),
+  CONST(ERR_ETIMEDOUT),
+  CONST(ERR_ETOOMANYREFS),
+  CONST(ERR_ETXTBSY),
+  CONST(ERR_EUCLEAN),
+  CONST(ERR_EUNATCH),
+  CONST(ERR_EUSERS),
+  CONST(ERR_EWOULDBLOCK),
+  CONST(ERR_EXDEV),
+  CONST(ERR_EXFULL),
+};
+
+static const int n_constants = sizeof(constants) / sizeof(constants[0]);
+
+PyObject *
+pygpgme_make_constants(PyObject *self, PyObject *args)
+{
+    PyObject *dict;
+    int i;
+
+    if (!PyArg_ParseTuple(args, "O!", &PyDict_Type, &dict))
+        return NULL;
+
+    for (i = 0; i < n_constants; i++) {
+        PyObject *item;
+
+        item = PyInt_FromLong(constants[i].value);
+        PyDict_SetItemString(dict, constants[i].name, item);
+        Py_DECREF(item);
+    }
+    Py_RETURN_NONE;
+}
diff --git a/src/pygpgme-context.c b/src/pygpgme-context.c
new file mode 100644 (file)
index 0000000..cd3983d
--- /dev/null
@@ -0,0 +1,1168 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+    pygpgme - a Python wrapper for the gpgme library
+    Copyright (C) 2006  James Henstridge
+
+    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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#include "pygpgme.h"
+
+static gpgme_error_t
+pygpgme_passphrase_cb(void *hook, const char *uid_hint,
+                      const char *passphrase_info, int prev_was_bad,
+                      int fd)
+{
+    PyObject *callback, *ret;
+    PyGILState_STATE state;
+    gpgme_error_t err;
+
+    state = PyGILState_Ensure();
+    callback = (PyObject *)hook;
+    ret = PyObject_CallFunction(callback, "zzii", uid_hint, passphrase_info,
+                                prev_was_bad, fd);
+    err = pygpgme_check_pyerror();
+    Py_XDECREF(ret);
+    PyGILState_Release(state);
+    return err;
+}
+
+static void
+pygpgme_progress_cb(void *hook, const char *what, int type,
+                    int current, int total)
+{
+    PyObject *callback, *ret;
+    PyGILState_STATE state;
+
+    state = PyGILState_Ensure();
+    callback = (PyObject *)hook;
+    ret = PyObject_CallFunction(callback, "ziii", what, type, current, total);
+    PyErr_Clear();
+    Py_XDECREF(ret);
+    PyGILState_Release(state);
+}
+
+static void
+pygpgme_context_dealloc(PyGpgmeContext *self)
+{
+    gpgme_passphrase_cb_t passphrase_cb;
+    gpgme_progress_cb_t progress_cb;
+    PyObject *callback;
+
+    if (self->ctx) {
+        /* free the passphrase callback */
+        gpgme_get_passphrase_cb(self->ctx, &passphrase_cb, (void **)&callback);
+        if (passphrase_cb == pygpgme_passphrase_cb) {
+            Py_DECREF(callback);
+        }
+
+        /* free the progress callback */
+        gpgme_get_progress_cb(self->ctx, &progress_cb, (void **)&callback);
+        if (progress_cb == pygpgme_progress_cb) {
+            Py_DECREF(callback);
+        }
+
+        gpgme_release(self->ctx);
+    }
+    self->ctx = NULL;
+    PyObject_Del(self);
+}
+
+static int
+pygpgme_context_init(PyGpgmeContext *self, PyObject *args, PyObject *kwargs)
+{
+    static char *kwlist[] = { NULL };
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
+        return -1;
+
+    if (self->ctx != NULL) {
+        PyErr_SetString(PyExc_ValueError, "context already initialised");
+        return -1;
+    }
+
+    if (pygpgme_check_error(gpgme_new(&self->ctx)))
+        return -1;
+
+    return 0;
+}
+
+static PyObject *
+pygpgme_context_get_protocol(PyGpgmeContext *self)
+{
+    return PyInt_FromLong(gpgme_get_protocol(self->ctx));
+}
+
+static int
+pygpgme_context_set_protocol(PyGpgmeContext *self, PyObject *value)
+{
+    gpgme_protocol_t protocol;
+
+    protocol = PyInt_AsLong(value);
+    if (PyErr_Occurred())
+        return -1;
+
+    if (pygpgme_check_error(gpgme_set_protocol(self->ctx, protocol)))
+        return -1;
+
+    return 0;
+}
+
+static PyObject *
+pygpgme_context_get_armor(PyGpgmeContext *self)
+{
+    return PyBool_FromLong(gpgme_get_armor(self->ctx));
+}
+
+static int
+pygpgme_context_set_armor(PyGpgmeContext *self, PyObject *value)
+{
+    int armor;
+
+    armor = PyInt_AsLong(value) != 0;
+    if (PyErr_Occurred())
+        return -1;
+
+    gpgme_set_armor(self->ctx, armor);
+    return 0;
+}
+
+static PyObject *
+pygpgme_context_get_textmode(PyGpgmeContext *self)
+{
+    return PyBool_FromLong(gpgme_get_textmode(self->ctx));
+}
+
+static int
+pygpgme_context_set_textmode(PyGpgmeContext *self, PyObject *value)
+{
+    int textmode;
+
+    textmode = PyInt_AsLong(value) != 0;
+    if (PyErr_Occurred())
+        return -1;
+
+    gpgme_set_textmode(self->ctx, textmode);
+    return 0;
+}
+
+static PyObject *
+pygpgme_context_get_include_certs(PyGpgmeContext *self)
+{
+    return PyInt_FromLong(gpgme_get_include_certs(self->ctx));
+}
+
+static int
+pygpgme_context_set_include_certs(PyGpgmeContext *self, PyObject *value)
+{
+    int nr_of_certs;
+
+    nr_of_certs = PyInt_AsLong(value);
+    if (PyErr_Occurred())
+        return -1;
+
+    gpgme_set_include_certs(self->ctx, nr_of_certs);
+    return 0;
+}
+
+static PyObject *
+pygpgme_context_get_keylist_mode(PyGpgmeContext *self)
+{
+    return PyInt_FromLong(gpgme_get_keylist_mode(self->ctx));
+}
+
+static int
+pygpgme_context_set_keylist_mode(PyGpgmeContext *self, PyObject *value)
+{
+    gpgme_keylist_mode_t keylist_mode;
+
+    keylist_mode = PyInt_AsLong(value);
+    if (PyErr_Occurred())
+        return -1;
+
+    if (pygpgme_check_error(gpgme_set_keylist_mode(self->ctx, keylist_mode)))
+        return -1;
+
+    return 0;
+}
+
+static PyObject *
+pygpgme_context_get_passphrase_cb(PyGpgmeContext *self)
+{
+    gpgme_passphrase_cb_t passphrase_cb;
+    PyObject *callback;
+
+    /* free the passphrase callback */
+    gpgme_get_passphrase_cb(self->ctx, &passphrase_cb, (void **)&callback);
+    if (passphrase_cb == pygpgme_passphrase_cb) {
+        Py_INCREF(callback);
+        return callback;
+    } else {
+        Py_RETURN_NONE;
+    }
+}
+
+static int
+pygpgme_context_set_passphrase_cb(PyGpgmeContext *self, PyObject *value)
+{
+    gpgme_passphrase_cb_t passphrase_cb;
+    PyObject *callback;
+
+    /* free the passphrase callback */
+    gpgme_get_passphrase_cb(self->ctx, &passphrase_cb, (void **)&callback);
+    if (passphrase_cb == pygpgme_passphrase_cb) {
+        Py_DECREF(callback);
+    }
+
+    /* callback of None == unset */
+    if (value == Py_None)
+        value = NULL;
+
+    if (value != NULL) {
+        Py_INCREF(value);
+        gpgme_set_passphrase_cb(self->ctx, pygpgme_passphrase_cb, value);
+    } else {
+        gpgme_set_passphrase_cb(self->ctx, NULL, NULL);
+    }
+    return 0;
+}
+
+static PyObject *
+pygpgme_context_get_progress_cb(PyGpgmeContext *self)
+{
+    gpgme_progress_cb_t progress_cb;
+    PyObject *callback;
+
+    /* free the progress callback */
+    gpgme_get_progress_cb(self->ctx, &progress_cb, (void **)&callback);
+    if (progress_cb == pygpgme_progress_cb) {
+        Py_INCREF(callback);
+        return callback;
+    } else {
+        Py_RETURN_NONE;
+    }
+}
+
+static int
+pygpgme_context_set_progress_cb(PyGpgmeContext *self, PyObject *value)
+{
+    gpgme_progress_cb_t progress_cb;
+    PyObject *callback;
+
+    /* free the progress callback */
+    gpgme_get_progress_cb(self->ctx, &progress_cb, (void **)&callback);
+    if (progress_cb == pygpgme_progress_cb) {
+        Py_DECREF(callback);
+    }
+
+    /* callback of None == unset */
+    if (value == Py_None)
+        value = NULL;
+
+    if (value != NULL) {
+        Py_INCREF(value);
+        gpgme_set_progress_cb(self->ctx, pygpgme_progress_cb, value);
+    } else {
+        gpgme_set_progress_cb(self->ctx, NULL, NULL);
+    }
+    return 0;
+}
+
+static PyObject *
+pygpgme_context_get_signers(PyGpgmeContext *self)
+{
+    PyObject *list, *tuple;
+    gpgme_key_t key;
+    int i;
+
+    list = PyList_New(0);
+    for (i = 0, key = gpgme_signers_enum(self->ctx, 0);
+         key != NULL; key = gpgme_signers_enum(self->ctx, ++i)) {
+        PyObject *item;
+
+        item = pygpgme_key_new(key);
+        gpgme_key_unref(key);
+        if (item == NULL) {
+            Py_DECREF(list);
+            return NULL;
+        }
+        PyList_Append(list, item);
+        Py_DECREF(item);
+    }
+    tuple = PySequence_Tuple(list);
+    Py_DECREF(list);
+    return tuple;
+}
+
+static int
+pygpgme_context_set_signers(PyGpgmeContext *self, PyObject *value)
+{
+    PyObject *signers = NULL;
+    int i, length, ret = 0;
+
+    signers = PySequence_Fast(value, "signers must be a sequence of keys");
+    if (!signers) {
+        ret = -1;
+        goto end;
+    }
+
+    gpgme_signers_clear(self->ctx);
+    length = PySequence_Fast_GET_SIZE(signers);
+    for (i = 0; i < length; i++) {
+        PyObject *item = PySequence_Fast_GET_ITEM(signers, i);
+
+        if (!PyObject_TypeCheck(item, &PyGpgmeKey_Type)) {
+            PyErr_SetString(PyExc_TypeError,
+                            "signers must be a sequence of keys");
+            ret = -1;
+            goto end;
+        }
+        gpgme_signers_add(self->ctx, ((PyGpgmeKey *)item)->key);
+    }
+
+ end:
+    Py_XDECREF(signers);
+    return ret;
+}
+
+static PyGetSetDef pygpgme_context_getsets[] = {
+    { "protocol", (getter)pygpgme_context_get_protocol,
+      (setter)pygpgme_context_set_protocol },
+    { "armor", (getter)pygpgme_context_get_armor,
+      (setter)pygpgme_context_set_armor },
+    { "textmode", (getter)pygpgme_context_get_textmode,
+      (setter)pygpgme_context_set_textmode },
+    { "include_certs", (getter)pygpgme_context_get_include_certs,
+      (setter)pygpgme_context_set_include_certs },
+    { "keylist_mode", (getter)pygpgme_context_get_keylist_mode,
+      (setter)pygpgme_context_set_keylist_mode },
+    { "passphrase_cb", (getter)pygpgme_context_get_passphrase_cb,
+      (setter)pygpgme_context_set_passphrase_cb },
+    { "progress_cb", (getter)pygpgme_context_get_progress_cb,
+      (setter)pygpgme_context_set_progress_cb },
+    { "signers", (getter)pygpgme_context_get_signers,
+      (setter)pygpgme_context_set_signers },
+    { NULL, (getter)0, (setter)0 }
+};
+
+/* XXX: set_locale */
+static PyObject *
+pygpgme_context_set_locale(PyGpgmeContext *self, PyObject *args)
+{
+    int category;
+    const char *value;
+
+    if (!PyArg_ParseTuple(args, "iz", &category, &value))
+        return NULL;
+
+    if (pygpgme_check_error(gpgme_set_locale(self->ctx, category, value)))
+        return NULL;
+
+    Py_RETURN_NONE;
+}
+
+/* the following don't seem to be used */
+/* XXX: signers_clear */
+/* XXX: signers_add */
+/* XXX: signers_enum */
+
+static PyObject *
+pygpgme_context_get_key(PyGpgmeContext *self, PyObject *args)
+{
+    const char *fpr;
+    int secret = 0;
+    gpgme_error_t err;
+    gpgme_key_t key;
+    PyObject *ret;
+
+    if (!PyArg_ParseTuple(args, "s|i", &fpr, &secret))
+        return NULL;
+
+    Py_BEGIN_ALLOW_THREADS;
+    err = gpgme_get_key(self->ctx, fpr, &key, secret);
+    Py_END_ALLOW_THREADS;
+
+    if (pygpgme_check_error(err))
+        return NULL;
+
+    ret = pygpgme_key_new(key);
+    gpgme_key_unref(key);
+    return ret;
+}
+
+/* XXX: cancel -- not needed unless we wrap the async calls */
+
+/* annotate exception with encrypt_result data */
+static void
+decode_encrypt_result(PyGpgmeContext *self)
+{
+    PyObject *err_type, *err_value, *err_traceback;
+    gpgme_encrypt_result_t res;
+    gpgme_invalid_key_t key;
+    PyObject *list;
+
+    PyErr_Fetch(&err_type, &err_value, &err_traceback);
+    PyErr_NormalizeException(&err_type, &err_value, &err_traceback);
+
+    if (!PyErr_GivenExceptionMatches(err_type, pygpgme_error))
+        goto end;
+
+    res = gpgme_op_encrypt_result(self->ctx);
+    if (res == NULL)
+        goto end;
+
+    list = PyList_New(0);
+    for (key = res->invalid_recipients; key != NULL; key = key->next) {
+        PyObject *item, *err;
+
+        err = pygpgme_error_object(key->reason);
+        item = Py_BuildValue("(zN)", key->fpr, err);
+        PyList_Append(list, item);
+        Py_DECREF(item);
+    }
+
+    PyObject_SetAttrString(err_value, "invalid_recipients", list);
+    Py_DECREF(list);
+
+ end:
+    PyErr_Restore(err_type, err_value, err_traceback);
+}
+
+static PyObject *
+pygpgme_context_encrypt(PyGpgmeContext *self, PyObject *args)
+{
+    PyObject *py_recp, *py_plain, *py_cipher;
+    int flags, i, length;
+    gpgme_key_t *recp;
+    gpgme_data_t plain, cipher;
+    gpgme_error_t err;
+
+    if (!PyArg_ParseTuple(args, "OiOO", &py_recp, &flags,
+                          &py_plain, &py_cipher))
+        return NULL;
+
+    py_recp = PySequence_Fast(py_recp, "first argument must be a sequence");
+    if (py_recp == NULL)
+        return NULL;
+
+    length = PySequence_Fast_GET_SIZE(py_recp);
+    recp = malloc((length + 1) * sizeof (gpgme_key_t));
+    for (i = 0; i < length; i++) {
+        PyObject *item = PySequence_Fast_GET_ITEM(py_recp, i);
+
+        if (!PyObject_TypeCheck(item, &PyGpgmeKey_Type)) {
+            free(recp);
+            Py_DECREF(py_recp);
+            PyErr_SetString(PyExc_TypeError, "items in first argument must "
+                            "be gpgme.Key objects");
+            return NULL;
+        }
+        recp[i] = ((PyGpgmeKey *)item)->key;
+    }
+    recp[i] = NULL;
+
+    if (pygpgme_data_new(&plain, py_plain)) {
+        free(recp);
+        Py_DECREF(py_recp);
+        return NULL;    
+    }
+    if (pygpgme_data_new(&cipher, py_cipher)) {
+        free(recp);
+        Py_DECREF(py_recp);
+        gpgme_data_release(plain);
+        return NULL;    
+    }
+
+    Py_BEGIN_ALLOW_THREADS;
+    err = gpgme_op_encrypt(self->ctx, recp, flags, plain, cipher);
+    Py_END_ALLOW_THREADS;
+
+    free(recp);
+    Py_DECREF(py_recp);
+    gpgme_data_release(plain);
+    gpgme_data_release(cipher);
+
+    if (pygpgme_check_error(err)) {
+        decode_encrypt_result(self);
+        return NULL;
+    }
+
+    Py_RETURN_NONE;
+}
+
+static PyObject *
+pygpgme_context_encrypt_sign(PyGpgmeContext *self, PyObject *args)
+{
+    PyObject *py_recp, *py_plain, *py_cipher;
+    int flags, i, length;
+    gpgme_key_t *recp;
+    gpgme_data_t plain, cipher;
+    gpgme_error_t err;
+    gpgme_sign_result_t result;
+
+    if (!PyArg_ParseTuple(args, "OiOO", &py_recp, &flags,
+                          &py_plain, &py_cipher))
+        return NULL;
+
+    py_recp = PySequence_Fast(py_recp, "first argument must be a sequence");
+    if (py_recp == NULL)
+        return NULL;
+
+    length = PySequence_Fast_GET_SIZE(py_recp);
+    recp = malloc((length + 1) * sizeof (gpgme_key_t));
+    for (i = 0; i < length; i++) {
+        PyObject *item = PySequence_Fast_GET_ITEM(py_recp, i);
+
+        if (!PyObject_TypeCheck(item, &PyGpgmeKey_Type)) {
+            free(recp);
+            Py_DECREF(py_recp);
+            PyErr_SetString(PyExc_TypeError, "items in first argument must "
+                            "be gpgme.Key objects");
+            return NULL;
+        }
+        recp[i] = ((PyGpgmeKey *)item)->key;
+    }
+    recp[i] = NULL;
+
+    if (pygpgme_data_new(&plain, py_plain)) {
+        free(recp);
+        Py_DECREF(py_recp);
+        return NULL;    
+    }
+    if (pygpgme_data_new(&cipher, py_cipher)) {
+        free(recp);
+        Py_DECREF(py_recp);
+        gpgme_data_release(plain);
+        return NULL;    
+    }
+
+    Py_BEGIN_ALLOW_THREADS;
+    err = gpgme_op_encrypt_sign(self->ctx, recp, flags, plain, cipher);
+    Py_END_ALLOW_THREADS;
+
+    free(recp);
+    Py_DECREF(py_recp);
+    gpgme_data_release(plain);
+    gpgme_data_release(cipher);
+
+    result = gpgme_op_sign_result(self->ctx);
+
+    /* annotate exception */
+    if (pygpgme_check_error(err)) {
+        PyObject *err_type, *err_value, *err_traceback;
+        PyObject *list;
+        gpgme_invalid_key_t key;
+
+        decode_encrypt_result(self);
+
+        PyErr_Fetch(&err_type, &err_value, &err_traceback);
+        PyErr_NormalizeException(&err_type, &err_value, &err_traceback);
+
+        if (result == NULL)
+            goto end;
+
+        if (!PyErr_GivenExceptionMatches(err_type, pygpgme_error))
+            goto end;
+
+        list = PyList_New(0);
+        for (key = result->invalid_signers; key != NULL; key = key->next) {
+            PyObject *item, *err;
+
+            err = pygpgme_error_object(key->reason);
+            item = Py_BuildValue("(zN)", key->fpr, err);
+            PyList_Append(list, item);
+            Py_DECREF(item);
+        }
+        PyObject_SetAttrString(err_value, "invalid_signers", list);
+        Py_DECREF(list);
+
+        list = pygpgme_newsiglist_new(result->signatures);
+        PyObject_SetAttrString(err_value, "signatures", list);
+        Py_DECREF(list);
+    end:
+        PyErr_Restore(err_type, err_value, err_traceback);
+        return NULL;
+    }
+
+    if (result)
+        return pygpgme_newsiglist_new(result->signatures);
+    else
+        return PyList_New(0);
+}
+
+static void
+decode_decrypt_result(PyGpgmeContext *self)
+{
+    PyObject *err_type, *err_value, *err_traceback;
+    PyObject *value;
+    gpgme_decrypt_result_t res;
+
+    PyErr_Fetch(&err_type, &err_value, &err_traceback);
+    PyErr_NormalizeException(&err_type, &err_value, &err_traceback);
+
+    if (!PyErr_GivenExceptionMatches(err_type, pygpgme_error))
+        goto end;
+
+    res = gpgme_op_decrypt_result(self->ctx);
+    if (res == NULL)
+        goto end;
+
+    if (res->unsupported_algorithm) {
+        value = PyString_FromString(res->unsupported_algorithm);
+    } else {
+        Py_INCREF(Py_None);
+        value = Py_None;
+    }
+    if (value) {
+        PyObject_SetAttrString(err_value, "unsupported_algorithm", value);
+        Py_DECREF(value);
+    }
+
+    value = PyBool_FromLong(res->wrong_key_usage);
+    if (value) {
+        PyObject_SetAttrString(err_value, "wrong_key_usage", value);
+        Py_DECREF(value);
+    }
+
+ end:
+    PyErr_Restore(err_type, err_value, err_traceback);
+}
+
+static PyObject *
+pygpgme_context_decrypt(PyGpgmeContext *self, PyObject *args)
+{
+    PyObject *py_cipher, *py_plain;
+    gpgme_data_t cipher, plain;
+    gpgme_error_t err;
+
+    if (!PyArg_ParseTuple(args, "OO", &py_cipher, &py_plain))
+        return NULL;
+
+    if (pygpgme_data_new(&cipher, py_cipher)) {
+        return NULL;
+    }
+
+    if (pygpgme_data_new(&plain, py_plain)) {
+        gpgme_data_release(cipher);
+        return NULL;    
+    }
+
+    Py_BEGIN_ALLOW_THREADS;
+    err = gpgme_op_decrypt(self->ctx, cipher, plain);
+    Py_END_ALLOW_THREADS;
+
+    gpgme_data_release(cipher);
+    gpgme_data_release(plain);
+
+    if (pygpgme_check_error(err)) {
+        decode_decrypt_result(self);
+        return NULL;
+    }
+
+    Py_RETURN_NONE;
+}
+
+static PyObject *
+pygpgme_context_decrypt_verify(PyGpgmeContext *self, PyObject *args)
+{
+    PyObject *py_cipher, *py_plain;
+    gpgme_data_t cipher, plain;
+    gpgme_error_t err;
+    gpgme_verify_result_t result;
+
+    if (!PyArg_ParseTuple(args, "OO", &py_cipher, &py_plain))
+        return NULL;
+
+    if (pygpgme_data_new(&cipher, py_cipher)) {
+        return NULL;
+    }
+
+    if (pygpgme_data_new(&plain, py_plain)) {
+        gpgme_data_release(cipher);
+        return NULL;    
+    }
+
+    Py_BEGIN_ALLOW_THREADS;
+    err = gpgme_op_decrypt_verify(self->ctx, cipher, plain);
+    Py_END_ALLOW_THREADS;
+
+    gpgme_data_release(cipher);
+    gpgme_data_release(plain);
+
+    if (pygpgme_check_error(err)) {
+        decode_decrypt_result(self);
+        return NULL;
+    }
+
+    result = gpgme_op_verify_result(self->ctx);
+
+    /* annotate exception */
+    if (pygpgme_check_error(err)) {
+        PyObject *err_type, *err_value, *err_traceback;
+        PyObject *list;
+
+        PyErr_Fetch(&err_type, &err_value, &err_traceback);
+        PyErr_NormalizeException(&err_type, &err_value, &err_traceback);
+
+        if (result == NULL)
+            goto end;
+
+        if (!PyErr_GivenExceptionMatches(err_type, pygpgme_error))
+            goto end;
+
+        list = pygpgme_siglist_new(result->signatures);
+        PyObject_SetAttrString(err_value, "signatures", list);
+        Py_DECREF(list);
+    end:
+        PyErr_Restore(err_type, err_value, err_traceback);
+        return NULL;
+    }
+
+    if (result)
+        return pygpgme_siglist_new(result->signatures);
+    else
+        return PyList_New(0);
+}
+
+static PyObject *
+pygpgme_context_sign(PyGpgmeContext *self, PyObject *args)
+{
+    PyObject *py_plain, *py_sig;
+    gpgme_data_t plain, sig;
+    int sig_mode = GPGME_SIG_MODE_NORMAL;
+    gpgme_error_t err;
+    gpgme_sign_result_t result;
+
+    if (!PyArg_ParseTuple(args, "OO|i", &py_plain, &py_sig, &sig_mode))
+        return NULL;
+
+    if (pygpgme_data_new(&plain, py_plain))
+        return NULL;    
+
+    if (pygpgme_data_new(&sig, py_sig)) {
+        gpgme_data_release(plain);
+        return NULL;    
+    }
+
+    Py_BEGIN_ALLOW_THREADS;
+    err = gpgme_op_sign(self->ctx, plain, sig, sig_mode);
+    Py_END_ALLOW_THREADS;
+
+    gpgme_data_release(plain);
+    gpgme_data_release(sig);
+
+    result = gpgme_op_sign_result(self->ctx);
+
+    /* annotate exception */
+    if (pygpgme_check_error(err)) {
+        PyObject *err_type, *err_value, *err_traceback;
+        PyObject *list;
+        gpgme_invalid_key_t key;
+
+        PyErr_Fetch(&err_type, &err_value, &err_traceback);
+        PyErr_NormalizeException(&err_type, &err_value, &err_traceback);
+
+        if (result == NULL)
+            goto end;
+
+        if (!PyErr_GivenExceptionMatches(err_type, pygpgme_error))
+            goto end;
+
+        list = PyList_New(0);
+        for (key = result->invalid_signers; key != NULL; key = key->next) {
+            PyObject *item, *err;
+
+            err = pygpgme_error_object(key->reason);
+            item = Py_BuildValue("(zN)", key->fpr, err);
+            PyList_Append(list, item);
+            Py_DECREF(item);
+        }
+        PyObject_SetAttrString(err_value, "invalid_signers", list);
+        Py_DECREF(list);
+
+        list = pygpgme_newsiglist_new(result->signatures);
+        PyObject_SetAttrString(err_value, "signatures", list);
+        Py_DECREF(list);
+    end:
+        PyErr_Restore(err_type, err_value, err_traceback);
+        return NULL;
+    }
+
+    if (result)
+        return pygpgme_newsiglist_new(result->signatures);
+    else
+        return PyList_New(0);
+}
+
+static PyObject *
+pygpgme_context_verify(PyGpgmeContext *self, PyObject *args)
+{
+    PyObject *py_sig, *py_signed_text, *py_plaintext;
+    gpgme_data_t sig, signed_text, plaintext;
+    gpgme_error_t err;
+    gpgme_verify_result_t result;
+
+    if (!PyArg_ParseTuple(args, "OOO", &py_sig, &py_signed_text,
+                          &py_plaintext))
+        return NULL;
+
+    if (pygpgme_data_new(&sig, py_sig)) {
+        return NULL;
+    }
+    if (pygpgme_data_new(&signed_text, py_signed_text)) {
+        gpgme_data_release(sig);
+        return NULL;    
+    }
+    if (pygpgme_data_new(&plaintext, py_plaintext)) {
+        gpgme_data_release(sig);
+        gpgme_data_release(signed_text);
+        return NULL;    
+    }
+
+    Py_BEGIN_ALLOW_THREADS;
+    err = gpgme_op_verify(self->ctx, sig, signed_text, plaintext);
+    Py_END_ALLOW_THREADS;
+
+    gpgme_data_release(sig);
+    gpgme_data_release(signed_text);
+    gpgme_data_release(plaintext);
+
+    result = gpgme_op_verify_result(self->ctx);
+
+    /* annotate exception */
+    if (pygpgme_check_error(err)) {
+        PyObject *err_type, *err_value, *err_traceback;
+        PyObject *list;
+
+        PyErr_Fetch(&err_type, &err_value, &err_traceback);
+        PyErr_NormalizeException(&err_type, &err_value, &err_traceback);
+
+        if (result == NULL)
+            goto end;
+
+        if (!PyErr_GivenExceptionMatches(err_type, pygpgme_error))
+            goto end;
+
+        list = pygpgme_siglist_new(result->signatures);
+        PyObject_SetAttrString(err_value, "signatures", list);
+        Py_DECREF(list);
+    end:
+        PyErr_Restore(err_type, err_value, err_traceback);
+        return NULL;
+    }
+
+    if (result)
+        return pygpgme_siglist_new(result->signatures);
+    else
+        return PyList_New(0);
+}
+
+static PyObject *
+pygpgme_context_import(PyGpgmeContext *self, PyObject *args)
+{
+    PyObject *py_keydata, *result;
+    gpgme_data_t keydata;
+    gpgme_error_t err;
+
+    if (!PyArg_ParseTuple(args, "O", &py_keydata))
+        return NULL;
+
+    if (pygpgme_data_new(&keydata, py_keydata))
+        return NULL;
+
+    Py_BEGIN_ALLOW_THREADS;
+    err = gpgme_op_import(self->ctx, keydata);
+    Py_END_ALLOW_THREADS;
+
+    gpgme_data_release(keydata);
+    result = pygpgme_import_result(self->ctx);
+    if (pygpgme_check_error(err)) {
+        PyObject *err_type, *err_value, *err_traceback;
+
+        PyErr_Fetch(&err_type, &err_value, &err_traceback);
+        PyErr_NormalizeException(&err_type, &err_value, &err_traceback);
+
+        if (!PyErr_GivenExceptionMatches(err_type, pygpgme_error))
+            goto end;
+
+        if (result != NULL) {
+            PyObject_SetAttrString(err_value, "result", result);
+            Py_DECREF(result);
+        }
+    end:
+        PyErr_Restore(err_type, err_value, err_traceback);
+        return NULL;
+    }
+    return result;
+}
+
+static PyObject *
+pygpgme_context_export(PyGpgmeContext *self, PyObject *args)
+{
+    PyObject *py_pattern, *py_keydata;
+    const char *pattern;
+    const char **patterns;
+    int i, length;
+    gpgme_data_t keydata;
+    gpgme_error_t err;
+
+    if (!PyArg_ParseTuple(args, "OO", &py_pattern, &py_keydata))
+        return NULL;
+
+    if (py_pattern == Py_None) {
+        Py_INCREF(py_pattern);
+        pattern = NULL;
+        patterns = NULL;
+    } else if (PyString_Check(py_pattern)) {
+        Py_INCREF(py_pattern);
+        pattern = PyString_AsString(py_pattern);
+        patterns = NULL;
+    } else {
+        py_pattern = PySequence_Fast(py_pattern,
+            "first argument must be a string or sequence of strings");
+        if (py_pattern == NULL)
+            return NULL;
+        length = PySequence_Fast_GET_SIZE(py_pattern);
+        pattern = NULL;
+        patterns = malloc((length + 1) * sizeof(const char *));
+        for (i = 0; i < length; i++) {
+            PyObject *item = PySequence_Fast_GET_ITEM(py_pattern, i);
+
+            if (!PyString_Check(item)) {
+                PyErr_SetString(PyExc_TypeError,
+                    "first argument must be a string or sequence of strings");
+                free(patterns);
+                Py_DECREF(py_pattern);
+                return NULL;
+            }
+            patterns[i] = PyString_AsString(item);
+        }
+        patterns[i] = NULL;
+    }
+
+    if (pygpgme_data_new(&keydata, py_keydata)) {
+        Py_DECREF(py_pattern);
+        if (patterns)
+            free(patterns);
+        return NULL;
+    }
+
+    Py_BEGIN_ALLOW_THREADS;
+    if (patterns)
+        err = gpgme_op_export_ext(self->ctx, patterns, 0, keydata);
+    else
+        err = gpgme_op_export(self->ctx, pattern, 0, keydata);
+    Py_END_ALLOW_THREADS;
+
+    Py_DECREF(py_pattern);
+    if (patterns)
+        free(patterns);
+    gpgme_data_release(keydata);
+    if (pygpgme_check_error(err))
+        return NULL;
+    Py_RETURN_NONE;
+}
+
+// pygpgme_context_genkey
+
+static PyObject *
+pygpgme_context_delete(PyGpgmeContext *self, PyObject *args)
+{
+    PyGpgmeKey *key;
+    int allow_secret = 0;
+    gpgme_error_t err;
+
+    if (!PyArg_ParseTuple(args, "O!|i", &PyGpgmeKey_Type, &key, &allow_secret))
+        return NULL;
+
+    Py_BEGIN_ALLOW_THREADS;
+    err = gpgme_op_delete(self->ctx, key->key, allow_secret);
+    Py_END_ALLOW_THREADS;
+
+    if (pygpgme_check_error(err))
+        return NULL;
+    Py_RETURN_NONE;
+}
+
+static gpgme_error_t
+pygpgme_edit_cb(void *user_data, gpgme_status_code_t status,
+                const char *args, int fd)
+{
+    PyObject *callback, *ret;
+    PyGILState_STATE state;
+    gpgme_error_t err;
+
+    state = PyGILState_Ensure();
+    callback = (PyObject *)user_data;
+    ret = PyObject_CallFunction(callback, "lzi", (long)status, args, fd);
+    err = pygpgme_check_pyerror();
+    Py_XDECREF(ret);
+    PyGILState_Release(state);
+    return err;
+}
+
+static PyObject *
+pygpgme_context_edit(PyGpgmeContext *self, PyObject *args)
+{
+    PyGpgmeKey *key;
+    PyObject *callback, *py_out;
+    gpgme_data_t out;
+    gpgme_error_t err;
+
+    if (!PyArg_ParseTuple(args, "O!OO", &PyGpgmeKey_Type, &key, &callback,
+                          &py_out))
+        return NULL;
+
+    if (pygpgme_data_new(&out, py_out))
+        return NULL;
+
+    Py_BEGIN_ALLOW_THREADS;
+    err = gpgme_op_edit(self->ctx, key->key,
+                        pygpgme_edit_cb, (void *)callback, out);
+    Py_END_ALLOW_THREADS;
+
+    gpgme_data_release(out);
+
+    if (pygpgme_check_error(err))
+        return NULL;
+    Py_RETURN_NONE;
+}
+
+static PyObject *
+pygpgme_context_card_edit(PyGpgmeContext *self, PyObject *args)
+{
+    PyGpgmeKey *key;
+    PyObject *callback, *py_out;
+    gpgme_data_t out;
+    gpgme_error_t err;
+
+    if (!PyArg_ParseTuple(args, "O!OO", &PyGpgmeKey_Type, &key, &callback,
+                          &py_out))
+        return NULL;
+
+    if (pygpgme_data_new(&out, py_out))
+        return NULL;
+
+    Py_BEGIN_ALLOW_THREADS;
+    err = gpgme_op_card_edit(self->ctx, key->key,
+                             pygpgme_edit_cb, (void *)callback, out);
+    Py_END_ALLOW_THREADS;
+
+    gpgme_data_release(out);
+
+    if (pygpgme_check_error(err))
+        return NULL;
+    Py_RETURN_NONE;
+}
+
+static PyObject *
+pygpgme_context_keylist(PyGpgmeContext *self, PyObject *args)
+{
+    PyObject *py_pattern = Py_None;
+    const char *pattern;
+    const char **patterns;
+    int secret_only = 0, i, length;
+    gpgme_error_t err;
+    PyGpgmeKeyIter *ret;
+
+    if (!PyArg_ParseTuple(args, "|Oi", &py_pattern, &secret_only))
+        return NULL;
+
+    if (py_pattern == Py_None) {
+        Py_INCREF(py_pattern);
+        pattern = NULL;
+        patterns = NULL;
+    } else if (PyString_Check(py_pattern)) {
+        Py_INCREF(py_pattern);
+        pattern = PyString_AsString(py_pattern);
+        patterns = NULL;
+    } else {
+        py_pattern = PySequence_Fast(py_pattern,
+            "first argument must be a string or sequence of strings");
+        if (py_pattern == NULL)
+            return NULL;
+        length = PySequence_Fast_GET_SIZE(py_pattern);
+        pattern = NULL;
+        patterns = malloc((length + 1) * sizeof(const char *));
+        for (i = 0; i < length; i++) {
+            PyObject *item = PySequence_Fast_GET_ITEM(py_pattern, i);
+
+            if (!PyString_Check(item)) {
+                PyErr_SetString(PyExc_TypeError,
+                    "first argument must be a string or sequence of strings");
+                free(patterns);
+                Py_DECREF(py_pattern);
+                return NULL;
+            }
+            patterns[i] = PyString_AsString(item);
+        }
+        patterns[i] = NULL;
+    }
+
+    Py_BEGIN_ALLOW_THREADS;
+    if (patterns)
+        err = gpgme_op_keylist_ext_start(self->ctx, patterns, secret_only, 0);
+    else
+        err = gpgme_op_keylist_start(self->ctx, pattern, secret_only);
+    Py_END_ALLOW_THREADS;
+
+    Py_DECREF(py_pattern);
+    if (patterns)
+        free(patterns);
+
+    if (pygpgme_check_error(err))
+        return NULL;
+
+    /* return a KeyIter object */
+    ret = PyObject_New(PyGpgmeKeyIter, &PyGpgmeKeyIter_Type);
+    if (!ret)
+        return NULL;
+    Py_INCREF(self);
+    ret->ctx = self;
+    return (PyObject *)ret;
+}
+
+// pygpgme_context_trustlist
+
+static PyMethodDef pygpgme_context_methods[] = {
+    { "set_locale", (PyCFunction)pygpgme_context_set_locale, METH_VARARGS },
+    { "get_key", (PyCFunction)pygpgme_context_get_key, METH_VARARGS },
+    { "encrypt", (PyCFunction)pygpgme_context_encrypt, METH_VARARGS },
+    { "encrypt_sign", (PyCFunction)pygpgme_context_encrypt_sign, METH_VARARGS },
+    { "decrypt", (PyCFunction)pygpgme_context_decrypt, METH_VARARGS },
+    { "decrypt_verify", (PyCFunction)pygpgme_context_decrypt_verify, METH_VARARGS },
+    { "sign", (PyCFunction)pygpgme_context_sign, METH_VARARGS },
+    { "verify", (PyCFunction)pygpgme_context_verify, METH_VARARGS },
+    { "import_", (PyCFunction)pygpgme_context_import, METH_VARARGS },
+    { "export", (PyCFunction)pygpgme_context_export, METH_VARARGS },
+    // genkey
+    { "delete", (PyCFunction)pygpgme_context_delete, METH_VARARGS },
+    { "edit", (PyCFunction)pygpgme_context_edit, METH_VARARGS },
+    { "card_edit", (PyCFunction)pygpgme_context_card_edit, METH_VARARGS },
+    { "keylist", (PyCFunction)pygpgme_context_keylist, METH_VARARGS },
+    // trustlist
+    { NULL, 0, 0 }
+};
+
+PyTypeObject PyGpgmeContext_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "gpgme.Context",
+    sizeof(PyGpgmeContext),
+    .tp_flags = Py_TPFLAGS_DEFAULT,
+    .tp_dealloc = (destructor)pygpgme_context_dealloc,
+    .tp_init = (initproc)pygpgme_context_init,
+    .tp_getset = pygpgme_context_getsets,
+    .tp_methods = pygpgme_context_methods,
+};
diff --git a/src/pygpgme-data.c b/src/pygpgme-data.c
new file mode 100644 (file)
index 0000000..d04ff2d
--- /dev/null
@@ -0,0 +1,181 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+    pygpgme - a Python wrapper for the gpgme library
+    Copyright (C) 2006  James Henstridge
+
+    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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#include <Python.h>
+#include <errno.h>
+#include "pygpgme.h"
+
+/* called when a Python exception is set.  Clears the exception and tries
+ * to set errno appropriately. */
+static void
+set_errno(void)
+{
+    PyObject *exc, *value, *tb, *py_errno;
+
+    PyErr_Fetch(&exc, &value, &tb);
+
+    /* if we have an IOError, try and get the actual errno */
+    if (PyErr_GivenExceptionMatches(exc, PyExc_IOError) && value != NULL) {
+        py_errno = PyObject_GetAttrString(value, "errno");
+        if (py_errno != NULL && PyInt_Check(py_errno)) {
+            errno = PyInt_AsLong(py_errno);
+        } else {
+            PyErr_Clear();
+            errno = EINVAL;
+        }
+        Py_XDECREF(py_errno);
+    } else {
+        errno = EINVAL;
+    }
+    Py_XDECREF(tb);
+    Py_XDECREF(value);
+    Py_DECREF(exc);
+}
+
+static ssize_t
+read_cb(void *handle, void *buffer, size_t size)
+{
+    PyGILState_STATE state;
+    PyObject *fp = handle;
+    PyObject *result;
+    int result_size;
+
+    state = PyGILState_Ensure();
+    result = PyObject_CallMethod(fp, "read", "l", (long)size);
+    /* check for exceptions or non-string return values */
+    if (result == NULL) {
+        set_errno();
+        result_size = -1;
+        goto end;
+    }
+    /* if we don't have a string return value, consider that an error too */
+    if (!PyString_Check(result)) {
+        Py_DECREF(result);
+        errno = EINVAL;
+        result_size = -1;
+        goto end;
+    }
+    /* copy the result into the given buffer */
+    result_size = PyString_Size(result);
+    if (result_size > size)
+        result_size = size;
+    memcpy(buffer, PyString_AsString(result), result_size);
+    Py_DECREF(result);
+ end:
+    PyGILState_Release(state);
+    return result_size;
+}
+
+static ssize_t
+write_cb(void *handle, const void *buffer, size_t size)
+{
+    PyGILState_STATE state;
+    PyObject *fp = handle;
+    PyObject *result;
+    ssize_t bytes_written = 0;
+
+    state = PyGILState_Ensure();
+    result = PyObject_CallMethod(fp, "write", "s#", buffer, (int)size);
+    if (result == NULL) {
+        set_errno();
+        bytes_written = -1;
+        goto end;
+    }
+    Py_DECREF(result);
+    bytes_written = size;
+ end:
+    PyGILState_Release(state);
+    return bytes_written;
+}
+
+static off_t
+seek_cb(void *handle, off_t offset, int whence)
+{
+    PyGILState_STATE state;
+    PyObject *fp = handle;
+    PyObject *result;
+
+    state = PyGILState_Ensure();
+    result = PyObject_CallMethod(fp, "seek", "li", (long)offset, whence);
+    if (result == NULL) {
+        set_errno();
+        offset = -1;
+        goto end;
+    }
+    Py_DECREF(result);
+
+    /* now get the file location */
+    result = PyObject_CallMethod(fp, "tell", NULL);
+    if (result == NULL) {
+        set_errno();
+        offset = -1;
+        goto end;
+    }
+    if (!PyInt_Check(result)) {
+        Py_DECREF(result);
+        errno = EINVAL;
+        offset = -1;
+        goto end;
+    }
+    offset = PyInt_AsLong(result);
+    Py_DECREF(result);
+ end:
+    PyGILState_Release(state);
+    return offset;
+}
+
+static void
+release_cb(void *handle)
+{
+    PyGILState_STATE state;
+    PyObject *fp = handle;
+
+    state = PyGILState_Ensure();
+    Py_DECREF(fp);
+    PyGILState_Release(state);
+}
+
+static struct gpgme_data_cbs python_data_cbs = {
+    .read    = read_cb,
+    .write   = write_cb,
+    .seek    = seek_cb,
+    .release = release_cb,
+};
+
+/* create a gpgme data object wrapping a Python file like object */
+int
+pygpgme_data_new(gpgme_data_t *dh, PyObject *fp)
+{
+    gpgme_error_t error;
+
+    if (fp == Py_None) {
+        *dh = NULL;
+        return 0;
+    }
+
+    error = gpgme_data_new_from_cbs(dh, &python_data_cbs, fp);
+
+    if (pygpgme_check_error(error))
+        return -1;
+
+    /* if no error, then the new gpgme_data_t object owns a reference to
+     * the python object */
+    Py_INCREF(fp);
+    return 0;
+}
diff --git a/src/pygpgme-error.c b/src/pygpgme-error.c
new file mode 100644 (file)
index 0000000..14c1183
--- /dev/null
@@ -0,0 +1,132 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+    pygpgme - a Python wrapper for the gpgme library
+    Copyright (C) 2006  James Henstridge
+
+    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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#include <Python.h>
+#include "pygpgme.h"
+
+PyObject *pygpgme_error = NULL;
+
+PyObject *
+pygpgme_error_object(gpgme_error_t err)
+{
+    char buf[256] = { '\0' };
+    PyObject *exc, *attr;
+
+    if (err == GPG_ERR_NO_ERROR)
+        Py_RETURN_NONE;
+
+    /* get the error string */
+    if (gpgme_strerror_r(err, buf, 255) != 0)
+        strcpy(buf, "Unknown");
+
+    exc = PyObject_CallFunction(pygpgme_error, "lls",
+                                (long)gpgme_err_source(err),
+                                (long)gpgme_err_code(err),
+                                buf);
+    if (!exc)
+        return NULL;
+    /* set the source and code as attributes of the exception object: */
+    attr = PyInt_FromLong(gpgme_err_source(err));
+    PyObject_SetAttrString(exc, "source", attr);
+    Py_DECREF(attr);
+
+    attr = PyInt_FromLong(gpgme_err_code(err));
+    PyObject_SetAttrString(exc, "code", attr);
+    Py_DECREF(attr);
+
+    attr = PyString_FromString(buf);
+    PyObject_SetAttrString(exc, "message", attr);
+    Py_DECREF(attr);
+
+    return exc;
+}
+
+/* check whether the given gpgme_error_t value indicates an error.  If so,
+ * raise an equivalent Python exception and return TRUE */
+int
+pygpgme_check_error(gpgme_error_t err)
+{
+    PyObject *exc;
+
+    if (err == GPG_ERR_NO_ERROR)
+        return 0;
+
+    exc = pygpgme_error_object(err);
+    if (!exc)
+        return -1;
+
+    PyErr_SetObject(pygpgme_error, exc);
+
+    return -1;
+}
+
+gpgme_error_t
+pygpgme_check_pyerror(void)
+{
+    PyObject *err_type, *err_value, *err_traceback;
+    gpgme_error_t err;
+    PyObject *args = NULL, *source = NULL, *code = NULL;
+
+    if (!PyErr_Occurred())
+        return GPG_ERR_NO_ERROR;
+
+    PyErr_Fetch(&err_type, &err_value, &err_traceback);
+    PyErr_NormalizeException(&err_type, &err_value, &err_traceback);
+    err = gpgme_error(GPG_ERR_GENERAL);
+
+    /* get the first argument of the exception */
+    args = PyObject_GetAttrString(err_value, "args");
+    if (args == NULL)
+        goto end;
+
+    source = PyTuple_GetItem(args, 0);
+    if (source == NULL)
+        goto end;
+  
+    if (PyErr_GivenExceptionMatches(err_type, pygpgme_error)) {
+        code = PyTuple_GetItem(args, 1);
+        if (code == NULL)
+            goto end;
+
+        if (PyInt_Check(source) && PyInt_Check(code))
+            err = gpgme_err_make(PyInt_AsLong(source), PyInt_AsLong(code));
+    } else if (PyErr_GivenExceptionMatches(err_type, PyExc_IOError) ||
+               PyErr_GivenExceptionMatches(err_type, PyExc_OSError)) {
+        if (PyInt_Check(source))
+            err = gpgme_err_code_from_errno(PyInt_AsLong(source));
+    }
+
+ end:
+    Py_XDECREF(err_type);
+    Py_XDECREF(err_value);
+    Py_XDECREF(err_traceback);
+    Py_XDECREF(args);
+    PyErr_Clear();
+
+    return err;
+}
+
+int
+pygpgme_no_constructor(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+    PyErr_Format(PyExc_NotImplementedError,
+                 "can not directly create instances of %s",
+                 self->ob_type->tp_name);
+    return -1;
+}
diff --git a/src/pygpgme-import.c b/src/pygpgme-import.c
new file mode 100644 (file)
index 0000000..2f0d313
--- /dev/null
@@ -0,0 +1,133 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+    pygpgme - a Python wrapper for the gpgme library
+    Copyright (C) 2006  James Henstridge
+
+    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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#include "pygpgme.h"
+#include <structmember.h>
+
+static void
+pygpgme_import_dealloc(PyGpgmeImportResult *self)
+{
+    Py_XDECREF(self->considered);
+    Py_XDECREF(self->no_user_id);
+    Py_XDECREF(self->imported);
+    Py_XDECREF(self->imported_rsa);
+    Py_XDECREF(self->unchanged);
+    Py_XDECREF(self->new_user_ids);
+    Py_XDECREF(self->new_sub_keys);
+    Py_XDECREF(self->new_signatures);
+    Py_XDECREF(self->new_revocations);
+    Py_XDECREF(self->secret_read);
+    Py_XDECREF(self->secret_imported);
+    Py_XDECREF(self->secret_unchanged);
+    Py_XDECREF(self->skipped_new_keys);
+    Py_XDECREF(self->not_imported);
+    Py_XDECREF(self->imports);
+    PyObject_Del(self);
+}
+
+static PyMemberDef pygpgme_import_members[] = {
+    { "considered", T_OBJECT, offsetof(PyGpgmeImportResult, considered), RO},
+    { "no_user_id", T_OBJECT, offsetof(PyGpgmeImportResult, no_user_id), RO},
+    { "imported", T_OBJECT, offsetof(PyGpgmeImportResult, imported), RO},
+    { "imported_rsa", T_OBJECT, offsetof(PyGpgmeImportResult, imported_rsa), RO},
+    { "unchanged", T_OBJECT, offsetof(PyGpgmeImportResult, unchanged), RO},
+    { "new_user_ids", T_OBJECT, offsetof(PyGpgmeImportResult, new_user_ids), RO},
+    { "new_sub_keys", T_OBJECT, offsetof(PyGpgmeImportResult, new_sub_keys), RO},
+    { "new_signatures", T_OBJECT, offsetof(PyGpgmeImportResult, new_signatures), RO},
+    { "new_revocations", T_OBJECT,
+      offsetof(PyGpgmeImportResult, new_revocations), RO},
+    { "secret_read", T_OBJECT,
+      offsetof(PyGpgmeImportResult, secret_read), RO},
+    { "secret_imported", T_OBJECT,
+      offsetof(PyGpgmeImportResult, secret_imported), RO},
+    { "secret_unchanged", T_OBJECT,
+      offsetof(PyGpgmeImportResult, secret_unchanged), RO},
+    { "skipped_new_keys", T_OBJECT,
+      offsetof(PyGpgmeImportResult, skipped_new_keys), RO},
+    { "not_imported", T_OBJECT,
+      offsetof(PyGpgmeImportResult, not_imported), RO},
+    { "imports", T_OBJECT, offsetof(PyGpgmeImportResult, imports), RO},
+    { NULL, 0, 0, 0}
+};
+
+PyTypeObject PyGpgmeImportResult_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "gpgme.Import",
+    sizeof(PyGpgmeImportResult),
+    .tp_flags = Py_TPFLAGS_DEFAULT,
+    .tp_init = pygpgme_no_constructor,
+    .tp_dealloc = (destructor)pygpgme_import_dealloc,
+    .tp_members = pygpgme_import_members,
+};
+
+PyObject *
+pygpgme_import_result(gpgme_ctx_t ctx)
+{
+    gpgme_import_result_t result;
+    gpgme_import_status_t status;
+    PyGpgmeImportResult *self;
+
+    result = gpgme_op_import_result(ctx);
+
+    if (result == NULL)
+        Py_RETURN_NONE;
+
+    self = PyObject_New(PyGpgmeImportResult, &PyGpgmeImportResult_Type);
+    if (!self)
+        return NULL;
+
+#define ADD_INT(name) \
+    self->name = PyInt_FromLong(result->name)
+
+    ADD_INT(considered);
+    ADD_INT(no_user_id);
+    ADD_INT(imported);
+    ADD_INT(imported_rsa);
+    ADD_INT(unchanged);
+    ADD_INT(new_user_ids);
+    ADD_INT(new_sub_keys);
+    ADD_INT(new_signatures);
+    ADD_INT(new_revocations);
+    ADD_INT(secret_read);
+    ADD_INT(secret_imported);
+    ADD_INT(secret_unchanged);
+    ADD_INT(skipped_new_keys);
+    ADD_INT(not_imported);
+
+    self->imports = PyList_New(0);
+    if (!self->imports)
+        return NULL;
+    for (status = result->imports; status != NULL; status = status->next) {
+        PyObject *item;
+
+        item = Py_BuildValue("(zNi)",
+                             status->fpr,
+                             pygpgme_error_object(status->result),
+                             status->status);
+        if (!item) {
+            Py_DECREF(self);
+            return NULL;
+        }
+        PyList_Append(self->imports, item);
+        Py_DECREF(item);
+    }
+    
+    return (PyObject *)self;
+}
diff --git a/src/pygpgme-key.c b/src/pygpgme-key.c
new file mode 100644 (file)
index 0000000..0331f5e
--- /dev/null
@@ -0,0 +1,610 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+    pygpgme - a Python wrapper for the gpgme library
+    Copyright (C) 2006  James Henstridge
+
+    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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#include <Python.h>
+#include "pygpgme.h"
+
+static void
+pygpgme_subkey_dealloc(PyGpgmeSubkey *self)
+{
+    self->subkey = NULL;
+    Py_XDECREF(self->parent);
+    self->parent = NULL;
+    PyObject_Del(self);
+}
+
+static PyObject *
+pygpgme_subkey_get_revoked(PyGpgmeSubkey *self)
+{
+    return PyBool_FromLong(self->subkey->revoked);
+}
+
+static PyObject *
+pygpgme_subkey_get_expired(PyGpgmeSubkey *self)
+{
+    return PyBool_FromLong(self->subkey->expired);
+}
+
+static PyObject *
+pygpgme_subkey_get_disabled(PyGpgmeSubkey *self)
+{
+    return PyBool_FromLong(self->subkey->disabled);
+}
+
+static PyObject *
+pygpgme_subkey_get_invalid(PyGpgmeSubkey *self)
+{
+    return PyBool_FromLong(self->subkey->invalid);
+}
+
+static PyObject *
+pygpgme_subkey_get_can_encrypt(PyGpgmeSubkey *self)
+{
+    return PyBool_FromLong(self->subkey->can_encrypt);
+}
+
+static PyObject *
+pygpgme_subkey_get_can_sign(PyGpgmeSubkey *self)
+{
+    return PyBool_FromLong(self->subkey->can_sign);
+}
+
+static PyObject *
+pygpgme_subkey_get_can_certify(PyGpgmeSubkey *self)
+{
+    return PyBool_FromLong(self->subkey->can_certify);
+}
+
+static PyObject *
+pygpgme_subkey_get_secret(PyGpgmeSubkey *self)
+{
+    return PyBool_FromLong(self->subkey->secret);
+}
+
+static PyObject *
+pygpgme_subkey_get_can_authenticate(PyGpgmeSubkey *self)
+{
+    return PyBool_FromLong(self->subkey->can_authenticate);
+}
+
+static PyObject *
+pygpgme_subkey_get_pubkey_algo(PyGpgmeSubkey *self)
+{
+    return PyInt_FromLong(self->subkey->pubkey_algo);
+}
+
+static PyObject *
+pygpgme_subkey_get_length(PyGpgmeSubkey *self)
+{
+    return PyInt_FromLong(self->subkey->length);
+}
+
+static PyObject *
+pygpgme_subkey_get_keyid(PyGpgmeSubkey *self)
+{
+    if (self->subkey->keyid)
+        return PyString_FromString(self->subkey->keyid);
+    else
+        Py_RETURN_NONE;
+}
+
+static PyObject *
+pygpgme_subkey_get_fpr(PyGpgmeSubkey *self)
+{
+    if (self->subkey->fpr)
+        return PyString_FromString(self->subkey->fpr);
+    else
+        Py_RETURN_NONE;
+}
+
+static PyObject *
+pygpgme_subkey_get_timestamp(PyGpgmeSubkey *self)
+{
+    return PyInt_FromLong(self->subkey->timestamp);
+}
+
+static PyObject *
+pygpgme_subkey_get_expires(PyGpgmeSubkey *self)
+{
+    return PyInt_FromLong(self->subkey->expires);
+}
+
+static PyGetSetDef pygpgme_subkey_getsets[] = {
+    { "revoked", (getter)pygpgme_subkey_get_revoked },
+    { "expired", (getter)pygpgme_subkey_get_expired },
+    { "disabled", (getter)pygpgme_subkey_get_disabled },
+    { "invalid", (getter)pygpgme_subkey_get_invalid },
+    { "can_encrypt", (getter)pygpgme_subkey_get_can_encrypt },
+    { "can_sign", (getter)pygpgme_subkey_get_can_sign },
+    { "can_certify", (getter)pygpgme_subkey_get_can_certify },
+    { "secret", (getter)pygpgme_subkey_get_secret },
+    { "can_authenticate", (getter)pygpgme_subkey_get_can_authenticate },
+    { "pubkey_algo", (getter)pygpgme_subkey_get_pubkey_algo },
+    { "length", (getter)pygpgme_subkey_get_length },
+    { "keyid", (getter)pygpgme_subkey_get_keyid },
+    { "fpr", (getter)pygpgme_subkey_get_fpr },
+    { "timestamp", (getter)pygpgme_subkey_get_timestamp },
+    { "expires", (getter)pygpgme_subkey_get_expires },
+    { NULL, (getter)0, (setter)0 }
+};
+
+PyTypeObject PyGpgmeSubkey_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "gpgme.Subkey",
+    sizeof(PyGpgmeSubkey),
+    .tp_flags = Py_TPFLAGS_DEFAULT,
+    .tp_init = pygpgme_no_constructor,
+    .tp_dealloc = (destructor)pygpgme_subkey_dealloc,
+    .tp_getset = pygpgme_subkey_getsets,
+};
+
+static void
+pygpgme_key_sig_dealloc(PyGpgmeKeySig *self)
+{
+    self->key_sig = NULL;
+    Py_XDECREF(self->parent);
+    self->parent = NULL;
+    PyObject_Del(self);
+}
+
+static PyObject *
+pygpgme_key_sig_get_revoked(PyGpgmeKeySig *self)
+{
+    return PyBool_FromLong(self->key_sig->revoked);
+}
+
+static PyObject *
+pygpgme_key_sig_get_expired(PyGpgmeKeySig *self)
+{
+    return PyBool_FromLong(self->key_sig->expired);
+}
+
+static PyObject *
+pygpgme_key_sig_get_invalid(PyGpgmeKeySig *self)
+{
+    return PyBool_FromLong(self->key_sig->invalid);
+}
+
+static PyObject *
+pygpgme_key_sig_get_exportable(PyGpgmeKeySig *self)
+{
+    return PyBool_FromLong(self->key_sig->exportable);
+}
+
+static PyObject *
+pygpgme_key_sig_get_pubkey_algo(PyGpgmeKeySig *self)
+{
+    return PyInt_FromLong(self->key_sig->pubkey_algo);
+}
+
+static PyObject *
+pygpgme_key_sig_get_keyid(PyGpgmeKeySig *self)
+{
+    if (self->key_sig->keyid)
+        return PyString_FromString(self->key_sig->keyid);
+    else
+        Py_RETURN_NONE;
+}
+
+static PyObject *
+pygpgme_key_sig_get_timestamp(PyGpgmeKeySig *self)
+{
+    return PyInt_FromLong(self->key_sig->timestamp);
+}
+
+static PyObject *
+pygpgme_key_sig_get_expires(PyGpgmeKeySig *self)
+{
+    return PyInt_FromLong(self->key_sig->expires);
+}
+
+static PyObject *
+pygpgme_key_sig_get_status(PyGpgmeKeySig *self)
+{
+    return pygpgme_error_object(self->key_sig->status);
+}
+
+static PyObject *
+pygpgme_key_sig_get_uid(PyGpgmeKeySig *self)
+{
+    if (self->key_sig->uid)
+        return PyString_FromString(self->key_sig->uid);
+    else
+        Py_RETURN_NONE;
+}
+
+static PyObject *
+pygpgme_key_sig_get_name(PyGpgmeKeySig *self)
+{
+    if (self->key_sig->name)
+        return PyString_FromString(self->key_sig->name);
+    else
+        Py_RETURN_NONE;
+}
+
+static PyObject *
+pygpgme_key_sig_get_email(PyGpgmeKeySig *self)
+{
+    if (self->key_sig->email)
+        return PyString_FromString(self->key_sig->email);
+    else
+        Py_RETURN_NONE;
+}
+
+static PyObject *
+pygpgme_key_sig_get_comment(PyGpgmeKeySig *self)
+{
+    if (self->key_sig->comment)
+        return PyString_FromString(self->key_sig->comment);
+    else
+        Py_RETURN_NONE;
+}
+
+static PyObject *
+pygpgme_key_sig_get_sig_class(PyGpgmeKeySig *self)
+{
+    return PyInt_FromLong(self->key_sig->sig_class);
+}
+
+static PyGetSetDef pygpgme_key_sig_getsets[] = {
+    { "revoked", (getter)pygpgme_key_sig_get_revoked },
+    { "expired", (getter)pygpgme_key_sig_get_expired },
+    { "invalid", (getter)pygpgme_key_sig_get_invalid },
+    { "exportable", (getter)pygpgme_key_sig_get_exportable },
+    { "pubkey_algo", (getter)pygpgme_key_sig_get_pubkey_algo },
+    { "keyid", (getter)pygpgme_key_sig_get_keyid },
+    { "timestamp", (getter)pygpgme_key_sig_get_timestamp },
+    { "expires", (getter)pygpgme_key_sig_get_expires },
+    { "status", (getter)pygpgme_key_sig_get_status },
+    { "uid", (getter)pygpgme_key_sig_get_uid },
+    { "name", (getter)pygpgme_key_sig_get_name },
+    { "email", (getter)pygpgme_key_sig_get_email },
+    { "comment", (getter)pygpgme_key_sig_get_comment },
+    { "sig_class", (getter)pygpgme_key_sig_get_sig_class },
+    { NULL, (getter)0, (setter)0 }
+};
+
+PyTypeObject PyGpgmeKeySig_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "gpgme.KeySig",
+    sizeof(PyGpgmeKeySig),
+    .tp_flags = Py_TPFLAGS_DEFAULT,
+    .tp_init = pygpgme_no_constructor,
+    .tp_dealloc = (destructor)pygpgme_key_sig_dealloc,
+    .tp_getset = pygpgme_key_sig_getsets,
+};
+
+static void
+pygpgme_user_id_dealloc(PyGpgmeUserId *self)
+{
+    self->user_id = NULL;
+    Py_XDECREF(self->parent);
+    self->parent = NULL;
+    PyObject_Del(self);
+}
+
+static PyObject *
+pygpgme_user_id_get_revoked(PyGpgmeUserId *self)
+{
+    return PyBool_FromLong(self->user_id->revoked);
+}
+
+static PyObject *
+pygpgme_user_id_get_invalid(PyGpgmeUserId *self)
+{
+    return PyBool_FromLong(self->user_id->invalid);
+}
+
+static PyObject *
+pygpgme_user_id_get_validity(PyGpgmeUserId *self)
+{
+    return PyInt_FromLong(self->user_id->validity);
+}
+
+static PyObject *
+pygpgme_user_id_get_uid(PyGpgmeUserId *self)
+{
+    if (self->user_id->uid)
+        return PyString_FromString(self->user_id->uid);
+    else
+        Py_RETURN_NONE;
+}
+
+static PyObject *
+pygpgme_user_id_get_name(PyGpgmeUserId *self)
+{
+    if (self->user_id->name)
+        return PyString_FromString(self->user_id->name);
+    else
+        Py_RETURN_NONE;
+}
+
+static PyObject *
+pygpgme_user_id_get_email(PyGpgmeUserId *self)
+{
+    if (self->user_id->email)
+        return PyString_FromString(self->user_id->email);
+    else
+        Py_RETURN_NONE;
+}
+
+static PyObject *
+pygpgme_user_id_get_comment(PyGpgmeUserId *self)
+{
+    if (self->user_id->comment)
+        return PyString_FromString(self->user_id->comment);
+    else
+        Py_RETURN_NONE;
+}
+
+static PyObject *
+pygpgme_user_id_get_signatures(PyGpgmeUserId *self)
+{
+    PyObject *ret;
+    gpgme_key_sig_t sig;
+
+    ret = PyList_New(0);
+    if (ret == NULL)
+        return NULL;
+    for (sig = self->user_id->signatures; sig != NULL; sig = sig->next) {
+        PyGpgmeKeySig *item;
+
+        item = PyObject_New(PyGpgmeKeySig, &PyGpgmeKeySig_Type);
+        if (item == NULL) {
+            Py_DECREF(ret);
+            return NULL;
+        }
+        item->key_sig = sig;
+        Py_INCREF(self);
+        item->parent = (PyObject *)self;
+        PyList_Append(ret, (PyObject *)item);
+        Py_DECREF(item);
+    }
+    return ret;
+}
+
+static PyGetSetDef pygpgme_user_id_getsets[] = {
+    { "revoked", (getter)pygpgme_user_id_get_revoked },
+    { "invalid", (getter)pygpgme_user_id_get_invalid },
+    { "validity", (getter)pygpgme_user_id_get_validity },
+    { "uid", (getter)pygpgme_user_id_get_uid },
+    { "name", (getter)pygpgme_user_id_get_name },
+    { "email", (getter)pygpgme_user_id_get_email },
+    { "comment", (getter)pygpgme_user_id_get_comment },
+    { "signatures", (getter)pygpgme_user_id_get_signatures },
+    { NULL, (getter)0, (setter)0 }
+};
+
+PyTypeObject PyGpgmeUserId_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "gpgme.UserId",
+    sizeof(PyGpgmeUserId),
+    .tp_flags = Py_TPFLAGS_DEFAULT,
+    .tp_init = pygpgme_no_constructor,
+    .tp_dealloc = (destructor)pygpgme_user_id_dealloc,
+    .tp_getset = pygpgme_user_id_getsets,
+};
+
+static void
+pygpgme_key_dealloc(PyGpgmeKey *self)
+{
+    gpgme_key_unref(self->key);
+    self->key = NULL;
+    PyObject_Del(self);
+}
+
+static PyObject *
+pygpgme_key_get_revoked(PyGpgmeKey *self)
+{
+    return PyBool_FromLong(self->key->revoked);
+}
+
+static PyObject *
+pygpgme_key_get_expired(PyGpgmeKey *self)
+{
+    return PyBool_FromLong(self->key->expired);
+}
+
+static PyObject *
+pygpgme_key_get_disabled(PyGpgmeKey *self)
+{
+    return PyBool_FromLong(self->key->disabled);
+}
+
+static PyObject *
+pygpgme_key_get_invalid(PyGpgmeKey *self)
+{
+    return PyBool_FromLong(self->key->invalid);
+}
+
+static PyObject *
+pygpgme_key_get_can_encrypt(PyGpgmeKey *self)
+{
+    return PyBool_FromLong(self->key->can_encrypt);
+}
+
+static PyObject *
+pygpgme_key_get_can_sign(PyGpgmeKey *self)
+{
+    return PyBool_FromLong(self->key->can_sign);
+}
+
+static PyObject *
+pygpgme_key_get_can_certify(PyGpgmeKey *self)
+{
+    return PyBool_FromLong(self->key->can_certify);
+}
+
+static PyObject *
+pygpgme_key_get_secret(PyGpgmeKey *self)
+{
+    return PyBool_FromLong(self->key->secret);
+}
+
+static PyObject *
+pygpgme_key_get_can_authenticate(PyGpgmeKey *self)
+{
+    return PyBool_FromLong(self->key->can_authenticate);
+}
+
+static PyObject *
+pygpgme_key_get_protocol(PyGpgmeKey *self)
+{
+    return PyInt_FromLong(self->key->protocol);
+}
+
+static PyObject *
+pygpgme_key_get_issuer_serial(PyGpgmeKey *self)
+{
+    if (self->key->issuer_serial)
+        return PyString_FromString(self->key->issuer_serial);
+    else
+        Py_RETURN_NONE;
+}
+
+static PyObject *
+pygpgme_key_get_issuer_name(PyGpgmeKey *self)
+{
+    if (self->key->issuer_name)
+        return PyString_FromString(self->key->issuer_name);
+    else
+        Py_RETURN_NONE;
+}
+
+static PyObject *
+pygpgme_key_get_chain_id(PyGpgmeKey *self)
+{
+    if (self->key->chain_id)
+        return PyString_FromString(self->key->chain_id);
+    else
+        Py_RETURN_NONE;
+}
+
+static PyObject *
+pygpgme_key_get_owner_trust(PyGpgmeKey *self)
+{
+    return PyInt_FromLong(self->key->owner_trust);
+}
+
+static PyObject *
+pygpgme_key_get_subkeys(PyGpgmeKey *self)
+{
+    PyObject *ret;
+    gpgme_subkey_t subkey;
+
+    ret = PyList_New(0);
+    if (ret == NULL)
+        return NULL;
+    for (subkey = self->key->subkeys; subkey != NULL; subkey = subkey->next) {
+        PyGpgmeSubkey *item;
+
+        item = PyObject_New(PyGpgmeSubkey, &PyGpgmeSubkey_Type);
+        if (item == NULL) {
+            Py_DECREF(ret);
+            return NULL;
+        }
+        item->subkey = subkey;
+        Py_INCREF(self);
+        item->parent = (PyObject *)self;
+        PyList_Append(ret, (PyObject *)item);
+        Py_DECREF(item);
+    }
+    return ret;
+}
+
+static PyObject *
+pygpgme_key_get_uids(PyGpgmeKey *self)
+{
+    PyObject *ret;
+    gpgme_user_id_t uid;
+
+    ret = PyList_New(0);
+    if (ret == NULL)
+        return NULL;
+    for (uid = self->key->uids; uid != NULL; uid = uid->next) {
+        PyGpgmeUserId *item;
+
+        item = PyObject_New(PyGpgmeUserId, &PyGpgmeUserId_Type);
+        if (item == NULL) {
+            Py_DECREF(ret);
+            return NULL;
+        }
+        item->user_id = uid;
+        Py_INCREF(self);
+        item->parent = (PyObject *)self;
+        PyList_Append(ret, (PyObject *)item);
+        Py_DECREF(item);
+    }
+    return ret;
+}
+
+static PyObject *
+pygpgme_key_get_keylist_mode(PyGpgmeKey *self)
+{
+    return PyInt_FromLong(self->key->keylist_mode);
+}
+
+static PyGetSetDef pygpgme_key_getsets[] = {
+    { "revoked", (getter)pygpgme_key_get_revoked },
+    { "expired", (getter)pygpgme_key_get_expired },
+    { "disabled", (getter)pygpgme_key_get_disabled },
+    { "invalid", (getter)pygpgme_key_get_invalid },
+    { "can_encrypt", (getter)pygpgme_key_get_can_encrypt },
+    { "can_sign", (getter)pygpgme_key_get_can_sign },
+    { "can_certify", (getter)pygpgme_key_get_can_certify },
+    { "secret", (getter)pygpgme_key_get_secret },
+    { "can_authenticate", (getter)pygpgme_key_get_can_authenticate },
+    { "protocol", (getter)pygpgme_key_get_protocol },
+    { "issuer_serial", (getter)pygpgme_key_get_issuer_serial },
+    { "issuer_name", (getter)pygpgme_key_get_issuer_name },
+    { "chain_id", (getter)pygpgme_key_get_chain_id },
+    { "owner_trust", (getter)pygpgme_key_get_owner_trust },
+    { "subkeys", (getter)pygpgme_key_get_subkeys },
+    { "uids", (getter)pygpgme_key_get_uids },
+    { "keylist_mode", (getter)pygpgme_key_get_keylist_mode },
+    { NULL, (getter)0, (setter)0 }
+};
+
+PyTypeObject PyGpgmeKey_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "gpgme.Key",
+    sizeof(PyGpgmeKey),
+    .tp_flags = Py_TPFLAGS_DEFAULT,
+    .tp_init = pygpgme_no_constructor,
+    .tp_dealloc = (destructor)pygpgme_key_dealloc,
+    .tp_getset = pygpgme_key_getsets,
+};
+
+PyObject *
+pygpgme_key_new(gpgme_key_t key)
+{
+    PyGpgmeKey *self;
+
+    self = PyObject_New(PyGpgmeKey, &PyGpgmeKey_Type);
+    if (self == NULL)
+        return NULL;
+
+    gpgme_key_ref(key);
+    self->key = key;
+    return (PyObject *)self;
+}
diff --git a/src/pygpgme-keyiter.c b/src/pygpgme-keyiter.c
new file mode 100644 (file)
index 0000000..f5656ea
--- /dev/null
@@ -0,0 +1,85 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+    pygpgme - a Python wrapper for the gpgme library
+    Copyright (C) 2006  James Henstridge
+
+    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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#include "pygpgme.h"
+
+static void
+pygpgme_keyiter_dealloc(PyGpgmeKeyIter *self)
+{
+    if (self->ctx) {
+        gpgme_error_t err = gpgme_op_keylist_end(self->ctx->ctx);
+        PyObject *exc = pygpgme_error_object(err);
+
+        if (exc != NULL && exc != Py_None) {
+            PyErr_WriteUnraisable(exc);
+        }
+        Py_XDECREF(exc);
+        Py_DECREF(self->ctx);
+        self->ctx = NULL;
+    }
+    PyObject_Del(self);
+}
+
+static PyObject *
+pygpgme_keyiter_iter(PyGpgmeKeyIter *self)
+{
+    Py_INCREF(self);
+    return (PyObject *)self;
+}
+
+static PyObject *
+pygpgme_keyiter_next(PyGpgmeKeyIter *self)
+{
+    gpgme_key_t key = NULL;
+    gpgme_error_t err;
+    PyObject *ret;
+
+    Py_BEGIN_ALLOW_THREADS;
+    err = gpgme_op_keylist_next(self->ctx->ctx, &key);
+    Py_END_ALLOW_THREADS;
+
+    /* end iteration */
+    if (gpgme_err_source(err) == GPG_ERR_SOURCE_GPGME &&
+        gpgme_err_code(err) == GPG_ERR_EOF) {
+        PyErr_SetNone(PyExc_StopIteration);
+        return NULL;
+    }
+
+    if (pygpgme_check_error(err))
+        return NULL;
+
+    if (key == NULL)
+        Py_RETURN_NONE;
+
+    ret = pygpgme_key_new(key);
+    gpgme_key_unref(key);
+    return ret;
+}
+
+PyTypeObject PyGpgmeKeyIter_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "gpgme.KeyIter",
+    sizeof(PyGpgmeKeyIter),
+    .tp_flags = Py_TPFLAGS_DEFAULT,
+    .tp_init = pygpgme_no_constructor,
+    .tp_dealloc = (destructor)pygpgme_keyiter_dealloc,
+    .tp_iter = (getiterfunc)pygpgme_keyiter_iter,
+    .tp_iternext = (iternextfunc)pygpgme_keyiter_next,
+};
diff --git a/src/pygpgme-signature.c b/src/pygpgme-signature.c
new file mode 100644 (file)
index 0000000..e8a40cb
--- /dev/null
@@ -0,0 +1,180 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+    pygpgme - a Python wrapper for the gpgme library
+    Copyright (C) 2006  James Henstridge
+
+    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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#include "pygpgme.h"
+#include <structmember.h>
+
+static void
+pygpgme_newsig_dealloc(PyGpgmeNewSignature *self)
+{
+    Py_XDECREF(self->type);
+    Py_XDECREF(self->pubkey_algo);
+    Py_XDECREF(self->hash_algo);
+    Py_XDECREF(self->timestamp);
+    Py_XDECREF(self->fpr);
+    Py_XDECREF(self->sig_class);
+    PyObject_Del(self);
+}
+
+static PyMemberDef pygpgme_newsig_members[] = {
+    { "type", T_OBJECT, offsetof(PyGpgmeNewSignature, type), RO},
+    { "pubkey_algo", T_OBJECT, offsetof(PyGpgmeNewSignature, pubkey_algo), RO},
+    { "hash_algo", T_OBJECT, offsetof(PyGpgmeNewSignature, hash_algo), RO},
+    { "timestamp", T_OBJECT, offsetof(PyGpgmeNewSignature, timestamp), RO},
+    { "fpr", T_OBJECT, offsetof(PyGpgmeNewSignature, fpr), RO},
+    { "sig_class", T_OBJECT, offsetof(PyGpgmeNewSignature, sig_class), RO},
+    { NULL, 0, 0, 0}
+};
+
+PyTypeObject PyGpgmeNewSignature_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "gpgme.NewSignature",
+    sizeof(PyGpgmeNewSignature),
+    .tp_flags = Py_TPFLAGS_DEFAULT,
+    .tp_init = pygpgme_no_constructor,
+    .tp_dealloc = (destructor)pygpgme_newsig_dealloc,
+    .tp_members = pygpgme_newsig_members,
+};
+
+PyObject *
+pygpgme_newsiglist_new(gpgme_new_signature_t siglist)
+{
+    PyObject *list;
+    gpgme_new_signature_t sig;
+
+    list = PyList_New(0);
+    for (sig = siglist; sig != NULL; sig = sig->next) {
+        PyGpgmeNewSignature *item = PyObject_New(PyGpgmeNewSignature,
+                                                 &PyGpgmeNewSignature_Type);
+        if (item == NULL) {
+            Py_DECREF(list);
+            return NULL;
+        }
+        item->type = PyInt_FromLong(sig->type);
+        item->pubkey_algo = PyInt_FromLong(sig->pubkey_algo);
+        item->hash_algo = PyInt_FromLong(sig->hash_algo);
+        item->timestamp = PyInt_FromLong(sig->timestamp);
+        if (sig->fpr) {
+            item->fpr = PyString_FromString(sig->fpr);
+        } else {
+            Py_INCREF(Py_None);
+            item->fpr = Py_None;
+        }
+        item->sig_class = PyInt_FromLong(sig->sig_class);
+        if (PyErr_Occurred()) {
+            Py_DECREF(item);
+            Py_DECREF(list);
+            return NULL;
+        }
+        PyList_Append(list, (PyObject *)item);
+        Py_DECREF(item);
+    }
+    return list;
+}
+
+static void
+pygpgme_sig_dealloc(PyGpgmeSignature *self)
+{
+    Py_XDECREF(self->summary);
+    Py_XDECREF(self->fpr);
+    Py_XDECREF(self->status);
+    Py_XDECREF(self->notations);
+    Py_XDECREF(self->timestamp);
+    Py_XDECREF(self->exp_timestamp);
+    Py_XDECREF(self->wrong_key_usage);
+    Py_XDECREF(self->validity);
+    Py_XDECREF(self->validity_reason);
+    PyObject_Del(self);
+}
+
+static PyMemberDef pygpgme_sig_members[] = {
+    { "summary", T_OBJECT, offsetof(PyGpgmeSignature, summary), RO},
+    { "fpr", T_OBJECT, offsetof(PyGpgmeSignature, fpr), RO},
+    { "status", T_OBJECT, offsetof(PyGpgmeSignature, status), RO},
+    { "notations", T_OBJECT, offsetof(PyGpgmeSignature, notations), RO},
+    { "timestamp", T_OBJECT, offsetof(PyGpgmeSignature, timestamp), RO},
+    { "exp_timestamp", T_OBJECT,
+      offsetof(PyGpgmeSignature, exp_timestamp), RO},
+    { "wrong_key_usage", T_OBJECT,
+      offsetof(PyGpgmeSignature, wrong_key_usage), RO},
+    { "validity", T_OBJECT, offsetof(PyGpgmeSignature, validity), RO},
+    { "validity_reason", T_OBJECT,
+      offsetof(PyGpgmeSignature, validity_reason), RO},
+    { NULL, 0, 0, 0}
+};
+
+PyTypeObject PyGpgmeSignature_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "gpgme.Signature",
+    sizeof(PyGpgmeSignature),
+    .tp_flags = Py_TPFLAGS_DEFAULT,
+    .tp_init = pygpgme_no_constructor,
+    .tp_dealloc = (destructor)pygpgme_sig_dealloc,
+    .tp_members = pygpgme_sig_members,
+};
+
+PyObject *
+pygpgme_siglist_new(gpgme_signature_t siglist)
+{
+    PyObject *list;
+    gpgme_signature_t sig;
+    gpgme_sig_notation_t not;
+
+    list = PyList_New(0);
+    for (sig = siglist; sig != NULL; sig = sig->next) {
+        PyGpgmeSignature *item = PyObject_New(PyGpgmeSignature,
+                                              &PyGpgmeSignature_Type);
+        if (item == NULL) {
+            Py_DECREF(list);
+            return NULL;
+        }
+        item->summary = PyInt_FromLong(sig->summary);
+        if (sig->fpr) {
+            item->fpr = PyString_FromString(sig->fpr);
+        } else {
+            Py_INCREF(Py_None);
+            item->fpr = Py_None;
+        }
+        item->status = pygpgme_error_object(sig->status);
+        item->notations = PyList_New(0);
+        for (not = sig->notations; not != NULL; not = not->next) {
+            PyObject *pynot = Py_BuildValue("(zz)", not->name, not->value);
+
+            if (!pynot)
+                break;
+            PyList_Append(item->notations, pynot);
+            Py_DECREF(pynot);
+        }
+        item->timestamp = PyInt_FromLong(sig->timestamp);
+        item->exp_timestamp = PyInt_FromLong(sig->exp_timestamp);
+        item->wrong_key_usage = PyBool_FromLong(sig->wrong_key_usage);
+        item->validity = PyInt_FromLong(sig->validity);
+        item->validity_reason = pygpgme_error_object(sig->validity_reason);
+        if (PyErr_Occurred()) {
+            Py_DECREF(item);
+            Py_DECREF(list);
+            return NULL;
+        }
+        PyList_Append(list, (PyObject *)item);
+        Py_DECREF(item);
+    }
+    return list;
+}
diff --git a/src/pygpgme.h b/src/pygpgme.h
new file mode 100644 (file)
index 0000000..8f47aa0
--- /dev/null
@@ -0,0 +1,128 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+    pygpgme - a Python wrapper for the gpgme library
+    Copyright (C) 2006  James Henstridge
+
+    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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#ifndef PYGPGME_H
+#define PYGPGME_H
+
+#include <Python.h>
+#include <gpgme.h>
+
+#define HIDDEN __attribute__((visibility("hidden")))
+
+typedef struct {
+    PyObject_HEAD
+    gpgme_ctx_t ctx;
+} PyGpgmeContext;
+
+typedef struct {
+    PyObject_HEAD
+    gpgme_key_t key;
+} PyGpgmeKey;
+
+typedef struct {
+    PyObject_HEAD
+    gpgme_subkey_t subkey;
+    PyObject *parent;
+} PyGpgmeSubkey;
+
+typedef struct {
+    PyObject_HEAD
+    gpgme_user_id_t user_id;
+    PyObject *parent;
+} PyGpgmeUserId;
+
+typedef struct {
+    PyObject_HEAD
+    gpgme_key_sig_t key_sig;
+    PyObject *parent;
+} PyGpgmeKeySig;
+
+typedef struct {
+    PyObject_HEAD
+    PyObject *type;
+    PyObject *pubkey_algo;
+    PyObject *hash_algo;
+    PyObject *timestamp;
+    PyObject *fpr;
+    PyObject *sig_class;
+} PyGpgmeNewSignature;
+
+typedef struct {
+    PyObject_HEAD
+    PyObject *summary;
+    PyObject *fpr;
+    PyObject *status;
+    PyObject *notations;
+    PyObject *timestamp;
+    PyObject *exp_timestamp;
+    PyObject *wrong_key_usage;
+    PyObject *validity;
+    PyObject *validity_reason;
+} PyGpgmeSignature;
+
+typedef struct {
+    PyObject_HEAD
+    PyObject *considered;
+    PyObject *no_user_id;
+    PyObject *imported;
+    PyObject *imported_rsa;
+    PyObject *unchanged;
+    PyObject *new_user_ids;
+    PyObject *new_sub_keys;
+    PyObject *new_signatures;
+    PyObject *new_revocations;
+    PyObject *secret_read;
+    PyObject *secret_imported;
+    PyObject *secret_unchanged;
+    PyObject *skipped_new_keys;
+    PyObject *not_imported;
+    PyObject *imports;
+} PyGpgmeImportResult;
+
+typedef struct {
+    PyObject_HEAD
+    PyGpgmeContext *ctx;
+} PyGpgmeKeyIter;
+
+extern HIDDEN PyObject *pygpgme_error;
+extern HIDDEN PyTypeObject PyGpgmeContext_Type;
+extern HIDDEN PyTypeObject PyGpgmeKey_Type;
+extern HIDDEN PyTypeObject PyGpgmeSubkey_Type;
+extern HIDDEN PyTypeObject PyGpgmeUserId_Type;
+extern HIDDEN PyTypeObject PyGpgmeKeySig_Type;
+extern HIDDEN PyTypeObject PyGpgmeNewSignature_Type;
+extern HIDDEN PyTypeObject PyGpgmeSignature_Type;
+extern HIDDEN PyTypeObject PyGpgmeImportResult_Type;
+extern HIDDEN PyTypeObject PyGpgmeKeyIter_Type;
+
+HIDDEN int           pygpgme_check_error    (gpgme_error_t err);
+HIDDEN PyObject     *pygpgme_error_object   (gpgme_error_t err);
+HIDDEN gpgme_error_t pygpgme_check_pyerror  (void);
+HIDDEN int           pygpgme_no_constructor (PyObject *self, PyObject *args,
+                                             PyObject *kwargs);
+
+HIDDEN int           pygpgme_data_new       (gpgme_data_t *dh, PyObject *fp);
+HIDDEN PyObject     *pygpgme_key_new        (gpgme_key_t key);
+HIDDEN PyObject     *pygpgme_newsiglist_new (gpgme_new_signature_t siglist);
+HIDDEN PyObject     *pygpgme_siglist_new    (gpgme_signature_t siglist);
+HIDDEN PyObject     *pygpgme_import_result  (gpgme_ctx_t ctx);
+
+HIDDEN PyObject     *pygpgme_make_constants (PyObject *self, PyObject *args);
+
+#endif
diff --git a/test_all.py b/test_all.py
new file mode 100644 (file)
index 0000000..2051ec3
--- /dev/null
@@ -0,0 +1,10 @@
+import sys
+import unittest
+
+import gpgme.tests
+
+def test_suite():
+    return gpgme.tests.test_suite()
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')