1 # Copyright (C) 2000-2009 by the Free Software Foundation, Inc.
3 # This file is part of GNU Mailman.
5 # GNU Mailman is free software: you can redistribute it and/or modify it under
6 # the terms of the GNU General Public License as published by the Free
7 # Software Foundation, either version 3 of the License, or (at your option)
10 # GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 # You should have received a copy of the GNU General Public License along with
16 # GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
18 """Outgoing queue runner."""
25 from datetime
import datetime
26 from lazr
.config
import as_timedelta
28 from mailman
.config
import config
29 from mailman
.interfaces
.mta
import SomeRecipientsFailed
30 from mailman
.queue
import Runner
31 from mailman
.queue
.bounce
import BounceMixin
32 from mailman
.utilities
.modules
import find_name
35 # This controls how often _do_periodic() will try to deal with deferred
36 # permanent failures. It is a count of calls to _do_periodic()
37 DEAL_WITH_PERMFAILURES_EVERY
= 10
39 log
= logging
.getLogger('mailman.error')
43 class OutgoingRunner(Runner
, BounceMixin
):
44 """The outgoing queue runner."""
46 def __init__(self
, slice=None, numslices
=1):
47 Runner
.__init
__(self
, slice, numslices
)
48 BounceMixin
.__init
__(self
)
49 # We look this function up only at startup time.
50 self
._func
= find_name(config
.mta
.outgoing
)
51 # This prevents smtp server connection problems from filling up the
52 # error log. It gets reset if the message was successfully sent, and
53 # set if there was a socket.error.
55 self
._retryq
= config
.switchboards
['retry']
57 def _dispose(self
, mlist
, msg
, msgdata
):
58 # See if we should retry delivery of this message again.
59 deliver_after
= msgdata
.get('deliver_after', datetime
.fromtimestamp(0))
60 if datetime
.now() < deliver_after
:
62 # Make sure we have the most up-to-date state
65 self
._func
(mlist
, msg
, msgdata
)
66 # Failsafe -- a child may have leaked through.
67 if pid
<> os
.getpid():
68 log
.error('child process leaked thru: %s', pid
)
72 # There was a problem connecting to the SMTP server. Log this
73 # once, but crank up our sleep time so we don't fill the error
75 port
= int(config
.mta
.port
)
80 log
.error('Cannot connect to SMTP server %s on port %s',
81 config
.mta
.host
, port
)
84 except SomeRecipientsFailed
as error
:
85 # Handle local rejects of probe messages differently.
86 if msgdata
.get('probe_token') and error
.permanent_failures
:
87 self
._probe
_bounce
(mlist
, msgdata
['probe_token'])
89 # Delivery failed at SMTP time for some or all of the
90 # recipients. Permanent failures are registered as bounces,
91 # but temporary failures are retried for later.
93 # BAW: msg is going to be the original message that failed
94 # delivery, not a bounce message. This may be confusing if
95 # this is what's sent to the user in the probe message. Maybe
96 # we should craft a bounce-like message containing information
97 # about the permanent SMTP failure?
98 if error
.permanent_failures
:
100 mlist
.fqdn_listname
, error
.permanent_failures
, msg
)
101 # Move temporary failures to the qfiles/retry queue which will
102 # occasionally move them back here for another shot at
104 if error
.temporary_failures
:
106 recips
= error
.temporary_failures
107 last_recip_count
= msgdata
.get('last_recip_count', 0)
108 deliver_until
= msgdata
.get('deliver_until', now
)
109 if len(recips
) == last_recip_count
:
110 # We didn't make any progress, so don't attempt
111 # delivery any longer. BAW: is this the best
113 if now
> deliver_until
:
116 # Keep trying to delivery this message for a while
117 deliver_until
= now
+ as_timedelta(
118 config
.mta
.delivery_retry_period
)
119 msgdata
['last_recip_count'] = len(recips
)
120 msgdata
['deliver_until'] = deliver_until
121 msgdata
['recipients'] = recips
122 self
._retryq
.enqueue(msg
, msgdata
)
123 # We've successfully completed handling of this message
126 _do_periodic
= BounceMixin
._do
_periodic
129 BounceMixin
._clean
_up
(self
)
130 Runner
._clean
_up
(self
)