aboutsummaryrefslogtreecommitdiff
path: root/src/mailman_pgp/mta/deliver.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/mailman_pgp/mta/deliver.py')
-rw-r--r--src/mailman_pgp/mta/deliver.py136
1 files changed, 136 insertions, 0 deletions
diff --git a/src/mailman_pgp/mta/deliver.py b/src/mailman_pgp/mta/deliver.py
new file mode 100644
index 0000000..050a740
--- /dev/null
+++ b/src/mailman_pgp/mta/deliver.py
@@ -0,0 +1,136 @@
+# Copyright (C) 2017 Jan Jancar
+#
+# This file is a part of the Mailman PGP plugin.
+#
+# This program 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.
+#
+# This program 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
+# this program. If not, see <http://www.gnu.org/licenses/>.
+
+""""""
+import logging
+import time
+
+from mailman.config import config
+from mailman.interfaces.mailinglist import Personalization
+from mailman.interfaces.mta import SomeRecipientsFailed
+from mailman.mta.bulk import BulkDelivery
+from mailman.mta.deliver import Deliver
+from mailman.utilities.string import expand
+from public import public
+
+from mailman_pgp.model.list import PGPMailingList
+from mailman_pgp.mta.bulk import PGPBulkDelivery
+from mailman_pgp.mta.personalized import PGPPersonalizedDelivery
+
+COMMA = ','
+log = logging.getLogger('mailman.smtp')
+
+
+@public
+def deliver(mlist, msg, msgdata):
+ """Deliver a message to the outgoing mail server."""
+ # If there are no recipients, there's nothing to do.
+ recipients = msgdata.get('recipients')
+ if not recipients:
+ # Could be None, could be an empty sequence.
+ return
+ # Which delivery agent should we use? Several situations can cause us to
+ # use individual delivery. If not specified, use bulk delivery. See the
+ # to-outgoing handler for when the 'verp' key is set in the metadata.
+ personalized_agent = Deliver
+ bulk_agent = BulkDelivery
+
+ pgp_list = PGPMailingList.for_list(mlist)
+ if pgp_list:
+ personalized_agent = PGPPersonalizedDelivery
+ bulk_agent = PGPBulkDelivery
+
+ if msgdata.get('verp', False):
+ agent = personalized_agent()
+ elif mlist.personalize != Personalization.none:
+ agent = personalized_agent()
+ else:
+ agent = bulk_agent(int(config.mta.max_recipients))
+ log.debug('Using agent: %s', agent)
+ # Keep track of the original recipients and the original sender for
+ # logging purposes.
+ original_recipients = msgdata['recipients']
+ original_sender = msgdata.get('original-sender', msg.sender)
+ # Let the agent attempt to deliver to the recipients. Record all failures
+ # for re-delivery later.
+ t0 = time.time()
+ refused = agent.deliver(mlist, msg, msgdata)
+ t1 = time.time()
+ # Log this posting.
+ size = getattr(msg, 'original_size', msgdata.get('original_size'))
+ if size is None:
+ size = len(msg.as_string())
+ substitutions = dict(
+ msgid=msg.get('message-id', 'n/a'), # noqa: E221, E251
+ listname=mlist.fqdn_listname, # noqa: E221, E251
+ sender=original_sender, # noqa: E221, E251
+ recip=len(original_recipients), # noqa: E221, E251
+ size=size, # noqa: E221, E251
+ time=t1 - t0, # noqa: E221, E251
+ refused=len(refused), # noqa: E221, E251
+ smtpcode='n/a', # noqa: E221, E251
+ smtpmsg='n/a', # noqa: E221, E251
+ )
+ template = config.logging.smtp.every
+ if template.lower() != 'no':
+ log.info('%s', expand(template, mlist, substitutions))
+ if refused:
+ template = config.logging.smtp.refused
+ if template.lower() != 'no':
+ log.info('%s', expand(template, mlist, substitutions))
+ else:
+ # Log the successful post, but if it was not destined to the mailing
+ # list (e.g. to the owner or admin), print the actual recipients
+ # instead of just the number.
+ if not msgdata.get('tolist', False):
+ recips = msg.get_all('to', [])
+ recips.extend(msg.get_all('cc', []))
+ substitutions['recips'] = COMMA.join(recips)
+ template = config.logging.smtp.success
+ if template.lower() != 'no':
+ log.info('%s', expand(template, mlist, substitutions))
+ # Process any failed deliveries.
+ temporary_failures = []
+ permanent_failures = []
+ for recipient, (code, smtp_message) in refused.items():
+ # RFC 5321, $4.5.3.1.10 says:
+ #
+ # RFC 821 [1] incorrectly listed the error where an SMTP server
+ # exhausts its implementation limit on the number of RCPT commands
+ # ("too many recipients") as having reply code 552. The correct
+ # reply code for this condition is 452. Clients SHOULD treat a 552
+ # code in this case as a temporary, rather than permanent, failure
+ # so the logic below works.
+ #
+ if code >= 500 and code != 552:
+ # A permanent failure
+ permanent_failures.append(recipient)
+ else:
+ # Deal with persistent transient failures by queuing them up for
+ # future delivery. TBD: this could generate lots of log entries!
+ temporary_failures.append(recipient)
+ template = config.logging.smtp.failure
+ if template.lower() != 'no':
+ substitutions.update(
+ recip=recipient, # noqa: E221, E251
+ smtpcode=code, # noqa: E221, E251
+ smtpmsg=smtp_message # noqa: E221, E251
+ )
+ log.info('%s', expand(template, mlist, substitutions))
+ # Return the results
+ if temporary_failures or permanent_failures:
+ raise SomeRecipientsFailed(temporary_failures, permanent_failures)