Code

Sending of PGP-Encrypted mail to all users or selected users (via roles)
authorschlatterbeck <schlatterbeck@57a73879-2fb5-44c3-a270-3262357dd7e2>
Fri, 7 Oct 2011 18:04:00 +0000 (18:04 +0000)
committerschlatterbeck <schlatterbeck@57a73879-2fb5-44c3-a270-3262357dd7e2>
Fri, 7 Oct 2011 18:04:00 +0000 (18:04 +0000)
is now working. (Ralf)

git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/roundup/trunk@4655 57a73879-2fb5-44c3-a270-3262357dd7e2

CHANGES.txt
roundup/roundupdb.py
test/db_test_base.py
test/gpgmelib.py [new file with mode: 0644]
test/test_mailgw.py

index 1cab75f26211ebf24d8fde373882d7992f02a72c..468436538d281552b8291f76f62e1db8ee3abe13 100644 (file)
@@ -13,6 +13,8 @@ Features:
 - Allow to turn off translation of generated html options in menu method
   of LinkHTMLProperty and MultilinkHTMLProperty -- default is
   translation as it used to be (Ralf)
 - Allow to turn off translation of generated html options in menu method
   of LinkHTMLProperty and MultilinkHTMLProperty -- default is
   translation as it used to be (Ralf)
+- Sending of PGP-Encrypted mail to all users or selected users (via
+  roles) is now working. (Ralf)
 
 Fixed:
 
 
 Fixed:
 
@@ -32,9 +34,7 @@ Fixed:
 - PGP support is again working (pyme API has changed significantly) and
   we now have a regression test. We now take care that bounce-messages
   for incoming encrypted mails or mails where the policy dictates that
 - PGP support is again working (pyme API has changed significantly) and
   we now have a regression test. We now take care that bounce-messages
   for incoming encrypted mails or mails where the policy dictates that
-  outgoing traffic should be encrypted is actually pgp-encrypted. Note
-  that the new pgp encrypt option for outgoing mails works only for
-  bounces for now. (Ralf)
+  outgoing traffic should be encrypted is actually pgp-encrypted. (Ralf)
 
 2011-07-15 1.4.19 (r4638)
 
 
 2011-07-15 1.4.19 (r4638)
 
index 91d855c9282bfae8df9fdd5f59ffa092d57b303f..ddcf0c37ccaa092ee7b64156a10165036808f7bd 100644 (file)
@@ -30,16 +30,23 @@ from email.Utils import formataddr
 from email.Header import Header
 from email.MIMEText import MIMEText
 from email.MIMEBase import MIMEBase
 from email.Header import Header
 from email.MIMEText import MIMEText
 from email.MIMEBase import MIMEBase
+from email.MIMEMultipart import MIMEMultipart
 
 from anypy.email_ import FeedParser
 
 from roundup import password, date, hyperdb
 from roundup.i18n import _
 
 from anypy.email_ import FeedParser
 
 from roundup import password, date, hyperdb
 from roundup.i18n import _
+from roundup.hyperdb import iter_roles
 
 
-# MessageSendError is imported for backwards compatibility
 from roundup.mailer import Mailer, MessageSendError, encode_quopri, \
     nice_sender_header
 
 from roundup.mailer import Mailer, MessageSendError, encode_quopri, \
     nice_sender_header
 
+try:
+    import pyme, pyme.core
+except ImportError:
+    pyme = None
+
+
 class Database:
 
     # remember the journal uid for the current journaltag so that:
 class Database:
 
     # remember the journal uid for the current journaltag so that:
@@ -212,14 +219,26 @@ class IssueClass:
         The "bcc" argument also indicates additional recipients to send the
         message to that may not be specified in the message's recipients
         list. These recipients will not be included in the To: or Cc:
         The "bcc" argument also indicates additional recipients to send the
         message to that may not be specified in the message's recipients
         list. These recipients will not be included in the To: or Cc:
