10a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#! /usr/bin/env python 20a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 30a8c90248264a8b26970b4473770bcc3df8515fJosh Gao'''SMTP/ESMTP client class. 40a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 50a8c90248264a8b26970b4473770bcc3df8515fJosh GaoThis should follow RFC 821 (SMTP), RFC 1869 (ESMTP), RFC 2554 (SMTP 60a8c90248264a8b26970b4473770bcc3df8515fJosh GaoAuthentication) and RFC 2487 (Secure SMTP over TLS). 70a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 80a8c90248264a8b26970b4473770bcc3df8515fJosh GaoNotes: 90a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 100a8c90248264a8b26970b4473770bcc3df8515fJosh GaoPlease remember, when doing ESMTP, that the names of the SMTP service 110a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoextensions are NOT the same thing as the option keywords for the RCPT 120a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoand MAIL commands! 130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 140a8c90248264a8b26970b4473770bcc3df8515fJosh GaoExample: 150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao >>> import smtplib 170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao >>> s=smtplib.SMTP("localhost") 180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao >>> print s.help() 190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao This is Sendmail version 8.8.4 200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Topics: 210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao HELO EHLO MAIL RCPT DATA 220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao RSET NOOP QUIT HELP VRFY 230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao EXPN VERB ETRN DSN 240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao For more info use "HELP <topic>". 250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao To report bugs in the implementation send email to 260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao sendmail-bugs@sendmail.org. 270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao For local information send email to Postmaster at your site. 280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao End of HELP info 290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao >>> s.putcmd("vrfy","someone@here") 300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao >>> s.getreply() 310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (250, "Somebody OverHere <somebody@here.my.org>") 320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao >>> s.quit() 330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao''' 340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# Author: The Dragon De Monsyne <dragondm@integral.org> 360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# ESMTP support, test code and doc fixes added by 370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# Eric S. Raymond <esr@thyrsus.com> 380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# Better RFC 821 compliance (MAIL and RCPT, and CRLF in data) 390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# by Carey Evans <c.evans@clear.net.nz>, for picky mail servers. 400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# RFC 2554 (authentication) support by Gerhard Haering <gerhard@bigfoot.de>. 410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# 420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# This was modified from the Python 1.5 library HTTP lib. 430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 440a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport socket 450a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport re 460a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport email.utils 470a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport base64 480a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport hmac 490a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom email.base64mime import encode as encode_base64 500a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom sys import stderr 510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao__all__ = ["SMTPException", "SMTPServerDisconnected", "SMTPResponseException", 530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao "SMTPSenderRefused", "SMTPRecipientsRefused", "SMTPDataError", 540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao "SMTPConnectError", "SMTPHeloError", "SMTPAuthenticationError", 550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao "quoteaddr", "quotedata", "SMTP"] 560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 570a8c90248264a8b26970b4473770bcc3df8515fJosh GaoSMTP_PORT = 25 580a8c90248264a8b26970b4473770bcc3df8515fJosh GaoSMTP_SSL_PORT = 465 590a8c90248264a8b26970b4473770bcc3df8515fJosh GaoCRLF = "\r\n" 600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 610a8c90248264a8b26970b4473770bcc3df8515fJosh GaoOLDSTYLE_AUTH = re.compile(r"auth=(.*)", re.I) 620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# Exception classes used by this module. 650a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass SMTPException(Exception): 660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Base class for all exceptions raised by this module.""" 670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 680a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass SMTPServerDisconnected(SMTPException): 690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Not connected to any SMTP server. 700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao This exception is raised when the server unexpectedly disconnects, 720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao or when an attempt is made to use the SMTP instance before 730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao connecting it to a server. 740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 760a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass SMTPResponseException(SMTPException): 770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Base class for all exceptions that include an SMTP error code. 780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao These exceptions are generated in some instances when the SMTP 800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao server returns an error code. The error code is stored in the 810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao `smtp_code' attribute of the error, and the `smtp_error' attribute 820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao is set to the error message. 830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __init__(self, code, msg): 860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.smtp_code = code 870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.smtp_error = msg 880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.args = (code, msg) 890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 900a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass SMTPSenderRefused(SMTPResponseException): 910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Sender address refused. 920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao In addition to the attributes set by on all SMTPResponseException 940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao exceptions, this sets `sender' to the string that the SMTP refused. 950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __init__(self, code, msg, sender): 980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.smtp_code = code 990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.smtp_error = msg 1000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.sender = sender 1010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.args = (code, msg, sender) 1020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1030a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass SMTPRecipientsRefused(SMTPException): 1040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """All recipient addresses refused. 1050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao The errors for each recipient are accessible through the attribute 1070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 'recipients', which is a dictionary of exactly the same sort as 1080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao SMTP.sendmail() returns. 1090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 1100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __init__(self, recipients): 1120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.recipients = recipients 1130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.args = (recipients,) 1140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1160a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass SMTPDataError(SMTPResponseException): 1170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """The SMTP server didn't accept the data.""" 1180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1190a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass SMTPConnectError(SMTPResponseException): 1200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Error during connection establishment.""" 1210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1220a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass SMTPHeloError(SMTPResponseException): 1230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """The server refused our HELO reply.""" 1240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1250a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass SMTPAuthenticationError(SMTPResponseException): 1260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Authentication error. 1270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Most probably the server didn't accept the username/password 1290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao combination provided. 1300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 1310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1330a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef quoteaddr(addr): 1340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Quote a subset of the email addresses defined by RFC 821. 1350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Should be able to handle anything rfc822.parseaddr can handle. 1370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 1380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao m = (None, None) 1390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 1400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao m = email.utils.parseaddr(addr)[1] 1410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao except AttributeError: 1420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao pass 1430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if m == (None, None): # Indicates parse failure or AttributeError 1440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # something weird here.. punt -ddm 1450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return "<%s>" % addr 1460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao elif m is None: 1470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # the sender wants an empty return address 1480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return "<>" 1490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 1500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return "<%s>" % m 1510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1520a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef _addr_only(addrstring): 1530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao displayname, addr = email.utils.parseaddr(addrstring) 1540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if (displayname, addr) == ('', ''): 1550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # parseaddr couldn't parse it, so use it as is. 1560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return addrstring 1570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return addr 1580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1590a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef quotedata(data): 1600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Quote data for email. 1610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Double leading '.', and change Unix newline '\\n', or Mac '\\r' into 1630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Internet CRLF end-of-line. 1640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 1650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return re.sub(r'(?m)^\.', '..', 1660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data)) 1670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1690a8c90248264a8b26970b4473770bcc3df8515fJosh Gaotry: 1700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao import ssl 1710a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoexcept ImportError: 1720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao _have_ssl = False 1730a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoelse: 1740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao class SSLFakeFile: 1750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """A fake file like object that really wraps a SSLObject. 1760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao It only supports what is needed in smtplib. 1780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 1790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __init__(self, sslobj): 1800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.sslobj = sslobj 1810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def readline(self): 1830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao str = "" 1840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao chr = None 1850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao while chr != "\n": 1860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao chr = self.sslobj.read(1) 1870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if not chr: 1880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao break 1890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao str += chr 1900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return str 1910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def close(self): 1930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao pass 1940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao _have_ssl = True 1960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1970a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass SMTP: 1980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """This class manages a connection to an SMTP or ESMTP server. 1990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao SMTP Objects: 2000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao SMTP objects have the following attributes: 2010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao helo_resp 2020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao This is the message given by the server in response to the 2030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao most recent HELO command. 2040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ehlo_resp 2060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao This is the message given by the server in response to the 2070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao most recent EHLO command. This is usually multiline. 2080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao does_esmtp 2100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao This is a True value _after you do an EHLO command_, if the 2110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao server supports ESMTP. 2120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao esmtp_features 2140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao This is a dictionary, which, if the server supports ESMTP, 2150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao will _after you do an EHLO command_, contain the names of the 2160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao SMTP service extensions this server supports, and their 2170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao parameters (if any). 2180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Note, all extension names are mapped to lower case in the 2200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao dictionary. 2210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao See each method's docstrings for details. In general, there is a 2230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao method of the same name to perform each SMTP command. There is also a 2240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao method called 'sendmail' that will do an entire mail transaction. 2250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 2260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao debuglevel = 0 2270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao file = None 2280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao helo_resp = None 2290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ehlo_msg = "ehlo" 2300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ehlo_resp = None 2310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao does_esmtp = 0 2320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao default_port = SMTP_PORT 2330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __init__(self, host='', port=0, local_hostname=None, 2350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao timeout=socket._GLOBAL_DEFAULT_TIMEOUT): 2360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Initialize a new instance. 2370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao If specified, `host' is the name of the remote host to which to 2390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao connect. If specified, `port' specifies the port to which to connect. 2400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao By default, smtplib.SMTP_PORT is used. If a host is specified the 2410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao connect method is called, and if it returns anything other than 2420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao a success code an SMTPConnectError is raised. If specified, 2430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao `local_hostname` is used as the FQDN of the local host. By default, 2440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao the local hostname is found using socket.getfqdn(). 2450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 2470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.timeout = timeout 2480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.esmtp_features = {} 2490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if host: 2500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (code, msg) = self.connect(host, port) 2510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if code != 220: 2520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise SMTPConnectError(code, msg) 2530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if local_hostname is not None: 2540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.local_hostname = local_hostname 2550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 2560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # RFC 2821 says we should use the fqdn in the EHLO/HELO verb, and 2570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # if that can't be calculated, that we should use a domain literal 2580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # instead (essentially an encoded IP address like [A.B.C.D]). 2590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao fqdn = socket.getfqdn() 2600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if '.' in fqdn: 2610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.local_hostname = fqdn 2620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 2630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # We can't find an fqdn hostname, so use a domain literal 2640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao addr = '127.0.0.1' 2650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 2660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao addr = socket.gethostbyname(socket.gethostname()) 2670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao except socket.gaierror: 2680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao pass 2690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.local_hostname = '[%s]' % addr 2700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def set_debuglevel(self, debuglevel): 2720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Set the debug output level. 2730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao A non-false value results in debug messages for connection and for all 2750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao messages sent to and received from the server. 2760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 2780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.debuglevel = debuglevel 2790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def _get_socket(self, host, port, timeout): 2810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # This makes it simpler for SMTP_SSL to use the SMTP connect code 2820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # and just alter the socket connection bit. 2830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.debuglevel > 0: 2840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print>>stderr, 'connect:', (host, port) 2850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return socket.create_connection((host, port), timeout) 2860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def connect(self, host='localhost', port=0): 2880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Connect to a host on a given port. 2890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao If the hostname ends with a colon (`:') followed by a number, and 2910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao there is no port specified, that suffix will be stripped off and the 2920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao number interpreted as the port number to use. 2930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Note: This method is automatically invoked by __init__, if a host is 2950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao specified during instantiation. 2960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 2980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if not port and (host.find(':') == host.rfind(':')): 2990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao i = host.rfind(':') 3000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if i >= 0: 3010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao host, port = host[:i], host[i + 1:] 3020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 3030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao port = int(port) 3040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao except ValueError: 3050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise socket.error, "nonnumeric port" 3060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if not port: 3070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao port = self.default_port 3080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.debuglevel > 0: 3090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print>>stderr, 'connect:', (host, port) 3100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.sock = self._get_socket(host, port, self.timeout) 3110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (code, msg) = self.getreply() 3120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.debuglevel > 0: 3130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print>>stderr, "connect:", msg 3140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return (code, msg) 3150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def send(self, str): 3170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Send `str' to the server.""" 3180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.debuglevel > 0: 3190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print>>stderr, 'send:', repr(str) 3200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if hasattr(self, 'sock') and self.sock: 3210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 3220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.sock.sendall(str) 3230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao except socket.error: 3240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.close() 3250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise SMTPServerDisconnected('Server not connected') 3260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 3270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise SMTPServerDisconnected('please run connect() first') 3280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def putcmd(self, cmd, args=""): 3300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Send a command to the server.""" 3310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if args == "": 3320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao str = '%s%s' % (cmd, CRLF) 3330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 3340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao str = '%s %s%s' % (cmd, args, CRLF) 3350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.send(str) 3360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def getreply(self): 3380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Get a reply from the server. 3390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Returns a tuple consisting of: 3410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao - server response code (e.g. '250', or such, if all goes well) 3430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Note: returns -1 if it can't read response code. 3440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao - server response string corresponding to response code (multiline 3460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao responses are converted to a single, multiline string). 3470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Raises SMTPServerDisconnected if end-of-file is reached. 3490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 3500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao resp = [] 3510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.file is None: 3520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.file = self.sock.makefile('rb') 3530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao while 1: 3540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 3550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao line = self.file.readline() 3560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao except socket.error as e: 3570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.close() 3580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise SMTPServerDisconnected("Connection unexpectedly closed: " 3590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao + str(e)) 3600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if line == '': 3610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.close() 3620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise SMTPServerDisconnected("Connection unexpectedly closed") 3630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.debuglevel > 0: 3640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print>>stderr, 'reply:', repr(line) 3650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao resp.append(line[4:].strip()) 3660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao code = line[:3] 3670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Check that the error code is syntactically correct. 3680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Don't attempt to read a continuation line if it is broken. 3690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 3700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao errcode = int(code) 3710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao except ValueError: 3720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao errcode = -1 3730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao break 3740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Check if multiline response. 3750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if line[3:4] != "-": 3760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao break 3770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao errmsg = "\n".join(resp) 3790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.debuglevel > 0: 3800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print>>stderr, 'reply: retcode (%s); Msg: %s' % (errcode, errmsg) 3810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return errcode, errmsg 3820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def docmd(self, cmd, args=""): 3840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Send a command, and return its response code.""" 3850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.putcmd(cmd, args) 3860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return self.getreply() 3870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # std smtp commands 3890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def helo(self, name=''): 3900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """SMTP 'helo' command. 3910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Hostname to send for this command defaults to the FQDN of the local 3920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao host. 3930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 3940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.putcmd("helo", name or self.local_hostname) 3950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (code, msg) = self.getreply() 3960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.helo_resp = msg 3970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return (code, msg) 3980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def ehlo(self, name=''): 4000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ SMTP 'ehlo' command. 4010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Hostname to send for this command defaults to the FQDN of the local 4020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao host. 4030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 4040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.esmtp_features = {} 4050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.putcmd(self.ehlo_msg, name or self.local_hostname) 4060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (code, msg) = self.getreply() 4070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # According to RFC1869 some (badly written) 4080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # MTA's will disconnect on an ehlo. Toss an exception if 4090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # that happens -ddm 4100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if code == -1 and len(msg) == 0: 4110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.close() 4120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise SMTPServerDisconnected("Server not connected") 4130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.ehlo_resp = msg 4140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if code != 250: 4150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return (code, msg) 4160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.does_esmtp = 1 4170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao #parse the ehlo response -ddm 4180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao resp = self.ehlo_resp.split('\n') 4190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao del resp[0] 4200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for each in resp: 4210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # To be able to communicate with as many SMTP servers as possible, 4220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # we have to take the old-style auth advertisement into account, 4230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # because: 4240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # 1) Else our SMTP feature parser gets confused. 4250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # 2) There are some servers that only advertise the auth methods we 4260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # support using the old style. 4270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao auth_match = OLDSTYLE_AUTH.match(each) 4280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if auth_match: 4290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # This doesn't remove duplicates, but that's no problem 4300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.esmtp_features["auth"] = self.esmtp_features.get("auth", "") \ 4310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao + " " + auth_match.groups(0)[0] 4320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao continue 4330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 4340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # RFC 1869 requires a space between ehlo keyword and parameters. 4350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # It's actually stricter, in that only spaces are allowed between 4360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # parameters, but were not going to check for that here. Note 4370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # that the space isn't present if there are no parameters. 4380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao m = re.match(r'(?P<feature>[A-Za-z0-9][A-Za-z0-9\-]*) ?', each) 4390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if m: 4400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao feature = m.group("feature").lower() 4410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao params = m.string[m.end("feature"):].strip() 4420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if feature == "auth": 4430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.esmtp_features[feature] = self.esmtp_features.get(feature, "") \ 4440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao + " " + params 4450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 4460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.esmtp_features[feature] = params 4470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return (code, msg) 4480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 4490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def has_extn(self, opt): 4500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Does the server support a given SMTP service extension?""" 4510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return opt.lower() in self.esmtp_features 4520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 4530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def help(self, args=''): 4540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """SMTP 'help' command. 4550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Returns help text from server.""" 4560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.putcmd("help", args) 4570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return self.getreply()[1] 4580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 4590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def rset(self): 4600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """SMTP 'rset' command -- resets session.""" 4610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return self.docmd("rset") 4620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 4630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def noop(self): 4640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """SMTP 'noop' command -- doesn't do anything :>""" 4650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return self.docmd("noop") 4660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 4670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def mail(self, sender, options=[]): 4680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """SMTP 'mail' command -- begins mail xfer session.""" 4690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao optionlist = '' 4700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if options and self.does_esmtp: 4710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao optionlist = ' ' + ' '.join(options) 4720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.putcmd("mail", "FROM:%s%s" % (quoteaddr(sender), optionlist)) 4730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return self.getreply() 4740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 4750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def rcpt(self, recip, options=[]): 4760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """SMTP 'rcpt' command -- indicates 1 recipient for this mail.""" 4770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao optionlist = '' 4780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if options and self.does_esmtp: 4790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao optionlist = ' ' + ' '.join(options) 4800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.putcmd("rcpt", "TO:%s%s" % (quoteaddr(recip), optionlist)) 4810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return self.getreply() 4820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 4830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def data(self, msg): 4840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """SMTP 'DATA' command -- sends message data to server. 4850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 4860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Automatically quotes lines beginning with a period per rfc821. 4870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Raises SMTPDataError if there is an unexpected reply to the 4880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao DATA command; the return value from this method is the final 4890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao response code received when the all data is sent. 4900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 4910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.putcmd("data") 4920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (code, repl) = self.getreply() 4930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.debuglevel > 0: 4940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print>>stderr, "data:", (code, repl) 4950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if code != 354: 4960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise SMTPDataError(code, repl) 4970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 4980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao q = quotedata(msg) 4990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if q[-2:] != CRLF: 5000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao q = q + CRLF 5010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao q = q + "." + CRLF 5020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.send(q) 5030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (code, msg) = self.getreply() 5040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.debuglevel > 0: 5050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print>>stderr, "data:", (code, msg) 5060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return (code, msg) 5070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def verify(self, address): 5090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """SMTP 'verify' command -- checks for address validity.""" 5100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.putcmd("vrfy", _addr_only(address)) 5110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return self.getreply() 5120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # a.k.a. 5130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao vrfy = verify 5140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def expn(self, address): 5160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """SMTP 'expn' command -- expands a mailing list.""" 5170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.putcmd("expn", _addr_only(address)) 5180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return self.getreply() 5190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # some useful methods 5210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def ehlo_or_helo_if_needed(self): 5230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Call self.ehlo() and/or self.helo() if needed. 5240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao If there has been no previous EHLO or HELO command this session, this 5260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao method tries ESMTP EHLO first. 5270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao This method may raise the following exceptions: 5290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao SMTPHeloError The server didn't reply properly to 5310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao the helo greeting. 5320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 5330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.helo_resp is None and self.ehlo_resp is None: 5340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if not (200 <= self.ehlo()[0] <= 299): 5350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (code, resp) = self.helo() 5360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if not (200 <= code <= 299): 5370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise SMTPHeloError(code, resp) 5380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def login(self, user, password): 5400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Log in on an SMTP server that requires authentication. 5410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao The arguments are: 5430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao - user: The user name to authenticate with. 5440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao - password: The password for the authentication. 5450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao If there has been no previous EHLO or HELO command this session, this 5470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao method tries ESMTP EHLO first. 5480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao This method will return normally if the authentication was successful. 5500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao This method may raise the following exceptions: 5520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao SMTPHeloError The server didn't reply properly to 5540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao the helo greeting. 5550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao SMTPAuthenticationError The server didn't accept the username/ 5560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao password combination. 5570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao SMTPException No suitable authentication method was 5580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao found. 5590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 5600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def encode_cram_md5(challenge, user, password): 5620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao challenge = base64.decodestring(challenge) 5630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao response = user + " " + hmac.HMAC(password, challenge).hexdigest() 5640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return encode_base64(response, eol="") 5650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def encode_plain(user, password): 5670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return encode_base64("\0%s\0%s" % (user, password), eol="") 5680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao AUTH_PLAIN = "PLAIN" 5710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao AUTH_CRAM_MD5 = "CRAM-MD5" 5720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao AUTH_LOGIN = "LOGIN" 5730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.ehlo_or_helo_if_needed() 5750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if not self.has_extn("auth"): 5770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise SMTPException("SMTP AUTH extension not supported by server.") 5780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Authentication methods the server supports: 5800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao authlist = self.esmtp_features["auth"].split() 5810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # List of authentication methods we support: from preferred to 5830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # less preferred methods. Except for the purpose of testing the weaker 5840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # ones, we prefer stronger methods like CRAM-MD5: 5850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao preferred_auths = [AUTH_CRAM_MD5, AUTH_PLAIN, AUTH_LOGIN] 5860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Determine the authentication method we'll use 5880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao authmethod = None 5890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for method in preferred_auths: 5900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if method in authlist: 5910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao authmethod = method 5920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao break 5930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 5940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if authmethod == AUTH_CRAM_MD5: 5950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (code, resp) = self.docmd("AUTH", AUTH_CRAM_MD5) 5960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if code == 503: 5970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # 503 == 'Error: already authenticated' 5980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return (code, resp) 5990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (code, resp) = self.docmd(encode_cram_md5(resp, user, password)) 6000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao elif authmethod == AUTH_PLAIN: 6010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (code, resp) = self.docmd("AUTH", 6020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao AUTH_PLAIN + " " + encode_plain(user, password)) 6030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao elif authmethod == AUTH_LOGIN: 6040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (code, resp) = self.docmd("AUTH", 6050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao "%s %s" % (AUTH_LOGIN, encode_base64(user, eol=""))) 6060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if code != 334: 6070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise SMTPAuthenticationError(code, resp) 6080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (code, resp) = self.docmd(encode_base64(password, eol="")) 6090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao elif authmethod is None: 6100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise SMTPException("No suitable authentication method found.") 6110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if code not in (235, 503): 6120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # 235 == 'Authentication successful' 6130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # 503 == 'Error: already authenticated' 6140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise SMTPAuthenticationError(code, resp) 6150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return (code, resp) 6160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 6170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def starttls(self, keyfile=None, certfile=None): 6180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Puts the connection to the SMTP server into TLS mode. 6190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 6200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao If there has been no previous EHLO or HELO command this session, this 6210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao method tries ESMTP EHLO first. 6220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 6230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao If the server supports TLS, this will encrypt the rest of the SMTP 6240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao session. If you provide the keyfile and certfile parameters, 6250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao the identity of the SMTP server and client can be checked. This, 6260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao however, depends on whether the socket module really checks the 6270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao certificates. 6280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 6290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao This method may raise the following exceptions: 6300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 6310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao SMTPHeloError The server didn't reply properly to 6320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao the helo greeting. 6330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 6340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.ehlo_or_helo_if_needed() 6350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if not self.has_extn("starttls"): 6360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise SMTPException("STARTTLS extension not supported by server.") 6370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (resp, reply) = self.docmd("STARTTLS") 6380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if resp == 220: 6390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if not _have_ssl: 6400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise RuntimeError("No SSL support included in this Python") 6410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.sock = ssl.wrap_socket(self.sock, keyfile, certfile) 6420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.file = SSLFakeFile(self.sock) 6430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # RFC 3207: 6440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # The client MUST discard any knowledge obtained from 6450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # the server, such as the list of SMTP service extensions, 6460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # which was not obtained from the TLS negotiation itself. 6470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.helo_resp = None 6480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.ehlo_resp = None 6490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.esmtp_features = {} 6500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.does_esmtp = 0 6510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return (resp, reply) 6520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 6530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def sendmail(self, from_addr, to_addrs, msg, mail_options=[], 6540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao rcpt_options=[]): 6550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """This command performs an entire mail transaction. 6560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 6570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao The arguments are: 6580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao - from_addr : The address sending this mail. 6590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao - to_addrs : A list of addresses to send this mail to. A bare 6600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao string will be treated as a list with 1 address. 6610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao - msg : The message to send. 6620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao - mail_options : List of ESMTP options (such as 8bitmime) for the 6630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao mail command. 6640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao - rcpt_options : List of ESMTP options (such as DSN commands) for 6650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao all the rcpt commands. 6660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 6670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao If there has been no previous EHLO or HELO command this session, this 6680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao method tries ESMTP EHLO first. If the server does ESMTP, message size 6690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao and each of the specified options will be passed to it. If EHLO 6700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao fails, HELO will be tried and ESMTP options suppressed. 6710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 6720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao This method will return normally if the mail is accepted for at least 6730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao one recipient. It returns a dictionary, with one entry for each 6740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao recipient that was refused. Each entry contains a tuple of the SMTP 6750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao error code and the accompanying error message sent by the server. 6760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 6770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao This method may raise the following exceptions: 6780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 6790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao SMTPHeloError The server didn't reply properly to 6800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao the helo greeting. 6810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao SMTPRecipientsRefused The server rejected ALL recipients 6820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (no mail was sent). 6830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao SMTPSenderRefused The server didn't accept the from_addr. 6840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao SMTPDataError The server replied with an unexpected 6850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao error code (other than a refusal of 6860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao a recipient). 6870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 6880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Note: the connection will be open even after an exception is raised. 6890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 6900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Example: 6910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 6920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao >>> import smtplib 6930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao >>> s=smtplib.SMTP("localhost") 6940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao >>> tolist=["one@one.org","two@two.org","three@three.org","four@four.org"] 6950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao >>> msg = '''\\ 6960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ... From: Me@my.org 6970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ... Subject: testin'... 6980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ... 6990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ... This is a test ''' 7000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao >>> s.sendmail("me@my.org",tolist,msg) 7010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao { "three@three.org" : ( 550 ,"User unknown" ) } 7020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao >>> s.quit() 7030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 7040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao In the above example, the message was accepted for delivery to three 7050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao of the four addresses, and one was rejected, with the error code 7060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 550. If all addresses are accepted, then the method will return an 7070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao empty dictionary. 7080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 7090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 7100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.ehlo_or_helo_if_needed() 7110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao esmtp_opts = [] 7120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.does_esmtp: 7130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Hmmm? what's this? -ddm 7140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # self.esmtp_features['7bit']="" 7150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.has_extn('size'): 7160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao esmtp_opts.append("size=%d" % len(msg)) 7170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for option in mail_options: 7180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao esmtp_opts.append(option) 7190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 7200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (code, resp) = self.mail(from_addr, esmtp_opts) 7210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if code != 250: 7220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.rset() 7230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise SMTPSenderRefused(code, resp, from_addr) 7240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao senderrs = {} 7250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if isinstance(to_addrs, basestring): 7260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao to_addrs = [to_addrs] 7270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for each in to_addrs: 7280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (code, resp) = self.rcpt(each, rcpt_options) 7290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if (code != 250) and (code != 251): 7300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao senderrs[each] = (code, resp) 7310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if len(senderrs) == len(to_addrs): 7320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # the server refused all our recipients 7330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.rset() 7340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise SMTPRecipientsRefused(senderrs) 7350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (code, resp) = self.data(msg) 7360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if code != 250: 7370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.rset() 7380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise SMTPDataError(code, resp) 7390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao #if we got here then somebody got our mail 7400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return senderrs 7410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 7420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 7430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def close(self): 7440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Close the connection to the SMTP server.""" 7450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.file: 7460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.file.close() 7470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.file = None 7480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.sock: 7490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.sock.close() 7500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.sock = None 7510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 7520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 7530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def quit(self): 7540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Terminate the SMTP session.""" 7550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao res = self.docmd("quit") 7560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.close() 7570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return res 7580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 7590a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoif _have_ssl: 7600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 7610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao class SMTP_SSL(SMTP): 7620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ This is a subclass derived from SMTP that connects over an SSL encrypted 7630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao socket (to use this class you need a socket module that was compiled with SSL 7640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao support). If host is not specified, '' (the local host) is used. If port is 7650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao omitted, the standard SMTP-over-SSL port (465) is used. keyfile and certfile 7660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao are also optional - they can contain a PEM formatted private key and 7670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao certificate chain file for the SSL connection. 7680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """ 7690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 7700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao default_port = SMTP_SSL_PORT 7710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 7720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __init__(self, host='', port=0, local_hostname=None, 7730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao keyfile=None, certfile=None, 7740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao timeout=socket._GLOBAL_DEFAULT_TIMEOUT): 7750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.keyfile = keyfile 7760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.certfile = certfile 7770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao SMTP.__init__(self, host, port, local_hostname, timeout) 7780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 7790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def _get_socket(self, host, port, timeout): 7800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.debuglevel > 0: 7810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print>>stderr, 'connect:', (host, port) 7820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao new_socket = socket.create_connection((host, port), timeout) 7830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao new_socket = ssl.wrap_socket(new_socket, self.keyfile, self.certfile) 7840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.file = SSLFakeFile(new_socket) 7850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return new_socket 7860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 7870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao __all__.append("SMTP_SSL") 7880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 7890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# 7900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# LMTP extension 7910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# 7920a8c90248264a8b26970b4473770bcc3df8515fJosh GaoLMTP_PORT = 2003 7930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 7940a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass LMTP(SMTP): 7950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """LMTP - Local Mail Transfer Protocol 7960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 7970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao The LMTP protocol, which is very similar to ESMTP, is heavily based 7980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao on the standard SMTP client. It's common to use Unix sockets for LMTP, 7990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao so our connect() method must support that as well as a regular 8000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao host:port server. To specify a Unix socket, you must use an absolute 8010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao path as the host, starting with a '/'. 8020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 8030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Authentication is supported, using the regular SMTP mechanism. When 8040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao using a Unix socket, LMTP generally don't support or require any 8050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao authentication, but your mileage might vary.""" 8060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 8070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ehlo_msg = "lhlo" 8080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 8090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __init__(self, host='', port=LMTP_PORT, local_hostname=None): 8100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Initialize a new instance.""" 8110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao SMTP.__init__(self, host, port, local_hostname) 8120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 8130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def connect(self, host='localhost', port=0): 8140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao """Connect to the LMTP daemon, on either a Unix or a TCP socket.""" 8150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if host[0] != '/': 8160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return SMTP.connect(self, host, port) 8170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 8180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Handle Unix-domain sockets. 8190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao try: 8200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 8210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.sock.connect(host) 8220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao except socket.error: 8230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.debuglevel > 0: 8240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print>>stderr, 'connect fail:', host 8250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.sock: 8260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.sock.close() 8270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.sock = None 8280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao raise 8290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao (code, msg) = self.getreply() 8300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.debuglevel > 0: 8310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print>>stderr, "connect:", msg 8320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return (code, msg) 8330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 8340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 8350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# Test the sendmail method, which tests most of the others. 8360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# Note: This always sends to localhost. 8370a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoif __name__ == '__main__': 8380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao import sys 8390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 8400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def prompt(prompt): 8410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao sys.stdout.write(prompt + ": ") 8420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return sys.stdin.readline().strip() 8430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 8440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao fromaddr = prompt("From") 8450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao toaddrs = prompt("To").split(',') 8460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print "Enter message, end with ^D:" 8470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao msg = '' 8480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao while 1: 8490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao line = sys.stdin.readline() 8500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if not line: 8510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao break 8520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao msg = msg + line 8530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print "Message length is %d" % len(msg) 8540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 8550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao server = SMTP('localhost') 8560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao server.set_debuglevel(1) 8570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao server.sendmail(fromaddr, toaddrs, msg) 8580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao server.quit() 859