diff options
Diffstat (limited to 'src/mailman/database')
| -rw-r--r-- | src/mailman/database/mailinglist.py | 196 | ||||
| -rw-r--r-- | src/mailman/database/mailman.sql | 21 | ||||
| -rw-r--r-- | src/mailman/database/mime.py | 52 |
3 files changed, 234 insertions, 35 deletions
diff --git a/src/mailman/database/mailinglist.py b/src/mailman/database/mailinglist.py index fa4af5ad0..22de044e3 100644 --- a/src/mailman/database/mailinglist.py +++ b/src/mailman/database/mailinglist.py @@ -22,6 +22,7 @@ from __future__ import absolute_import, unicode_literals __metaclass__ = type __all__ = [ 'MailingList', + 'adapt_mailing_list_to_acceptable_alias_set', ] @@ -37,10 +38,12 @@ from zope.interface import implements from mailman.config import config from mailman.database import roster from mailman.database.digests import OneLastDigest +from mailman.database.mime import ContentFilter from mailman.database.model import Model from mailman.database.types import Enum from mailman.interfaces.mailinglist import ( - IAcceptableAlias, IMailingList, Personalization) + IAcceptableAlias, IAcceptableAliasSet, IMailingList, Personalization) +from mailman.interfaces.mime import FilterType from mailman.utilities.filesystem import makedirs from mailman.utilities.string import expand @@ -92,6 +95,10 @@ class MailingList(Model): autoresponse_postings_text = Unicode() autorespond_requests = Enum() autoresponse_request_text = Unicode() + # Content filters. + filter_content = Bool() + collapse_alternatives = Bool() + convert_html_to_plaintext = Bool() # Bounces and bans. ban_list = Pickle() bounce_info_stale_after = TimeDelta() @@ -103,8 +110,6 @@ class MailingList(Model): bounce_unrecognized_goes_to_list_owner = Bool() bounce_you_are_disabled_warnings = Int() bounce_you_are_disabled_warnings_interval = TimeDelta() - collapse_alternatives = Bool() - convert_html_to_plaintext = Bool() default_member_moderation = Bool() description = Unicode() digest_footer = Unicode() @@ -117,10 +122,6 @@ class MailingList(Model): discard_these_nonmembers = Pickle() emergency = Bool() encode_ascii_prefixes = Bool() - filter_action = Int() - filter_content = Bool() - filter_filename_extensions = Pickle() - filter_mime_types = Pickle() first_strip_reply_to = Bool() forward_auto_discards = Bool() gateway_to_mail = Bool() @@ -149,8 +150,6 @@ class MailingList(Model): nondigestable = Bool() nonmember_rejection_notice = Unicode() obscure_addresses = Bool() - pass_filename_extensions = Pickle() - pass_mime_types = Pickle() personalize = Enum() pipeline = Unicode() post_id = Int() @@ -230,41 +229,51 @@ class MailingList(Model): @property def posting_address(self): + """See `IMailingList`.""" return self.fqdn_listname @property def no_reply_address(self): + """See `IMailingList`.""" return '{0}@{1}'.format(config.mailman.noreply_address, self.host_name) @property def owner_address(self): + """See `IMailingList`.""" return '{0}-owner@{1}'.format(self.list_name, self.host_name) @property def request_address(self): + """See `IMailingList`.""" return '{0}-request@{1}'.format(self.list_name, self.host_name) @property def bounces_address(self): + """See `IMailingList`.""" return '{0}-bounces@{1}'.format(self.list_name, self.host_name) @property def join_address(self): + """See `IMailingList`.""" return '{0}-join@{1}'.format(self.list_name, self.host_name) @property def leave_address(self): + """See `IMailingList`.""" return '{0}-leave@{1}'.format(self.list_name, self.host_name) @property def subscribe_address(self): + """See `IMailingList`.""" return '{0}-subscribe@{1}'.format(self.list_name, self.host_name) @property def unsubscribe_address(self): + """See `IMailingList`.""" return '{0}-unsubscribe@{1}'.format(self.list_name, self.host_name) def confirm_address(self, cookie): + """See `IMailingList`.""" local_part = expand(config.mta.verp_confirm_format, dict( address = '{0}-confirm'.format(self.list_name), cookie = cookie)) @@ -272,10 +281,12 @@ class MailingList(Model): @property def preferred_language(self): + """See `IMailingList`.""" return config.languages[self._preferred_language] @preferred_language.setter def preferred_language(self, language): + """See `IMailingList`.""" # Accept both a language code and a `Language` instance. try: self._preferred_language = language.code @@ -298,31 +309,109 @@ class MailingList(Model): results.remove() return recipients - def clear_acceptable_aliases(self): + @property + def filter_types(self): """See `IMailingList`.""" - Store.of(self).find( - AcceptableAlias, - AcceptableAlias.mailing_list == self).remove() + results = Store.of(self).find( + ContentFilter, + And(ContentFilter.mailing_list == self, + ContentFilter.filter_type == FilterType.filter_mime)) + for content_filter in results: + yield content_filter.filter_pattern - 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) + @filter_types.setter + def filter_types(self, sequence): + """See `IMailingList`.""" + # First, delete all existing MIME type filter patterns. + store = Store.of(self) + results = store.find( + ContentFilter, + And(ContentFilter.mailing_list == self, + ContentFilter.filter_type == FilterType.filter_mime)) + results.remove() + # Now add all the new filter types. + for mime_type in sequence: + content_filter = ContentFilter( + self, mime_type, FilterType.filter_mime) + store.add(content_filter) - def remove_acceptable_alias(self, alias): - Store.of(self).find( - AcceptableAlias, - And(AcceptableAlias.mailing_list == self, - AcceptableAlias.alias == alias.lower())).remove() + @property + def pass_types(self): + """See `IMailingList`.""" + results = Store.of(self).find( + ContentFilter, + And(ContentFilter.mailing_list == self, + ContentFilter.filter_type == FilterType.pass_mime)) + for content_filter in results: + yield content_filter.filter_pattern + + @pass_types.setter + def pass_types(self, sequence): + """See `IMailingList`.""" + # First, delete all existing MIME type pass patterns. + store = Store.of(self) + results = store.find( + ContentFilter, + And(ContentFilter.mailing_list == self, + ContentFilter.filter_type == FilterType.pass_mime)) + results.remove() + # Now add all the new filter types. + for mime_type in sequence: + content_filter = ContentFilter( + self, mime_type, FilterType.pass_mime) + store.add(content_filter) @property - def acceptable_aliases(self): - aliases = Store.of(self).find( - AcceptableAlias, - AcceptableAlias.mailing_list == self) - for alias in aliases: - yield alias.alias + def filter_extensions(self): + """See `IMailingList`.""" + results = Store.of(self).find( + ContentFilter, + And(ContentFilter.mailing_list == self, + ContentFilter.filter_type == FilterType.filter_extension)) + for content_filter in results: + yield content_filter.filter_pattern + + @filter_extensions.setter + def filter_extensions(self, sequence): + """See `IMailingList`.""" + # First, delete all existing file extensions filter patterns. + store = Store.of(self) + results = store.find( + ContentFilter, + And(ContentFilter.mailing_list == self, + ContentFilter.filter_type == FilterType.filter_extension)) + results.remove() + # Now add all the new filter types. + for mime_type in sequence: + content_filter = ContentFilter( + self, mime_type, FilterType.filter_extension) + store.add(content_filter) + + @property + def pass_extensions(self): + """See `IMailingList`.""" + results = Store.of(self).find( + ContentFilter, + And(ContentFilter.mailing_list == self, + ContentFilter.filter_type == FilterType.pass_extension)) + for content_filter in results: + yield content_filter.pass_pattern + + @pass_extensions.setter + def pass_extensions(self, sequence): + """See `IMailingList`.""" + # First, delete all existing file extensions pass patterns. + store = Store.of(self) + results = store.find( + ContentFilter, + And(ContentFilter.mailing_list == self, + ContentFilter.filter_type == FilterType.pass_extension)) + results.remove() + # Now add all the new filter types. + for mime_type in sequence: + content_filter = ContentFilter( + self, mime_type, FilterType.pass_extension) + store.add(content_filter) @@ -339,3 +428,52 @@ class AcceptableAlias(Model): def __init__(self, mailing_list, alias): self.mailing_list = mailing_list self.alias = alias + + +class AcceptableAliasSet: + implements(IAcceptableAliasSet) + + def __init__(self, mailing_list): + self._mailing_list = mailing_list + + def clear(self): + """See `IAcceptableAliasSet`.""" + Store.of(self._mailing_list).find( + AcceptableAlias, + AcceptableAlias.mailing_list == self._mailing_list).remove() + + def add(self, alias): + if not (alias.startswith('^') or '@' in alias): + raise ValueError(alias) + alias = AcceptableAlias(self._mailing_list, alias.lower()) + Store.of(self._mailing_list).add(alias) + + def remove(self, alias): + Store.of(self._mailing_list).find( + AcceptableAlias, + And(AcceptableAlias.mailing_list == self._mailing_list, + AcceptableAlias.alias == alias.lower())).remove() + + @property + def aliases(self): + aliases = Store.of(self._mailing_list).find( + AcceptableAlias, + AcceptableAlias.mailing_list == self._mailing_list) + for alias in aliases: + yield alias.alias + + + +def adapt_mailing_list_to_acceptable_alias_set(iface, obj): + """Adapt an `IMailingList` to an `IAcceptableAliasSet`. + + :param iface: The interface to adapt to. + :type iface: `zope.interface.Interface` + :param obj: The object being adapted. + :type obj: `IMailingList` + :return: An `IAcceptableAliasSet` instance if adaptation succeeded or None + if it didn't. + """ + return (AcceptableAliasSet(obj) + if IMailingList.providedBy(obj) and iface is IAcceptableAliasSet + else None) diff --git a/src/mailman/database/mailman.sql b/src/mailman/database/mailman.sql index d64be9815..6f73c3974 100644 --- a/src/mailman/database/mailman.sql +++ b/src/mailman/database/mailman.sql @@ -54,6 +54,19 @@ CREATE INDEX ix_autoresponserecord_address_id CREATE INDEX ix_autoresponserecord_mailing_list_id ON autoresponserecord (mailing_list_id); +CREATE TABLE contentfilter ( + id INTEGER NOT NULL, + mailing_list_id INTEGER, + filter_pattern TEXT, + filter_type INTEGER, + PRIMARY KEY (id), + CONSTRAINT contentfilter_mailing_list_id + FOREIGN KEY (mailing_list_id) + REFERENCES mailinglist (id) + ); +CREATE INDEX ix_contentfilter_mailing_list_id + ON contentfilter (mailing_list_id); + CREATE TABLE language ( id INTEGER NOT NULL, code TEXT, @@ -99,6 +112,8 @@ CREATE TABLE mailinglist ( bounce_unrecognized_goes_to_list_owner BOOLEAN, bounce_you_are_disabled_warnings INTEGER, bounce_you_are_disabled_warnings_interval TEXT, + -- Content filtering. + filter_content BOOLEAN, collapse_alternatives BOOLEAN, convert_html_to_plaintext BOOLEAN, default_member_moderation BOOLEAN, @@ -113,10 +128,6 @@ CREATE TABLE mailinglist ( discard_these_nonmembers BLOB, emergency BOOLEAN, encode_ascii_prefixes BOOLEAN, - filter_action INTEGER, - filter_content BOOLEAN, - filter_filename_extensions BLOB, - filter_mime_types BLOB, first_strip_reply_to BOOLEAN, forward_auto_discards BOOLEAN, gateway_to_mail BOOLEAN, @@ -145,8 +156,6 @@ CREATE TABLE mailinglist ( nondigestable BOOLEAN, nonmember_rejection_notice TEXT, obscure_addresses BOOLEAN, - pass_filename_extensions BLOB, - pass_mime_types BLOB, personalize TEXT, pipeline TEXT, post_id INTEGER, diff --git a/src/mailman/database/mime.py b/src/mailman/database/mime.py new file mode 100644 index 000000000..31c2aacbe --- /dev/null +++ b/src/mailman/database/mime.py @@ -0,0 +1,52 @@ +# Copyright (C) 2009 by the Free Software Foundation, Inc. +# +# This file is part of GNU Mailman. +# +# GNU Mailman is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) +# any later version. +# +# GNU Mailman 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 General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along with +# GNU Mailman. If not, see <http://www.gnu.org/licenses/>. + +"""Module stuff.""" + +from __future__ import absolute_import, unicode_literals + +__metaclass__ = type +__all__ = [ + 'ContentFilter' + ] + + +from storm.locals import Bool, Int, Reference, Unicode +from zope.interface import implements + +from mailman.database.model import Model +from mailman.database.types import Enum +from mailman.interfaces.mime import IContentFilter + + + +class ContentFilter(Model): + """A single filter criteria.""" + implements(IContentFilter) + + id = Int(primary=True) + + mailing_list_id = Int() + mailing_list = Reference(mailing_list_id, 'MailingList.id') + + filter_type = Enum() + filter_pattern = Unicode() + + def __init__(self, mailing_list, filter_pattern, filter_type): + self.mailing_list = mailing_list + self.filter_pattern = filter_pattern + self.filter_type = filter_type |
