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