| Barry Warsaw | 4c4bec8 | 1998-12-22 03:02:20 +0000 | [diff] [blame] | 1 | #! /usr/bin/env python | 
 | 2 |  | 
| Barry Warsaw | a1ae884 | 2000-07-09 21:24:31 +0000 | [diff] [blame] | 3 | '''SMTP/ESMTP client class. | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 4 |  | 
| Guido van Rossum | f7fcf5e | 2001-09-14 16:08:44 +0000 | [diff] [blame] | 5 | This should follow RFC 821 (SMTP), RFC 1869 (ESMTP), RFC 2554 (SMTP | 
 | 6 | Authentication) and RFC 2487 (Secure SMTP over TLS). | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 7 |  | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 8 | Notes: | 
 | 9 |  | 
 | 10 | Please remember, when doing ESMTP, that the names of the SMTP service | 
| Barry Warsaw | 4c4bec8 | 1998-12-22 03:02:20 +0000 | [diff] [blame] | 11 | extensions are NOT the same thing as the option keywords for the RCPT | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 12 | and MAIL commands! | 
 | 13 |  | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 14 | Example: | 
 | 15 |  | 
| Barry Warsaw | a7d9bdf | 1998-12-22 03:24:27 +0000 | [diff] [blame] | 16 |   >>> import smtplib | 
 | 17 |   >>> s=smtplib.SMTP("localhost") | 
| Guido van Rossum | 7131f84 | 2007-02-09 20:13:25 +0000 | [diff] [blame] | 18 |   >>> print(s.help()) | 
| Barry Warsaw | a7d9bdf | 1998-12-22 03:24:27 +0000 | [diff] [blame] | 19 |   This is Sendmail version 8.8.4 | 
 | 20 |   Topics: | 
 | 21 |       HELO    EHLO    MAIL    RCPT    DATA | 
 | 22 |       RSET    NOOP    QUIT    HELP    VRFY | 
 | 23 |       EXPN    VERB    ETRN    DSN | 
 | 24 |   For more info use "HELP <topic>". | 
 | 25 |   To report bugs in the implementation send email to | 
 | 26 |       sendmail-bugs@sendmail.org. | 
 | 27 |   For local information send email to Postmaster at your site. | 
 | 28 |   End of HELP info | 
 | 29 |   >>> s.putcmd("vrfy","someone@here") | 
 | 30 |   >>> s.getreply() | 
 | 31 |   (250, "Somebody OverHere <somebody@here.my.org>") | 
 | 32 |   >>> s.quit() | 
| Barry Warsaw | a1ae884 | 2000-07-09 21:24:31 +0000 | [diff] [blame] | 33 | ''' | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 34 |  | 
| Guido van Rossum | 98d9fd3 | 2000-02-28 15:12:25 +0000 | [diff] [blame] | 35 | # Author: The Dragon De Monsyne <dragondm@integral.org> | 
 | 36 | # ESMTP support, test code and doc fixes added by | 
 | 37 | #     Eric S. Raymond <esr@thyrsus.com> | 
 | 38 | # Better RFC 821 compliance (MAIL and RCPT, and CRLF in data) | 
 | 39 | #     by Carey Evans <c.evans@clear.net.nz>, for picky mail servers. | 
| Guido van Rossum | ae01046 | 2001-09-11 15:57:46 +0000 | [diff] [blame] | 40 | # RFC 2554 (authentication) support by Gerhard Haering <gerhard@bigfoot.de>. | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 41 | # | 
| Guido van Rossum | 98d9fd3 | 2000-02-28 15:12:25 +0000 | [diff] [blame] | 42 | # This was modified from the Python 1.5 library HTTP lib. | 
 | 43 |  | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 44 | import socket | 
| Barry Warsaw | 0720177 | 1998-12-22 20:37:36 +0000 | [diff] [blame] | 45 | import re | 
| Thomas Wouters | b213704 | 2007-02-01 18:02:27 +0000 | [diff] [blame] | 46 | import email.utils | 
| Guido van Rossum | ae01046 | 2001-09-11 15:57:46 +0000 | [diff] [blame] | 47 | import base64 | 
 | 48 | import hmac | 
| Barry Warsaw | 2cc1f6d | 2007-08-30 14:28:55 +0000 | [diff] [blame] | 49 | from email.base64mime import body_encode as encode_base64 | 
| Brett Cannon | e6f8a89 | 2004-07-10 23:14:30 +0000 | [diff] [blame] | 50 | from sys import stderr | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 51 |  | 
| Skip Montanaro | 0de6580 | 2001-02-15 22:15:14 +0000 | [diff] [blame] | 52 | __all__ = ["SMTPException","SMTPServerDisconnected","SMTPResponseException", | 
 | 53 |            "SMTPSenderRefused","SMTPRecipientsRefused","SMTPDataError", | 
| Guido van Rossum | ae01046 | 2001-09-11 15:57:46 +0000 | [diff] [blame] | 54 |            "SMTPConnectError","SMTPHeloError","SMTPAuthenticationError", | 
| Thomas Wouters | 47b49bf | 2007-08-30 22:15:33 +0000 | [diff] [blame] | 55 |            "quoteaddr","quotedata","SMTP"] | 
| Skip Montanaro | 0de6580 | 2001-02-15 22:15:14 +0000 | [diff] [blame] | 56 |  | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 57 | SMTP_PORT = 25 | 
| Thomas Wouters | 89f507f | 2006-12-13 04:49:30 +0000 | [diff] [blame] | 58 | SMTP_SSL_PORT = 465 | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 59 | CRLF="\r\n" | 
 | 60 |  | 
| Piers Lauder | 385a77a | 2002-07-27 00:38:30 +0000 | [diff] [blame] | 61 | OLDSTYLE_AUTH = re.compile(r"auth=(.*)", re.I) | 
 | 62 |  | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 63 | # Exception classes used by this module. | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 64 | class SMTPException(Exception): | 
 | 65 |     """Base class for all exceptions raised by this module.""" | 
 | 66 |  | 
 | 67 | class SMTPServerDisconnected(SMTPException): | 
 | 68 |     """Not connected to any SMTP server. | 
 | 69 |  | 
 | 70 |     This exception is raised when the server unexpectedly disconnects, | 
 | 71 |     or when an attempt is made to use the SMTP instance before | 
 | 72 |     connecting it to a server. | 
 | 73 |     """ | 
 | 74 |  | 
 | 75 | class SMTPResponseException(SMTPException): | 
 | 76 |     """Base class for all exceptions that include an SMTP error code. | 
 | 77 |  | 
 | 78 |     These exceptions are generated in some instances when the SMTP | 
 | 79 |     server returns an error code.  The error code is stored in the | 
 | 80 |     `smtp_code' attribute of the error, and the `smtp_error' attribute | 
 | 81 |     is set to the error message. | 
 | 82 |     """ | 
 | 83 |  | 
 | 84 |     def __init__(self, code, msg): | 
 | 85 |         self.smtp_code = code | 
 | 86 |         self.smtp_error = msg | 
 | 87 |         self.args = (code, msg) | 
 | 88 |  | 
 | 89 | class SMTPSenderRefused(SMTPResponseException): | 
 | 90 |     """Sender address refused. | 
| Guido van Rossum | ae01046 | 2001-09-11 15:57:46 +0000 | [diff] [blame] | 91 |  | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 92 |     In addition to the attributes set by on all SMTPResponseException | 
| Barry Warsaw | d25c1b7 | 1999-11-28 17:11:06 +0000 | [diff] [blame] | 93 |     exceptions, this sets `sender' to the string that the SMTP refused. | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 94 |     """ | 
 | 95 |  | 
 | 96 |     def __init__(self, code, msg, sender): | 
 | 97 |         self.smtp_code = code | 
 | 98 |         self.smtp_error = msg | 
 | 99 |         self.sender = sender | 
 | 100 |         self.args = (code, msg, sender) | 
 | 101 |  | 