-        address lists.
+        address lists. Note that the list of bcc users *is* updated in
+        the recipient list of the message, so this field has to be
+        protected (using appropriate permissions), otherwise the bcc
+        will be decuceable for users who have web access to the tracker.
 
         The cc_emails and bcc_emails arguments take a list of additional
         recipient email addresses (just the mail address not roundup users)
 
         The cc_emails and bcc_emails arguments take a list of additional
         recipient email addresses (just the mail address not roundup users)
-        this can be useful for sending to additional email addresses which are no
-        roundup users. These arguments are currently not used by roundups
-        nosyreaction but can be used by customized (nosy-)reactors.
+        this can be useful for sending to additional email addresses
+        which are no roundup users. These arguments are currently not
+        used by roundups nosyreaction but can be used by customized
+        (nosy-)reactors.
+
+        A note on encryption: If pgp encryption for outgoing mails is
+        turned on in the configuration and no specific pgp roles are
+        defined, we try to send encrypted mail to *all* users
+        *including* cc, bcc, cc_emails and bcc_emails and this might
+        fail if not all the keys are available in roundups keyring.
         """
         """
+        encrypt = self.db.config.PGP_ENABLE and self.db.config.PGP_ENCRYPT
+        pgproles = self.db.config.PGP_ROLES
         if msgid:
             authid = self.db.msg.get(msgid, 'author')
             recipients = self.db.msg.get(msgid, 'recipients', [])
         if msgid:
             authid = self.db.msg.get(msgid, 'author')
             recipients = self.db.msg.get(msgid, 'recipients', [])
@@ -228,8 +247,8 @@ class IssueClass:
             authid = None
             recipients = []
 
             authid = None
             recipients = []
 
-        sendto = []
-        bcc_sendto = []
+        sendto = dict (plain = [], crypt = [])
+        bcc_sendto = dict (plain = [], crypt = [])
         seen_message = {}
         for recipient in recipients:
             seen_message[recipient] = 1
         seen_message = {}
         for recipient in recipients:
             seen_message[recipient] = 1
@@ -238,7 +257,10 @@ class IssueClass:
             """ make sure they have an address """
             address = self.db.user.get(userid, 'address')
             if address:
             """ make sure they have an address """
             address = self.db.user.get(userid, 'address')
             if address:
-                to.append(address)
+                ciphered = encrypt and (not pgproles or
+                    self.db.user.has_role(userid, *iter_roles(pgproles)))
+                type = ['plain', 'crypt'][ciphered]
+                to[type].append(address)
                 recipients.append(userid)
 
         def good_recipient(userid):
                 recipients.append(userid)
 
         def good_recipient(userid):
@@ -273,13 +295,19 @@ class IssueClass:
         for userid in cc + self.get(issueid, whichnosy):
             if good_recipient(userid):
                 add_recipient(userid, sendto)
         for userid in cc + self.get(issueid, whichnosy):
             if good_recipient(userid):
                 add_recipient(userid, sendto)
-        sendto.extend (cc_emails)
+        if encrypt and not pgproles:
+            sendto['crypt'].extend (cc_emails)
+        else:
+            sendto['plain'].extend (cc_emails)
 
         # now deal with bcc people.
         for userid in bcc:
             if good_recipient(userid):
                 add_recipient(userid, bcc_sendto)
 
         # now deal with bcc people.
         for userid in bcc:
             if good_recipient(userid):
                 add_recipient(userid, bcc_sendto)
-        bcc_sendto.extend (bcc_emails)
+        if encrypt and not pgproles:
+            bcc_sendto['crypt'].extend (bcc_emails)
+        else:
+            bcc_sendto['plain'].extend (bcc_emails)
 
         if oldvalues:
             note = self.generateChangeNote(issueid, oldvalues)
 
         if oldvalues:
             note = self.generateChangeNote(issueid, oldvalues)
