diff options
Diffstat (limited to 'src/mailman/interfaces')
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.""") |
