blob: f898a2fe1e26d8efbdbd2f3664d314a0e6e2af71 [file] [log] [blame]
Barry Warsaw4c4bec81998-12-22 03:02:20 +00001#! /usr/bin/env python
2
Barry Warsawa1ae8842000-07-09 21:24:31 +00003'''SMTP/ESMTP client class.
Guido van Rossumbbe323e1998-01-29 17:24:40 +00004
Guido van Rossum95e6f701998-06-25 02:15:50 +00005This should follow RFC 821 (SMTP) and RFC 1869 (ESMTP).
Guido van Rossumbbe323e1998-01-29 17:24:40 +00006
Guido van Rossumfcfb6321998-08-04 15:29:54 +00007Notes:
8
9Please remember, when doing ESMTP, that the names of the SMTP service
Barry Warsaw4c4bec81998-12-22 03:02:20 +000010extensions are NOT the same thing as the option keywords for the RCPT
Guido van Rossumfcfb6321998-08-04 15:29:54 +000011and MAIL commands!
12
Guido van Rossumbbe323e1998-01-29 17:24:40 +000013Example:
14
Barry Warsawa7d9bdf1998-12-22 03:24:27 +000015 >>> import smtplib
16 >>> s=smtplib.SMTP("localhost")
17 >>> print s.help()
18 This is Sendmail version 8.8.4
19 Topics:
20 HELO EHLO MAIL RCPT DATA
21 RSET NOOP QUIT HELP VRFY
22 EXPN VERB ETRN DSN
23 For more info use "HELP <topic>".
24 To report bugs in the implementation send email to
25 sendmail-bugs@sendmail.org.
26 For local information send email to Postmaster at your site.
27 End of HELP info
28 >>> s.putcmd("vrfy","someone@here")
29 >>> s.getreply()
30 (250, "Somebody OverHere <somebody@here.my.org>")
31 >>> s.quit()
Barry Warsawa1ae8842000-07-09 21:24:31 +000032'''
Guido van Rossumbbe323e1998-01-29 17:24:40 +000033
Guido van Rossum98d9fd32000-02-28 15:12:25 +000034# Author: The Dragon De Monsyne <dragondm@integral.org>
35# ESMTP support, test code and doc fixes added by
36# Eric S. Raymond <esr@thyrsus.com>
37# Better RFC 821 compliance (MAIL and RCPT, and CRLF in data)
38# by Carey Evans <c.evans@clear.net.nz>, for picky mail servers.
39#
40# This was modified from the Python 1.5 library HTTP lib.
41
Guido van Rossumbbe323e1998-01-29 17:24:40 +000042import socket
Barry Warsaw07201771998-12-22 20:37:36 +000043import string
44import re
Guido van Rossumfcfb6321998-08-04 15:29:54 +000045import rfc822
Jeremy Hylton31bb8ce1998-08-13 19:57:46 +000046import types
Guido van Rossumbbe323e1998-01-29 17:24:40 +000047
48SMTP_PORT = 25
49CRLF="\r\n"
50
Guido van Rossum296e1431999-04-07 15:03:39 +000051# Exception classes used by this module.
52class SMTPException(Exception):
53 """Base class for all exceptions raised by this module."""
54
55class SMTPServerDisconnected(SMTPException):
56 """Not connected to any SMTP server.
57
58 This exception is raised when the server unexpectedly disconnects,
59 or when an attempt is made to use the SMTP instance before
60 connecting it to a server.
61 """
62
63class SMTPResponseException(SMTPException):
64 """Base class for all exceptions that include an SMTP error code.
65
66 These exceptions are generated in some instances when the SMTP
67 server returns an error code. The error code is stored in the
68 `smtp_code' attribute of the error, and the `smtp_error' attribute
69 is set to the error message.
70 """
71
72 def __init__(self, code, msg):
73 self.smtp_code = code
74 self.smtp_error = msg
75 self.args = (code, msg)
76
77class SMTPSenderRefused(SMTPResponseException):
78 """Sender address refused.
79 In addition to the attributes set by on all SMTPResponseException
Barry Warsawd25c1b71999-11-28 17:11:06 +000080 exceptions, this sets `sender' to the string that the SMTP refused.
Guido van Rossum296e1431999-04-07 15:03:39 +000081 """
82
83 def __init__(self, code, msg, sender):
84 self.smtp_code = code
85 self.smtp_error = msg
86 self.sender = sender
87 self.args = (code, msg, sender)
88
Guido van Rossum20c92281999-04-21 16:52:20 +000089class SMTPRecipientsRefused(SMTPException):
Barry Warsawd25c1b71999-11-28 17:11:06 +000090 """All recipient addresses refused.
Thomas Wouters7e474022000-07-16 12:04:32 +000091 The errors for each recipient are accessible through the attribute
Guido van Rossum296e1431999-04-07 15:03:39 +000092 'recipients', which is a dictionary of exactly the same sort as
93 SMTP.sendmail() returns.
94 """
95
96 def __init__(self, recipients):
97 self.recipients = recipients
98 self.args = ( recipients,)
99
100
Guido van Rossum296e1431999-04-07 15:03:39 +0000101class SMTPDataError(SMTPResponseException):
102 """The SMTP server didn't accept the data."""
103
104class SMTPConnectError(SMTPResponseException):
Barry Warsawd25c1b71999-11-28 17:11:06 +0000105 """Error during connection establishment."""
Guido van Rossum296e1431999-04-07 15:03:39 +0000106
107class SMTPHeloError(SMTPResponseException):
Barry Warsawd25c1b71999-11-28 17:11:06 +0000108 """The server refused our HELO reply."""
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000109
Peter Schneider-Kamp7bc82bb2000-08-10 14:02:23 +0000110
Guido van Rossum69a79bc1998-07-13 15:18:49 +0000111def quoteaddr(addr):
112 """Quote a subset of the email addresses defined by RFC 821.
113
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000114 Should be able to handle anything rfc822.parseaddr can handle.
115 """
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000116 m=None
Guido van Rossum69a79bc1998-07-13 15:18:49 +0000117 try:
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000118 m=rfc822.parseaddr(addr)[1]
119 except AttributeError:
120 pass
121 if not m:
122 #something weird here.. punt -ddm
123 return addr
124 else:
125 return "<%s>" % m
Guido van Rossum69a79bc1998-07-13 15:18:49 +0000126
127def quotedata(data):
128 """Quote data for email.
129
Barry Warsawd25c1b71999-11-28 17:11:06 +0000130 Double leading '.', and change Unix newline '\\n', or Mac '\\r' into
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000131 Internet CRLF end-of-line.
132 """
Guido van Rossum69a79bc1998-07-13 15:18:49 +0000133 return re.sub(r'(?m)^\.', '..',
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000134 re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data))
Guido van Rossum69a79bc1998-07-13 15:18:49 +0000135
Peter Schneider-Kamp7bc82bb2000-08-10 14:02:23 +0000136def _get_fqdn_hostname(name):
137 name = string.strip(name)
138 if len(name) == 0:
139 name = socket.gethostname()
140 try:
141 hostname, aliases, ipaddrs = socket.gethostbyaddr(name)
142 except socket.error:
143 pass
144 else:
145 aliases.insert(0, hostname)
146 for name in aliases:
147 if '.' in name:
148 break
149 else:
150 name = hostname
151 return name
152
153
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000154class SMTP:
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000155 """This class manages a connection to an SMTP or ESMTP server.
156 SMTP Objects:
157 SMTP objects have the following attributes:
158 helo_resp
Barry Warsawd25c1b71999-11-28 17:11:06 +0000159 This is the message given by the server in response to the
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000160 most recent HELO command.
161
162 ehlo_resp
Barry Warsawd25c1b71999-11-28 17:11:06 +0000163 This is the message given by the server in response to the
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000164 most recent EHLO command. This is usually multiline.
165
166 does_esmtp
167 This is a True value _after you do an EHLO command_, if the
168 server supports ESMTP.
169
170 esmtp_features
171 This is a dictionary, which, if the server supports ESMTP,
Barry Warsawd25c1b71999-11-28 17:11:06 +0000172 will _after you do an EHLO command_, contain the names of the
173 SMTP service extensions this server supports, and their
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000174 parameters (if any).
Barry Warsawd25c1b71999-11-28 17:11:06 +0000175
176 Note, all extension names are mapped to lower case in the
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000177 dictionary.
178
Barry Warsawd25c1b71999-11-28 17:11:06 +0000179 See each method's docstrings for details. In general, there is a
180 method of the same name to perform each SMTP command. There is also a
181 method called 'sendmail' that will do an entire mail transaction.
182 """
Guido van Rossum95e6f701998-06-25 02:15:50 +0000183 debuglevel = 0
184 file = None
185 helo_resp = None
186 ehlo_resp = None
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000187 does_esmtp = 0
Guido van Rossum95e6f701998-06-25 02:15:50 +0000188
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000189 def __init__(self, host = '', port = 0):
190 """Initialize a new instance.
191
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000192 If specified, `host' is the name of the remote host to which to
193 connect. If specified, `port' specifies the port to which to connect.
Barry Warsawd25c1b71999-11-28 17:11:06 +0000194 By default, smtplib.SMTP_PORT is used. An SMTPConnectError is raised
195 if the specified `host' doesn't respond correctly.
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000196
197 """
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000198 self.esmtp_features = {}
Guido van Rossum296e1431999-04-07 15:03:39 +0000199 if host:
200 (code, msg) = self.connect(host, port)
201 if code != 220:
202 raise SMTPConnectError(code, msg)
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000203
204 def set_debuglevel(self, debuglevel):
205 """Set the debug output level.
206
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000207 A non-false value results in debug messages for connection and for all
208 messages sent to and received from the server.
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000209
210 """
211 self.debuglevel = debuglevel
212
213 def connect(self, host='localhost', port = 0):
214 """Connect to a host on a given port.
Guido van Rossum95e6f701998-06-25 02:15:50 +0000215
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000216 If the hostname ends with a colon (`:') followed by a number, and
217 there is no port specified, that suffix will be stripped off and the
218 number interpreted as the port number to use.
Guido van Rossum95e6f701998-06-25 02:15:50 +0000219
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000220 Note: This method is automatically invoked by __init__, if a host is
221 specified during instantiation.
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000222
223 """
224 if not port:
225 i = string.find(host, ':')
226 if i >= 0:
227 host, port = host[:i], host[i+1:]
228 try: port = string.atoi(port)
229 except string.atoi_error:
230 raise socket.error, "nonnumeric port"
231 if not port: port = SMTP_PORT
232 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
233 if self.debuglevel > 0: print 'connect:', (host, port)
Guido van Rossum93a7c0f2000-03-28 21:45:46 +0000234 self.sock.connect((host, port))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000235 (code,msg)=self.getreply()
236 if self.debuglevel >0 : print "connect:", msg
Guido van Rossum296e1431999-04-07 15:03:39 +0000237 return (code,msg)
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000238
239 def send(self, str):
240 """Send `str' to the server."""
241 if self.debuglevel > 0: print 'send:', `str`
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000242 if self.sock:
Guido van Rossum2880f6e1998-08-10 20:07:00 +0000243 try:
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000244 self.sock.send(str)
Guido van Rossum2880f6e1998-08-10 20:07:00 +0000245 except socket.error:
Guido van Rossum40233ea1999-01-15 03:23:55 +0000246 raise SMTPServerDisconnected('Server not connected')
Guido van Rossumfc40a831998-01-29 17:26:45 +0000247 else:
Guido van Rossum40233ea1999-01-15 03:23:55 +0000248 raise SMTPServerDisconnected('please run connect() first')
Guido van Rossumfc40a831998-01-29 17:26:45 +0000249
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000250 def putcmd(self, cmd, args=""):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000251 """Send a command to the server."""
Guido van Rossumdb23d3d1999-06-09 15:13:10 +0000252 if args == "":
253 str = '%s%s' % (cmd, CRLF)
254 else:
255 str = '%s %s%s' % (cmd, args, CRLF)
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000256 self.send(str)
257
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000258 def getreply(self):
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000259 """Get a reply from the server.
260
261 Returns a tuple consisting of:
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000262
263 - server response code (e.g. '250', or such, if all goes well)
264 Note: returns -1 if it can't read response code.
265
266 - server response string corresponding to response code (multiline
267 responses are converted to a single, multiline string).
Guido van Rossumf123f841999-03-29 20:33:21 +0000268
269 Raises SMTPServerDisconnected if end-of-file is reached.
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000270 """
271 resp=[]
Guido van Rossum296e1431999-04-07 15:03:39 +0000272 if self.file is None:
273 self.file = self.sock.makefile('rb')
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000274 while 1:
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000275 line = self.file.readline()
Guido van Rossum296e1431999-04-07 15:03:39 +0000276 if line == '':
277 self.close()
278 raise SMTPServerDisconnected("Connection unexpectedly closed")
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000279 if self.debuglevel > 0: print 'reply:', `line`
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000280 resp.append(string.strip(line[4:]))
281 code=line[:3]
Guido van Rossum296e1431999-04-07 15:03:39 +0000282 # Check that the error code is syntactically correct.
283 # Don't attempt to read a continuation line if it is broken.
284 try:
285 errcode = string.atoi(code)
286 except ValueError:
287 errcode = -1
288 break
Guido van Rossumf123f841999-03-29 20:33:21 +0000289 # Check if multiline response.
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000290 if line[3:4]!="-":
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000291 break
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000292
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000293 errmsg = string.join(resp,"\n")
294 if self.debuglevel > 0:
Guido van Rossumfc40a831998-01-29 17:26:45 +0000295 print 'reply: retcode (%s); Msg: %s' % (errcode,errmsg)
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000296 return errcode, errmsg
297
298 def docmd(self, cmd, args=""):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000299 """Send a command, and return its response code."""
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000300 self.putcmd(cmd,args)
Guido van Rossum296e1431999-04-07 15:03:39 +0000301 return self.getreply()
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000302
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000303 # std smtp commands
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000304 def helo(self, name=''):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000305 """SMTP 'helo' command.
306 Hostname to send for this command defaults to the FQDN of the local
307 host.
308 """
Peter Schneider-Kamp7bc82bb2000-08-10 14:02:23 +0000309 self.putcmd("helo", _get_fqdn_hostname(name))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000310 (code,msg)=self.getreply()
311 self.helo_resp=msg
Guido van Rossum296e1431999-04-07 15:03:39 +0000312 return (code,msg)
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000313
Guido van Rossum95e6f701998-06-25 02:15:50 +0000314 def ehlo(self, name=''):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000315 """ SMTP 'ehlo' command.
316 Hostname to send for this command defaults to the FQDN of the local
317 host.
318 """
Peter Schneider-Kamp7bc82bb2000-08-10 14:02:23 +0000319 self.putcmd("ehlo", _get_fqdn_hostname(name))
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000320 (code,msg)=self.getreply()
321 # According to RFC1869 some (badly written)
322 # MTA's will disconnect on an ehlo. Toss an exception if
323 # that happens -ddm
324 if code == -1 and len(msg) == 0:
Guido van Rossum40233ea1999-01-15 03:23:55 +0000325 raise SMTPServerDisconnected("Server not connected")
Guido van Rossum95e6f701998-06-25 02:15:50 +0000326 self.ehlo_resp=msg
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000327 if code<>250:
Guido van Rossum296e1431999-04-07 15:03:39 +0000328 return (code,msg)
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000329 self.does_esmtp=1
Thomas Wouters7e474022000-07-16 12:04:32 +0000330 #parse the ehlo response -ddm
Guido van Rossum2880f6e1998-08-10 20:07:00 +0000331 resp=string.split(self.ehlo_resp,'\n')
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000332 del resp[0]
Guido van Rossum2880f6e1998-08-10 20:07:00 +0000333 for each in resp:
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000334 m=re.match(r'(?P<feature>[A-Za-z0-9][A-Za-z0-9\-]*)',each)
335 if m:
336 feature=string.lower(m.group("feature"))
337 params=string.strip(m.string[m.end("feature"):])
338 self.esmtp_features[feature]=params
Guido van Rossum296e1431999-04-07 15:03:39 +0000339 return (code,msg)
Guido van Rossum95e6f701998-06-25 02:15:50 +0000340
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000341 def has_extn(self, opt):
342 """Does the server support a given SMTP service extension?"""
343 return self.esmtp_features.has_key(string.lower(opt))
Guido van Rossum95e6f701998-06-25 02:15:50 +0000344
Guido van Rossum18586f41998-04-03 17:03:13 +0000345 def help(self, args=''):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000346 """SMTP 'help' command.
347 Returns help text from server."""
Guido van Rossum18586f41998-04-03 17:03:13 +0000348 self.putcmd("help", args)
Guido van Rossum296e1431999-04-07 15:03:39 +0000349 return self.getreply()
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000350
351 def rset(self):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000352 """SMTP 'rset' command -- resets session."""
Guido van Rossum296e1431999-04-07 15:03:39 +0000353 return self.docmd("rset")
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000354
355 def noop(self):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000356 """SMTP 'noop' command -- doesn't do anything :>"""
Guido van Rossum296e1431999-04-07 15:03:39 +0000357 return self.docmd("noop")
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000358
Guido van Rossum95e6f701998-06-25 02:15:50 +0000359 def mail(self,sender,options=[]):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000360 """SMTP 'mail' command -- begins mail xfer session."""
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000361 optionlist = ''
362 if options and self.does_esmtp:
Guido van Rossumdb23d3d1999-06-09 15:13:10 +0000363 optionlist = ' ' + string.join(options, ' ')
364 self.putcmd("mail", "FROM:%s%s" % (quoteaddr(sender) ,optionlist))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000365 return self.getreply()
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000366
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000367 def rcpt(self,recip,options=[]):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000368 """SMTP 'rcpt' command -- indicates 1 recipient for this mail."""
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000369 optionlist = ''
370 if options and self.does_esmtp:
Guido van Rossum348fd061999-01-14 04:18:46 +0000371 optionlist = ' ' + string.join(options, ' ')
372 self.putcmd("rcpt","TO:%s%s" % (quoteaddr(recip),optionlist))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000373 return self.getreply()
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000374
375 def data(self,msg):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000376 """SMTP 'DATA' command -- sends message data to server.
Guido van Rossum296e1431999-04-07 15:03:39 +0000377
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000378 Automatically quotes lines beginning with a period per rfc821.
Guido van Rossum296e1431999-04-07 15:03:39 +0000379 Raises SMTPDataError if there is an unexpected reply to the
380 DATA command; the return value from this method is the final
381 response code received when the all data is sent.
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000382 """
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000383 self.putcmd("data")
384 (code,repl)=self.getreply()
385 if self.debuglevel >0 : print "data:", (code,repl)
386 if code <> 354:
Guido van Rossum296e1431999-04-07 15:03:39 +0000387 raise SMTPDataError(code,repl)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000388 else:
Guido van Rossum20c92281999-04-21 16:52:20 +0000389 q = quotedata(msg)
390 if q[-2:] != CRLF:
391 q = q + CRLF
392 q = q + "." + CRLF
393 self.send(q)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000394 (code,msg)=self.getreply()
395 if self.debuglevel >0 : print "data:", (code,msg)
Guido van Rossum296e1431999-04-07 15:03:39 +0000396 return (code,msg)
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000397
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000398 def verify(self, address):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000399 """SMTP 'verify' command -- checks for address validity."""
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000400 self.putcmd("vrfy", quoteaddr(address))
401 return self.getreply()
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000402 # a.k.a.
403 vrfy=verify
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000404
405 def expn(self, address):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000406 """SMTP 'verify' command -- checks for address validity."""
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000407 self.putcmd("expn", quoteaddr(address))
408 return self.getreply()
409
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000410 # some useful methods
Jeremy Hylton31bb8ce1998-08-13 19:57:46 +0000411 def sendmail(self, from_addr, to_addrs, msg, mail_options=[],
412 rcpt_options=[]):
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000413 """This command performs an entire mail transaction.
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000414
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000415 The arguments are:
416 - from_addr : The address sending this mail.
417 - to_addrs : A list of addresses to send this mail to. A bare
418 string will be treated as a list with 1 address.
419 - msg : The message to send.
420 - mail_options : List of ESMTP options (such as 8bitmime) for the
421 mail command.
422 - rcpt_options : List of ESMTP options (such as DSN commands) for
423 all the rcpt commands.
424
425 If there has been no previous EHLO or HELO command this session, this
426 method tries ESMTP EHLO first. If the server does ESMTP, message size
427 and each of the specified options will be passed to it. If EHLO
428 fails, HELO will be tried and ESMTP options suppressed.
429
430 This method will return normally if the mail is accepted for at least
Barry Warsawd25c1b71999-11-28 17:11:06 +0000431 one recipient. It returns a dictionary, with one entry for each
432 recipient that was refused. Each entry contains a tuple of the SMTP
433 error code and the accompanying error message sent by the server.
Guido van Rossum296e1431999-04-07 15:03:39 +0000434
435 This method may raise the following exceptions:
436
437 SMTPHeloError The server didn't reply properly to
438 the helo greeting.
Barry Warsawd25c1b71999-11-28 17:11:06 +0000439 SMTPRecipientsRefused The server rejected ALL recipients
Guido van Rossum296e1431999-04-07 15:03:39 +0000440 (no mail was sent).
441 SMTPSenderRefused The server didn't accept the from_addr.
442 SMTPDataError The server replied with an unexpected
443 error code (other than a refusal of
444 a recipient).
445
446 Note: the connection will be open even after an exception is raised.
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000447
Guido van Rossum95e6f701998-06-25 02:15:50 +0000448 Example:
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000449
450 >>> import smtplib
451 >>> s=smtplib.SMTP("localhost")
Guido van Rossumfc40a831998-01-29 17:26:45 +0000452 >>> tolist=["one@one.org","two@two.org","three@three.org","four@four.org"]
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000453 >>> msg = '''
454 ... From: Me@my.org
455 ... Subject: testin'...
456 ...
457 ... This is a test '''
458 >>> s.sendmail("me@my.org",tolist,msg)
459 { "three@three.org" : ( 550 ,"User unknown" ) }
460 >>> s.quit()
461
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000462 In the above example, the message was accepted for delivery to three
463 of the four addresses, and one was rejected, with the error code
Barry Warsawd25c1b71999-11-28 17:11:06 +0000464 550. If all addresses are accepted, then the method will return an
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000465 empty dictionary.
466
467 """
Guido van Rossum296e1431999-04-07 15:03:39 +0000468 if self.helo_resp is None and self.ehlo_resp is None:
469 if not (200 <= self.ehlo()[0] <= 299):
470 (code,resp) = self.helo()
471 if not (200 <= code <= 299):
472 raise SMTPHeloError(code, resp)
Guido van Rossum95e6f701998-06-25 02:15:50 +0000473 esmtp_opts = []
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000474 if self.does_esmtp:
475 # Hmmm? what's this? -ddm
476 # self.esmtp_features['7bit']=""
477 if self.has_extn('size'):
478 esmtp_opts.append("size=" + `len(msg)`)
479 for option in mail_options:
Guido van Rossum95e6f701998-06-25 02:15:50 +0000480 esmtp_opts.append(option)
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000481
Guido van Rossum95e6f701998-06-25 02:15:50 +0000482 (code,resp) = self.mail(from_addr, esmtp_opts)
483 if code <> 250:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000484 self.rset()
Guido van Rossum296e1431999-04-07 15:03:39 +0000485 raise SMTPSenderRefused(code, resp, from_addr)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000486 senderrs={}
Jeremy Hylton31bb8ce1998-08-13 19:57:46 +0000487 if type(to_addrs) == types.StringType:
488 to_addrs = [to_addrs]
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000489 for each in to_addrs:
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000490 (code,resp)=self.rcpt(each, rcpt_options)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000491 if (code <> 250) and (code <> 251):
Guido van Rossumfc40a831998-01-29 17:26:45 +0000492 senderrs[each]=(code,resp)
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000493 if len(senderrs)==len(to_addrs):
Guido van Rossum95e6f701998-06-25 02:15:50 +0000494 # the server refused all our recipients
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000495 self.rset()
Guido van Rossum296e1431999-04-07 15:03:39 +0000496 raise SMTPRecipientsRefused(senderrs)
497 (code,resp)=self.data(msg)
498 if code <> 250:
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000499 self.rset()
Guido van Rossum296e1431999-04-07 15:03:39 +0000500 raise SMTPDataError(code, resp)
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000501 #if we got here then somebody got our mail
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000502 return senderrs
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000503
504
505 def close(self):
506 """Close the connection to the SMTP server."""
507 if self.file:
508 self.file.close()
509 self.file = None
510 if self.sock:
511 self.sock.close()
512 self.sock = None
513
514
515 def quit(self):
Guido van Rossum95e6f701998-06-25 02:15:50 +0000516 """Terminate the SMTP session."""
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000517 self.docmd("quit")
518 self.close()
Guido van Rossum95e6f701998-06-25 02:15:50 +0000519
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000520
Guido van Rossum95e6f701998-06-25 02:15:50 +0000521# Test the sendmail method, which tests most of the others.
522# Note: This always sends to localhost.
523if __name__ == '__main__':
524 import sys, rfc822
525
526 def prompt(prompt):
527 sys.stdout.write(prompt + ": ")
528 return string.strip(sys.stdin.readline())
529
530 fromaddr = prompt("From")
531 toaddrs = string.splitfields(prompt("To"), ',')
532 print "Enter message, end with ^D:"
533 msg = ''
534 while 1:
535 line = sys.stdin.readline()
536 if not line:
537 break
538 msg = msg + line
539 print "Message length is " + `len(msg)`
540
541 server = SMTP('localhost')
542 server.set_debuglevel(1)
543 server.sendmail(fromaddr, toaddrs, msg)
544 server.quit()