@@ -288,17 +316,53 @@ class IssueClass:
 
         # If we have new recipients, update the message's recipients
         # and send the mail.
 
         # If we have new recipients, update the message's recipients
         # and send the mail.
-        if sendto or bcc_sendto:
+        if sendto['plain'] or sendto['crypt']:
+            # update msgid and recipients only if non-bcc have changed
             if msgid is not None:
                 self.db.msg.set(msgid, recipients=recipients)
             if msgid is not None:
                 self.db.msg.set(msgid, recipients=recipients)
-            self.send_message(issueid, msgid, note, sendto, from_address,
-                bcc_sendto)
+        if sendto['plain'] or bcc_sendto['plain']:
+            self.send_message(issueid, msgid, note, sendto['plain'],
+                from_address, bcc_sendto['plain'])
+        if sendto['crypt'] or bcc_sendto['crypt']:
+            self.send_message(issueid, msgid, note, sendto['crypt'],
+                from_address, bcc_sendto['crypt'], crypt=True)
 
     # backwards compatibility - don't remove
     sendmessage = nosymessage
 
 
     # backwards compatibility - don't remove
     sendmessage = nosymessage
 
+    def encrypt_to(self, message, sendto):
+        """ Encrypt given message to sendto receivers.
+            Returns a new RFC 3156 conforming message.
+        """
+        plain = pyme.core.Data(message.as_string())
+        cipher = pyme.core.Data()
+        ctx = pyme.core.Context()
+        ctx.set_armor(1)
+        keys = []
+        for adr in sendto:
+            ctx.op_keylist_start(adr, 0)
+            # only first key per email
+            k = ctx.op_keylist_next()
+            if k is not None:
+                keys.append(k)
+            else:
+                msg = _('No key for "%(adr)s" in keyring')%locals()
+                raise MessageSendError, msg
+            ctx.op_keylist_end()
+        ctx.op_encrypt(keys, 1, plain, cipher)
+        cipher.seek(0,0)
+        msg = MIMEMultipart('encrypted', boundary=None, _subparts=None,
+            protocol="application/pgp-encrypted")
+        part = MIMEBase('application', 'pgp-encrypted')
+        part.set_payload("Version: 1\r\n")
+        msg.attach(part)
+        part = MIMEBase('application', 'octet-stream')
+        part.set_payload(cipher.read())
+        msg.attach(part)
+        return msg
+
     def send_message(self, issueid, msgid, note, sendto, from_address=None,
     def send_message(self, issueid, msgid, note, sendto, from_address=None,
-            bcc_sendto=[]):
+            bcc_sendto=[], crypt=False):
         '''Actually send the nominated message from this issue to the sendto
            recipients, with the note appended.
         '''
         '''Actually send the nominated message from this issue to the sendto
            recipients, with the note appended.
         '''
@@ -430,7 +494,6 @@ class IssueClass:
             mailer = Mailer(self.db.config)
 
             message = mailer.get_standard_message(multipart=message_files)
             mailer = Mailer(self.db.config)
 
             message = mailer.get_standard_message(multipart=message_files)
-            mailer.set_message_attributes(message, sendto, subject, author)
 
             # set reply-to to the tracker
             message['Reply-To'] = tracker_name
 
             # set reply-to to the tracker
             message['Reply-To'] = tracker_name
@@ -526,10 +589,29 @@ class IssueClass:
                 message.set_payload(body)
                 encode_quopri(message)
 
                 message.set_payload(body)
                 encode_quopri(message)
 
-            if first:
-                mailer.smtp_send(sendto + bcc_sendto, message.as_string())
+            if crypt:
+                send_msg = self.encrypt_to (message, sendto)
             else:
             else:
