summaryrefslogtreecommitdiff
path: root/src/mailman/mta/base.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/mailman/mta/base.py')
-rw-r--r--src/mailman/mta/base.py127
1 files changed, 127 insertions, 0 deletions
diff --git a/src/mailman/mta/base.py b/src/mailman/mta/base.py
new file mode 100644
index 000000000..5942386a6
--- /dev/null
+++ b/src/mailman/mta/base.py
@@ -0,0 +1,127 @@
+# 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/>.
+
+"""Base delivery class."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'BaseDelivery',
+ ]
+
+
+import logging
+import smtplib
+
+from zope.interface import implements
+
+from mailman.config import config
+from mailman.interfaces.mta import IMailTransportAgentDelivery
+from mailman.mta.connection import Connection
+
+
+log = logging.getLogger('mailman.smtp')
+
+
+
+class BaseDelivery:
+ """Base delivery class."""
+
+ implements(IMailTransportAgentDelivery)
+
+ def __init__(self, max_recipients=None):
+ """Create a basic deliverer.
+
+ :param max_recipients: The maximum number of recipients per delivery
+ chunk. None, zero or less means to group all recipients into one
+ big chunk.
+ :type max_recipients: integer
+ """
+ self._max_recipients = (max_recipients
+ if max_recipients is not None
+ else 0)
+ self._connection = Connection(
+ config.mta.smtp_host, int(config.mta.smtp_port),
+ self._max_recipients)
+
+ def _deliver_to_recipients(self, mlist, msg, msgdata,
+ sender, recipients):
+ """Low-level delivery to a set of recipients.
+
+ :param mlist: The mailing list being delivered to.
+ :type mlist: `IMailingList`
+ :param msg: The original message being delivered.
+ :type msg: `Message`
+ :param msgdata: Additional message metadata for this delivery.
+ :type msgdata: dictionary
+ :param sender: The envelope sender.
+ :type sender: string
+ :param recipients: The recipients of this message.
+ :type recipients: sequence
+ :return: delivery failures as defined by `smtplib.SMTP.sendmail`
+ :rtype: dictionary
+ """
+ message_id = msg['message-id']
+ try:
+ refused = self._connection.sendmail(
+ sender, recipients, msg.as_string())
+ except smtplib.SMTPRecipientsRefused as error:
+ log.error('%s recipients refused: %s', message_id, error)
+ refused = error.recipients
+ except smtplib.SMTPResponseException as error:
+ log.error('%s response exception: %s', message_id, error)
+ refused = dict(
+ # recipient -> (code, error)
+ (recipient, (error.smtp_code, error.smtp_error))
+ for recipient in recipients)
+ except (socket.error, IOError, smtplib.SMTPException) as error:
+ # MTA not responding, or other socket problems, or any other
+ # kind of SMTPException. In that case, nothing got delivered,
+ # so treat this as a temporary failure. We use error code 444
+ # for this (temporary, unspecified failure, cf RFC 5321).
+ log.error('%s low level smtp error: %s', message_id, error)
+ error = str(error)
+ refused = dict(
+ # recipient -> (code, error)
+ (recipient, (444, error))
+ for recipient in recipients)
+ return refused
+
+ def _get_sender(self, mlist, msg, msgdata):
+ """Return the envelope sender to use.
+
+ The message metadata can override the calculation of the sender, but
+ otherwise it falls to the list's -bounces robot. If this message is
+ not intended for any specific mailing list, the site owner's address
+ is used.
+
+ :param mlist: The mailing list being delivered to.
+ :type mlist: `IMailingList`
+ :param msg: The original message being delivered.
+ :type msg: `Message`
+ :param msgdata: Additional message metadata for this delivery.
+ :type msgdata: dictionary
+ :return: The envelope sender.
+ :rtype: string
+ """
+ sender = msgdata.get('sender')
+ if sender is None:
+ return (config.mailman.site_owner
+ if mlist is None
+ else mlist.bounces_address)
+ return sender