| Guido van Rossum | 20c9228 | 1999-04-21 16:52:20 +0000 | [diff] [blame] | 102 | class SMTPRecipientsRefused(SMTPException): | 
| Barry Warsaw | d25c1b7 | 1999-11-28 17:11:06 +0000 | [diff] [blame] | 103 |     """All recipient addresses refused. | 
| Guido van Rossum | ae01046 | 2001-09-11 15:57:46 +0000 | [diff] [blame] | 104 |  | 
| Thomas Wouters | 7e47402 | 2000-07-16 12:04:32 +0000 | [diff] [blame] | 105 |     The errors for each recipient are accessible through the attribute | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 106 |     'recipients', which is a dictionary of exactly the same sort as | 
 | 107 |     SMTP.sendmail() returns. | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 108 |     """ | 
 | 109 |  | 
 | 110 |     def __init__(self, recipients): | 
 | 111 |         self.recipients = recipients | 
 | 112 |         self.args = ( recipients,) | 
 | 113 |  | 
 | 114 |  | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 115 | class SMTPDataError(SMTPResponseException): | 
 | 116 |     """The SMTP server didn't accept the data.""" | 
 | 117 |  | 
 | 118 | class SMTPConnectError(SMTPResponseException): | 
| Barry Warsaw | d25c1b7 | 1999-11-28 17:11:06 +0000 | [diff] [blame] | 119 |     """Error during connection establishment.""" | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 120 |  | 
 | 121 | class SMTPHeloError(SMTPResponseException): | 
| Barry Warsaw | d25c1b7 | 1999-11-28 17:11:06 +0000 | [diff] [blame] | 122 |     """The server refused our HELO reply.""" | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 123 |  | 
| Guido van Rossum | ae01046 | 2001-09-11 15:57:46 +0000 | [diff] [blame] | 124 | class SMTPAuthenticationError(SMTPResponseException): | 
 | 125 |     """Authentication error. | 
 | 126 |  | 
 | 127 |     Most probably the server didn't accept the username/password | 
 | 128 |     combination provided. | 
 | 129 |     """ | 
| Peter Schneider-Kamp | 7bc82bb | 2000-08-10 14:02:23 +0000 | [diff] [blame] | 130 |  | 
| Guido van Rossum | 69a79bc | 1998-07-13 15:18:49 +0000 | [diff] [blame] | 131 | def quoteaddr(addr): | 
 | 132 |     """Quote a subset of the email addresses defined by RFC 821. | 
 | 133 |  | 
| Barry Warsaw | a7d9bdf | 1998-12-22 03:24:27 +0000 | [diff] [blame] | 134 |     Should be able to handle anything rfc822.parseaddr can handle. | 
 | 135 |     """ | 
| Raymond Hettinger | 342456d | 2002-09-05 01:14:07 +0000 | [diff] [blame] | 136 |     m = (None, None) | 
| Guido van Rossum | 69a79bc | 1998-07-13 15:18:49 +0000 | [diff] [blame] | 137 |     try: | 
| Thomas Wouters | b213704 | 2007-02-01 18:02:27 +0000 | [diff] [blame] | 138 |         m = email.utils.parseaddr(addr)[1] | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 139 |     except AttributeError: | 
 | 140 |         pass | 
| Raymond Hettinger | 342456d | 2002-09-05 01:14:07 +0000 | [diff] [blame] | 141 |     if m == (None, None): # Indicates parse failure or AttributeError | 
| Georg Brandl | bd3bc4d | 2006-02-17 09:52:53 +0000 | [diff] [blame] | 142 |         # something weird here.. punt -ddm | 
| Raymond Hettinger | 342456d | 2002-09-05 01:14:07 +0000 | [diff] [blame] | 143 |         return "<%s>" % addr | 
| Georg Brandl | bd3bc4d | 2006-02-17 09:52:53 +0000 | [diff] [blame] | 144 |     elif m is None: | 
 | 145 |         # the sender wants an empty return address | 
 | 146 |         return "<>" | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 147 |     else: | 
 | 148 |         return "<%s>" % m | 
| Guido van Rossum | 69a79bc | 1998-07-13 15:18:49 +0000 | [diff] [blame] | 149 |  | 
 | 150 | def quotedata(data): | 
 | 151 |     """Quote data for email. | 
 | 152 |  | 
| Barry Warsaw | d25c1b7 | 1999-11-28 17:11:06 +0000 | [diff] [blame] | 153 |     Double leading '.', and change Unix newline '\\n', or Mac '\\r' into | 
| Barry Warsaw | a7d9bdf | 1998-12-22 03:24:27 +0000 | [diff] [blame] | 154 |     Internet CRLF end-of-line. | 
 | 155 |     """ | 
| Guido van Rossum | 69a79bc | 1998-07-13 15:18:49 +0000 | [diff] [blame] | 156 |     return re.sub(r'(?m)^\.', '..', | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 157 |         re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data)) | 
| Guido van Rossum | 69a79bc | 1998-07-13 15:18:49 +0000 | [diff] [blame] | 158 |  | 
| Thomas Wouters | 47b49bf | 2007-08-30 22:15:33 +0000 | [diff] [blame] | 159 | try: | 
 | 160 |     import ssl | 
 | 161 | except ImportError: | 
 | 162 |     _have_ssl = False | 
 | 163 | else: | 
| Thomas Wouters | 47b49bf | 2007-08-30 22:15:33 +0000 | [diff] [blame] | 164 |     class SSLFakeFile: | 
 | 165 |         """A fake file like object that really wraps a SSLObject. | 
 | 166 |  | 
 | 167 |         It only supports what is needed in smtplib. | 
 | 168 |         """ | 
 | 169 |         def __init__(self, sslobj): | 
 | 170 |             self.sslobj = sslobj | 
 | 171 |  | 
 | 172 |         def readline(self): | 
 | 173 |             str = b"" | 
 | 174 |             chr = None | 
 | 175 |             while chr != b"\n": | 
 | 176 |                 chr = self.sslobj.read(1) | 
 | 177 |                 str += chr | 
 | 178 |             return str | 
 | 179 |  | 
 | 180 |         def close(self): | 
 | 181 |             pass | 
 | 182 |  | 
 | 183 |     _have_ssl = True | 
 | 184 |  | 
| Peter Schneider-Kamp | 7bc82bb | 2000-08-10 14:02:23 +0000 | [diff] [blame] | 185 |  | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 186 | class SMTP: | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 187 |     """This class manages a connection to an SMTP or ESMTP server. | 
 | 188 |     SMTP Objects: | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 189 |         SMTP objects have the following attributes: | 
 | 190 |             helo_resp | 
 | 191 |                 This is the message given by the server in response to the | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 192 |                 most recent HELO command. | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 193 |  | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 194 |             ehlo_resp | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 195 |                 This is the message given by the server in response to the | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 196 |                 most recent EHLO command. This is usually multiline. | 
 | 197 |  | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 198 |             does_esmtp | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 199 |                 This is a True value _after you do an EHLO command_, if the | 
 | 200 |                 server supports ESMTP. | 
 | 201 |  | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 202 |             esmtp_features | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 203 |                 This is a dictionary, which, if the server supports ESMTP, | 
| Barry Warsaw | d25c1b7 | 1999-11-28 17:11:06 +0000 | [diff] [blame] | 204 |                 will _after you do an EHLO command_, contain the names of the | 
 | 205 |                 SMTP service extensions this server supports, and their | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 206 |                 parameters (if any). | 
| Barry Warsaw | d25c1b7 | 1999-11-28 17:11:06 +0000 | [diff] [blame] | 207 |  | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 208 |                 Note, all extension names are mapped to lower case in the | 
 | 209 |                 dictionary. | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 210 |  | 
| Barry Warsaw | d25c1b7 | 1999-11-28 17:11:06 +0000 | [diff] [blame] | 211 |         See each method's docstrings for details.  In general, there is a | 
 | 212 |         method of the same name to perform each SMTP command.  There is also a | 
 | 213 |         method called 'sendmail' that will do an entire mail transaction. | 
 | 214 |         """ | 
| Guido van Rossum | 95e6f70 | 1998-06-25 02:15:50 +0000 | [diff] [blame] | 215 |     debuglevel = 0 | 
 | 216 |     file = None | 
 | 217 |     helo_resp = None | 