-                mailer.smtp_send(sendto, message.as_string())
+                send_msg = message
+            mailer.set_message_attributes(send_msg, sendto, subject, author)
+            send_msg ['Message-Id'] = message ['Message-Id']
+            send_msg ['Reply-To'] = message ['Reply-To']
+            if message.get ('In-Reply-To'):
+                send_msg ['In-Reply-To'] = message ['In-Reply-To']
+            mailer.smtp_send(sendto, send_msg.as_string())
+            if first:
+                if crypt:
+                    # send individual bcc mails, otherwise receivers can
+                    # deduce bcc recipients from keys in message
+                    for bcc in bcc_sendto:
+                        send_msg = self.encrypt_to (message, [bcc])
+                        send_msg ['Message-Id'] = message ['Message-Id']
+                        send_msg ['Reply-To'] = message ['Reply-To']
+                        if message.get ('In-Reply-To'):
+                            send_msg ['In-Reply-To'] = message ['In-Reply-To']
+                        mailer.smtp_send([bcc], send_msg.as_string())
+                elif bcc_sendto:
+                    mailer.smtp_send(bcc_sendto, send_msg.as_string())
             first = False
 
     def email_signature(self, issueid, msgid):
             first = False
 
     def email_signature(self, issueid, msgid):
index 428bc1d5e6b287eb8066cbd397386b57ee766777..ea468e749bc9221be88e5b2a55fa9bdbd1ba32d0 100644 (file)
 # $Id: db_test_base.py,v 1.101 2008-08-19 01:40:59 richard Exp $
 
 import unittest, os, shutil, errno, imp, sys, time, pprint, base64, os.path
 # $Id: db_test_base.py,v 1.101 2008-08-19 01:40:59 richard Exp $
 
 import unittest, os, shutil, errno, imp, sys, time, pprint, base64, os.path
+import gpgmelib
 # Python 2.3 ... 2.6 compatibility:
 from roundup.anypy.sets_ import set
 # Python 2.3 ... 2.6 compatibility:
 from roundup.anypy.sets_ import set
+from email.parser import FeedParser
 
 from roundup.hyperdb import String, Password, Link, Multilink, Date, \
     Interval, DatabaseError, Boolean, Number, Node
 
 from roundup.hyperdb import String, Password, Link, Multilink, Date, \
     Interval, DatabaseError, Boolean, Number, Node
@@ -1933,6 +1935,78 @@ class DBTest(commonDBTest):
             roundupdb._ = old_translate_
             Mailer.smtp_send = backup
 
             roundupdb._ = old_translate_
             Mailer.smtp_send = backup
 
