summaryrefslogtreecommitdiff
path: root/src/mailman/interfaces
diff options
context:
space:
mode:
Diffstat (limited to 'src/mailman/interfaces')
-rw-r--r--src/mailman/interfaces/__init__.py44
-rw-r--r--src/mailman/interfaces/address.py101
-rw-r--r--src/mailman/interfaces/archiver.py78
-rw-r--r--src/mailman/interfaces/chain.py110
-rw-r--r--src/mailman/interfaces/command.py68
-rw-r--r--src/mailman/interfaces/database.py97
-rw-r--r--src/mailman/interfaces/domain.py85
-rw-r--r--src/mailman/interfaces/errors.py35
-rw-r--r--src/mailman/interfaces/handler.py45
-rw-r--r--src/mailman/interfaces/languages.py89
-rw-r--r--src/mailman/interfaces/listmanager.py84
-rw-r--r--src/mailman/interfaces/mailinglist.py273
-rw-r--r--src/mailman/interfaces/member.py187
-rw-r--r--src/mailman/interfaces/messages.py111
-rw-r--r--src/mailman/interfaces/mlistrequest.py37
-rw-r--r--src/mailman/interfaces/mta.py42
-rw-r--r--src/mailman/interfaces/pending.py99
-rw-r--r--src/mailman/interfaces/permissions.py36
-rw-r--r--src/mailman/interfaces/pipeline.py40
-rw-r--r--src/mailman/interfaces/preferences.py77
-rw-r--r--src/mailman/interfaces/registrar.py83
-rw-r--r--src/mailman/interfaces/requests.py115
-rw-r--r--src/mailman/interfaces/roster.py61
-rw-r--r--src/mailman/interfaces/rules.py53
-rw-r--r--src/mailman/interfaces/runner.py119
-rw-r--r--src/mailman/interfaces/styles.py120
-rw-r--r--src/mailman/interfaces/switchboard.py90
-rw-r--r--src/mailman/interfaces/user.py84
-rw-r--r--src/mailman/interfaces/usermanager.py96
29 files changed, 2559 insertions, 0 deletions
diff --git a/src/mailman/interfaces/__init__.py b/src/mailman/interfaces/__init__.py
new file mode 100644
index 000000000..a9f3fb982
--- /dev/null
+++ b/src/mailman/interfaces/__init__.py
@@ -0,0 +1,44 @@
+# Copyright (C) 2007-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/>.
+
+__metaclass__ = type
+__all__ = [
+ 'Action',
+ 'NewsModeration',
+ ]
+
+
+from munepy import Enum
+
+
+
+class Action(Enum):
+ hold = 0
+ reject = 1
+ discard = 2
+ accept = 3
+ defer = 4
+
+
+
+class NewsModeration(Enum):
+ # The newsgroup is not moderated
+ none = 0
+ # The newsgroup is moderated, but allows for an open posting policy.
+ open_moderated = 1
+ # The newsgroup is moderated
+ moderated = 2
diff --git a/src/mailman/interfaces/address.py b/src/mailman/interfaces/address.py
new file mode 100644
index 000000000..968ded3ee
--- /dev/null
+++ b/src/mailman/interfaces/address.py
@@ -0,0 +1,101 @@
+# Copyright (C) 2007-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/>.
+
+"""Interface for email address related information."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'AddressAlreadyLinkedError',
+ 'AddressError',
+ 'AddressNotLinkedError',
+ 'ExistingAddressError',
+ 'IAddress',
+ ]
+
+
+from zope.interface import Interface, Attribute
+from mailman.interfaces.errors import MailmanError
+
+
+
+class AddressError(MailmanError):
+ """A general address-related error occurred."""
+
+
+class ExistingAddressError(AddressError):
+ """The given email address already exists."""
+
+
+class AddressAlreadyLinkedError(AddressError):
+ """The address is already linked to a user."""
+
+
+class AddressNotLinkedError(AddressError):
+ """The address is not linked to the user."""
+
+
+
+
+class IAddress(Interface):
+ """Email address related information."""
+
+ address = Attribute(
+ """Read-only text email address.""")
+
+ original_address = Attribute(
+ """Read-only original case-preserved address.
+
+ For almost all intents and purposes, addresses in Mailman are case
+ insensitive, however because RFC 2821 allows for case sensitive local
+ parts, Mailman preserves the case of the original address when
+ emailing the user.
+
+ `original_address` will be the same as address if the original address
+ was all lower case. Otherwise `original_address` will be the case
+ preserved address; `address` will always be lower case.
+ """)
+
+ real_name = Attribute(
+ """Optional real name associated with the email address.""")
+
+ registered_on = Attribute(
+ """The date and time at which this email address was registered.
+
+ Registeration is really the date at which this address became known to
+ us. It may have been explicitly registered by a user, or it may have
+ been implicitly registered, e.g. by showing up in a non-member
+ posting.""")
+
+ verified_on = Attribute(
+ """The date and time at which this email address was validated, or
+ None if the email address has not yet been validated. The specific
+ method of validation is not defined here.""")
+
+ def subscribe(mailing_list, role):
+ """Subscribe the address to the given mailing list with the given role.
+
+ :param mailing_list: The IMailingList being subscribed to.
+ :param role: A MemberRole enum value.
+ :return: The IMember representing this subscription.
+ :raises AlreadySubscribedError: If the address is already subscribed
+ to the mailing list with the given role.
+ """
+
+ preferences = Attribute(
+ """This address's preferences.""")
diff --git a/src/mailman/interfaces/archiver.py b/src/mailman/interfaces/archiver.py
new file mode 100644
index 000000000..35fee2c9e
--- /dev/null
+++ b/src/mailman/interfaces/archiver.py
@@ -0,0 +1,78 @@
+# Copyright (C) 2008-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/>.
+
+"""Interface for archiving schemes."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'IArchiver',
+ 'IPipermailMailingList',
+ ]
+
+
+from zope.interface import Interface, Attribute
+from mailman.interfaces.mailinglist import IMailingList
+
+
+
+class IArchiver(Interface):
+ """An interface to the archiver."""
+
+ name = Attribute('The name of this archiver')
+
+ def list_url(mlist):
+ """Return the url to the top of the list's archive.
+
+ :param mlist: The IMailingList object.
+ :returns: The url string.
+ """
+
+ def permalink(mlist, message):
+ """Return the url to the message in the archive.
+
+ This url points directly to the message in the archive. This method
+ only calculates the url, it does not actually archive the message.
+
+ :param mlist: The IMailingList object.
+ :param message: The message object.
+ :returns: The url string or None if the message's archive url cannot
+ be calculated.
+ """
+
+ def archive_message(mlist, message):
+ """Send the message to the archiver.
+
+ :param mlist: The IMailingList object.
+ :param message: The message object.
+ :returns: The url string or None if the message's archive url cannot
+ be calculated.
+ """
+
+ # XXX How to handle attachments?
+
+
+
+class IPipermailMailingList(IMailingList):
+ """An interface that adapts IMailingList as needed for Pipermail."""
+
+ def archive_dir():
+ """The directory for storing Pipermail artifacts.
+
+ Pipermail expects this to be a function, not a property.
+ """
diff --git a/src/mailman/interfaces/chain.py b/src/mailman/interfaces/chain.py
new file mode 100644
index 000000000..2685b7980
--- /dev/null
+++ b/src/mailman/interfaces/chain.py
@@ -0,0 +1,110 @@
+# Copyright (C) 2007-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/>.
+
+"""Interfaces describing the basics of chains and links."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'IChain',
+ 'IChainIterator',
+ 'IChainLink',
+ 'IMutableChain',
+ 'LinkAction',
+ ]
+
+
+from munepy import Enum
+from zope.interface import Interface, Attribute
+
+
+
+class LinkAction(Enum):
+ # Jump to another chain.
+ jump = 0
+ # Take a detour to another chain, returning to the original chain when
+ # completed (if no other jump occurs).
+ detour = 1
+ # Stop processing all chains.
+ stop = 2
+ # Continue processing the next link in the chain.
+ defer = 3
+ # Run a function and continue processing.
+ run = 4
+
+
+
+class IChainLink(Interface):
+ """A link in the chain."""
+
+ rule = Attribute('The rule to run for this link.')
+
+ action = Attribute('The LinkAction to take if this rule matches.')
+
+ chain = Attribute('The chain to jump or detour to.')
+
+ function = Attribute(
+ """The function to execute.
+
+ The function takes three arguments and returns nothing.
+ :param mlist: the IMailingList object
+ :param msg: the message being processed
+ :param msgdata: the message metadata dictionary
+ """)
+
+
+
+class IChain(Interface):
+ """A chain of rules."""
+
+ name = Attribute('Chain name; must be unique.')
+ description = Attribute('A brief description of the chain.')
+
+ def get_links(mlist, msg, msgdata):
+ """Get an `IChainIterator` for processing.
+
+ :param mlist: the IMailingList object
+ :param msg: the message being processed
+ :param msgdata: the message metadata dictionary
+ :return: An `IChainIterator`.
+ """
+
+
+
+class IChainIterator(Interface):
+ """An iterator over chain rules."""
+
+ def __iter__():
+ """Iterate over all the IChainLinks in this chain.
+
+ :return: an IChainLink.
+ """
+
+
+
+class IMutableChain(IChain):
+ """Like `IChain` but can be mutated."""
+
+ def append_link(link):
+ """Add a new chain link to the end of this chain.
+
+ :param link: The chain link to add.
+ """
+
+ def flush():
+ """Delete all links in this chain."""
diff --git a/src/mailman/interfaces/command.py b/src/mailman/interfaces/command.py
new file mode 100644
index 000000000..a9ad292f4
--- /dev/null
+++ b/src/mailman/interfaces/command.py
@@ -0,0 +1,68 @@
+# Copyright (C) 2008-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/>.
+
+"""Interfaces defining email commands."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'ContinueProcessing',
+ 'IEmailCommand',
+ 'IEmailResults',
+ ]
+
+
+from munepy import Enum
+from zope.interface import Interface, Attribute
+
+
+
+class ContinueProcessing(Enum):
+ """Should `IEmailCommand.process()` continue or not."""
+ no = 0
+ yes = 1
+
+
+
+class IEmailResults(Interface):
+ """The email command results object."""
+
+ output = Attribute('An output file object for printing results to.')
+
+
+
+class IEmailCommand(Interface):
+ """An email command."""
+
+ name = Attribute('Command name as seen in a -request email.')
+
+ argument_description = Attribute('Description of command arguments.')
+
+ description = Attribute('Command help.')
+
+ def process(mlist, msg, msgdata, arguments, results):
+ """Process the email command.
+
+ :param mlist: The mailing list target of the command.
+ :param msg: The original message object.
+ :param msgdata: The message metadata.
+ :param arguments: The command arguments tuple.
+ :param results: An IEmailResults object for these commands.
+ :return: A `ContinueProcessing` enum specifying whether to continue
+ processing or not.
+ """
diff --git a/src/mailman/interfaces/database.py b/src/mailman/interfaces/database.py
new file mode 100644
index 000000000..4a9d6cde5
--- /dev/null
+++ b/src/mailman/interfaces/database.py
@@ -0,0 +1,97 @@
+# Copyright (C) 2007-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/>.
+
+"""Interfaces for database interaction.
+
+By providing an object with this interface and declaring it in a package
+setup.py file as an entry point in the 'mailman.database' group with the name
+'initializer', you can distribute entirely different database layers for
+Mailman's back end.
+"""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'DatabaseError',
+ 'IDatabase',
+ 'SchemaVersionMismatchError',
+ ]
+
+from zope.interface import Interface, Attribute
+
+from mailman.interfaces.errors import MailmanError
+from mailman.version import DATABASE_SCHEMA_VERSION
+
+
+
+class DatabaseError(MailmanError):
+ """A problem with the database occurred."""
+
+
+class SchemaVersionMismatchError(DatabaseError):
+ """The database schema version number did not match what was expected."""
+
+ def __init__(self, got):
+ super(SchemaVersionMismatchError, self).__init__()
+ self._got = got
+
+ def __str__(self):
+ return ('Incompatible database schema version '
+ '(got: {0}, expected: {1})'.format(
+ self._got, DATABASE_SCHEMA_VERSION))
+
+
+
+class IDatabase(Interface):
+ """Database layer interface."""
+
+ def initialize(debug=None):
+ """Initialize the database layer, using whatever means necessary.
+
+ :param debug: When None (the default), the configuration file
+ determines whether the database layer should have increased
+ debugging or not. When True or False, this overrides the
+ configuration file setting.
+ """
+
+ def _reset():
+ """Reset the database to its pristine state.
+
+ This is only used by the test framework.
+ """
+
+ def begin():
+ """Begin the current transaction."""
+
+ def commit():
+ """Commit the current transaction."""
+
+ def abort():
+ """Abort the current transaction."""
+
+ list_manager = Attribute(
+ """The IListManager instance provided by the database layer.""")
+
+ user_manager = Attribute(
+ """The IUserManager instance provided by the database layer.""")
+
+ message_store = Attribute(
+ """The IMessageStore instance provided by the database layer.""")
+
+ pendings = Attribute(
+ """The IPending instance provided by the database layer.""")
diff --git a/src/mailman/interfaces/domain.py b/src/mailman/interfaces/domain.py
new file mode 100644
index 000000000..8c2a27e79
--- /dev/null
+++ b/src/mailman/interfaces/domain.py
@@ -0,0 +1,85 @@
+# Copyright (C) 2007-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/>.
+
+"""Interface representing domains."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'IDomain',
+ ]
+
+
+from zope.interface import Interface, Attribute
+
+
+
+class IDomain(Interface):
+ """Interface representing domains."""
+
+ email_host = Attribute(
+ """The host name for email for this domain.
+
+ :type: string
+ """)
+
+ url_host = Attribute(
+ """The host name for the web interface for this domain.
+
+ :type: string
+ """)
+
+ base_url = Attribute(
+ """The base url for the Mailman server at this domain.
+
+ The base url includes the scheme and host name.
+
+ :type: string
+ """)
+
+ description = Attribute(
+ """The human readable description of the domain name.
+
+ :type: string
+ """)
+
+ contact_address = Attribute(
+ """The contact address for the human at this domain.
+
+ E.g. postmaster@python.org.
+
+ :type: string
+ """)
+
+ def confirm_address(token=''):
+ """The address used for various forms of email confirmation.
+
+ :param token: The confirmation token to use in the email address.
+ :type token: string
+ :return: The email confirmation address.
+ :rtype: string
+ """
+
+ def confirm_url(token=''):
+ """The url used for various forms of confirmation.
+
+ :param token: The confirmation token to use in the url.
+ :type token: string
+ :return: The confirmation url.
+ :rtype: string
+ """
diff --git a/src/mailman/interfaces/errors.py b/src/mailman/interfaces/errors.py
new file mode 100644
index 000000000..608193f7a
--- /dev/null
+++ b/src/mailman/interfaces/errors.py
@@ -0,0 +1,35 @@
+# Copyright (C) 1998-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/>.
+
+"""Base Mailman exceptions.
+
+The exceptions in this module are those that are commonly shared among many
+components. More specific exceptions will be located in the relevant
+interfaces.
+"""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'MailmanError',
+ ]
+
+
+
+class MailmanError(Exception):
+ """Base class for all Mailman exceptions."""
diff --git a/src/mailman/interfaces/handler.py b/src/mailman/interfaces/handler.py
new file mode 100644
index 000000000..41d791d5d
--- /dev/null
+++ b/src/mailman/interfaces/handler.py
@@ -0,0 +1,45 @@
+# Copyright (C) 2008-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/>.
+
+"""Interface describing a pipeline handler."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'IHandler',
+ ]
+
+
+from zope.interface import Interface, Attribute
+
+
+
+class IHandler(Interface):
+ """A basic pipeline handler."""
+
+ name = Attribute('Handler name; must be unique.')
+
+ description = Attribute('A brief description of the handler.')
+
+ def process(mlist, msg, msgdata):
+ """Run the handler.
+
+ :param mlist: The mailing list object.
+ :param msg: The message object.
+ :param msgdata: The message metadata.
+ """
diff --git a/src/mailman/interfaces/languages.py b/src/mailman/interfaces/languages.py
new file mode 100644
index 000000000..272cf4372
--- /dev/null
+++ b/src/mailman/interfaces/languages.py
@@ -0,0 +1,89 @@
+# Copyright (C) 2007-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/>.
+
+"""Interfaces for managing languages."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'ILanguage',
+ 'ILanguageManager',
+ ]
+
+
+from zope.interface import Interface, Attribute
+
+
+
+class ILanguageManager(Interface):
+ """A language manager.
+
+ Current, disabling languages is not supported.
+ """
+
+ def add_language(code, description, charset, enable=True):
+ """Teach the language manager about a language.
+
+ :param code: The short two-character language code for the
+ language. If the language manager already knows about this code,
+ the old language binding is lost.
+ :param description: The English description of the language,
+ e.g. 'English' or 'French'.
+ :param charset: The character set that the language uses,
+ e.g. 'us-ascii', 'iso-8859-1', or 'utf-8'
+ :param enable: Enable the language at the same time.
+ """
+
+ def enable_language(code):
+ """Enable a language that the manager already knows about.
+
+ :raises KeyError: when the manager does not know about the given
+ language code.
+ """
+
+ def get_description(code):
+ """Return the language description for the given code.
+
+ :param code: The two letter language code to look up.
+ :returns: The English description of the language.
+ :raises KeyError: when the code has not been added.
+ """
+
+ def get_charset(code):
+ """Return the character set for the given code.
+
+ :param code: The two letter language code to look up.
+ :returns: The character set of the language.
+ :raises KeyError: when the code has not been added.
+ """
+
+ known_codes = Attribute(
+ """An iterator over all known codes.""")
+
+ enabled_codes = Attribute(
+ """An iterator over all enabled codes.""")
+
+ enabled_names = Attribute(
+ """An iterator over all enabled language names.""")
+
+
+
+class ILanguage(Interface):
+ """The representation of a language."""
+
+ code = Attribute("""The 2-character language code.""")
diff --git a/src/mailman/interfaces/listmanager.py b/src/mailman/interfaces/listmanager.py
new file mode 100644
index 000000000..e7cdd9da7
--- /dev/null
+++ b/src/mailman/interfaces/listmanager.py
@@ -0,0 +1,84 @@
+# Copyright (C) 2007-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/>.
+
+"""Interface for list storage, deleting, and finding."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'IListManager',
+ 'ListAlreadyExistsError',
+ ]
+
+
+from zope.interface import Interface, Attribute
+from mailman.interfaces.errors import MailmanError
+
+
+
+class ListAlreadyExistsError(MailmanError):
+ """Attempted to create a mailing list that already exists.
+
+ Mailing list objects must be uniquely named by their fully qualified list
+ name.
+ """
+
+
+
+class IListManager(Interface):
+ """The interface of the global list manager.
+
+ The list manager manages `IMailingList` objects. You can add and remove
+ `IMailingList` objects from the list manager, and you can retrieve them
+ from the manager via their fully qualified list name, e.g.:
+ `mylist@example.com`.
+ """
+
+ def create(fqdn_listname):
+ """Create a mailing list with the given name.
+
+ :type fqdn_listname: Unicode
+ :param fqdn_listname: The fully qualified name of the mailing list,
+ e.g. `mylist@example.com`.
+ :return: The newly created `IMailingList`.
+ :raise `ListAlreadyExistsError` if the named list already exists.
+ """
+
+ def get(fqdn_listname):
+ """Return the mailing list with the given name, if it exists.
+
+ :type fqdn_listname: Unicode.
+ :param fqdn_listname: The fully qualified name of the mailing list.
+ :return: the matching `IMailingList` or None if the named list does
+ not exist.
+ """
+
+ def delete(mlist):
+ """Remove the mailing list from the database.
+
+ :type mlist: `IMailingList`
+ :param mlist: The mailing list to delete.
+ """
+
+ mailing_lists = Attribute(
+ """An iterator over all the mailing list objects managed by this list
+ manager.""")
+
+ names = Attribute(
+ """An iterator over the fully qualified list names of all mailing
+ lists managed by this list manager.""")
diff --git a/src/mailman/interfaces/mailinglist.py b/src/mailman/interfaces/mailinglist.py
new file mode 100644
index 000000000..cd7b11d64
--- /dev/null
+++ b/src/mailman/interfaces/mailinglist.py
@@ -0,0 +1,273 @@
+# Copyright (C) 2007-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/>.
+
+"""Interface for a mailing list."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'IMailingList',
+ 'Personalization',
+ 'ReplyToMunging',
+ ]
+
+
+from munepy import Enum
+from zope.interface import Interface, Attribute
+
+
+
+class Personalization(Enum):
+ none = 0
+ # Everyone gets a unique copy of the message, and there are a few more
+ # substitution variables, but no headers are modified.
+ individual = 1
+ # All of the 'individual' personalization plus recipient header
+ # modification.
+ full = 2
+
+
+
+class ReplyToMunging(Enum):
+ # The Reply-To header is passed through untouched
+ no_munging = 0
+ # The mailing list's posting address is appended to the Reply-To header
+ point_to_list = 1
+ # An explicit Reply-To header is added
+ explicit_header = 2
+
+
+
+class IMailingList(Interface):
+ """A mailing list."""
+
+ list_name = Attribute(
+ """The read-only short name of the mailing list. Note that where a
+ Mailman installation supports multiple domains, this short name may
+ not be unique. Use the fqdn_listname attribute for a guaranteed
+ unique id for the mailing list. This short name is always the local
+ part of the posting email address. For example, if messages are
+ posted to mylist@example.com, then the list_name is 'mylist'.
+ """)
+
+ real_name = Attribute(
+ """The short human-readable descriptive name for the mailing list. By
+ default, this is the capitalized `list_name`, but it can be changed to
+ anything. This is used in locations such as the message footers and
+ Subject prefix.
+ """)
+
+ host_name = Attribute(
+ """The read-only domain name 'hosting' this mailing list. This is
+ always the domain name part of the posting email address, and it may
+ bear no relationship to the web url used to access this mailing list.
+ For example, if messages are posted to mylist@example.com, then the
+ host_name is 'example.com'.
+ """)
+
+ fqdn_listname = Attribute(
+ """The read-only fully qualified name of the mailing list. This is
+ the guaranteed unique id for the mailing list, and it is always the
+ address to which messages are posted, e.g. mylist@example.com. It is
+ always comprised of the list_name + '@' + host_name.
+ """)
+
+ posting_address = Attribute(
+ """The address to which messages are posted for copying to the full
+ list membership, where 'full' of course means those members for which
+ delivery is currently enabled.
+ """)
+
+ no_reply_address = Attribute(
+ """The address to which all messages will be immediately discarded,
+ without prejudice or record. This address is specific to the ddomain,
+ even though it's available on the IMailingListAddresses interface.
+ Generally, humans should never respond directly to this address.
+ """)
+
+ owner_address = Attribute(
+ """The address which reaches the owners and moderators of the mailing
+ list. There is no address which reaches just the owners or just the
+ moderators of a mailing list.
+ """)
+
+ request_address = Attribute(
+ """The address which reaches the email robot for this mailing list.
+ This robot can process various email commands such as changing
+ delivery options, getting information or help about the mailing list,
+ or processing subscrptions and unsubscriptions (although for the
+ latter two, it's better to use the join_address and leave_address.
+ """)
+
+ bounces_address = Attribute(
+ """The address which reaches the automated bounce processor for this
+ mailing list. Generally, humans should never respond directly to this
+ address.
+ """)
+
+ join_address = Attribute(
+ """The address to which subscription requests should be sent. See
+ subscribe_address for a backward compatible alias.
+ """)
+
+ leave_address = Attribute(
+ """The address to which unsubscription requests should be sent. See
+ unsubscribe_address for a backward compatible alias.
+ """)
+
+ subscribe_address = Attribute(
+ """Deprecated address to which subscription requests may be sent.
+ This address is provided for backward compatibility only. See
+ join_address for the preferred alias.
+ """)
+
+ leave_address = Attribute(
+ """Deprecated address to which unsubscription requests may be sent.
+ This address is provided for backward compatibility only. See
+ leave_address for the preferred alias.
+ """)
+
+ def confirm_address(cookie=''):
+ """The address used for various forms of email confirmation."""
+
+ creation_date = Attribute(
+ """The date and time that the mailing list was created.""")
+
+ last_post_date = Attribute(
+ """The date and time a message was last posted to the mailing list.""")
+
+ post_id = Attribute(
+ """A monotonically increasing integer sequentially assigned to each
+ list posting.""")
+
+ last_digest_date = Attribute(
+ """The date and time a digest of this mailing list was last sent.""")
+
+ owners = Attribute(
+ """The IUser owners of this mailing list.
+
+ This does not include the IUsers who are moderators but not owners of
+ the mailing list.""")
+
+ moderators = Attribute(
+ """The IUser moderators of this mailing list.
+
+ This does not include the IUsers who are owners but not moderators of
+ the mailing list.""")
+
+ administrators = Attribute(
+ """The IUser administrators of this mailing list.
+
+ This includes the IUsers who are both owners and moderators of the
+ mailing list.""")
+
+ members = Attribute(
+ """An iterator over all the members of the mailing list, regardless of
+ whether they are to receive regular messages or digests, or whether
+ they have their delivery disabled or not.""")
+
+ regular_members = Attribute(
+ """An iterator over all the IMembers who are to receive regular
+ postings (i.e. non-digests) from the mailing list, regardless of
+ whether they have their delivery disabled or not.""")
+
+ digest_members = Attribute(
+ """An iterator over all the IMembers who are to receive digests of
+ postings to this mailing list, regardless of whether they have their
+ deliver disabled or not, or of the type of digest they are to
+ receive.""")
+
+ subscribers = Attribute(
+ """An iterator over all IMembers subscribed to this list, with any
+ role.
+ """)
+
+ volume_number = Attribute(
+ """A monotonically increasing integer sequentially assigned to each
+ new digest volume. The volume number may be bumped either
+ automatically (i.e. on a defined schedule) or manually. When the
+ volume number is bumped, the digest number is always reset to 1.""")
+
+ digest_number = Attribute(
+ """A sequence number for a specific digest in a given volume. When
+ the digest volume number is bumped, the digest number is reset to
+ 1.""")
+
+ def bump():
+ """Bump the digest's volume number to the next integer in the
+ sequence, and reset the digest number to 1.
+ """
+ message_count = Attribute(
+ """The number of messages in the digest currently being collected.""")
+
+ digest_size = Attribute(
+ """The approximate size in kilobytes of the digest currently being
+ collected.""")
+
+ messages = Attribute(
+ """An iterator over all the messages in the digest currently being
+ created. Returns individual IPostedMessage objects.
+ """)
+
+ limits = Attribute(
+ """An iterator over the IDigestLimiters associated with this digest.
+ Each limiter can make a determination of whether the digest has
+ reached the threshold for being automatically sent.""")
+
+ def send():
+ """Send this digest now."""
+
+ decorators = Attribute(
+ """An iterator over all the IDecorators associated with this digest.
+ When a digest is being sent, each decorator may modify the final
+ digest text.""")
+
+ protocol = Attribute(
+ """The protocol scheme used to contact this list's server.
+
+ The web server on thi protocol provides the web interface for this
+ mailing list. The protocol scheme should be 'http' or 'https'.""")
+
+ web_host = Attribute(
+ """This list's web server's domain.
+
+ The read-only domain name of the host to contact for interacting with
+ the web interface of the mailing list.""")
+
+ def script_url(target, context=None):
+ """Return the url to the given script target.
+
+ If 'context' is not given, or is None, then an absolute url is
+ returned. If context is given, it must be an IMailingListRequest
+ object, and the returned url will be relative to that object's
+ 'location' attribute.
+ """
+
+ pipeline = Attribute(
+ """The name of this mailing list's processing pipeline.
+
+ Every mailing list has a processing pipeline that messages flow
+ through once they've been accepted.
+ """)
+
+ data_path = Attribute(
+ """The file system path to list-specific data.
+
+ An example of list-specific data is the temporary digest mbox file
+ that gets created to accumlate messages for the digest.
+ """)
diff --git a/src/mailman/interfaces/member.py b/src/mailman/interfaces/member.py
new file mode 100644
index 000000000..2e966879d
--- /dev/null
+++ b/src/mailman/interfaces/member.py
@@ -0,0 +1,187 @@
+# Copyright (C) 2007-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/>.
+
+"""Interface describing the basics of a member."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'AlreadySubscribedError',
+ 'DeliveryMode',
+ 'DeliveryStatus',
+ 'IMember',
+ 'MemberRole',
+ ]
+
+
+from munepy import Enum
+from zope.interface import Interface, Attribute
+
+from mailman.core.errors import SubscriptionError
+
+
+
+class DeliveryMode(Enum):
+ # Regular (i.e. non-digest) delivery
+ regular = 1
+ # Plain text digest delivery
+ plaintext_digests = 2
+ # MIME digest delivery
+ mime_digests = 3
+ # Summary digests
+ summary_digests = 4
+
+
+
+class DeliveryStatus(Enum):
+ # Delivery is enabled
+ enabled = 1
+ # Delivery was disabled by the user
+ by_user = 2
+ # Delivery was disabled due to bouncing addresses
+ by_bounces = 3
+ # Delivery was disabled by an administrator or moderator
+ by_moderator = 4
+ # Disabled for unknown reasons.
+ unknown = 5
+
+
+
+class MemberRole(Enum):
+ member = 1
+ owner = 2
+ moderator = 3
+
+
+
+class AlreadySubscribedError(SubscriptionError):
+ """The member is already subscribed to the mailing list with this role."""
+
+ def __init__(self, fqdn_listname, address, role):
+ super(AlreadySubscribedError, self).__init__()
+ self._fqdn_listname = fqdn_listname
+ self._address = address
+ self._role = role
+
+ def __str__(self):
+ return '{0} is already a {1} of mailing list {2}'.format(
+ self._address, self._role, self._fqdn_listname)
+
+
+
+class IMember(Interface):
+ """A member of a mailing list."""
+
+ mailing_list = Attribute(
+ """The mailing list subscribed to.""")
+
+ address = Attribute(
+ """The email address that's subscribed to the list.""")
+
+ preferences = Attribute(
+ """This member's preferences.""")
+
+ role = Attribute(
+ """The role of this membership.""")
+
+ is_moderated = Attribute(
+ """True if the membership is moderated, otherwise False.""")
+
+ def unsubscribe():
+ """Unsubscribe (and delete) this member from the mailing list."""
+
+ acknowledge_posts = Attribute(
+ """Send an acknowledgment for every posting?
+
+ Unlike going through the preferences, this attribute return the
+ preference value based on the following lookup order:
+
+ 1. The member
+ 2. The address
+ 3. The user
+ 4. System default
+ """)
+
+ preferred_language = Attribute(
+ """The preferred language for interacting with a mailing list.
+
+ Unlike going through the preferences, this attribute return the
+ preference value based on the following lookup order:
+
+ 1. The member
+ 2. The address
+ 3. The user
+ 4. System default
+ """)
+
+ receive_list_copy = Attribute(
+ """Should an explicit recipient receive a list copy?
+
+ Unlike going through the preferences, this attribute return the
+ preference value based on the following lookup order:
+
+ 1. The member
+ 2. The address
+ 3. The user
+ 4. System default
+ """)
+
+ receive_own_postings = Attribute(
+ """Should the poster get a list copy of their own messages?
+
+ Unlike going through the preferences, this attribute return the
+ preference value based on the following lookup order:
+
+ 1. The member
+ 2. The address
+ 3. The user
+ 4. System default
+ """)
+
+ delivery_mode = Attribute(
+ """The preferred delivery mode.
+
+ Unlike going through the preferences, this attribute return the
+ preference value based on the following lookup order:
+
+ 1. The member
+ 2. The address
+ 3. The user
+ 4. System default
+ """)
+
+ delivery_status = Attribute(
+ """The delivery status.
+
+ Unlike going through the preferences, this attribute return the
+ preference value based on the following lookup order:
+
+ 1. The member
+ 2. The address
+ 3. The user
+ 4. System default
+
+ XXX I'm not sure this is the right place to put this.""")
+
+ options_url = Attribute(
+ """Return the url for the given member's option page.
+
+ XXX This needs a serious re-think in the face of the unified user
+ database, since a member's options aren't tied to any specific mailing
+ list. So in what part of the web-space does the user's options live?
+ """)
diff --git a/src/mailman/interfaces/messages.py b/src/mailman/interfaces/messages.py
new file mode 100644
index 000000000..72efe960f
--- /dev/null
+++ b/src/mailman/interfaces/messages.py
@@ -0,0 +1,111 @@
+# Copyright (C) 2007-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/>.
+
+"""The message storage service."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'IMessage',
+ 'IMessageStore',
+ ]
+
+
+from zope.interface import Interface, Attribute
+
+
+
+class IMessageStore(Interface):
+ """The interface of the global message storage service.
+
+ All messages that are stored in the system live in the message storage
+ service. A message stored in this service must have a Message-ID header.
+ The store writes an X-Message-ID-Hash header which contains the Base32
+ encoded SHA1 hash of the message's Message-ID header. Any existing
+ X-Message-ID-Hash header is overwritten.
+
+ Either the Message-ID or the X-Message-ID-Hash header can be used to
+ uniquely identify this message in the storage service. While it is
+ possible to see duplicate Message-IDs, this is never correct and the
+ service is allowed to drop any subsequent colliding messages, or overwrite
+ earlier messages with later ones.
+
+ The combination of the List-Archive header and either the Message-ID or
+ X-Message-ID-Hash header can be used to retrieve the message from the
+ internet facing interface for the message store. This can be considered a
+ globally unique URI to the message.
+
+ For example, a message with the following headers:
+
+ Message-ID: <87myycy5eh.fsf@uwakimon.sk.tsukuba.ac.jp>
+ Date: Wed, 04 Jul 2007 16:49:58 +0900
+ List-Archive: http://archive.example.com/
+ X-Message-ID-Hash: RXTJ357KFOTJP3NFJA6KMO65X7VQOHJI
+
+ the globally unique URI would be:
+
+ http://archive.example.com/RXTJ357KFOTJP3NFJA6KMO65X7VQOHJI
+ """
+
+ def add(message):
+ """Add the message to the store.
+
+ :param message: An email.message.Message instance containing at least
+ a unique Message-ID header. The message will be given an
+ X-Message-ID-Hash header, overriding any existing such header.
+ :returns: The calculated X-Message-ID-Hash header.
+ :raises ValueError: if the message is missing a Message-ID header.
+ The storage service is also allowed to raise this exception if it
+ find, but disallows collisions.
+ """
+
+ def get_message_by_id(message_id):
+ """Return the message with a matching Message-ID.
+
+ :param message_id: The Message-ID header contents to search for.
+ :returns: The message, or None if no matching message was found.
+ """
+
+ def get_message_by_hash(message_id_hash):
+ """Return the message with the matching X-Message-ID-Hash.
+
+ :param message_id_hash: The X-Message-ID-Hash header contents to
+ search for.
+ :returns: The message, or None if no matching message was found.
+ """
+
+ def delete_message(message_id):
+ """Remove the given message from the store.
+
+ :param message: The Message-ID of the mesage to delete from the store.
+ :raises LookupError: if there is no such message.
+ """
+
+ messages = Attribute(
+ """An iterator over all messages in this message store.""")
+
+
+
+class IMessage(Interface):
+ """The representation of an email message."""
+
+ message_id = Attribute("""The message's Message-ID header.""")
+
+ message_id_hash = Attribute("""The unique SHA1 hash of the message.""")
+
+ path = Attribute("""The filesystem path to the message object.""")
diff --git a/src/mailman/interfaces/mlistrequest.py b/src/mailman/interfaces/mlistrequest.py
new file mode 100644
index 000000000..924421406
--- /dev/null
+++ b/src/mailman/interfaces/mlistrequest.py
@@ -0,0 +1,37 @@
+# Copyright (C) 2007-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/>.
+
+"""Interface for a web request accessing a mailing list."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'IMailingListRequest',
+ ]
+
+
+from zope.interface import Interface, Attribute
+
+
+
+class IMailingListRequest(Interface):
+ """The web request accessing a mailing list."""
+
+ location = Attribute(
+ """The url location of the request, used to calculate relative urls by
+ other components.""")
diff --git a/src/mailman/interfaces/mta.py b/src/mailman/interfaces/mta.py
new file mode 100644
index 000000000..a8c794750
--- /dev/null
+++ b/src/mailman/interfaces/mta.py
@@ -0,0 +1,42 @@
+# 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/>.
+
+"""Interface for mail transport agent integration."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'IMailTransportAgent',
+ ]
+
+
+from zope.interface import Interface
+
+
+
+class IMailTransportAgent(Interface):
+ """Interface to the MTA."""
+
+ def create(mlist):
+ """Tell the MTA that the mailing list was created."""
+
+ def delete(mlist):
+ """Tell the MTA that the mailing list was deleted."""
+
+ def regenerate():
+ """Regenerate the full aliases file."""
diff --git a/src/mailman/interfaces/pending.py b/src/mailman/interfaces/pending.py
new file mode 100644
index 000000000..0e43278e0
--- /dev/null
+++ b/src/mailman/interfaces/pending.py
@@ -0,0 +1,99 @@
+# Copyright (C) 2007-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/>.
+
+"""Interfaces for the pending database.
+
+The pending database contains events that must be confirmed by the user. It
+maps these events to a unique hash that can be used as a token for end user
+confirmation.
+"""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'IPendable',
+ 'IPended',
+ 'IPendedKeyValue',
+ 'IPendings',
+ ]
+
+
+from zope.interface import Interface, Attribute
+
+
+
+class IPendable(Interface):
+ """A pendable object."""
+
+ def keys():
+ """The keys of the pending event data, all of which are strings."""
+
+ def values():
+ """The values of the pending event data, all of which are strings."""
+
+ def items():
+ """The key/value pairs of the pending event data.
+
+ Both the keys and values must be strings.
+ """
+
+
+
+class IPended(Interface):
+ """A pended event, tied to a token."""
+
+ token = Attribute("""The pended token.""")
+
+ expiration_date = Attribute("""The expiration date of the pended event.""")
+
+
+
+class IPendedKeyValue(Interface):
+ """A pended key/value pair."""
+
+ key = Attribute("""The pended key.""")
+
+ value = Attribute("""The pended value.""")
+
+
+
+class IPendings(Interface):
+ """Interface to pending database."""
+
+ def add(pendable, lifetime=None):
+ """Create a new entry in the pending database, returning a token.
+
+ :param pendable: The IPendable instance to add.
+ :param lifetime: The amount of time, as a `datetime.timedelta` that
+ the pended item should remain in the database. When None is
+ given, a system default maximum lifetime is used.
+ :return: A token string for inclusion in urls and email confirmations.
+ """
+
+ def confirm(token, expunge=True):
+ """Return the IPendable matching the token.
+
+ :param token: The token string for the IPendable given by the `.add()`
+ method.
+ :param expunge: A flag indicating whether the pendable record should
+ also be removed from the database or not.
+ :return: The matching IPendable or None if no match was found.
+ """
+
+ def evict():
+ """Remove all pended items whose lifetime has expired."""
diff --git a/src/mailman/interfaces/permissions.py b/src/mailman/interfaces/permissions.py
new file mode 100644
index 000000000..7613064f5
--- /dev/null
+++ b/src/mailman/interfaces/permissions.py
@@ -0,0 +1,36 @@
+# Copyright (C) 2007-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/>.
+
+"""Interfaces for various permissions."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'IPostingPermission',
+ ]
+
+
+from zope.interface import Interface, Attribute
+
+
+
+class IPostingPermission(Interface):
+ """Posting related permissions."""
+
+ okay_to_post = Attribute(
+ """Boolean specifying whether it is okay to post to the list.""")
diff --git a/src/mailman/interfaces/pipeline.py b/src/mailman/interfaces/pipeline.py
new file mode 100644
index 000000000..0268693b7
--- /dev/null
+++ b/src/mailman/interfaces/pipeline.py
@@ -0,0 +1,40 @@
+# Copyright (C) 2008-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/>.
+
+"""Interface for describing pipelines."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'IPipeline',
+ ]
+
+
+from zope.interface import Interface, Attribute
+
+
+
+class IPipeline(Interface):
+ """A pipeline of handlers."""
+
+ name = Attribute('Pipeline name; must be unique.')
+ description = Attribute('A brief description of this pipeline.')
+
+ def __iter__():
+ """Iterate over all the handlers in this pipeline."""
+
diff --git a/src/mailman/interfaces/preferences.py b/src/mailman/interfaces/preferences.py
new file mode 100644
index 000000000..9aeed8496
--- /dev/null
+++ b/src/mailman/interfaces/preferences.py
@@ -0,0 +1,77 @@
+# Copyright (C) 2007-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/>.
+
+"""Interface for preferences."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'IPreferences',
+ ]
+
+
+from zope.interface import Interface, Attribute
+
+
+
+class IPreferences(Interface):
+ """Delivery related information."""
+
+ acknowledge_posts = Attribute(
+ """Send an acknowledgment for every posting?
+
+ This preference can be True, False, or None. True means the user is
+ sent a receipt for each message they send to the mailing list. False
+ means that no receipt is sent. None means no preference is
+ specified.""")
+
+ preferred_language = Attribute(
+ """The preferred language for interacting with a mailing list.
+
+ This is either the language code for the preferred language, or None
+ meaning no preferred language is specified.""")
+
+ receive_list_copy = Attribute(
+ """Should an explicit recipient receive a list copy?
+
+ When a list member is explicitly named in a message's recipients
+ (e.g. the To or CC headers), and this preference is True, the
+ recipient will still receive a list copy of the message. When False,
+ this list copy will be suppressed. None means no preference is
+ specified.""")
+
+ receive_own_postings = Attribute(
+ """Should the poster get a list copy of their own messages?
+
+ When this preference is True, a list copy will be sent to the poster
+ of all messages. When False, this list copy will be suppressed. None
+ means no preference is specified.""")
+
+ delivery_mode = Attribute(
+ """The preferred delivery mode.
+
+ This is an enum constant of the type DeliveryMode. It may also be
+ None which means that no preference is specified.""")
+
+ delivery_status = Attribute(
+ """The delivery status.
+
+ This is an enum constant of type DeliveryStatus. It may also be None
+ which means that no preference is specified.
+
+ XXX I'm not sure this is the right place to put this.""")
diff --git a/src/mailman/interfaces/registrar.py b/src/mailman/interfaces/registrar.py
new file mode 100644
index 000000000..d90966e2d
--- /dev/null
+++ b/src/mailman/interfaces/registrar.py
@@ -0,0 +1,83 @@
+# Copyright (C) 2007-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/>.
+
+"""Interface describing a user registration service.
+
+This is a higher level interface to user registration, address confirmation,
+etc. than the IUserManager. The latter does no validation, syntax checking,
+or confirmation, while this interface does.
+"""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'IRegistrar',
+ ]
+
+
+from zope.interface import Interface
+
+
+
+class IRegistrar(Interface):
+ """Interface for registering and verifying addresses and users.
+
+ This is a higher level interface to user registration, address
+ confirmation, etc. than the IUserManager. The latter does no validation,
+ syntax checking, or confirmation, while this interface does.
+ """
+
+ def register(address, real_name=None, mlist=None):
+ """Register the email address, requesting verification.
+
+ No IAddress or IUser is created during this step, but after successful
+ confirmation, it is guaranteed that an IAddress with a linked IUser
+ will exist. When a verified IAddress matching address already exists,
+ this method will do nothing, except link a new IUser to the IAddress
+ if one is not yet associated with the address.
+
+ In all cases, the email address is sanity checked for validity first.
+
+ :param address: The textual email address to register.
+ :param real_name: The optional real name of the user.
+ :return: The confirmation token string.
+ :raises InvalidEmailAddress: if the address is not allowed.
+ """
+
+ def confirm(token):
+ """Confirm the pending registration matched to the given `token`.
+
+ Confirmation ensures that the IAddress exists and is linked to an
+ IUser, with the latter being created and linked if necessary.
+
+ :param token: A token matching a pending event with a type of
+ 'registration'.
+ :return: Boolean indicating whether the confirmation succeeded or
+ not. It may fail if the token is no longer in the database, or if
+ the token did not match a registration event.
+ """
+
+ def discard(token):
+ """Discard the pending registration matched to the given `token`.
+
+ The event record is discarded and the IAddress is not verified. No
+ IUser is created.
+
+ :param token: A token matching a pending event with a type of
+ 'registration'.
+ """
diff --git a/src/mailman/interfaces/requests.py b/src/mailman/interfaces/requests.py
new file mode 100644
index 000000000..e3e316fa0
--- /dev/null
+++ b/src/mailman/interfaces/requests.py
@@ -0,0 +1,115 @@
+# Copyright (C) 2007-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/>.
+
+"""Interfaces for the request database.
+
+The request database handles events that must be approved by the list
+moderators, such as subscription requests and held messages.
+"""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'IListRequests',
+ 'IRequests',
+ 'RequestType',
+ ]
+
+
+from munepy import Enum
+from zope.interface import Interface, Attribute
+
+
+
+class RequestType(Enum):
+ held_message = 1
+ subscription = 2
+ unsubscription = 3
+
+
+
+class IListRequests(Interface):
+ """Held requests for a specific mailing list."""
+
+ mailing_list = Attribute(
+ """The IMailingList for these requests.""")
+
+ count = Attribute(
+ """The total number of requests held for the mailing list.""")
+
+ def count_of(request_type):
+ """The total number of requests held of the given request type.
+
+ :param request_type: A `RequestType` enum value.
+ :return: An integer.
+ """
+
+ def hold_request(request_type, key, data=None):
+ """Hold some data for moderator approval.
+
+ :param request_type: A `RequestType` enum value.
+ :param key: The key piece of request data being held.
+ :param data: Additional optional data in the form of a dictionary that
+ is associated with the held request.
+ :return: A unique id for this held request.
+ """
+
+ held_requests = Attribute(
+ """An iterator over the held requests.
+
+ Returned items have two attributes:
+ * `id` is the held request's unique id;
+ * `type` is a `RequestType` enum value.
+ """)
+
+ def of_type(request_type):
+ """An iterator over the held requests of the given type.
+
+ Returned items have two attributes:
+ * `id` is the held request's unique id;
+ * `type` is a `RequestType` enum value.
+
+ Only items with a matching `type' are returned.
+ """
+
+ def get_request(request_id):
+ """Get the data associated with the request id, or None.
+
+ :param request_id: The unique id for the request.
+ :return: A 2-tuple of the key and data originally held, or None if the
+ `request_id` is not in the database.
+ """
+
+ def delete_request(request_id):
+ """Delete the request associated with the id.
+
+ :param request_id: The unique id for the request.
+ :raises KeyError: If `request_id` is not in the database.
+ """
+
+
+
+class IRequests(Interface):
+ """The requests database."""
+
+ def get_list_requests(mailing_list):
+ """Return the `IListRequests` object for the given mailing list.
+
+ :param mailing_list: An `IMailingList`.
+ :return: An `IListRequests` object for the mailing list.
+ """
diff --git a/src/mailman/interfaces/roster.py b/src/mailman/interfaces/roster.py
new file mode 100644
index 000000000..9fb648be1
--- /dev/null
+++ b/src/mailman/interfaces/roster.py
@@ -0,0 +1,61 @@
+# Copyright (C) 2007-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/>.
+
+"""Interface for a roster of members."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'IRoster',
+ ]
+
+
+from zope.interface import Interface, Attribute
+
+
+
+class IRoster(Interface):
+ """A roster is a collection of IMembers."""
+
+ name = Attribute(
+ """The name for this roster.
+
+ Rosters are considered equal if they have the same name.""")
+
+ members = Attribute(
+ """An iterator over all the IMembers managed by this roster.""")
+
+ users = Attribute(
+ """An iterator over all the IUsers reachable by this roster.
+
+ This returns all the users for all the members managed by this roster.
+ """)
+
+ addresses = Attribute(
+ """An iterator over all the IAddresses reachable by this roster.
+
+ This returns all the addresses for all the users for all the members
+ managed by this roster.
+ """)
+
+ def get_member(address):
+ """Return the IMember for the given address.
+
+ 'address' is a text email address. If no matching member is found,
+ None is returned.
+ """
diff --git a/src/mailman/interfaces/rules.py b/src/mailman/interfaces/rules.py
new file mode 100644
index 000000000..632cc85de
--- /dev/null
+++ b/src/mailman/interfaces/rules.py
@@ -0,0 +1,53 @@
+# Copyright (C) 2007-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/>.
+
+"""Interface describing the basics of rules."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'IRule',
+ ]
+
+
+from zope.interface import Interface, Attribute
+
+
+
+class IRule(Interface):
+ """A basic rule."""
+
+ name = Attribute('Rule name; must be unique.')
+
+ description = Attribute('A brief description of the rule.')
+
+ record = Attribute(
+ """Should this rule's success or failure be recorded?
+
+ This is a boolean; if True then this rule's hit or miss will be
+ recorded in a message header. If False, it won't.
+ """)
+
+ def check(mlist, msg, msgdata):
+ """Run the rule.
+
+ :param mlist: The mailing list object.
+ :param msg: The message object.
+ :param msgdata: The message metadata.
+ :returns: a boolean specifying whether the rule matched or not.
+ """
diff --git a/src/mailman/interfaces/runner.py b/src/mailman/interfaces/runner.py
new file mode 100644
index 000000000..e923e099b
--- /dev/null
+++ b/src/mailman/interfaces/runner.py
@@ -0,0 +1,119 @@
+# Copyright (C) 2007-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/>.
+
+"""Interface for queue runners."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'IRunner',
+ ]
+
+
+from zope.interface import Interface, Attribute
+
+
+
+class IRunner(Interface):
+ """The queue runner."""
+
+ def run():
+ """Start the queue runner."""
+
+ def stop():
+ """Stop the queue runner on the next iteration through the loop."""
+
+ queue_directory = Attribute(
+ 'The queue directory. Overridden in subclasses.')
+
+ sleep_time = Attribute("""\
+ The number of seconds this queue runner will sleep between iterations
+ through the main loop. If given, overrides
+ `config.QRUNNER_SLEEP_TIME`
+ """)
+
+ def _one_iteration():
+ """The work done in one iteration of the main loop.
+
+ Can be overridden by subclasses.
+
+ :return: The number of files still left to process.
+ :rtype: int
+ """
+
+ def _process_one_file(msg, msgdata):
+ """Process one queue file.
+
+ :param msg: The message object.
+ :type msg: `email.message.Message`
+ :param msgdata: The message metadata.
+ :type msgdata: dict
+ """
+
+ def _clean_up():
+ """Clean up upon exit from the main processing loop.
+
+ Called when the queue runner's main loop is stopped, this should
+ perform any necessary resource deallocation.
+ """
+
+ def _dispose(mlist, msg, msgdata):
+ """Dispose of a single message destined for a mailing list.
+
+ Called for each message that the queue runner is responsible for, this
+ is the primary overridable method for processing each message.
+ Subclasses, must provide implementation for this method.
+
+ :param mlist: The mailing list this message is destined for.
+ :type mlist: `IMailingList`
+ :param msg: The message being processed.
+ :type msg: `email.message.Message`
+ :param msgdata: The message metadata.
+ :type msgdata: dict
+ :return: True if the message should continue to be queue, False if the
+ message should be deleted automatically.
+ :rtype: bool
+ """
+
+ def _do_periodic():
+ """Do some arbitrary periodic processing.
+
+ Called every once in a while both from the queue runner's main loop,
+ and from the runner's hash slice processing loop. You can do whatever
+ special periodic processing you want here.
+ """
+
+ def _snooze(filecnt):
+ """Sleep for a little while.
+
+ :param filecnt: The number of messages in the queue the last time
+ through. Queue runners can decide to continue to do work, or
+ sleep for a while based on this value. By default, the base queue
+ runner only snoozes when there was nothing to do last time around.
+ :type filecnt: int
+ """
+
+ def _short_circuit():
+ """Should processing be short-circuited?
+
+ :return: True if the file processing loop should exit before it's
+ finished processing each message in the current slice of hash
+ space. False tells _one_iteration() to continue processing until
+ the current snapshot of hash space is exhausted.
+ :rtype: bool
+ """
diff --git a/src/mailman/interfaces/styles.py b/src/mailman/interfaces/styles.py
new file mode 100644
index 000000000..e70050f02
--- /dev/null
+++ b/src/mailman/interfaces/styles.py
@@ -0,0 +1,120 @@
+# Copyright (C) 2007-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/>.
+
+"""Interfaces for list styles."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'DuplicateStyleError',
+ 'IStyle',
+ 'IStyleManager',
+ ]
+
+
+from zope.interface import Interface, Attribute
+from mailman.interfaces.errors import MailmanError
+
+
+
+class DuplicateStyleError(MailmanError):
+ """A style with the same name is already registered."""
+
+
+
+class IStyle(Interface):
+ """Application of a style to an existing mailing list."""
+
+ name = Attribute(
+ """The name of this style. Must be unique.""")
+
+ priority = Attribute(
+ """The priority of this style, as an integer.""")
+
+ def apply(mailing_list):
+ """Apply the style to the mailing list.
+
+ :type mailing_list: `IMailingList`.
+ :param mailing_list: the mailing list to apply the style to.
+ """
+
+ def match(mailing_list, styles):
+ """Give this list a chance to match the mailing list.
+
+ If the style's internal matching rules match the `mailing_list`, then
+ the style may append itself to the `styles` list. This list will be
+ ordered when returned from `IStyleManager.lookup()`.
+
+ :type mailing_list: `IMailingList`.
+ :param mailing_list: the mailing list object.
+ :param styles: ordered list of `IStyles` matched so far.
+ """
+
+
+
+class IStyleManager(Interface):
+ """A manager of styles and style chains."""
+
+ def get(name):
+ """Return the named style or None.
+
+ :type name: Unicode
+ :param name: A style name.
+ :return: the named `IStyle` or None if the style doesn't exist.
+ """
+
+ def lookup(mailing_list):
+ """Return a list of styles for the given mailing list.
+
+ Use various registered rules to find an `IStyle` for the given mailing
+ list. The returned styles are ordered by their priority.
+
+ Style matches can be registered and reordered by plugins.
+
+ :type mailing_list: `IMailingList`.
+ :param mailing_list: The mailing list object to find a style for.
+ :return: ordered list of `IStyles`. Zero is the lowest priority.
+ """
+
+ styles = Attribute(
+ """An iterator over all the styles known by this manager.
+
+ Styles are ordered by their priority, which may be changed.
+ """)
+
+ def register(style):
+ """Register a style with this manager.
+
+ :param style: an IStyle.
+ :raises DuplicateStyleError: if a style with the same name was already
+ registered.
+ """
+
+ def unregister(style):
+ """Unregister the style.
+
+ :param style: an IStyle.
+ :raises KeyError: If the style's name is not currently registered.
+ """
+
+ def populate():
+ """Populate the styles from the configuration files.
+
+ This clears the current set of styles and resets them from those
+ defined in the configuration files.
+ """
diff --git a/src/mailman/interfaces/switchboard.py b/src/mailman/interfaces/switchboard.py
new file mode 100644
index 000000000..866093d12
--- /dev/null
+++ b/src/mailman/interfaces/switchboard.py
@@ -0,0 +1,90 @@
+# Copyright (C) 2007-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/>.
+
+"""Interface for switchboards."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'ISwitchboard',
+ ]
+
+
+from zope.interface import Interface, Attribute
+
+
+
+class ISwitchboard(Interface):
+ """The switchboard."""
+
+ queue_directory = Attribute(
+ """The name of the queue directory this switchboard is responsible for.
+
+ This should be a subdirectory of the system-wide top-level queue
+ directory.
+ """)
+
+ def enqueue(_msg, _metadata=None, **_kws):
+ """Store the message and metadata in the switchboard's queue.
+
+ When metadata is not given, an empty metadata dictionary is used. The
+ keyword arguments are added to the metadata dictonary, with precedence
+ given to the keyword arguments.
+
+ The base name of the message file is returned.
+ """
+
+ def dequeue(filebase):
+ """Return the message and metadata contained in the named file.
+
+ filebase is the base name of the message file as returned by the
+ .enqueue() method. This file must exist and contain a message and
+ metadata. The message file is preserved in a backup file, which must
+ be removed by calling the .finish() method.
+
+ Returned is a 2-tuple of the form (message, metadata).
+ """
+
+ def finish(filebase, preserve=False):
+ """Remove the backup file for filebase.
+
+ If preserve is True, then the backup file is actually just renamed to
+ a preservation file instead of being unlinked.
+ """
+
+ files = Attribute(
+ """An iterator over all the .pck files in the queue directory.
+
+ The base names of the matching files are returned.
+ """)
+
+ def get_files(extension='.pck'):
+ """Like the 'files' attribute, but accepts an alternative extension.
+
+ Only the files in the queue directory that have a matching extension
+ are returned. Like 'files', the base names of the matching files are
+ returned.
+ """
+
+ def recover_backup_files():
+ """Move all backup files to active message files.
+
+ It is impossible for both the .bak and .pck files to exist at the same
+ time, so moving them is enough to ensure that a normal dequeing
+ operation will handle them.
+ """
diff --git a/src/mailman/interfaces/user.py b/src/mailman/interfaces/user.py
new file mode 100644
index 000000000..5c3ff58cd
--- /dev/null
+++ b/src/mailman/interfaces/user.py
@@ -0,0 +1,84 @@
+# Copyright (C) 2007-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/>.
+
+"""Interface describing the basics of a user."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'IUser',
+ ]
+
+
+from zope.interface import Interface, Attribute
+
+
+
+class IUser(Interface):
+ """A basic user."""
+
+ real_name = Attribute(
+ """This user's Real Name.""")
+
+ password = Attribute(
+ """This user's password information.""")
+
+ addresses = Attribute(
+ """An iterator over all the IAddresses controlled by this user.""")
+
+ memberships = Attribute(
+ """A roster of this user's membership.""")
+
+ def register(address, real_name=None):
+ """Register the given email address and link it to this user.
+
+ In this case, 'address' is a text email address, not an IAddress
+ object. If real_name is not given, the empty string is used.
+
+ Raises AddressAlreadyLinkedError if this IAddress is already linked to
+ another user. If the corresponding IAddress already exists but is not
+ linked, then it is simply linked to the user, in which case
+ real_name is ignored.
+
+ Return the new IAddress object.
+ """
+
+ def link(address):
+ """Link this user to the given IAddress.
+
+ Raises AddressAlreadyLinkedError if this IAddress is already linked to
+ another user.
+ """
+
+ def unlink(address):
+ """Unlink this IAddress from the user.
+
+ Raises AddressNotLinkedError if this address is not linked to this
+ user, either because it's not linked to any user or it's linked to
+ some other user.
+ """
+
+ def controls(address):
+ """Determine whether this user controls the given email address.
+
+ 'address' is a text email address. This method returns true if the
+ user controls the given email address, otherwise false.
+ """
+
+ preferences = Attribute(
+ """This user's preferences.""")
diff --git a/src/mailman/interfaces/usermanager.py b/src/mailman/interfaces/usermanager.py
new file mode 100644
index 000000000..41bec49ba
--- /dev/null
+++ b/src/mailman/interfaces/usermanager.py
@@ -0,0 +1,96 @@
+# Copyright (C) 2007-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/>.
+
+"""Interface describing a user manager service."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'IUserManager',
+ ]
+
+
+from zope.interface import Interface, Attribute
+
+
+
+class IUserManager(Interface):
+ """The interface of a global user manager service.
+
+ Different user managers have different concepts of what a user is, and the
+ users managed by different IUserManagers are completely independent. This
+ is how you can separate the user contexts for different domains, on a
+ multiple domain system.
+
+ There is one special roster, the null roster ('') which contains all
+ IUsers in all IRosters.
+ """
+
+ def create_user(address=None, real_name=None):
+ """Create and return an IUser.
+
+ When address is given, an IAddress is also created and linked to the
+ new IUser object. If the address already exists, an
+ `ExistingAddressError` is raised. If the address exists but is
+ already linked to another user, an AddressAlreadyLinkedError is
+ raised.
+
+ When real_name is given, the IUser's real_name is set to this string.
+ If an IAddress is also created and linked, its real_name is set to the
+ same string.
+ """
+
+ def delete_user(user):
+ """Delete the given IUser."""
+
+ def get_user(address):
+ """Get the user that controls the given email address, or None.
+
+ 'address' is a text email address.
+ """
+
+ users = Attribute(
+ """An iterator over all the IUsers managed by this user manager.""")
+
+ def create_address(address, real_name=None):
+ """Create and return an unlinked IAddress object.
+
+ address is the text email address. If real_name is not given, it
+ defaults to the empty string. If the IAddress already exists an
+ ExistingAddressError is raised.
+ """
+
+ def delete_address(address):
+ """Delete the given IAddress object.
+
+ If this IAddress linked to a user, it is first unlinked before it is
+ deleted.
+ """
+
+ def get_address(address):
+ """Find and return the `IAddress` matching a text address.
+
+ :param address: the text email address
+ :type address: string
+ :return: The matching `IAddress` object, or None if no registered
+ `IAddress` matches the text address
+ :rtype: `IAddress` or None
+ """
+
+ addresses = Attribute(
+ """An iterator over all the IAddresses managed by this manager.""")