| Guido van Rossum | d8faa36 | 2007-04-27 19:54:29 +0000 | [diff] [blame] | 218 |     ehlo_msg = "ehlo" | 
| Guido van Rossum | 95e6f70 | 1998-06-25 02:15:50 +0000 | [diff] [blame] | 219 |     ehlo_resp = None | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 220 |     does_esmtp = 0 | 
| Guido van Rossum | 95e6f70 | 1998-06-25 02:15:50 +0000 | [diff] [blame] | 221 |  | 
| Guido van Rossum | d8faa36 | 2007-04-27 19:54:29 +0000 | [diff] [blame] | 222 |     def __init__(self, host='', port=0, local_hostname=None, timeout=None): | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 223 |         """Initialize a new instance. | 
 | 224 |  | 
| Barry Warsaw | a7d9bdf | 1998-12-22 03:24:27 +0000 | [diff] [blame] | 225 |         If specified, `host' is the name of the remote host to which to | 
 | 226 |         connect.  If specified, `port' specifies the port to which to connect. | 
| Barry Warsaw | d25c1b7 | 1999-11-28 17:11:06 +0000 | [diff] [blame] | 227 |         By default, smtplib.SMTP_PORT is used.  An SMTPConnectError is raised | 
| Neil Schemenauer | 6730f26 | 2002-03-24 15:30:40 +0000 | [diff] [blame] | 228 |         if the specified `host' doesn't respond correctly.  If specified, | 
| Tim Peters | 863ac44 | 2002-04-16 01:38:40 +0000 | [diff] [blame] | 229 |         `local_hostname` is used as the FQDN of the local host.  By default, | 
 | 230 |         the local hostname is found using socket.getfqdn(). | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 231 |  | 
 | 232 |         """ | 
| Guido van Rossum | d8faa36 | 2007-04-27 19:54:29 +0000 | [diff] [blame] | 233 |         self.timeout = timeout | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 234 |         self.esmtp_features = {} | 
| Thomas Wouters | 89f507f | 2006-12-13 04:49:30 +0000 | [diff] [blame] | 235 |         self.default_port = SMTP_PORT | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 236 |         if host: | 
 | 237 |             (code, msg) = self.connect(host, port) | 
 | 238 |             if code != 220: | 
 | 239 |                 raise SMTPConnectError(code, msg) | 
| Raymond Hettinger | f13eb55 | 2002-06-02 00:40:05 +0000 | [diff] [blame] | 240 |         if local_hostname is not None: | 
| Barry Warsaw | 13e34f7 | 2002-03-26 20:27:35 +0000 | [diff] [blame] | 241 |             self.local_hostname = local_hostname | 
| Neil Schemenauer | 6730f26 | 2002-03-24 15:30:40 +0000 | [diff] [blame] | 242 |         else: | 
| Barry Warsaw | 13e34f7 | 2002-03-26 20:27:35 +0000 | [diff] [blame] | 243 |             # RFC 2821 says we should use the fqdn in the EHLO/HELO verb, and | 
 | 244 |             # if that can't be calculated, that we should use a domain literal | 
 | 245 |             # instead (essentially an encoded IP address like [A.B.C.D]). | 
 | 246 |             fqdn = socket.getfqdn() | 
 | 247 |             if '.' in fqdn: | 
 | 248 |                 self.local_hostname = fqdn | 
 | 249 |             else: | 
 | 250 |                 # We can't find an fqdn hostname, so use a domain literal | 
| Thomas Wouters | 49fd7fa | 2006-04-21 10:40:58 +0000 | [diff] [blame] | 251 |                 addr = '127.0.0.1' | 
 | 252 |                 try: | 
 | 253 |                     addr = socket.gethostbyname(socket.gethostname()) | 
 | 254 |                 except socket.gaierror: | 
 | 255 |                     pass | 
| Barry Warsaw | 13e34f7 | 2002-03-26 20:27:35 +0000 | [diff] [blame] | 256 |                 self.local_hostname = '[%s]' % addr | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 257 |  | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 258 |     def set_debuglevel(self, debuglevel): | 
 | 259 |         """Set the debug output level. | 
 | 260 |  | 
| Barry Warsaw | a7d9bdf | 1998-12-22 03:24:27 +0000 | [diff] [blame] | 261 |         A non-false value results in debug messages for connection and for all | 
 | 262 |         messages sent to and received from the server. | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 263 |  | 
 | 264 |         """ | 
 | 265 |         self.debuglevel = debuglevel | 
 | 266 |  | 
| Guido van Rossum | d8faa36 | 2007-04-27 19:54:29 +0000 | [diff] [blame] | 267 |     def _get_socket(self, port, host, timeout): | 
| Thomas Wouters | 89f507f | 2006-12-13 04:49:30 +0000 | [diff] [blame] | 268 |         # This makes it simpler for SMTP_SSL to use the SMTP connect code | 
 | 269 |         # and just alter the socket connection bit. | 
| Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 270 |         if self.debuglevel > 0: print('connect:', (host, port), file=stderr) | 
| Guido van Rossum | d8faa36 | 2007-04-27 19:54:29 +0000 | [diff] [blame] | 271 |         return socket.create_connection((port, host), timeout) | 
| Thomas Wouters | 89f507f | 2006-12-13 04:49:30 +0000 | [diff] [blame] | 272 |  | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 273 |     def connect(self, host='localhost', port = 0): | 
 | 274 |         """Connect to a host on a given port. | 
| Guido van Rossum | 95e6f70 | 1998-06-25 02:15:50 +0000 | [diff] [blame] | 275 |  | 
| Barry Warsaw | 4c4bec8 | 1998-12-22 03:02:20 +0000 | [diff] [blame] | 276 |         If the hostname ends with a colon (`:') followed by a number, and | 
 | 277 |         there is no port specified, that suffix will be stripped off and the | 
 | 278 |         number interpreted as the port number to use. | 
| Guido van Rossum | 95e6f70 | 1998-06-25 02:15:50 +0000 | [diff] [blame] | 279 |  | 
| Barry Warsaw | a7d9bdf | 1998-12-22 03:24:27 +0000 | [diff] [blame] | 280 |         Note: This method is automatically invoked by __init__, if a host is | 
 | 281 |         specified during instantiation. | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 282 |  | 
 | 283 |         """ | 
| Martin v. Löwis | 4eb5940 | 2001-07-26 13:37:33 +0000 | [diff] [blame] | 284 |         if not port and (host.find(':') == host.rfind(':')): | 
| Martin v. Löwis | a43c2f8 | 2001-07-24 20:34:08 +0000 | [diff] [blame] | 285 |             i = host.rfind(':') | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 286 |             if i >= 0: | 
 | 287 |                 host, port = host[:i], host[i+1:] | 
| Eric S. Raymond | c013f30 | 2001-02-09 05:40:38 +0000 | [diff] [blame] | 288 |                 try: port = int(port) | 
| Eric S. Raymond | 8d87603 | 2001-02-09 10:14:53 +0000 | [diff] [blame] | 289 |                 except ValueError: | 
| Guido van Rossum | d8faa36 | 2007-04-27 19:54:29 +0000 | [diff] [blame] | 290 |                     raise socket.error("nonnumeric port") | 
| Thomas Wouters | 89f507f | 2006-12-13 04:49:30 +0000 | [diff] [blame] | 291 |         if not port: port = self.default_port | 
| Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 292 |         if self.debuglevel > 0: print('connect:', (host, port), file=stderr) | 
| Guido van Rossum | d8faa36 | 2007-04-27 19:54:29 +0000 | [diff] [blame] | 293 |         self.sock = self._get_socket(host, port, self.timeout) | 
| Martin v. Löwis | a43c2f8 | 2001-07-24 20:34:08 +0000 | [diff] [blame] | 294 |         (code, msg) = self.getreply() | 
| Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 295 |         if self.debuglevel > 0: print("connect:", msg, file=stderr) | 
| Martin v. Löwis | a43c2f8 | 2001-07-24 20:34:08 +0000 | [diff] [blame] | 296 |         return (code, msg) | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 297 |  | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 298 |     def send(self, str): | 
 | 299 |         """Send `str' to the server.""" | 
| Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 300 |         if self.debuglevel > 0: print('send:', repr(str), file=stderr) | 
| Guido van Rossum | 45e2fbc | 1998-03-26 21:13:24 +0000 | [diff] [blame] | 301 |         if self.sock: | 
| Guido van Rossum | 2880f6e | 1998-08-10 20:07:00 +0000 | [diff] [blame] | 302 |             try: | 
| Martin v. Löwis | e12454f | 2002-02-16 23:06:19 +0000 | [diff] [blame] | 303 |                 self.sock.sendall(str) | 
| Guido van Rossum | 2880f6e | 1998-08-10 20:07:00 +0000 | [diff] [blame] | 304 |             except socket.error: | 
| Barry Warsaw | 7675097 | 2001-12-14 20:34:20 +0000 | [diff] [blame] | 305 |                 self.close() | 
| Guido van Rossum | 40233ea | 1999-01-15 03:23:55 +0000 | [diff] [blame] | 306 |                 raise SMTPServerDisconnected('Server not connected') | 
| Guido van Rossum | fc40a83 | 1998-01-29 17:26:45 +0000 | [diff] [blame] | 307 |         else: | 
| Guido van Rossum | 40233ea | 1999-01-15 03:23:55 +0000 | [diff] [blame] | 308 |             raise SMTPServerDisconnected('please run connect() first') | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 309 |  | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 310 |     def putcmd(self, cmd, args=""): | 
| Barry Warsaw | a7d9bdf | 1998-12-22 03:24:27 +0000 | [diff] [blame] | 311 |         """Send a command to the server.""" | 
| Guido van Rossum | db23d3d | 1999-06-09 15:13:10 +0000 | [diff] [blame] | 312 |         if args == "": | 
 | 313 |             str = '%s%s' % (cmd, CRLF) | 
 | 314 |         else: | 
 | 315 |             str = '%s %s%s' % (cmd, args, CRLF) | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 316 |         self.send(str) | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 317 |  | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 318 |     def getreply(self): | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 319 |         """Get a reply from the server. | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 320 |  | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 321 |         Returns a tuple consisting of: | 
| Barry Warsaw | a7d9bdf | 1998-12-22 03:24:27 +0000 | [diff] [blame] | 322 |  | 
 | 323 |           - server response code (e.g. '250', or such, if all goes well) | 
 | 324 |             Note: returns -1 if it can't read response code. | 
 | 325 |  | 
 | 326 |           - server response string corresponding to response code (multiline | 
 | 327 |             responses are converted to a single, multiline string). | 
| Guido van Rossum | f123f84 | 1999-03-29 20:33:21 +0000 | [diff] [blame] | 328 |  | 
 | 329 |         Raises SMTPServerDisconnected if end-of-file is reached. | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 330 |         """ | 
 | 331 |         resp=[] | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 332 |         if self.file is None: | 
 | 333 |             self.file = self.sock.makefile('rb') | 
| Guido van Rossum | 45e2fbc | 1998-03-26 21:13:24 +0000 | [diff] [blame] | 334 |         while 1: | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 335 |             line = self.file.readline() | 
| Guido van Rossum | 806c246 | 2007-08-06 23:33:07 +0000 | [diff] [blame] | 336 |             if not line: | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 337 |                 self.close() | 
 | 338 |                 raise SMTPServerDisconnected("Connection unexpectedly closed") | 
| Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 339 |             if self.debuglevel > 0: print('reply:', repr(line), file=stderr) | 
| Guido van Rossum | 806c246 | 2007-08-06 23:33:07 +0000 | [diff] [blame] | 340 |             resp.append(line[4:].strip(b' \t\r\n')) | 
| Guido van Rossum | 45e2fbc | 1998-03-26 21:13:24 +0000 | [diff] [blame] | 341 |             code=line[:3] | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 342 |             # Check that the error code is syntactically correct. | 
 | 343 |             # Don't attempt to read a continuation line if it is broken. | 
 | 344 |             try: | 
| Eric S. Raymond | c013f30 | 2001-02-09 05:40:38 +0000 | [diff] [blame] | 345 |                 errcode = int(code) | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 346 |             except ValueError: | 
 | 347 |                 errcode = -1 | 
 | 348 |                 break | 
| Guido van Rossum | f123f84 | 1999-03-29 20:33:21 +0000 | [diff] [blame] | 349 |             # Check if multiline response. | 
| Guido van Rossum | 806c246 | 2007-08-06 23:33:07 +0000 | [diff] [blame] | 350 |             if line[3:4] != b"-": | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 351 |                 break | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 352 |  | 
| Guido van Rossum | c43e79f | 2007-06-18 18:26:36 +0000 | [diff] [blame] | 353 |         errmsg = b"\n".join(resp) | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 354 |         if self.debuglevel > 0: | 
| Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 355 |             print('reply: retcode (%s); Msg: %s' % (errcode,errmsg), file=stderr) | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 356 |         return errcode, errmsg | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 357 |  | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 358 |     def docmd(self, cmd, args=""): | 
| Barry Warsaw | a7d9bdf | 1998-12-22 03:24:27 +0000 | [diff] [blame] | 359 |         """Send a command, and return its response code.""" | 
| Guido van Rossum | 45e2fbc | 1998-03-26 21:13:24 +0000 | [diff] [blame] | 360 |         self.putcmd(cmd,args) | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 361 |         return self.getreply() | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 362 |  | 
| Barry Warsaw | a7d9bdf | 1998-12-22 03:24:27 +0000 | [diff] [blame] | 363 |     # std smtp commands | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 364 |     def helo(self, name=''): | 
| Barry Warsaw | a7d9bdf | 1998-12-22 03:24:27 +0000 | [diff] [blame] | 365 |         """SMTP 'helo' command. | 
 | 366 |         Hostname to send for this command defaults to the FQDN of the local | 
 | 367 |         host. | 
 | 368 |         """ | 
| Neil Schemenauer | 6730f26 | 2002-03-24 15:30:40 +0000 | [diff] [blame] | 369 |         self.putcmd("helo", name or self.local_hostname) | 
| Guido van Rossum | 45e2fbc | 1998-03-26 21:13:24 +0000 | [diff] [blame] | 370 |         (code,msg)=self.getreply() | 
 | 371 |         self.helo_resp=msg | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 372 |         return (code,msg) | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 373 |  | 
| Guido van Rossum | 95e6f70 | 1998-06-25 02:15:50 +0000 | [diff] [blame] | 374 |     def ehlo(self, name=''): | 
| Barry Warsaw | a7d9bdf | 1998-12-22 03:24:27 +0000 | [diff] [blame] | 375 |         """ SMTP 'ehlo' command. | 
 | 376 |         Hostname to send for this command defaults to the FQDN of the local | 
 | 377 |         host. | 
 | 378 |         """ | 
| Guido van Rossum | f7fcf5e | 2001-09-14 16:08:44 +0000 | [diff] [blame] | 379 |         self.esmtp_features = {} | 
| Guido van Rossum | d8faa36 | 2007-04-27 19:54:29 +0000 | [diff] [blame] | 380 |         self.putcmd(self.ehlo_msg, name or self.local_hostname) | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 381 |         (code,msg)=self.getreply() | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 382 |         # According to RFC1869 some (badly written) | 
 | 383 |         # MTA's will disconnect on an ehlo. Toss an exception if | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 384 |         # that happens -ddm | 
 | 385 |         if code == -1 and len(msg) == 0: | 
| Barry Warsaw | 7675097 | 2001-12-14 20:34:20 +0000 | [diff] [blame] | 386 |             self.close() | 
| Guido van Rossum | 40233ea | 1999-01-15 03:23:55 +0000 | [diff] [blame] | 387 |             raise SMTPServerDisconnected("Server not connected") | 
| Guido van Rossum | 95e6f70 | 1998-06-25 02:15:50 +0000 | [diff] [blame] | 388 |         self.ehlo_resp=msg | 
| Fred Drake | 8152d32 | 2000-12-12 23:20:45 +0000 | [diff] [blame] | 389 |         if code != 250: | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 390 |             return (code,msg) | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 391 |         self.does_esmtp=1 | 
| Thomas Wouters | 7e47402 | 2000-07-16 12:04:32 +0000 | [diff] [blame] | 392 |         #parse the ehlo response -ddm | 
| Guido van Rossum | 04110fb | 2007-08-24 16:32:05 +0000 | [diff] [blame] | 393 |         assert isinstance(self.ehlo_resp, bytes), repr(self.ehlo_resp) | 
 | 394 |         resp=self.ehlo_resp.decode("latin-1").split('\n') | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 395 |         del resp[0] | 
| Guido van Rossum | 2880f6e | 1998-08-10 20:07:00 +0000 | [diff] [blame] | 396 |         for each in resp: | 
| Piers Lauder | 385a77a | 2002-07-27 00:38:30 +0000 | [diff] [blame] | 397 |             # To be able to communicate with as many SMTP servers as possible, | 
 | 398 |             # we have to take the old-style auth advertisement into account, | 
 | 399 |             # because: | 
 | 400 |             # 1) Else our SMTP feature parser gets confused. | 
 | 401 |             # 2) There are some servers that only advertise the auth methods we | 
 | 402 |             #    support using the old style. | 
 | 403 |             auth_match = OLDSTYLE_AUTH.match(each) | 
 | 404 |             if auth_match: | 
 | 405 |                 # This doesn't remove duplicates, but that's no problem | 
 | 406 |                 self.esmtp_features["auth"] = self.esmtp_features.get("auth", "") \ | 
 | 407 |                         + " " + auth_match.groups(0)[0] | 
 | 408 |                 continue | 
 | 409 |  | 