+    def testPGPNosyMail(self) :
+        """Creates one issue with two attachments, one smaller and one larger
+           than the set max_attachment_size. Recipients are one with and
+           one without encryption enabled via a gpg group.
+        """
+        if gpgmelib.pyme is None:
+            print "Skipping PGPNosy test"
+            return
+        old_translate_ = roundupdb._
+        roundupdb._ = i18n.get_translation(language='C').gettext
+        db = self.db
+        db.config.NOSY_MAX_ATTACHMENT_SIZE = 4096
+        db.config['PGP_HOMEDIR'] = gpgmelib.pgphome
+        db.config['PGP_ROLES'] = 'pgp'
+        db.config['PGP_ENABLE'] = True
+        db.config['PGP_ENCRYPT'] = True
+        gpgmelib.setUpPGP()
+        res = []
+        def dummy_snd(s, to, msg, res=res) :
+            res.append (dict (mail_to = to, mail_msg = msg))
+        backup, Mailer.smtp_send = Mailer.smtp_send, dummy_snd
+        try :
+            john = db.user.create(username="john", roles='User,pgp',
+                address='john@test.test', realname='John Doe')
+            f1 = db.file.create(name="test1.txt", content="x" * 20)
+            f2 = db.file.create(name="test2.txt", content="y" * 5000)
+            m  = db.msg.create(content="one two", author="admin",
+                files = [f1, f2])
+            i  = db.issue.create(title='spam', files = [f1, f2],
+                messages = [m], nosy = [db.user.lookup("fred"), john])
+
+            db.issue.nosymessage(i, m, {})
+            res.sort(key=lambda x: x['mail_to'])
+            self.assertEqual(res[0]["mail_to"], ["fred@example.com"])
+            self.assertEqual(res[1]["mail_to"], ["john@test.test"])
+            mail_msg = str(res[0]["mail_msg"])
+            self.assert_("From: admin" in mail_msg)
+            self.assert_("Subject: [issue1] spam" in mail_msg)
+            self.assert_("New submission from admin" in mail_msg)
+            self.assert_("one two" in mail_msg)
+            self.assert_("File 'test1.txt' not attached" not in mail_msg)
+            self.assert_(base64.encodestring("xxx").rstrip() in mail_msg)
+            self.assert_("File 'test2.txt' not attached" in mail_msg)
+            self.assert_(base64.encodestring("yyy").rstrip() not in mail_msg)
+            fp = FeedParser()
+            mail_msg = str(res[1]["mail_msg"])
+            fp.feed(mail_msg)
+            parts = fp.close().get_payload()
+            self.assertEqual(len(parts),2)
+            self.assertEqual(parts[0].get_payload().strip(), 'Version: 1')
+            crypt = gpgmelib.pyme.core.Data(parts[1].get_payload())
+            plain = gpgmelib.pyme.core.Data()
+            ctx = gpgmelib.pyme.core.Context()
+            res = ctx.op_decrypt(crypt, plain)
+            self.assertEqual(res, None)
+            plain.seek(0,0)
+            fp = FeedParser()
+            fp.feed(plain.read())
+            self.assert_("From: admin" in mail_msg)
+            self.assert_("Subject: [issue1] spam" in mail_msg)
+            mail_msg = str(fp.close())
+            self.assert_("New submission from admin" in mail_msg)
+            self.assert_("one two" in mail_msg)
+            self.assert_("File 'test1.txt' not attached" not in mail_msg)
+            self.assert_(base64.encodestring("xxx").rstrip() in mail_msg)
+            self.assert_("File 'test2.txt' not attached" in mail_msg)
+            self.assert_(base64.encodestring("yyy").rstrip() not in mail_msg)
+        finally :
+            roundupdb._ = old_translate_
+            Mailer.smtp_send = backup
+            gpgmelib.tearDownPGP()
+
 class ROTest(MyTestCase):
     def setUp(self):
         # remove previous test, ignore errors
 class ROTest(MyTestCase):
     def setUp(self):
         # remove previous test, ignore errors
