aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJ08nY2017-06-07 01:27:54 +0200
committerJ08nY2017-06-07 01:27:54 +0200
commit25487795779c05ff8e97680550948443924b98c0 (patch)
tree8646cdebfa7f780851437b4c4099ab8e06c63dea /src
parenta662f9f9681964e10d375c6380cf17e7203421d7 (diff)
downloadmailman-pgp-25487795779c05ff8e97680550948443924b98c0.tar.gz
mailman-pgp-25487795779c05ff8e97680550948443924b98c0.tar.zst
mailman-pgp-25487795779c05ff8e97680550948443924b98c0.zip
Diffstat (limited to 'src')
-rw-r--r--src/pgpmailman/commands/eml_key.py2
-rw-r--r--src/pgpmailman/config/__init__.py8
-rw-r--r--src/pgpmailman/config/mailman.cfg18
-rw-r--r--src/pgpmailman/config/pgpmailman.cfg10
-rw-r--r--src/pgpmailman/database/__init__.py36
-rw-r--r--src/pgpmailman/model/__init__.py0
-rw-r--r--src/pgpmailman/model/base.py8
-rw-r--r--src/pgpmailman/model/list.py24
-rw-r--r--src/pgpmailman/plugin.py34
-rw-r--r--src/pgpmailman/rest/lists.py49
-rw-r--r--src/pgpmailman/rest/root.py11
-rw-r--r--src/pgpmailman/rest/users.py1
-rw-r--r--src/pgpmailman/runners/incoming.py30
-rw-r--r--src/pgpmailman/runners/outgoing.py17
-rw-r--r--src/pgpmailman/styles/announce.py11
-rw-r--r--src/pgpmailman/styles/base.py23
-rw-r--r--src/pgpmailman/styles/discussion.py10
17 files changed, 260 insertions, 32 deletions
diff --git a/src/pgpmailman/commands/eml_key.py b/src/pgpmailman/commands/eml_key.py
index ba538fa..03c0877 100644
--- a/src/pgpmailman/commands/eml_key.py
+++ b/src/pgpmailman/commands/eml_key.py
@@ -13,7 +13,7 @@ class KeyCommand:
short_description = ''
description = ''
- def process(mlist, msg, msgdata, arguments, results):
+ def process(self, mlist, msg, msgdata, arguments, results):
"""See `IEmailCommand`."""
if len(arguments) == 0:
print('No sub-command specified,'
diff --git a/src/pgpmailman/config/__init__.py b/src/pgpmailman/config/__init__.py
new file mode 100644
index 0000000..a6f7004
--- /dev/null
+++ b/src/pgpmailman/config/__init__.py
@@ -0,0 +1,8 @@
+""""""
+
+from configparser import ConfigParser
+
+from public.public import public
+
+config = ConfigParser()
+public(config=config)
diff --git a/src/pgpmailman/config/mailman.cfg b/src/pgpmailman/config/mailman.cfg
new file mode 100644
index 0000000..38ff416
--- /dev/null
+++ b/src/pgpmailman/config/mailman.cfg
@@ -0,0 +1,18 @@
+
+[plugin.pgp]
+class: pgpmailman.plugin.PGPMailman
+path: pgpmailman
+enable: yes
+configuration: python:pgpmailman.config.pgpmailman
+
+[runner.in]
+class: pgpmailman.runners.incoming.IncomingRunner
+
+[runner.in_default]
+class: mailman.runners.incoming.IncomingRunner
+
+[runner.out]
+class: pgpmailman.runners.outgoing.OutgoingRunner
+
+[runner.out_default]
+class: mailman.runners.outgoing.OutgoingRunner
diff --git a/src/pgpmailman/config/pgpmailman.cfg b/src/pgpmailman/config/pgpmailman.cfg
new file mode 100644
index 0000000..541e9c6
--- /dev/null
+++ b/src/pgpmailman/config/pgpmailman.cfg
@@ -0,0 +1,10 @@
+[db]
+url = sqlite:////$DATA_DIR/pgp.db
+
+[keyrings]
+core = $DATA_DIR/pgp_core.gpp
+users = $DATA_DIR/pgp_users.gpg
+
+[queues]
+in = in_default
+out = out_default \ No newline at end of file
diff --git a/src/pgpmailman/database/__init__.py b/src/pgpmailman/database/__init__.py
index e69de29..01975ad 100644
--- a/src/pgpmailman/database/__init__.py
+++ b/src/pgpmailman/database/__init__.py
@@ -0,0 +1,36 @@
+""""""
+
+from contextlib import contextmanager
+
+from mailman.config import config as mailman_config
+from mailman.utilities.string import expand
+from public import public
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+
+from pgpmailman.config import config
+from pgpmailman.model.base import Base
+
+
+@public
+class Database:
+ def __init__(self):
+ url = config.get('db', 'url')
+ self.url = expand(url, None, mailman_config.paths)
+ self.engine = create_engine(self.url)
+ Session = sessionmaker(bind=self.engine)
+ self.session = Session()
+ Base.metadata.create_all(self.engine)
+ self.session.commit()
+
+
+@public
+@contextmanager
+def transaction():
+ try:
+ yield
+ except:
+ config.db.session.abort()
+ raise
+ else:
+ config.db.session.commit()
diff --git a/src/pgpmailman/model/__init__.py b/src/pgpmailman/model/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/pgpmailman/model/__init__.py
diff --git a/src/pgpmailman/model/base.py b/src/pgpmailman/model/base.py
new file mode 100644
index 0000000..e25407f
--- /dev/null
+++ b/src/pgpmailman/model/base.py
@@ -0,0 +1,8 @@
+""""""
+
+from public import public
+from sqlalchemy.ext.declarative import declarative_base
+
+
+Base = declarative_base()
+public(Base=Base)
diff --git a/src/pgpmailman/model/list.py b/src/pgpmailman/model/list.py
new file mode 100644
index 0000000..98b0078
--- /dev/null
+++ b/src/pgpmailman/model/list.py
@@ -0,0 +1,24 @@
+""""""
+
+from mailman.database.types import Enum, SAUnicode
+from mailman.interfaces.action import Action
+from public import public
+from sqlalchemy import Boolean, Column, Integer
+
+from pgpmailman.model.base import Base
+
+
+@public
+class EncryptedMailingList(Base):
+ __tablename__ = 'encrypted_lists'
+
+ id = Column(Integer, primary_key=True)
+ list_id = Column(SAUnicode)
+ key_fingerprint = Column(SAUnicode)
+ unsigned_msg_action = Column(Enum(Action))
+ nonencrypted_msg_action = Column(Enum(Action))
+ strip_original_signature = Column(Boolean)
+ sign_outgoing = Column(Boolean)
+
+ def __init__(self, mlist):
+ self.list_id = mlist.list_id
diff --git a/src/pgpmailman/plugin.py b/src/pgpmailman/plugin.py
index 5ae51a0..820d0a9 100644
--- a/src/pgpmailman/plugin.py
+++ b/src/pgpmailman/plugin.py
@@ -1,21 +1,28 @@
-""" A PGP plugin for GNU Mailman."""
-
+"""A PGP plugin for GNU Mailman."""
+from mailman.app import events
+from mailman.config import config as mailman_config
+from mailman.interfaces.listmanager import ListDeletedEvent
from mailman.interfaces.plugin import IPlugin
-from pgpmailman.rest.root import RESTRoot
+from mailman.utilities.modules import expand_path
from public import public
from zope.interface import implementer
+from pgpmailman.config import config
+from pgpmailman.database import Database, transaction
+from pgpmailman.model.list import EncryptedMailingList
+from pgpmailman.rest.root import RESTRoot
+
@public
@implementer(IPlugin)
class PGPMailman:
-
- def __init__(self):
- self._rest = RESTRoot()
-
def pre_hook(self):
"""See `IPlugin`."""
- pass
+ config.read(
+ expand_path(
+ dict(mailman_config.plugin_configs)[self.name].configuration))
+ config.db = Database()
+ config.name = self.name
def post_hook(self):
"""See `IPlugin`."""
@@ -23,4 +30,13 @@ class PGPMailman:
def rest_object(self):
"""See `IPlugin`."""
- return self._rest
+ return RESTRoot()
+
+
+@events.subscribe(ListDeletedEvent)
+def on_delete(mlist):
+ encrypted_list = config.db.session.query(EncryptedMailingList).filter_by(
+ list_id=mlist.list_id).first()
+ if encrypted_list:
+ with transaction():
+ config.db.session.delete(encrypted_list)
diff --git a/src/pgpmailman/rest/lists.py b/src/pgpmailman/rest/lists.py
index 2c4a58a..fa785ee 100644
--- a/src/pgpmailman/rest/lists.py
+++ b/src/pgpmailman/rest/lists.py
@@ -1,15 +1,54 @@
""""""
+from mailman.rest.helpers import (
+ child, CollectionMixin, etag, not_found, NotFound, okay)
from public import public
+from pgpmailman.config import config
+from pgpmailman.model.list import EncryptedMailingList
+
+
+class _EncryptedBase(CollectionMixin):
+ def _resource_as_dict(self, emlist):
+ """See `CollectionMixin`."""
+ return dict(list_id=emlist.list_id,
+ key_fingerprint=emlist.key_fingerprint,
+ unsigned_msg_action=emlist.unsigned_msg_action,
+ nonencrypted_msg_action=emlist.nonencrypted_msg_action,
+ strip_original_signature=emlist.strip_original_signature,
+ sign_outgoing=emlist.sign_outgoing,
+ self_link=self.api.path_to(
+ '/plugins/{}/lists/{}'.format(config.name,
+ emlist.list_id)))
+
+ def _get_collection(self, request):
+ """See `CollectionMixin`."""
+ return config.db.session.query(EncryptedMailingList).all()
+
@public
-class AllEncryptedLists:
- pass
+class AllEncryptedLists(_EncryptedBase):
+ def on_get(self, request, response):
+ """/lists"""
+ resource = self._make_collection(response)
+ return okay(response, etag(resource))
@public
-class AnEncryptedList:
+class AnEncryptedList(_EncryptedBase):
+ def __init__(self, list_id):
+ self._mlist = config.db.session.query(EncryptedMailingList).filter_by(
+ list_id=list_id).first()
+
+ def on_get(self, request, response):
+ if self._mlist is None:
+ return not_found()
+ else:
+ okay(response, self._resource_as_json(self._mlist))
- def __init__(self, list_name):
- pass
+ @child
+ def key(self, context, segments):
+ if self._mlist is None:
+ return NotFound(), []
+ else:
+ pass
diff --git a/src/pgpmailman/rest/root.py b/src/pgpmailman/rest/root.py
index b937fd3..68afa96 100644
--- a/src/pgpmailman/rest/root.py
+++ b/src/pgpmailman/rest/root.py
@@ -14,10 +14,11 @@ REST root.
"""
from mailman.rest.helpers import child
-from pgpmailman.rest.lists import AllEncryptedLists, AnEncryptedList
-from pgpmailman.rest.users import AUser, AllUsers
from public import public
+from pgpmailman.rest.lists import AllEncryptedLists, AnEncryptedList
+from pgpmailman.rest.users import AllUsers, AUser
+
@public
class RESTRoot:
@@ -26,9 +27,8 @@ class RESTRoot:
if len(segments) == 0:
return AllEncryptedLists(), []
else:
- list_name = segments.pop(0)
- # WIP Check whether it's an encrypted list we know of here.
- return AnEncryptedList(list_name), segments
+ list_id = segments.pop(0)
+ return AnEncryptedList(list_id), segments
@child()
def users(self, context, segments):
@@ -36,5 +36,4 @@ class RESTRoot:
return AllUsers(), []
else:
uid = segments.pop(0)
- # WIP Check whether it's an encrypted user we know of here.
return AUser(uid), segments
diff --git a/src/pgpmailman/rest/users.py b/src/pgpmailman/rest/users.py
index bc6798d..09990f6 100644
--- a/src/pgpmailman/rest/users.py
+++ b/src/pgpmailman/rest/users.py
@@ -10,5 +10,4 @@ class AllUsers:
@public
class AUser:
-
pass
diff --git a/src/pgpmailman/runners/incoming.py b/src/pgpmailman/runners/incoming.py
index ccf90f4..69bbba9 100644
--- a/src/pgpmailman/runners/incoming.py
+++ b/src/pgpmailman/runners/incoming.py
@@ -1,15 +1,39 @@
"""The encryption-aware incoming runner."""
+from mailman.config import config as mailman_config
from mailman.core.runner import Runner
+from mailman.email.message import Message
+from mailman.model.mailinglist import MailingList
from public import public
+from pgpmailman.config import config
+from pgpmailman.model.list import EncryptedMailingList
+
@public
class IncomingRunner(Runner):
- def _dispose(self, mlist, msg, msgdata):
+ def _dispose(self, mlist: MailingList, msg: Message, msgdata: dict):
"""See `IRunner`."""
- pass
# Is the message for an encrypted mailing list? If not, pass to default
# incoming runner. If yes, go on.
-
+ encrypted_list = config.db.query(EncryptedMailingList).filter_by(
+ list_id=mlist.list_id).first()
+ if not encrypted_list:
+ inq = config.get('queues', 'in')
+ mailman_config.switchboards[inq].enqueue(msg, msgdata,
+ listid=mlist.list_id)
+ return False
# Is the message encrypted?
+ if msg.get_content_type() == 'multipart/signed' and msg.get_param(
+ 'protocol') == 'application/pgp-signature':
+ # only signed.
+ pass
+ elif msg.get_content_type() == 'multipart/encrypted' and msg.get_param(
+ 'protocol') == 'application/pgp-encrypted':
+ # definitely encrypted, might still be signed
+ pass
+ else:
+ # not encrypted or signed
+ pass
+ from email.iterators import _structure
+ _structure(msg)
diff --git a/src/pgpmailman/runners/outgoing.py b/src/pgpmailman/runners/outgoing.py
index d7d74bc..11118a2 100644
--- a/src/pgpmailman/runners/outgoing.py
+++ b/src/pgpmailman/runners/outgoing.py
@@ -1,11 +1,24 @@
"""The encryption-aware outgoing runner"""
+from mailman.config import config as mailman_config
from mailman.core.runner import Runner
+from mailman.email.message import Message
+from mailman.model.mailinglist import MailingList
from public import public
+from pgpmailman.config import config
+from pgpmailman.model.list import EncryptedMailingList
+
@public
class OutgoingRunner(Runner):
- def _dispose(self, mlist, msg, msgdata):
+ def _dispose(self, mlist: MailingList, msg: Message, msgdata: dict):
"""See `IRunner`."""
- pass
+ encrypted_list = config.db.query(EncryptedMailingList).filter_by(
+ list_id=mlist.list_id).first()
+ if not encrypted_list:
+ outq = config.get('queues', 'out')
+ mailman_config.switchboards[outq].enqueue(msg,
+ msgdata,
+ listid=mlist.list_id)
+ return False
diff --git a/src/pgpmailman/styles/announce.py b/src/pgpmailman/styles/announce.py
index f83c2ea..26fc01f 100644
--- a/src/pgpmailman/styles/announce.py
+++ b/src/pgpmailman/styles/announce.py
@@ -3,10 +3,15 @@
from mailman.styles.default import LegacyAnnounceOnly
from public import public
+from pgpmailman.styles.base import EncryptedStyle
+
@public
-class Announce(LegacyAnnounceOnly):
+class AnnounceStyle(LegacyAnnounceOnly, EncryptedStyle):
+ name = 'encrypted-announce'
+ description = 'Announce only encrypted mailing list style.'
+
def apply(self, mailing_list):
"""See `IStyle`."""
- super().apply(mailing_list)
- #
+ LegacyAnnounceOnly.apply(self, mailing_list)
+ EncryptedStyle.apply(self, mailing_list)
diff --git a/src/pgpmailman/styles/base.py b/src/pgpmailman/styles/base.py
new file mode 100644
index 0000000..633b95b
--- /dev/null
+++ b/src/pgpmailman/styles/base.py
@@ -0,0 +1,23 @@
+""""""
+
+from public import public
+
+from pgpmailman.config import config
+from pgpmailman.database import transaction
+from pgpmailman.model.list import EncryptedMailingList
+
+
+@public
+class EncryptedStyle:
+ def apply(self, mailing_list):
+ """Creates the encrypted mailing list instance for the list it's
+ applied to.
+ """
+ enc_list = config.db.session.query(EncryptedMailingList).filter_by(
+ list_id=mailing_list.list_id).first()
+ if enc_list:
+ return
+
+ enc_list = EncryptedMailingList(mailing_list)
+ with transaction():
+ config.db.session.add(enc_list)
diff --git a/src/pgpmailman/styles/discussion.py b/src/pgpmailman/styles/discussion.py
index 55304cd..e8c516a 100644
--- a/src/pgpmailman/styles/discussion.py
+++ b/src/pgpmailman/styles/discussion.py
@@ -3,9 +3,15 @@
from mailman.styles.default import LegacyDefaultStyle
from public import public
+from pgpmailman.styles.base import EncryptedStyle
+
@public
-class Discussion(LegacyDefaultStyle):
+class DiscussionStyle(LegacyDefaultStyle, EncryptedStyle):
+ name = 'encrypted-default'
+ description = 'Ordinary discussion encrypted mailing list style.'
+
def apply(self, mailing_list):
"""See `IStyle`."""
- super().apply(mailing_list)
+ LegacyDefaultStyle.apply(self, mailing_list)
+ EncryptedStyle.apply(self, mailing_list)