| Barry Warsaw | be22ae6 | 2002-04-15 20:03:30 +0000 | [diff] [blame] | 410 |             # RFC 1869 requires a space between ehlo keyword and parameters. | 
 | 411 |             # It's actually stricter, in that only spaces are allowed between | 
 | 412 |             # parameters, but were not going to check for that here.  Note | 
 | 413 |             # that the space isn't present if there are no parameters. | 
 | 414 |             m=re.match(r'(?P<feature>[A-Za-z0-9][A-Za-z0-9\-]*) ?',each) | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 415 |             if m: | 
| Eric S. Raymond | c013f30 | 2001-02-09 05:40:38 +0000 | [diff] [blame] | 416 |                 feature=m.group("feature").lower() | 
 | 417 |                 params=m.string[m.end("feature"):].strip() | 
| Piers Lauder | 385a77a | 2002-07-27 00:38:30 +0000 | [diff] [blame] | 418 |                 if feature == "auth": | 
 | 419 |                     self.esmtp_features[feature] = self.esmtp_features.get(feature, "") \ | 
 | 420 |                             + " " + params | 
 | 421 |                 else: | 
 | 422 |                     self.esmtp_features[feature]=params | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 423 |         return (code,msg) | 
| Guido van Rossum | 95e6f70 | 1998-06-25 02:15:50 +0000 | [diff] [blame] | 424 |  | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 425 |     def has_extn(self, opt): | 
 | 426 |         """Does the server support a given SMTP service extension?""" | 
| Raymond Hettinger | 54f0222 | 2002-06-01 14:18:47 +0000 | [diff] [blame] | 427 |         return opt.lower() in self.esmtp_features | 
| Guido van Rossum | 95e6f70 | 1998-06-25 02:15:50 +0000 | [diff] [blame] | 428 |  | 
| Guido van Rossum | 18586f4 | 1998-04-03 17:03:13 +0000 | [diff] [blame] | 429 |     def help(self, args=''): | 
| Barry Warsaw | a7d9bdf | 1998-12-22 03:24:27 +0000 | [diff] [blame] | 430 |         """SMTP 'help' command. | 
 | 431 |         Returns help text from server.""" | 
| Guido van Rossum | 18586f4 | 1998-04-03 17:03:13 +0000 | [diff] [blame] | 432 |         self.putcmd("help", args) | 
| Kurt B. Kaiser | 58bd190 | 2005-06-26 18:27:36 +0000 | [diff] [blame] | 433 |         return self.getreply()[1] | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 434 |  | 
 | 435 |     def rset(self): | 
| Barry Warsaw | a7d9bdf | 1998-12-22 03:24:27 +0000 | [diff] [blame] | 436 |         """SMTP 'rset' command -- resets session.""" | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 437 |         return self.docmd("rset") | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 438 |  | 
 | 439 |     def noop(self): | 
| Barry Warsaw | a7d9bdf | 1998-12-22 03:24:27 +0000 | [diff] [blame] | 440 |         """SMTP 'noop' command -- doesn't do anything :>""" | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 441 |         return self.docmd("noop") | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 442 |  | 
| Guido van Rossum | 95e6f70 | 1998-06-25 02:15:50 +0000 | [diff] [blame] | 443 |     def mail(self,sender,options=[]): | 
| Barry Warsaw | a7d9bdf | 1998-12-22 03:24:27 +0000 | [diff] [blame] | 444 |         """SMTP 'mail' command -- begins mail xfer session.""" | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 445 |         optionlist = '' | 
 | 446 |         if options and self.does_esmtp: | 
| Eric S. Raymond | c013f30 | 2001-02-09 05:40:38 +0000 | [diff] [blame] | 447 |             optionlist = ' ' + ' '.join(options) | 
| Guido van Rossum | db23d3d | 1999-06-09 15:13:10 +0000 | [diff] [blame] | 448 |         self.putcmd("mail", "FROM:%s%s" % (quoteaddr(sender) ,optionlist)) | 
| Guido van Rossum | 45e2fbc | 1998-03-26 21:13:24 +0000 | [diff] [blame] | 449 |         return self.getreply() | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 450 |  | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 451 |     def rcpt(self,recip,options=[]): | 
| Barry Warsaw | a7d9bdf | 1998-12-22 03:24:27 +0000 | [diff] [blame] | 452 |         """SMTP 'rcpt' command -- indicates 1 recipient for this mail.""" | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 453 |         optionlist = '' | 
 | 454 |         if options and self.does_esmtp: | 
| Eric S. Raymond | c013f30 | 2001-02-09 05:40:38 +0000 | [diff] [blame] | 455 |             optionlist = ' ' + ' '.join(options) | 
| Guido van Rossum | 348fd06 | 1999-01-14 04:18:46 +0000 | [diff] [blame] | 456 |         self.putcmd("rcpt","TO:%s%s" % (quoteaddr(recip),optionlist)) | 
| Guido van Rossum | 45e2fbc | 1998-03-26 21:13:24 +0000 | [diff] [blame] | 457 |         return self.getreply() | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 458 |  | 
 | 459 |     def data(self,msg): | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 460 |         """SMTP 'DATA' command -- sends message data to server. | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 461 |  | 
| Barry Warsaw | 4c4bec8 | 1998-12-22 03:02:20 +0000 | [diff] [blame] | 462 |         Automatically quotes lines beginning with a period per rfc821. | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 463 |         Raises SMTPDataError if there is an unexpected reply to the | 
 | 464 |         DATA command; the return value from this method is the final | 
 | 465 |         response code received when the all data is sent. | 
| Barry Warsaw | 4c4bec8 | 1998-12-22 03:02:20 +0000 | [diff] [blame] | 466 |         """ | 
| Guido van Rossum | 45e2fbc | 1998-03-26 21:13:24 +0000 | [diff] [blame] | 467 |         self.putcmd("data") | 
 | 468 |         (code,repl)=self.getreply() | 
| Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 469 |         if self.debuglevel >0 : print("data:", (code,repl), file=stderr) | 
| Fred Drake | 8152d32 | 2000-12-12 23:20:45 +0000 | [diff] [blame] | 470 |         if code != 354: | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 471 |             raise SMTPDataError(code,repl) | 
| Guido van Rossum | 45e2fbc | 1998-03-26 21:13:24 +0000 | [diff] [blame] | 472 |         else: | 
| Guido van Rossum | 20c9228 | 1999-04-21 16:52:20 +0000 | [diff] [blame] | 473 |             q = quotedata(msg) | 
 | 474 |             if q[-2:] != CRLF: | 
 | 475 |                 q = q + CRLF | 
 | 476 |             q = q + "." + CRLF | 
 | 477 |             self.send(q) | 
| Guido van Rossum | 45e2fbc | 1998-03-26 21:13:24 +0000 | [diff] [blame] | 478 |             (code,msg)=self.getreply() | 
| Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 479 |             if self.debuglevel >0 : print("data:", (code,msg), file=stderr) | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 480 |             return (code,msg) | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 481 |  | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 482 |     def verify(self, address): | 
| Barry Warsaw | a7d9bdf | 1998-12-22 03:24:27 +0000 | [diff] [blame] | 483 |         """SMTP 'verify' command -- checks for address validity.""" | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 484 |         self.putcmd("vrfy", quoteaddr(address)) | 
 | 485 |         return self.getreply() | 
| Barry Warsaw | 4c4bec8 | 1998-12-22 03:02:20 +0000 | [diff] [blame] | 486 |     # a.k.a. | 
 | 487 |     vrfy=verify | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 488 |  | 
 | 489 |     def expn(self, address): | 
| Barry Warsaw | a7d9bdf | 1998-12-22 03:24:27 +0000 | [diff] [blame] | 490 |         """SMTP 'verify' command -- checks for address validity.""" | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 491 |         self.putcmd("expn", quoteaddr(address)) | 
 | 492 |         return self.getreply() | 
 | 493 |  | 
