summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBarry Warsaw2009-02-23 14:13:32 -0500
committerBarry Warsaw2009-02-23 14:13:32 -0500
commitb6ed8a7c98ea02af9014793f3b508c601da6ea75 (patch)
treee0748f60a06bb54493624b031d0ac10b90cabd00
parent2d2d5393acc7db23baf4f3d43a0712bfa795c03e (diff)
downloadmailman-b6ed8a7c98ea02af9014793f3b508c601da6ea75.tar.gz
mailman-b6ed8a7c98ea02af9014793f3b508c601da6ea75.tar.zst
mailman-b6ed8a7c98ea02af9014793f3b508c601da6ea75.zip
-rw-r--r--src/mailman/database/mailinglist.py52
-rw-r--r--src/mailman/database/mailman.sql16
-rw-r--r--src/mailman/interfaces/mailinglist.py35
-rw-r--r--src/mailman/rules/docs/implicit-dest.txt54
-rw-r--r--src/mailman/rules/implicit_dest.py9
-rw-r--r--src/mailman/styles/default.py1
6 files changed, 149 insertions, 18 deletions
diff --git a/src/mailman/database/mailinglist.py b/src/mailman/database/mailinglist.py
index 2956cac57..fa4af5ad0 100644
--- a/src/mailman/database/mailinglist.py
+++ b/src/mailman/database/mailinglist.py
@@ -29,7 +29,8 @@ import os
import string
from storm.locals import (
- Bool, DateTime, Float, Int, Pickle, Store, TimeDelta, Unicode)
+ And, Bool, DateTime, Float, Int, Pickle, Reference, Store, TimeDelta,
+ Unicode)
from urlparse import urljoin
from zope.interface import implements
@@ -38,7 +39,8 @@ from mailman.database import roster
from mailman.database.digests import OneLastDigest
from mailman.database.model import Model
from mailman.database.types import Enum
-from mailman.interfaces.mailinglist import IMailingList, Personalization
+from mailman.interfaces.mailinglist import (
+ IAcceptableAlias, IMailingList, Personalization)
from mailman.utilities.filesystem import makedirs
from mailman.utilities.string import expand
@@ -67,11 +69,13 @@ class MailingList(Model):
digest_last_sent_at = DateTime()
volume = Int()
last_post_time = DateTime()
+ # Implicit destination.
+ acceptable_aliases_id = Int()
+ acceptable_alias = Reference(acceptable_aliases_id, 'AcceptableAlias.id')
# Attributes which are directly modifiable via the web u/i. The more
# complicated attributes are currently stored as pickles, though that
# will change as the schema and implementation is developed.
accept_these_nonmembers = Pickle()
- acceptable_aliases = Pickle()
admin_immed_notify = Bool()
admin_notify_mchanges = Bool()
administrivia = Bool()
@@ -293,3 +297,45 @@ class MailingList(Model):
for digest in results]
results.remove()
return recipients
+
+ def clear_acceptable_aliases(self):
+ """See `IMailingList`."""
+ Store.of(self).find(
+ AcceptableAlias,
+ AcceptableAlias.mailing_list == self).remove()
+
+ def add_acceptable_alias(self, alias):
+ if not (alias.startswith('^') or '@' in alias):
+ raise ValueError(alias)
+ alias = AcceptableAlias(self, alias.lower())
+ Store.of(self).add(alias)
+
+ def remove_acceptable_alias(self, alias):
+ Store.of(self).find(
+ AcceptableAlias,
+ And(AcceptableAlias.mailing_list == self,
+ AcceptableAlias.alias == alias.lower())).remove()
+
+ @property
+ def acceptable_aliases(self):
+ aliases = Store.of(self).find(
+ AcceptableAlias,
+ AcceptableAlias.mailing_list == self)
+ for alias in aliases:
+ yield alias.alias
+
+
+
+class AcceptableAlias(Model):
+ implements(IAcceptableAlias)
+
+ id = Int(primary=True)
+
+ mailing_list_id = Int()
+ mailing_list = Reference(mailing_list_id, MailingList.id)
+
+ alias = Unicode()
+
+ def __init__(self, mailing_list, alias):
+ self.mailing_list = mailing_list
+ self.alias = alias
diff --git a/src/mailman/database/mailman.sql b/src/mailman/database/mailman.sql
index 79a28574e..d64be9815 100644
--- a/src/mailman/database/mailman.sql
+++ b/src/mailman/database/mailman.sql
@@ -7,6 +7,20 @@ CREATE TABLE _request (
PRIMARY KEY (id),
CONSTRAINT _request_mailing_list_id_fk FOREIGN KEY(mailing_list_id) REFERENCES mailinglist (id)
);
+
+CREATE TABLE acceptablealias (
+ id INTEGER NOT NULL,
+ "alias" TEXT NOT NULL,
+ mailing_list_id INTEGER NOT NULL,
+ PRIMARY KEY (id),
+ CONSTRAINT acceptablealias_mailing_list_id_fk
+ FOREIGN KEY(mailing_list_id) REFERENCES mailinglist (id)
+ );
+CREATE INDEX ix_acceptablealias_mailing_list_id
+ ON acceptablealias (mailing_list_id);
+CREATE INDEX ix_acceptablealias_alias
+ ON acceptablealias ("alias");
+
CREATE TABLE address (
id INTEGER NOT NULL,
address TEXT,
@@ -57,7 +71,7 @@ CREATE TABLE mailinglist (
volume INTEGER,
last_post_time TIMESTAMP,
accept_these_nonmembers BLOB,
- acceptable_aliases BLOB,
+ acceptable_aliases_id INTEGER,
admin_immed_notify BOOLEAN,
admin_notify_mchanges BOOLEAN,
administrivia BOOLEAN,
diff --git a/src/mailman/interfaces/mailinglist.py b/src/mailman/interfaces/mailinglist.py
index 4e04fa39b..2cfb9f737 100644
--- a/src/mailman/interfaces/mailinglist.py
+++ b/src/mailman/interfaces/mailinglist.py
@@ -280,3 +280,38 @@ class IMailingList(Interface):
An example of list-specific data is the temporary digest mbox file
that gets created to accumlate messages for the digest.
""")
+
+ def clear_acceptable_aliases():
+ """Clear the set of acceptable posting aliases."""
+
+ def add_acceptable_alias(alias):
+ """Add the given address as an acceptable aliases for posting.
+
+ :param alias: The email address to accept as a recipient for implicit
+ destination posting purposes. The alias is coerced to lower
+ case. If `alias` begins with a '^' character, it is interpreted
+ as a regular expression, otherwise it must be an email address.
+ :type alias: string
+ :raises ValueError: when the alias neither starts with '^' nor has an
+ '@' sign in it.
+ """
+
+ def remove_acceptable_alias(alias):
+ """Remove the given address as an acceptable aliases for posting.
+
+ :param alias: The email address to no longer accept as a recipient for
+ implicit destination posting purposes.
+ :type alias: string
+ """
+
+ acceptable_aliases = Attribute(
+ """An iterator over all the acceptable aliases.""")
+
+
+
+class IAcceptableAlias(Interface):
+ """An acceptable alias for implicit destinations."""
+
+ mailing_list = Attribute('The associated mailing list.')
+
+ address = Attribute('The address or pattern to match against recipients.')
diff --git a/src/mailman/rules/docs/implicit-dest.txt b/src/mailman/rules/docs/implicit-dest.txt
index e5c340dcd..c0439cfca 100644
--- a/src/mailman/rules/docs/implicit-dest.txt
+++ b/src/mailman/rules/docs/implicit-dest.txt
@@ -4,16 +4,17 @@ Implicit destination
The 'implicit-dest' rule matches when the mailing list's posting address is
not explicitly mentioned in the set of message recipients.
- >>> mlist = config.db.list_manager.create(u'_xtest@example.com')
+ >>> mlist = create_list(u'_xtest@example.com')
>>> rule = config.rules['implicit-dest']
>>> print rule.name
implicit-dest
-This rule matches messages that have implicit destination, meaning that the
+This rule matches messages that have an implicit destination, meaning that the
mailing list's posting address isn't included in the explicit recipients.
>>> mlist.require_explicit_destination = True
- >>> mlist.acceptable_aliases = u''
+ >>> mlist.clear_acceptable_aliases()
+
>>> msg = message_from_string("""\
... From: aperson@example.org
... Subject: An implicit message
@@ -48,7 +49,8 @@ then the rule will not match.
>>> del msg['cc']
>>> rule.check(mlist, msg, {})
True
- >>> mlist.acceptable_aliases = u'myfriend@example.com'
+
+ >>> mlist.add_acceptable_alias(u'myfriend@example.com')
>>> rule.check(mlist, msg, {})
False
@@ -56,9 +58,36 @@ A message gated from NNTP will obviously have an implicit destination. Such
gated messages will not be held for implicit destination because it's assumed
that Mailman pulled it from the appropriate news group.
- >>> rule.check(mlist, msg, dict(fromusenet=True))
+ >>> rule.check(mlist, msg, dict(from_usenet=True))
+ False
+
+Additional aliases can be added.
+
+ >>> mlist.add_acceptable_alias(u'other@example.com')
+ >>> del msg['to']
+ >>> rule.check(mlist, msg, {})
+ True
+
+ >>> msg['To'] = 'other@example.com'
+ >>> rule.check(mlist, msg, {})
+ False
+
+Aliases can be removed.
+
+ >>> mlist.remove_acceptable_alias(u'other@example.com')
+ >>> rule.check(mlist, msg, {})
+ True
+
+Aliases can also be cleared.
+
+ >>> msg['Cc'] = u'myfriend@example.com'
+ >>> rule.check(mlist, msg, {})
False
+ >>> mlist.clear_acceptable_aliases()
+ >>> rule.check(mlist, msg, {})
+ True
+
Alias patterns
--------------
@@ -67,9 +96,22 @@ It's also possible to specify an alias pattern, i.e. a regular expression to
match against the recipients. For example, we can say that if there is a
recipient in the example.net domain, then the rule does not match.
- >>> mlist.acceptable_aliases = u'^.*@example.net'
+ >>> mlist.add_acceptable_alias(u'^.*@example.net')
>>> rule.check(mlist, msg, {})
True
+
>>> msg['To'] = 'you@example.net'
>>> rule.check(mlist, msg, {})
False
+
+
+Bad aliases
+-----------
+
+You cannot add an alias that looks like neither a pattern nor an email
+address.
+
+ >>> mlist.add_acceptable_alias('foobar')
+ Traceback (most recent call last):
+ ...
+ ValueError: foobar
diff --git a/src/mailman/rules/implicit_dest.py b/src/mailman/rules/implicit_dest.py
index 3ddffa2cf..69e6f8434 100644
--- a/src/mailman/rules/implicit_dest.py
+++ b/src/mailman/rules/implicit_dest.py
@@ -55,16 +55,11 @@ class ImplicitDestination:
# a caret (i.e. ^), then it's a regular expression to match against.
aliases = set()
alias_patterns = set()
- for alias in mlist.acceptable_aliases.splitlines():
- alias = alias.strip().lower()
+ for alias in mlist.acceptable_aliases:
if alias.startswith('^'):
alias_patterns.add(alias)
- elif '@' in alias:
- aliases.add(alias)
else:
- # This is not a regular expression, nor a fully-qualified
- # email address, so skip it.
- pass
+ aliases.add(alias)
# Add the list's posting address, i.e. the explicit address, to the
# set of acceptable aliases.
aliases.add(mlist.posting_address)
diff --git a/src/mailman/styles/default.py b/src/mailman/styles/default.py
index adea20582..ebfef6fca 100644
--- a/src/mailman/styles/default.py
+++ b/src/mailman/styles/default.py
@@ -68,7 +68,6 @@ class DefaultStyle:
mlist.admin_immed_notify = True
mlist.admin_notify_mchanges = False
mlist.require_explicit_destination = True
- mlist.acceptable_aliases = ''
mlist.send_reminders = True
mlist.send_welcome_msg = True
mlist.send_goodbye_msg = True