1 # Copyright (C) 2009-2016 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 """MTA connections."""
23 from contextlib
import suppress
24 from lazr
.config
import as_boolean
25 from mailman
import public
26 from mailman
.config
import config
29 log
= logging
.getLogger('mailman.smtp')
34 """Manage a connection to the SMTP server."""
35 def __init__(self
, host
, port
, sessions_per_connection
,
36 smtp_user
=None, smtp_pass
=None):
37 """Create a connection manager.
39 :param host: The host name of the SMTP server to connect to.
41 :param port: The port number of the SMTP server to connect to.
43 :param sessions_per_connection: The number of SMTP sessions per
44 connection to the SMTP server. After this number of sessions
45 has been reached, the connection is closed and a new one is
46 opened. Set to zero for an unlimited number of sessions per
47 connection (i.e. your MTA has no limit).
48 :type sessions_per_connection: integer
49 :param smtp_user: Optional SMTP authentication user name. If given,
50 `smtp_pass` must also be given.
52 :param smtp_pass: Optional SMTP authentication password. If given,
53 `smtp_user` must also be given.
57 self
._sessions
_per
_connection
= sessions_per_connection
58 self
._username
= smtp_user
59 self
._password
= smtp_pass
60 self
._session
_count
= None
61 self
._connection
= None
64 """Open a new connection."""
65 self
._connection
= smtplib
.SMTP()
66 log
.debug('Connecting to %s:%s', self
._host
, self
._port
)
67 self
._connection
.connect(self
._host
, self
._port
)
68 if self
._username
is not None and self
._password
is not None:
69 log
.debug('Logging in')
70 self
._connection
.login(self
._username
, self
._password
)
71 self
._session
_count
= self
._sessions
_per
_connection
73 def sendmail(self
, envsender
, recipients
, msgtext
):
74 """Mimic `smtplib.SMTP.sendmail`."""
75 if as_boolean(config
.devmode
.enabled
):
76 # Force the recipients to the specified address, but still deliver
77 # to the same number of recipients.
78 recipients
= [config
.devmode
.recipient
] * len(recipients
)
79 if self
._connection
is None:
82 log
.debug('envsender: %s, recipients: %s, size(msgtext): %s',
83 envsender
, recipients
, len(msgtext
))
84 results
= self
._connection
.sendmail(envsender
, recipients
, msgtext
)
85 except smtplib
.SMTPException
:
86 # For safety, close this connection. The next send attempt will
87 # automatically re-open it. Pass the exception on up.
90 # This session has been successfully completed.
91 self
._session
_count
-= 1
92 # By testing exactly for equality to 0, we automatically handle the
93 # case for SMTP_MAX_SESSIONS_PER_CONNECTION <= 0 meaning never close
94 # the connection. We won't worry about wraparound <wink>.
95 if self
._session
_count
== 0:
100 """Mimic `smtplib.SMTP.quit`."""
101 if self
._connection
is None:
103 with
suppress(smtplib
.SMTPException
):
104 self
._connection
.quit()
105 self
._connection
= None