| Barry Warsaw | 4c4bec8 | 1998-12-22 03:02:20 +0000 | [diff] [blame] | 494 |     # some useful methods | 
| Guido van Rossum | ae01046 | 2001-09-11 15:57:46 +0000 | [diff] [blame] | 495 |  | 
 | 496 |     def login(self, user, password): | 
 | 497 |         """Log in on an SMTP server that requires authentication. | 
 | 498 |  | 
 | 499 |         The arguments are: | 
 | 500 |             - user:     The user name to authenticate with. | 
 | 501 |             - password: The password for the authentication. | 
 | 502 |  | 
 | 503 |         If there has been no previous EHLO or HELO command this session, this | 
 | 504 |         method tries ESMTP EHLO first. | 
 | 505 |  | 
 | 506 |         This method will return normally if the authentication was successful. | 
 | 507 |  | 
 | 508 |         This method may raise the following exceptions: | 
 | 509 |  | 
 | 510 |          SMTPHeloError            The server didn't reply properly to | 
 | 511 |                                   the helo greeting. | 
 | 512 |          SMTPAuthenticationError  The server didn't accept the username/ | 
 | 513 |                                   password combination. | 
| Fred Drake | 2f8f4d3 | 2001-10-13 18:35:32 +0000 | [diff] [blame] | 514 |          SMTPException            No suitable authentication method was | 
| Guido van Rossum | ae01046 | 2001-09-11 15:57:46 +0000 | [diff] [blame] | 515 |                                   found. | 
 | 516 |         """ | 
 | 517 |  | 
 | 518 |         def encode_cram_md5(challenge, user, password): | 
 | 519 |             challenge = base64.decodestring(challenge) | 
 | 520 |             response = user + " " + hmac.HMAC(password, challenge).hexdigest() | 
| Barry Warsaw | 2cc1f6d | 2007-08-30 14:28:55 +0000 | [diff] [blame] | 521 |             return encode_base64(response) | 
| Guido van Rossum | ae01046 | 2001-09-11 15:57:46 +0000 | [diff] [blame] | 522 |  | 
 | 523 |         def encode_plain(user, password): | 
| Barry Warsaw | 2cc1f6d | 2007-08-30 14:28:55 +0000 | [diff] [blame] | 524 |             return encode_base64("\0%s\0%s" % (user, password)) | 
| Tim Peters | 469cdad | 2002-08-08 20:19:19 +0000 | [diff] [blame] | 525 |  | 
| Guido van Rossum | ae01046 | 2001-09-11 15:57:46 +0000 | [diff] [blame] | 526 |  | 
 | 527 |         AUTH_PLAIN = "PLAIN" | 
 | 528 |         AUTH_CRAM_MD5 = "CRAM-MD5" | 
| Piers Lauder | 385a77a | 2002-07-27 00:38:30 +0000 | [diff] [blame] | 529 |         AUTH_LOGIN = "LOGIN" | 
| Guido van Rossum | ae01046 | 2001-09-11 15:57:46 +0000 | [diff] [blame] | 530 |  | 
 | 531 |         if self.helo_resp is None and self.ehlo_resp is None: | 
 | 532 |             if not (200 <= self.ehlo()[0] <= 299): | 
 | 533 |                 (code, resp) = self.helo() | 
 | 534 |                 if not (200 <= code <= 299): | 
 | 535 |                     raise SMTPHeloError(code, resp) | 
 | 536 |  | 
 | 537 |         if not self.has_extn("auth"): | 
 | 538 |             raise SMTPException("SMTP AUTH extension not supported by server.") | 
 | 539 |  | 
 | 540 |         # Authentication methods the server supports: | 
 | 541 |         authlist = self.esmtp_features["auth"].split() | 
 | 542 |  | 
 | 543 |         # List of authentication methods we support: from preferred to | 
 | 544 |         # less preferred methods. Except for the purpose of testing the weaker | 
 | 545 |         # ones, we prefer stronger methods like CRAM-MD5: | 
| Piers Lauder | 385a77a | 2002-07-27 00:38:30 +0000 | [diff] [blame] | 546 |         preferred_auths = [AUTH_CRAM_MD5, AUTH_PLAIN, AUTH_LOGIN] | 
| Guido van Rossum | ae01046 | 2001-09-11 15:57:46 +0000 | [diff] [blame] | 547 |  | 
 | 548 |         # Determine the authentication method we'll use | 
 | 549 |         authmethod = None | 
 | 550 |         for method in preferred_auths: | 
 | 551 |             if method in authlist: | 
 | 552 |                 authmethod = method | 
 | 553 |                 break | 
| Tim Peters | b64bec3 | 2001-09-18 02:26:39 +0000 | [diff] [blame] | 554 |  | 
| Guido van Rossum | ae01046 | 2001-09-11 15:57:46 +0000 | [diff] [blame] | 555 |         if authmethod == AUTH_CRAM_MD5: | 
 | 556 |             (code, resp) = self.docmd("AUTH", AUTH_CRAM_MD5) | 
 | 557 |             if code == 503: | 
 | 558 |                 # 503 == 'Error: already authenticated' | 
 | 559 |                 return (code, resp) | 
 | 560 |             (code, resp) = self.docmd(encode_cram_md5(resp, user, password)) | 
 | 561 |         elif authmethod == AUTH_PLAIN: | 
| Tim Peters | b64bec3 | 2001-09-18 02:26:39 +0000 | [diff] [blame] | 562 |             (code, resp) = self.docmd("AUTH", | 
| Guido van Rossum | ae01046 | 2001-09-11 15:57:46 +0000 | [diff] [blame] | 563 |                 AUTH_PLAIN + " " + encode_plain(user, password)) | 
| Piers Lauder | 385a77a | 2002-07-27 00:38:30 +0000 | [diff] [blame] | 564 |         elif authmethod == AUTH_LOGIN: | 
 | 565 |             (code, resp) = self.docmd("AUTH", | 
| Barry Warsaw | 2cc1f6d | 2007-08-30 14:28:55 +0000 | [diff] [blame] | 566 |                 "%s %s" % (AUTH_LOGIN, encode_base64(user))) | 
| Piers Lauder | 385a77a | 2002-07-27 00:38:30 +0000 | [diff] [blame] | 567 |             if code != 334: | 
 | 568 |                 raise SMTPAuthenticationError(code, resp) | 
| Barry Warsaw | 2cc1f6d | 2007-08-30 14:28:55 +0000 | [diff] [blame] | 569 |             (code, resp) = self.docmd(encode_base64(password)) | 
| Raymond Hettinger | 7fdfc2d | 2002-05-31 17:49:10 +0000 | [diff] [blame] | 570 |         elif authmethod is None: | 
| Fred Drake | 2f8f4d3 | 2001-10-13 18:35:32 +0000 | [diff] [blame] | 571 |             raise SMTPException("No suitable authentication method found.") | 
| Raymond Hettinger | dbecd93 | 2005-02-06 06:57:08 +0000 | [diff] [blame] | 572 |         if code not in (235, 503): | 
| Guido van Rossum | ae01046 | 2001-09-11 15:57:46 +0000 | [diff] [blame] | 573 |             # 235 == 'Authentication successful' | 
 | 574 |             # 503 == 'Error: already authenticated' | 
 | 575 |             raise SMTPAuthenticationError(code, resp) | 
 | 576 |         return (code, resp) | 
 | 577 |  | 
| Guido van Rossum | f7fcf5e | 2001-09-14 16:08:44 +0000 | [diff] [blame] | 578 |     def starttls(self, keyfile = None, certfile = None): | 
 | 579 |         """Puts the connection to the SMTP server into TLS mode. | 
| Tim Peters | b64bec3 | 2001-09-18 02:26:39 +0000 | [diff] [blame] | 580 |  | 
| Guido van Rossum | f7fcf5e | 2001-09-14 16:08:44 +0000 | [diff] [blame] | 581 |         If the server supports TLS, this will encrypt the rest of the SMTP | 
 | 582 |         session. If you provide the keyfile and certfile parameters, | 
 | 583 |         the identity of the SMTP server and client can be checked. This, | 
 | 584 |         however, depends on whether the socket module really checks the | 
 | 585 |         certificates. | 
 | 586 |         """ | 
| Tim Peters | b64bec3 | 2001-09-18 02:26:39 +0000 | [diff] [blame] | 587 |         (resp, reply) = self.docmd("STARTTLS") | 
| Guido van Rossum | f7fcf5e | 2001-09-14 16:08:44 +0000 | [diff] [blame] | 588 |         if resp == 220: | 
| Thomas Wouters | 47b49bf | 2007-08-30 22:15:33 +0000 | [diff] [blame] | 589 |             if not _have_ssl: | 
 | 590 |                 raise RuntimeError("No SSL support included in this Python") | 