diff --git a/test/gpgmelib.py b/test/gpgmelib.py
new file mode 100644 (file)
index 0000000..6be147a
--- /dev/null
@@ -0,0 +1,139 @@
+# -*- encoding: utf-8 -*-
+#
+# Copyright (c) 2011 Ralf Schlatterbeck, rsc@runtux.com.
+# This module is free software, and you may redistribute it and/or modify
+# under the same terms as Python, so long as this copyright message and
+# disclaimer are retained in their original form.
+#
+# This module 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.
+
+import shutil, os
+try:
+    import pyme, pyme.core
+except ImportError:
+    pyme = None
+
+pgp_test_key = """
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.10 (GNU/Linux)
+
+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
+=3uFZ
+-----END PGP PRIVATE KEY BLOCK-----
+"""
+
+john_doe_key = """
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.10 (GNU/Linux)
+
+lQHYBE6NwvABBACxg7QqV2qHywwM3wae6HAHJVEo7EeYA6Lv0pZlW3Aw4CCCnpgJ
+jA7CekGFcmGmoCaN9ezuVAPTgUlK4yt8a7P6cT0vw1q341Om9IEKAu59RpNZN/H9
+6GfZ95bU51W/hdTFysH1DRwbCR3MowvLeA6Pk4cZlPsYHD0SD3De2i1BewARAQAB
+AAP+IRi4L6jKwPS3k3LFrj0SHhL0Fdgv5QTQjTxLNCyfN02iYhglqqoFWncm3jWc
+RU/YwGEYwrrBV97kBmVihzkhfgFRsxynE9PMGKKEAuRcAl21RPJDFA6Dlnp6M2No
+rR6eoAhrlZ8+KsK9JaXSMalzO/Yh4u3mOinq3f3XL96wAEkCAMAxeZMF5pnXARNR
+Y7u2clhNNnLuf+BzpENCFMaWzWPyTcvbf4xNK7ZHPxFVZpX5/qAPJ8rnTaOTHxnN
+5PgqbO8CAOxyrTw/muakTJLg+FXdn8BgxZGJXMT7KmkU9SReefjo7c1WlnZxKIAy
+6vLIG8WMGpdfCFDve0YLr/GGyDtOjDUB/RN3gn6qnAJThBnVk2wESZVx41fihbIF
+ACCKc9heFskzwurtvvp+bunM3quwrSH1hWvxiWJlDmGSn8zQFypGChifgLQZSm9o
+biBEb2UgPGpvaG5AdGVzdC50ZXN0Poi4BBMBAgAiBQJOjcLwAhsDBgsJCAcDAgYV
+CAIJCgsEFgIDAQIeAQIXgAAKCRC/z7qg+FujnPWiA/9T5SOGraRNIVVIyvJvYwkG
+OTAfQ0K3QMlLoQMPmaEbx9Q+isF15M9sOMcl1XGO4UNWuCPIIN8z/y/OLgAB0ZuL
+GlnAPPOOZ+MlaUXiMYo8oi416QZrMDf2H/Nkc10csiXm+zMl8RqeIQBEeljNyJ+t
+MG1EWn/PHTwFTd/VePuQdJ0B2AROjcLwAQQApw+72jKy0/wqg5SAtnVSkA1F3Jna
+/OG+ufz5dX57jkMFRvFoksWIWqHmiCjdE5QV8j+XTnjElhLsmrgjl7aAFveb30R6
+ImmcpKMN31vAp4RZlnyYbYUCY4IXFuz3n1CaUL+mRx5yNJykrZNfpWNf2pwozkZq
+lcDI69ymIW5acXUAEQEAAQAD/R7Jdf98l1scngMYo228ikYUxBqm2eX/fiQNXDWM
+ZR2u+TJ9O53MvFejfXX7Pd6lTDQUBwDFncjgXO0YYSrMzabhqpqoKLqOIpZmBuWC
+Hh1lvcFoIYoDR2LkiJ9EPBUEVUBDsUO8ajkILEE3G+DDpCaf9Vo82lCVyhDESqyt
+v4lxAgDOLpoq1Whv5Ejr6FifTWytCiQjH2P1SmePlQmy6oEJRUYA1t4zYrzCJUX8
+VAvPjh9JXilP6mhDbyQArWllewV9AgDPbVOf75ktRwfhje26tZsukqWYJCc1XvoH
+3PTzA7vH1HZZq7dvxa87PiSnkOLEsIAsI+4jpeMxpPlQRxUvHf1ZAf9rK3v3HMJ/
+2xVzwK24Oaj+g2O7D/fdqtLFGe5S5JobnTyp9xArDAhaZ/AKfDMYjUIKMP+bdNAf
+y8fQUtuawFltm1GInwQYAQIACQUCTo3C8AIbDAAKCRC/z7qg+FujnDzYA/9EU6Pv
+Ci1+DCtxjnq7IOvOjqExhFNGvN9Dw17Tl8HcyW3if9v5RxeSWYKl0DhzVdzMQgH/
+78q4F4W1q2IkB7SCpXizHLIc3eh8iZkbWZE+CGPvTpqyF03Yi16qhxpAbkGs2Yhq
+jTx5oJ4CL5fybBOZLg+BTlK4HIee6xEcbNoq+A==
+=ZKBW
+-----END PGP PRIVATE KEY BLOCK-----
+"""
+
+ownertrust = """
+723762CD5A5FECB76DC72DF85ADCFD5631C38741:6:
+2940C247A1FBAD508A1AF24BBFCFBAA0F85BA39C:6:
+"""
+
+pgphome = 'pgp-test-home'
+def setUpPGP():
+    os.mkdir(pgphome)
+    os.environ['GNUPGHOME'] = pgphome
+    ctx = pyme.core.Context()
+    key = pyme.core.Data(pgp_test_key)
+    ctx.op_import(key)
+    key = pyme.core.Data(john_doe_key)
+    ctx.op_import(key)
+    # trust-modelling with pyme isn't working in 0.8.1
+    # based on libgpgme11 1.2.0, also tried in C -- same thing.
+    otrust = os.popen ('gpg --import-ownertrust 2> /dev/null', 'w')
+    otrust.write(ownertrust)
+    otrust.close()
+
+def tearDownPGP():
+    if os.path.exists(pgphome):
+        shutil.rmtree(pgphome)
+
+
+# vim: set filetype=python sts=4 sw=4 et si :
index e0789ae2d015f6b2ab51ae3172605e22ea4ff89e..fde8c460c3846d3bf2ce9403a619b4c6e69c26d2 100644 (file)
@@ -14,6 +14,7 @@
 # TODO: test bcc
 
 import unittest, tempfile, os, shutil, errno, imp, sys, difflib, rfc822, time
 # TODO: test bcc
 
 import unittest, tempfile, os, shutil, errno, imp, sys, difflib, rfc822, time
