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