| Thomas Wouters | 1b7f891 | 2007-09-19 03:06:30 +0000 | [diff] [blame] | 591 |             self.sock = ssl.wrap_socket(self.sock, keyfile, certfile) | 
| Thomas Wouters | 47b49bf | 2007-08-30 22:15:33 +0000 | [diff] [blame] | 592 |             self.file = SSLFakeFile(self.sock) | 
| Guido van Rossum | f7fcf5e | 2001-09-14 16:08:44 +0000 | [diff] [blame] | 593 |         return (resp, reply) | 
| Tim Peters | b64bec3 | 2001-09-18 02:26:39 +0000 | [diff] [blame] | 594 |  | 
| Jeremy Hylton | 31bb8ce | 1998-08-13 19:57:46 +0000 | [diff] [blame] | 595 |     def sendmail(self, from_addr, to_addrs, msg, mail_options=[], | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 596 |                  rcpt_options=[]): | 
 | 597 |         """This command performs an entire mail transaction. | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 598 |  | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 599 |         The arguments are: | 
| Barry Warsaw | 4c4bec8 | 1998-12-22 03:02:20 +0000 | [diff] [blame] | 600 |             - from_addr    : The address sending this mail. | 
 | 601 |             - to_addrs     : A list of addresses to send this mail to.  A bare | 
 | 602 |                              string will be treated as a list with 1 address. | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 603 |             - msg          : The message to send. | 
| Barry Warsaw | 4c4bec8 | 1998-12-22 03:02:20 +0000 | [diff] [blame] | 604 |             - mail_options : List of ESMTP options (such as 8bitmime) for the | 
 | 605 |                              mail command. | 
 | 606 |             - rcpt_options : List of ESMTP options (such as DSN commands) for | 
 | 607 |                              all the rcpt commands. | 
 | 608 |  | 
 | 609 |         If there has been no previous EHLO or HELO command this session, this | 
 | 610 |         method tries ESMTP EHLO first.  If the server does ESMTP, message size | 
 | 611 |         and each of the specified options will be passed to it.  If EHLO | 
 | 612 |         fails, HELO will be tried and ESMTP options suppressed. | 
 | 613 |  | 
 | 614 |         This method will return normally if the mail is accepted for at least | 
| Barry Warsaw | d25c1b7 | 1999-11-28 17:11:06 +0000 | [diff] [blame] | 615 |         one recipient.  It returns a dictionary, with one entry for each | 
 | 616 |         recipient that was refused.  Each entry contains a tuple of the SMTP | 
 | 617 |         error code and the accompanying error message sent by the server. | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 618 |  | 
 | 619 |         This method may raise the following exceptions: | 
 | 620 |  | 
 | 621 |          SMTPHeloError          The server didn't reply properly to | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 622 |                                 the helo greeting. | 
| Barry Warsaw | d25c1b7 | 1999-11-28 17:11:06 +0000 | [diff] [blame] | 623 |          SMTPRecipientsRefused  The server rejected ALL recipients | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 624 |                                 (no mail was sent). | 
 | 625 |          SMTPSenderRefused      The server didn't accept the from_addr. | 
 | 626 |          SMTPDataError          The server replied with an unexpected | 
 | 627 |                                 error code (other than a refusal of | 
 | 628 |                                 a recipient). | 
 | 629 |  | 
 | 630 |         Note: the connection will be open even after an exception is raised. | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 631 |  | 
| Guido van Rossum | 95e6f70 | 1998-06-25 02:15:50 +0000 | [diff] [blame] | 632 |         Example: | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 633 |  | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 634 |          >>> import smtplib | 
 | 635 |          >>> s=smtplib.SMTP("localhost") | 
| Guido van Rossum | fc40a83 | 1998-01-29 17:26:45 +0000 | [diff] [blame] | 636 |          >>> tolist=["one@one.org","two@two.org","three@three.org","four@four.org"] | 
| Martin v. Löwis | 301b1cd | 2002-07-28 16:52:01 +0000 | [diff] [blame] | 637 |          >>> msg = '''\\ | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 638 |          ... From: Me@my.org | 
 | 639 |          ... Subject: testin'... | 
 | 640 |          ... | 
 | 641 |          ... This is a test ''' | 
 | 642 |          >>> s.sendmail("me@my.org",tolist,msg) | 
 | 643 |          { "three@three.org" : ( 550 ,"User unknown" ) } | 
 | 644 |          >>> s.quit() | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 645 |  | 
| Barry Warsaw | 4c4bec8 | 1998-12-22 03:02:20 +0000 | [diff] [blame] | 646 |         In the above example, the message was accepted for delivery to three | 
 | 647 |         of the four addresses, and one was rejected, with the error code | 
| Barry Warsaw | d25c1b7 | 1999-11-28 17:11:06 +0000 | [diff] [blame] | 648 |         550.  If all addresses are accepted, then the method will return an | 
| Barry Warsaw | 4c4bec8 | 1998-12-22 03:02:20 +0000 | [diff] [blame] | 649 |         empty dictionary. | 
 | 650 |  | 
 | 651 |         """ | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 652 |         if self.helo_resp is None and self.ehlo_resp is None: | 
 | 653 |             if not (200 <= self.ehlo()[0] <= 299): | 
 | 654 |                 (code,resp) = self.helo() | 
 | 655 |                 if not (200 <= code <= 299): | 
 | 656 |                     raise SMTPHeloError(code, resp) | 
| Guido van Rossum | 95e6f70 | 1998-06-25 02:15:50 +0000 | [diff] [blame] | 657 |         esmtp_opts = [] | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 658 |         if self.does_esmtp: | 
 | 659 |             # Hmmm? what's this? -ddm | 
 | 660 |             # self.esmtp_features['7bit']="" | 
 | 661 |             if self.has_extn('size'): | 
| Walter Dörwald | 70a6b49 | 2004-02-12 17:35:32 +0000 | [diff] [blame] | 662 |                 esmtp_opts.append("size=%d" % len(msg)) | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 663 |             for option in mail_options: | 
| Guido van Rossum | 95e6f70 | 1998-06-25 02:15:50 +0000 | [diff] [blame] | 664 |                 esmtp_opts.append(option) | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 665 |  | 
| Guido van Rossum | 95e6f70 | 1998-06-25 02:15:50 +0000 | [diff] [blame] | 666 |         (code,resp) = self.mail(from_addr, esmtp_opts) | 
| Fred Drake | 8152d32 | 2000-12-12 23:20:45 +0000 | [diff] [blame] | 667 |         if code != 250: | 
| Guido van Rossum | 45e2fbc | 1998-03-26 21:13:24 +0000 | [diff] [blame] | 668 |             self.rset() | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 669 |             raise SMTPSenderRefused(code, resp, from_addr) | 
| Guido van Rossum | 45e2fbc | 1998-03-26 21:13:24 +0000 | [diff] [blame] | 670 |         senderrs={} | 
| Guido van Rossum | 3172c5d | 2007-10-16 18:12:55 +0000 | [diff] [blame] | 671 |         if isinstance(to_addrs, str): | 
| Jeremy Hylton | 31bb8ce | 1998-08-13 19:57:46 +0000 | [diff] [blame] | 672 |             to_addrs = [to_addrs] | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 673 |         for each in to_addrs: | 
| Guido van Rossum | fcfb632 | 1998-08-04 15:29:54 +0000 | [diff] [blame] | 674 |             (code,resp)=self.rcpt(each, rcpt_options) | 
| Fred Drake | 8152d32 | 2000-12-12 23:20:45 +0000 | [diff] [blame] | 675 |             if (code != 250) and (code != 251): | 
| Guido van Rossum | fc40a83 | 1998-01-29 17:26:45 +0000 | [diff] [blame] | 676 |                 senderrs[each]=(code,resp) | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 677 |         if len(senderrs)==len(to_addrs): | 
| Guido van Rossum | 95e6f70 | 1998-06-25 02:15:50 +0000 | [diff] [blame] | 678 |             # the server refused all our recipients | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 679 |             self.rset() | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 680 |             raise SMTPRecipientsRefused(senderrs) | 
| Fred Drake | 8152d32 | 2000-12-12 23:20:45 +0000 | [diff] [blame] | 681 |         (code,resp) = self.data(msg) | 
 | 682 |         if code != 250: | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 683 |             self.rset() | 