+import gpgmelib
 from email.parser import FeedParser
 
 
 from email.parser import FeedParser
 
 
@@ -3063,7 +3064,7 @@ ownertrust = """
 """
 
 class MailgwPGPTestCase(MailgwTestAbstractBase):
 """
 
 class MailgwPGPTestCase(MailgwTestAbstractBase):
-    pgphome = 'pgp-test-home'
+    pgphome = gpgmelib.pgphome
     def setUp(self):
         MailgwTestAbstractBase.setUp(self)
         self.db.security.addRole(name = 'pgp', description = 'PGP Role')
     def setUp(self):
         MailgwTestAbstractBase.setUp(self)
         self.db.security.addRole(name = 'pgp', description = 'PGP Role')
@@ -3073,23 +3074,11 @@ class MailgwPGPTestCase(MailgwTestAbstractBase):
         self.instance.config['MAIL_DOMAIN'] = 'example.com'
         self.instance.config['ADMIN_EMAIL'] = 'roundup-admin@example.com'
         self.db.user.set(self.john_id, roles='User,pgp')
         self.instance.config['MAIL_DOMAIN'] = 'example.com'
         self.instance.config['ADMIN_EMAIL'] = 'roundup-admin@example.com'
         self.db.user.set(self.john_id, roles='User,pgp')
-        os.mkdir(self.pgphome)
-        os.environ['GNUPGHOME'] = self.pgphome
-        ctx = pyme.core.Context()
-        key = pyme.core.Data(pgp_test_key)
-        ctx.op_import(key)
-        key = pyme.core.Data(john_doe_key)
-        ctx.op_import(key)
-        # trust-modelling with pyme isn't working in 0.8.1
-        # based on libgpgme11 1.2.0, also tried in C -- same thing.
-        otrust = os.popen ('gpg --import-ownertrust 2> /dev/null', 'w')
-        otrust.write(ownertrust)
-        otrust.close()
+        gpgmelib.setUpPGP()
 
     def tearDown(self):
         MailgwTestAbstractBase.tearDown(self)
 
     def tearDown(self):
         MailgwTestAbstractBase.tearDown(self)
-        if os.path.exists(self.pgphome):
-            shutil.rmtree(self.pgphome)
+        gpgmelib.tearDownPGP()
 
     def testPGPUnsignedMessage(self):
         self.assertRaises(MailUsageError, self._handle_mail,
 
     def testPGPUnsignedMessage(self):
         self.assertRaises(MailUsageError, self._handle_mail,