| Guido van Rossum | 296e143 | 1999-04-07 15:03:39 +0000 | [diff] [blame] | 684 |             raise SMTPDataError(code, resp) | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 685 |         #if we got here then somebody got our mail | 
| Tim Peters | 495ad3c | 2001-01-15 01:36:40 +0000 | [diff] [blame] | 686 |         return senderrs | 
| Guido van Rossum | bbe323e | 1998-01-29 17:24:40 +0000 | [diff] [blame] | 687 |  | 
 | 688 |  | 
 | 689 |     def close(self): | 
 | 690 |         """Close the connection to the SMTP server.""" | 
 | 691 |         if self.file: | 
 | 692 |             self.file.close() | 
 | 693 |         self.file = None | 
 | 694 |         if self.sock: | 
 | 695 |             self.sock.close() | 
 | 696 |         self.sock = None | 
 | 697 |  | 
 | 698 |  | 
 | 699 |     def quit(self): | 
| Guido van Rossum | 95e6f70 | 1998-06-25 02:15:50 +0000 | [diff] [blame] | 700 |         """Terminate the SMTP session.""" | 
| Guido van Rossum | 45e2fbc | 1998-03-26 21:13:24 +0000 | [diff] [blame] | 701 |         self.docmd("quit") | 
 | 702 |         self.close() | 
| Guido van Rossum | 95e6f70 | 1998-06-25 02:15:50 +0000 | [diff] [blame] | 703 |  | 
| Thomas Wouters | 47b49bf | 2007-08-30 22:15:33 +0000 | [diff] [blame] | 704 | if _have_ssl: | 
| Thomas Wouters | 89f507f | 2006-12-13 04:49:30 +0000 | [diff] [blame] | 705 |  | 
| Thomas Wouters | 47b49bf | 2007-08-30 22:15:33 +0000 | [diff] [blame] | 706 |     class SMTP_SSL(SMTP): | 
 | 707 |         """ This is a subclass derived from SMTP that connects over an SSL encrypted | 
 | 708 |         socket (to use this class you need a socket module that was compiled with SSL | 
 | 709 |         support). If host is not specified, '' (the local host) is used. If port is | 
 | 710 |         omitted, the standard SMTP-over-SSL port (465) is used. keyfile and certfile | 
 | 711 |         are also optional - they can contain a PEM formatted private key and | 
 | 712 |         certificate chain file for the SSL connection. | 
 | 713 |         """ | 
 | 714 |         def __init__(self, host='', port=0, local_hostname=None, | 
 | 715 |                      keyfile=None, certfile=None, timeout=None): | 
 | 716 |             self.keyfile = keyfile | 
 | 717 |             self.certfile = certfile | 
 | 718 |             SMTP.__init__(self, host, port, local_hostname, timeout) | 
 | 719 |             self.default_port = SMTP_SSL_PORT | 
 | 720 |  | 
 | 721 |         def _get_socket(self, host, port, timeout): | 
 | 722 |             if self.debuglevel > 0: print('connect:', (host, port), file=stderr) | 
 | 723 |             self.sock = socket.create_connection((host, port), timeout) | 
| Thomas Wouters | 1b7f891 | 2007-09-19 03:06:30 +0000 | [diff] [blame] | 724 |             self.sock = ssl.wrap_socket(self.sock, self.keyfile, self.certfile) | 
| Thomas Wouters | 89d996e | 2007-09-08 17:39:28 +0000 | [diff] [blame] | 725 |             self.file = SSLFakeFile(self.sock) | 
| Thomas Wouters | 47b49bf | 2007-08-30 22:15:33 +0000 | [diff] [blame] | 726 |  | 
 | 727 |     __all__.append("SMTP_SSL") | 
| Barry Warsaw | 4c4bec8 | 1998-12-22 03:02:20 +0000 | [diff] [blame] | 728 |  | 
| Guido van Rossum | d8faa36 | 2007-04-27 19:54:29 +0000 | [diff] [blame] | 729 | # | 
 | 730 | # LMTP extension | 
 | 731 | # | 
 | 732 | LMTP_PORT = 2003 | 
 | 733 |  | 
 | 734 | class LMTP(SMTP): | 
 | 735 |     """LMTP - Local Mail Transfer Protocol | 
 | 736 |  | 
 | 737 |     The LMTP protocol, which is very similar to ESMTP, is heavily based | 
 | 738 |     on the standard SMTP client. It's common to use Unix sockets for LMTP, | 
 | 739 |     so our connect() method must support that as well as a regular | 
 | 740 |     host:port server. To specify a Unix socket, you must use an absolute | 
 | 741 |     path as the host, starting with a '/'. | 
 | 742 |  | 
 | 743 |     Authentication is supported, using the regular SMTP mechanism. When | 
 | 744 |     using a Unix socket, LMTP generally don't support or require any | 
 | 745 |     authentication, but your mileage might vary.""" | 
 | 746 |  | 
 | 747 |     ehlo_msg = "lhlo" | 
 | 748 |  | 
 | 749 |     def __init__(self, host = '', port = LMTP_PORT, local_hostname = None): | 
 | 750 |         """Initialize a new instance.""" | 
 | 751 |         SMTP.__init__(self, host, port, local_hostname) | 
 | 752 |  | 
 | 753 |     def connect(self, host = 'localhost', port = 0): | 
 | 754 |         """Connect to the LMTP daemon, on either a Unix or a TCP socket.""" | 
 | 755 |         if host[0] != '/': | 
 | 756 |             return SMTP.connect(self, host, port) | 
 | 757 |  | 
 | 758 |         # Handle Unix-domain sockets. | 
 | 759 |         try: | 
 | 760 |             self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | 
 | 761 |             self.sock.connect(host) | 
 | 762 |         except socket.error as msg: | 
 | 763 |             if self.debuglevel > 0: print>>stderr, 'connect fail:', host | 
 | 764 |             if self.sock: | 
 | 765 |                 self.sock.close() | 
 | 766 |             self.sock = None | 
 | 767 |             raise socket.error(msg) | 
 | 768 |         (code, msg) = self.getreply() | 
 | 769 |         if self.debuglevel > 0: print>>stderr, "connect:", msg | 
 | 770 |         return (code, msg) | 
 | 771 |  | 
 | 772 |  | 
| Guido van Rossum | 95e6f70 | 1998-06-25 02:15:50 +0000 | [diff] [blame] | 773 | # Test the sendmail method, which tests most of the others. | 
 | 774 | # Note: This always sends to localhost. | 
 | 775 | if __name__ == '__main__': | 
| Andrew M. Kuchling | 6be424f | 2001-08-13 14:41:39 +0000 | [diff] [blame] | 776 |     import sys | 
| Guido van Rossum | 95e6f70 | 1998-06-25 02:15:50 +0000 | [diff] [blame] | 777 |  | 
 | 778 |     def prompt(prompt): | 
 | 779 |         sys.stdout.write(prompt + ": ") | 
| Eric S. Raymond | c013f30 | 2001-02-09 05:40:38 +0000 | [diff] [blame] | 780 |         return sys.stdin.readline().strip() | 
| Guido van Rossum | 95e6f70 | 1998-06-25 02:15:50 +0000 | [diff] [blame] | 781 |  | 
 | 782 |     fromaddr = prompt("From") | 
| Eric S. Raymond | 38151ed | 2001-02-09 07:40:17 +0000 | [diff] [blame] | 783 |     toaddrs  = prompt("To").split(',') | 
| Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 784 |     print("Enter message, end with ^D:") | 
| Guido van Rossum | 95e6f70 | 1998-06-25 02:15:50 +0000 | [diff] [blame] | 785 |     msg = '' | 
 | 786 |     while 1: | 
 | 787 |         line = sys.stdin.readline() | 
 | 788 |         if not line: | 
 | 789 |             break | 
 | 790 |         msg = msg + line | 
| Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 791 |     print("Message length is %d" % len(msg)) | 
| Guido van Rossum | 95e6f70 | 1998-06-25 02:15:50 +0000 | [diff] [blame] | 792 |  | 
 | 793 |     server = SMTP('localhost') | 
 | 794 |     server.set_debuglevel(1) | 
 | 795 |     server.sendmail(fromaddr, toaddrs, msg) | 
 | 796 |     server.quit() |