1#! /usr/bin/env python3 2 3'''SMTP/ESMTP client class. 4 5This should follow RFC 821 (SMTP), RFC 1869 (ESMTP), RFC 2554 (SMTP 6Authentication) and RFC 2487 (Secure SMTP over TLS). 7 8Notes: 9 10Please remember, when doing ESMTP, that the names of the SMTP service 11extensions are NOT the same thing as the option keywords for the RCPT 12and MAIL commands! 13 14Example: 15 16 >>> import smtplib 17 >>> s=smtplib.SMTP("localhost") 18 >>> print(s.help()) 19 This is Sendmail version 8.8.4 20 Topics: 21 HELO EHLO MAIL RCPT DATA 22 RSET NOOP QUIT HELP VRFY 23 EXPN VERB ETRN DSN 24 For more info use "HELP <topic>". 25 To report bugs in the implementation send email to 26 sendmail-bugs@sendmail.org. 27 For local information send email to Postmaster at your site. 28 End of HELP info 29 >>> s.putcmd("vrfy","someone@here") 30 >>> s.getreply() 31 (250, "Somebody OverHere <somebody@here.my.org>") 32 >>> s.quit() 33''' 34 35# Author: The Dragon De Monsyne <dragondm@integral.org> 36# ESMTP support, test code and doc fixes added by 37# Eric S. Raymond <esr@thyrsus.com> 38# Better RFC 821 compliance (MAIL and RCPT, and CRLF in data) 39# by Carey Evans <c.evans@clear.net.nz>, for picky mail servers. 40# RFC 2554 (authentication) support by Gerhard Haering <gerhard@bigfoot.de>. 41# 42# This was modified from the Python 1.5 library HTTP lib. 43 44import socket 45import io 46import re 47import email.utils 48import email.message 49import email.generator 50import base64 51import hmac 52import copy 53import datetime 54import sys 55from email.base64mime import body_encode as encode_base64 56 57__all__ = ["SMTPException", "SMTPServerDisconnected", "SMTPResponseException", 58 "SMTPSenderRefused", "SMTPRecipientsRefused", "SMTPDataError", 59 "SMTPConnectError", "SMTPHeloError", "SMTPAuthenticationError", 60 "quoteaddr", "quotedata", "SMTP"] 61 62SMTP_PORT = 25 63SMTP_SSL_PORT = 465 64CRLF = "\r\n" 65bCRLF = b"\r\n" 66_MAXLINE = 8192 # more than 8 times larger than RFC 821, 4.5.3 67 68OLDSTYLE_AUTH = re.compile(r"auth=(.*)", re.I) 69 70# Exception classes used by this module. 71class SMTPException(OSError): 72 """Base class for all exceptions raised by this module.""" 73 74class SMTPNotSupportedError(SMTPException): 75 """The command or option is not supported by the SMTP server. 76 77 This exception is raised when an attempt is made to run a command or a 78 command with an option which is not supported by the server. 79 """ 80 81class SMTPServerDisconnected(SMTPException): 82 """Not connected to any SMTP server. 83 84 This exception is raised when the server unexpectedly disconnects, 85 or when an attempt is made to use the SMTP instance before 86 connecting it to a server. 87 """ 88 89class SMTPResponseException(SMTPException): 90 """Base class for all exceptions that include an SMTP error code. 91 92 These exceptions are generated in some instances when the SMTP 93 server returns an error code. The error code is stored in the 94 `smtp_code' attribute of the error, and the `smtp_error' attribute 95 is set to the error message. 96 """ 97 98 def __init__(self, code, msg): 99 self.smtp_code = code 100 self.smtp_error = msg 101 self.args = (code, msg) 102 103class SMTPSenderRefused(SMTPResponseException): 104 """Sender address refused. 105 106 In addition to the attributes set by on all SMTPResponseException 107 exceptions, this sets `sender' to the string that the SMTP refused. 108 """ 109 110 def __init__(self, code, msg, sender): 111 self.smtp_code = code 112 self.smtp_error = msg 113 self.sender = sender 114 self.args = (code, msg, sender) 115 116class SMTPRecipientsRefused(SMTPException): 117 """All recipient addresses refused. 118 119 The errors for each recipient are accessible through the attribute 120 'recipients', which is a dictionary of exactly the same sort as 121 SMTP.sendmail() returns. 122 """ 123 124 def __init__(self, recipients): 125 self.recipients = recipients 126 self.args = (recipients,) 127 128 129class SMTPDataError(SMTPResponseException): 130 """The SMTP server didn't accept the data.""" 131 132class SMTPConnectError(SMTPResponseException): 133 """Error during connection establishment.""" 134 135class SMTPHeloError(SMTPResponseException): 136 """The server refused our HELO reply.""" 137 138class SMTPAuthenticationError(SMTPResponseException): 139 """Authentication error. 140 141 Most probably the server didn't accept the username/password 142 combination provided. 143 """ 144 145def quoteaddr(addrstring): 146 """Quote a subset of the email addresses defined by RFC 821. 147 148 Should be able to handle anything email.utils.parseaddr can handle. 149 """ 150 displayname, addr = email.utils.parseaddr(addrstring) 151 if (displayname, addr) == ('', ''): 152 # parseaddr couldn't parse it, use it as is and hope for the best. 153 if addrstring.strip().startswith('<'): 154 return addrstring 155 return "<%s>" % addrstring 156 return "<%s>" % addr 157 158def _addr_only(addrstring): 159 displayname, addr = email.utils.parseaddr(addrstring) 160 if (displayname, addr) == ('', ''): 161 # parseaddr couldn't parse it, so use it as is. 162 return addrstring 163 return addr 164 165# Legacy method kept for backward compatibility. 166def quotedata(data): 167 """Quote data for email. 168 169 Double leading '.', and change Unix newline '\\n', or Mac '\\r' into 170 Internet CRLF end-of-line. 171 """ 172 return re.sub(r'(?m)^\.', '..', 173 re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data)) 174 175def _quote_periods(bindata): 176 return re.sub(br'(?m)^\.', b'..', bindata) 177 178def _fix_eols(data): 179 return re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data) 180 181try: 182 import ssl 183except ImportError: 184 _have_ssl = False 185else: 186 _have_ssl = True 187 188 189class SMTP: 190 """This class manages a connection to an SMTP or ESMTP server. 191 SMTP Objects: 192 SMTP objects have the following attributes: 193 helo_resp 194 This is the message given by the server in response to the 195 most recent HELO command. 196 197 ehlo_resp 198 This is the message given by the server in response to the 199 most recent EHLO command. This is usually multiline. 200 201 does_esmtp 202 This is a True value _after you do an EHLO command_, if the 203 server supports ESMTP. 204 205 esmtp_features 206 This is a dictionary, which, if the server supports ESMTP, 207 will _after you do an EHLO command_, contain the names of the 208 SMTP service extensions this server supports, and their 209 parameters (if any). 210 211 Note, all extension names are mapped to lower case in the 212 dictionary. 213 214 See each method's docstrings for details. In general, there is a 215 method of the same name to perform each SMTP command. There is also a 216 method called 'sendmail' that will do an entire mail transaction. 217 """ 218 debuglevel = 0 219 file = None 220 helo_resp = None 221 ehlo_msg = "ehlo" 222 ehlo_resp = None 223 does_esmtp = 0 224 default_port = SMTP_PORT 225 226 def __init__(self, host='', port=0, local_hostname=None, 227 timeout=socket._GLOBAL_DEFAULT_TIMEOUT, 228 source_address=None): 229 """Initialize a new instance. 230 231 If specified, `host' is the name of the remote host to which to 232 connect. If specified, `port' specifies the port to which to connect. 233 By default, smtplib.SMTP_PORT is used. If a host is specified the 234 connect method is called, and if it returns anything other than a 235 success code an SMTPConnectError is raised. If specified, 236 `local_hostname` is used as the FQDN of the local host in the HELO/EHLO 237 command. Otherwise, the local hostname is found using 238 socket.getfqdn(). The `source_address` parameter takes a 2-tuple (host, 239 port) for the socket to bind to as its source address before 240 connecting. If the host is '' and port is 0, the OS default behavior 241 will be used. 242 243 """ 244 self._host = host 245 self.timeout = timeout 246 self.esmtp_features = {} 247 self.command_encoding = 'ascii' 248 self.source_address = source_address 249 250 if host: 251 (code, msg) = self.connect(host, port) 252 if code != 220: 253 self.close() 254 raise SMTPConnectError(code, msg) 255 if local_hostname is not None: 256 self.local_hostname = local_hostname 257 else: 258 # RFC 2821 says we should use the fqdn in the EHLO/HELO verb, and 259 # if that can't be calculated, that we should use a domain literal 260 # instead (essentially an encoded IP address like [A.B.C.D]). 261 fqdn = socket.getfqdn() 262 if '.' in fqdn: 263 self.local_hostname = fqdn 264 else: 265 # We can't find an fqdn hostname, so use a domain literal 266 addr = '127.0.0.1' 267 try: 268 addr = socket.gethostbyname(socket.gethostname()) 269 except socket.gaierror: 270 pass 271 self.local_hostname = '[%s]' % addr 272 273 def __enter__(self): 274 return self 275 276 def __exit__(self, *args): 277 try: 278 code, message = self.docmd("QUIT") 279 if code != 221: 280 raise SMTPResponseException(code, message) 281 except SMTPServerDisconnected: 282 pass 283 finally: 284 self.close() 285 286 def set_debuglevel(self, debuglevel): 287 """Set the debug output level. 288 289 A non-false value results in debug messages for connection and for all 290 messages sent to and received from the server. 291 292 """ 293 self.debuglevel = debuglevel 294 295 def _print_debug(self, *args): 296 if self.debuglevel > 1: 297 print(datetime.datetime.now().time(), *args, file=sys.stderr) 298 else: 299 print(*args, file=sys.stderr) 300 301 def _get_socket(self, host, port, timeout): 302 # This makes it simpler for SMTP_SSL to use the SMTP connect code 303 # and just alter the socket connection bit. 304 if self.debuglevel > 0: 305 self._print_debug('connect: to', (host, port), self.source_address) 306 return socket.create_connection((host, port), timeout, 307 self.source_address) 308 309 def connect(self, host='localhost', port=0, source_address=None): 310 """Connect to a host on a given port. 311 312 If the hostname ends with a colon (`:') followed by a number, and 313 there is no port specified, that suffix will be stripped off and the 314 number interpreted as the port number to use. 315 316 Note: This method is automatically invoked by __init__, if a host is 317 specified during instantiation. 318 319 """ 320 321 if source_address: 322 self.source_address = source_address 323 324 if not port and (host.find(':') == host.rfind(':')): 325 i = host.rfind(':') 326 if i >= 0: 327 host, port = host[:i], host[i + 1:] 328 try: 329 port = int(port) 330 except ValueError: 331 raise OSError("nonnumeric port") 332 if not port: 333 port = self.default_port 334 if self.debuglevel > 0: 335 self._print_debug('connect:', (host, port)) 336 self.sock = self._get_socket(host, port, self.timeout) 337 self.file = None 338 (code, msg) = self.getreply() 339 if self.debuglevel > 0: 340 self._print_debug('connect:', repr(msg)) 341 return (code, msg) 342 343 def send(self, s): 344 """Send `s' to the server.""" 345 if self.debuglevel > 0: 346 self._print_debug('send:', repr(s)) 347 if hasattr(self, 'sock') and self.sock: 348 if isinstance(s, str): 349 # send is used by the 'data' command, where command_encoding 350 # should not be used, but 'data' needs to convert the string to 351 # binary itself anyway, so that's not a problem. 352 s = s.encode(self.command_encoding) 353 try: 354 self.sock.sendall(s) 355 except OSError: 356 self.close() 357 raise SMTPServerDisconnected('Server not connected') 358 else: 359 raise SMTPServerDisconnected('please run connect() first') 360 361 def putcmd(self, cmd, args=""): 362 """Send a command to the server.""" 363 if args == "": 364 str = '%s%s' % (cmd, CRLF) 365 else: 366 str = '%s %s%s' % (cmd, args, CRLF) 367 self.send(str) 368 369 def getreply(self): 370 """Get a reply from the server. 371 372 Returns a tuple consisting of: 373 374 - server response code (e.g. '250', or such, if all goes well) 375 Note: returns -1 if it can't read response code. 376 377 - server response string corresponding to response code (multiline 378 responses are converted to a single, multiline string). 379 380 Raises SMTPServerDisconnected if end-of-file is reached. 381 """ 382 resp = [] 383 if self.file is None: 384 self.file = self.sock.makefile('rb') 385 while 1: 386 try: 387 line = self.file.readline(_MAXLINE + 1) 388 except OSError as e: 389 self.close() 390 raise SMTPServerDisconnected("Connection unexpectedly closed: " 391 + str(e)) 392 if not line: 393 self.close() 394 raise SMTPServerDisconnected("Connection unexpectedly closed") 395 if self.debuglevel > 0: 396 self._print_debug('reply:', repr(line)) 397 if len(line) > _MAXLINE: 398 self.close() 399 raise SMTPResponseException(500, "Line too long.") 400 resp.append(line[4:].strip(b' \t\r\n')) 401 code = line[:3] 402 # Check that the error code is syntactically correct. 403 # Don't attempt to read a continuation line if it is broken. 404 try: 405 errcode = int(code) 406 except ValueError: 407 errcode = -1 408 break 409 # Check if multiline response. 410 if line[3:4] != b"-": 411 break 412 413 errmsg = b"\n".join(resp) 414 if self.debuglevel > 0: 415 self._print_debug('reply: retcode (%s); Msg: %a' % (errcode, errmsg)) 416 return errcode, errmsg 417 418 def docmd(self, cmd, args=""): 419 """Send a command, and return its response code.""" 420 self.putcmd(cmd, args) 421 return self.getreply() 422 423 # std smtp commands 424 def helo(self, name=''): 425 """SMTP 'helo' command. 426 Hostname to send for this command defaults to the FQDN of the local 427 host. 428 """ 429 self.putcmd("helo", name or self.local_hostname) 430 (code, msg) = self.getreply() 431 self.helo_resp = msg 432 return (code, msg) 433 434 def ehlo(self, name=''): 435 """ SMTP 'ehlo' command. 436 Hostname to send for this command defaults to the FQDN of the local 437 host. 438 """ 439 self.esmtp_features = {} 440 self.putcmd(self.ehlo_msg, name or self.local_hostname) 441 (code, msg) = self.getreply() 442 # According to RFC1869 some (badly written) 443 # MTA's will disconnect on an ehlo. Toss an exception if 444 # that happens -ddm 445 if code == -1 and len(msg) == 0: 446 self.close() 447 raise SMTPServerDisconnected("Server not connected") 448 self.ehlo_resp = msg 449 if code != 250: 450 return (code, msg) 451 self.does_esmtp = 1 452 #parse the ehlo response -ddm 453 assert isinstance(self.ehlo_resp, bytes), repr(self.ehlo_resp) 454 resp = self.ehlo_resp.decode("latin-1").split('\n') 455 del resp[0] 456 for each in resp: 457 # To be able to communicate with as many SMTP servers as possible, 458 # we have to take the old-style auth advertisement into account, 459 # because: 460 # 1) Else our SMTP feature parser gets confused. 461 # 2) There are some servers that only advertise the auth methods we 462 # support using the old style. 463 auth_match = OLDSTYLE_AUTH.match(each) 464 if auth_match: 465 # This doesn't remove duplicates, but that's no problem 466 self.esmtp_features["auth"] = self.esmtp_features.get("auth", "") \ 467 + " " + auth_match.groups(0)[0] 468 continue 469 470 # RFC 1869 requires a space between ehlo keyword and parameters. 471 # It's actually stricter, in that only spaces are allowed between 472 # parameters, but were not going to check for that here. Note 473 # that the space isn't present if there are no parameters. 474 m = re.match(r'(?P<feature>[A-Za-z0-9][A-Za-z0-9\-]*) ?', each) 475 if m: 476 feature = m.group("feature").lower() 477 params = m.string[m.end("feature"):].strip() 478 if feature == "auth": 479 self.esmtp_features[feature] = self.esmtp_features.get(feature, "") \ 480 + " " + params 481 else: 482 self.esmtp_features[feature] = params 483 return (code, msg) 484 485 def has_extn(self, opt): 486 """Does the server support a given SMTP service extension?""" 487 return opt.lower() in self.esmtp_features 488 489 def help(self, args=''): 490 """SMTP 'help' command. 491 Returns help text from server.""" 492 self.putcmd("help", args) 493 return self.getreply()[1] 494 495 def rset(self): 496 """SMTP 'rset' command -- resets session.""" 497 self.command_encoding = 'ascii' 498 return self.docmd("rset") 499 500 def _rset(self): 501 """Internal 'rset' command which ignores any SMTPServerDisconnected error. 502 503 Used internally in the library, since the server disconnected error 504 should appear to the application when the *next* command is issued, if 505 we are doing an internal "safety" reset. 506 """ 507 try: 508 self.rset() 509 except SMTPServerDisconnected: 510 pass 511 512 def noop(self): 513 """SMTP 'noop' command -- doesn't do anything :>""" 514 return self.docmd("noop") 515 516 def mail(self, sender, options=()): 517 """SMTP 'mail' command -- begins mail xfer session. 518 519 This method may raise the following exceptions: 520 521 SMTPNotSupportedError The options parameter includes 'SMTPUTF8' 522 but the SMTPUTF8 extension is not supported by 523 the server. 524 """ 525 optionlist = '' 526 if options and self.does_esmtp: 527 if any(x.lower()=='smtputf8' for x in options): 528 if self.has_extn('smtputf8'): 529 self.command_encoding = 'utf-8' 530 else: 531 raise SMTPNotSupportedError( 532 'SMTPUTF8 not supported by server') 533 optionlist = ' ' + ' '.join(options) 534 self.putcmd("mail", "FROM:%s%s" % (quoteaddr(sender), optionlist)) 535 return self.getreply() 536 537 def rcpt(self, recip, options=()): 538 """SMTP 'rcpt' command -- indicates 1 recipient for this mail.""" 539 optionlist = '' 540 if options and self.does_esmtp: 541 optionlist = ' ' + ' '.join(options) 542 self.putcmd("rcpt", "TO:%s%s" % (quoteaddr(recip), optionlist)) 543 return self.getreply() 544 545 def data(self, msg): 546 """SMTP 'DATA' command -- sends message data to server. 547 548 Automatically quotes lines beginning with a period per rfc821. 549 Raises SMTPDataError if there is an unexpected reply to the 550 DATA command; the return value from this method is the final 551 response code received when the all data is sent. If msg 552 is a string, lone '\\r' and '\\n' characters are converted to 553 '\\r\\n' characters. If msg is bytes, it is transmitted as is. 554 """ 555 self.putcmd("data") 556 (code, repl) = self.getreply() 557 if self.debuglevel > 0: 558 self._print_debug('data:', (code, repl)) 559 if code != 354: 560 raise SMTPDataError(code, repl) 561 else: 562 if isinstance(msg, str): 563 msg = _fix_eols(msg).encode('ascii') 564 q = _quote_periods(msg) 565 if q[-2:] != bCRLF: 566 q = q + bCRLF 567 q = q + b"." + bCRLF 568 self.send(q) 569 (code, msg) = self.getreply() 570 if self.debuglevel > 0: 571 self._print_debug('data:', (code, msg)) 572 return (code, msg) 573 574 def verify(self, address): 575 """SMTP 'verify' command -- checks for address validity.""" 576 self.putcmd("vrfy", _addr_only(address)) 577 return self.getreply() 578 # a.k.a. 579 vrfy = verify 580 581 def expn(self, address): 582 """SMTP 'expn' command -- expands a mailing list.""" 583 self.putcmd("expn", _addr_only(address)) 584 return self.getreply() 585 586 # some useful methods 587 588 def ehlo_or_helo_if_needed(self): 589 """Call self.ehlo() and/or self.helo() if needed. 590 591 If there has been no previous EHLO or HELO command this session, this 592 method tries ESMTP EHLO first. 593 594 This method may raise the following exceptions: 595 596 SMTPHeloError The server didn't reply properly to 597 the helo greeting. 598 """ 599 if self.helo_resp is None and self.ehlo_resp is None: 600 if not (200 <= self.ehlo()[0] <= 299): 601 (code, resp) = self.helo() 602 if not (200 <= code <= 299): 603 raise SMTPHeloError(code, resp) 604 605 def auth(self, mechanism, authobject, *, initial_response_ok=True): 606 """Authentication command - requires response processing. 607 608 'mechanism' specifies which authentication mechanism is to 609 be used - the valid values are those listed in the 'auth' 610 element of 'esmtp_features'. 611 612 'authobject' must be a callable object taking a single argument: 613 614 data = authobject(challenge) 615 616 It will be called to process the server's challenge response; the 617 challenge argument it is passed will be a bytes. It should return 618 an ASCII string that will be base64 encoded and sent to the server. 619 620 Keyword arguments: 621 - initial_response_ok: Allow sending the RFC 4954 initial-response 622 to the AUTH command, if the authentication methods supports it. 623 """ 624 # RFC 4954 allows auth methods to provide an initial response. Not all 625 # methods support it. By definition, if they return something other 626 # than None when challenge is None, then they do. See issue #15014. 627 mechanism = mechanism.upper() 628 initial_response = (authobject() if initial_response_ok else None) 629 if initial_response is not None: 630 response = encode_base64(initial_response.encode('ascii'), eol='') 631 (code, resp) = self.docmd("AUTH", mechanism + " " + response) 632 else: 633 (code, resp) = self.docmd("AUTH", mechanism) 634 # If server responds with a challenge, send the response. 635 if code == 334: 636 challenge = base64.decodebytes(resp) 637 response = encode_base64( 638 authobject(challenge).encode('ascii'), eol='') 639 (code, resp) = self.docmd(response) 640 if code in (235, 503): 641 return (code, resp) 642 raise SMTPAuthenticationError(code, resp) 643 644 def auth_cram_md5(self, challenge=None): 645 """ Authobject to use with CRAM-MD5 authentication. Requires self.user 646 and self.password to be set.""" 647 # CRAM-MD5 does not support initial-response. 648 if challenge is None: 649 return None 650 return self.user + " " + hmac.HMAC( 651 self.password.encode('ascii'), challenge, 'md5').hexdigest() 652 653 def auth_plain(self, challenge=None): 654 """ Authobject to use with PLAIN authentication. Requires self.user and 655 self.password to be set.""" 656 return "\0%s\0%s" % (self.user, self.password) 657 658 def auth_login(self, challenge=None): 659 """ Authobject to use with LOGIN authentication. Requires self.user and 660 self.password to be set.""" 661 if challenge is None: 662 return self.user 663 else: 664 return self.password 665 666 def login(self, user, password, *, initial_response_ok=True): 667 """Log in on an SMTP server that requires authentication. 668 669 The arguments are: 670 - user: The user name to authenticate with. 671 - password: The password for the authentication. 672 673 Keyword arguments: 674 - initial_response_ok: Allow sending the RFC 4954 initial-response 675 to the AUTH command, if the authentication methods supports it. 676 677 If there has been no previous EHLO or HELO command this session, this 678 method tries ESMTP EHLO first. 679 680 This method will return normally if the authentication was successful. 681 682 This method may raise the following exceptions: 683 684 SMTPHeloError The server didn't reply properly to 685 the helo greeting. 686 SMTPAuthenticationError The server didn't accept the username/ 687 password combination. 688 SMTPNotSupportedError The AUTH command is not supported by the 689 server. 690 SMTPException No suitable authentication method was 691 found. 692 """ 693 694 self.ehlo_or_helo_if_needed() 695 if not self.has_extn("auth"): 696 raise SMTPNotSupportedError( 697 "SMTP AUTH extension not supported by server.") 698 699 # Authentication methods the server claims to support 700 advertised_authlist = self.esmtp_features["auth"].split() 701 702 # Authentication methods we can handle in our preferred order: 703 preferred_auths = ['CRAM-MD5', 'PLAIN', 'LOGIN'] 704 705 # We try the supported authentications in our preferred order, if 706 # the server supports them. 707 authlist = [auth for auth in preferred_auths 708 if auth in advertised_authlist] 709 if not authlist: 710 raise SMTPException("No suitable authentication method found.") 711 712 # Some servers advertise authentication methods they don't really 713 # support, so if authentication fails, we continue until we've tried 714 # all methods. 715 self.user, self.password = user, password 716 for authmethod in authlist: 717 method_name = 'auth_' + authmethod.lower().replace('-', '_') 718 try: 719 (code, resp) = self.auth( 720 authmethod, getattr(self, method_name), 721 initial_response_ok=initial_response_ok) 722 # 235 == 'Authentication successful' 723 # 503 == 'Error: already authenticated' 724 if code in (235, 503): 725 return (code, resp) 726 except SMTPAuthenticationError as e: 727 last_exception = e 728 729 # We could not login successfully. Return result of last attempt. 730 raise last_exception 731 732 def starttls(self, keyfile=None, certfile=None, context=None): 733 """Puts the connection to the SMTP server into TLS mode. 734 735 If there has been no previous EHLO or HELO command this session, this 736 method tries ESMTP EHLO first. 737 738 If the server supports TLS, this will encrypt the rest of the SMTP 739 session. If you provide the keyfile and certfile parameters, 740 the identity of the SMTP server and client can be checked. This, 741 however, depends on whether the socket module really checks the 742 certificates. 743 744 This method may raise the following exceptions: 745 746 SMTPHeloError The server didn't reply properly to 747 the helo greeting. 748 """ 749 self.ehlo_or_helo_if_needed() 750 if not self.has_extn("starttls"): 751 raise SMTPNotSupportedError( 752 "STARTTLS extension not supported by server.") 753 (resp, reply) = self.docmd("STARTTLS") 754 if resp == 220: 755 if not _have_ssl: 756 raise RuntimeError("No SSL support included in this Python") 757 if context is not None and keyfile is not None: 758 raise ValueError("context and keyfile arguments are mutually " 759 "exclusive") 760 if context is not None and certfile is not None: 761 raise ValueError("context and certfile arguments are mutually " 762 "exclusive") 763 if keyfile is not None or certfile is not None: 764 import warnings 765 warnings.warn("keyfile and certfile are deprecated, use a " 766 "custom context instead", DeprecationWarning, 2) 767 if context is None: 768 context = ssl._create_stdlib_context(certfile=certfile, 769 keyfile=keyfile) 770 self.sock = context.wrap_socket(self.sock, 771 server_hostname=self._host) 772 self.file = None 773 # RFC 3207: 774 # The client MUST discard any knowledge obtained from 775 # the server, such as the list of SMTP service extensions, 776 # which was not obtained from the TLS negotiation itself. 777 self.helo_resp = None 778 self.ehlo_resp = None 779 self.esmtp_features = {} 780 self.does_esmtp = 0 781 else: 782 # RFC 3207: 783 # 501 Syntax error (no parameters allowed) 784 # 454 TLS not available due to temporary reason 785 raise SMTPResponseException(resp, reply) 786 return (resp, reply) 787 788 def sendmail(self, from_addr, to_addrs, msg, mail_options=(), 789 rcpt_options=()): 790 """This command performs an entire mail transaction. 791 792 The arguments are: 793 - from_addr : The address sending this mail. 794 - to_addrs : A list of addresses to send this mail to. A bare 795 string will be treated as a list with 1 address. 796 - msg : The message to send. 797 - mail_options : List of ESMTP options (such as 8bitmime) for the 798 mail command. 799 - rcpt_options : List of ESMTP options (such as DSN commands) for 800 all the rcpt commands. 801 802 msg may be a string containing characters in the ASCII range, or a byte 803 string. A string is encoded to bytes using the ascii codec, and lone 804 \\r and \\n characters are converted to \\r\\n characters. 805 806 If there has been no previous EHLO or HELO command this session, this 807 method tries ESMTP EHLO first. If the server does ESMTP, message size 808 and each of the specified options will be passed to it. If EHLO 809 fails, HELO will be tried and ESMTP options suppressed. 810 811 This method will return normally if the mail is accepted for at least 812 one recipient. It returns a dictionary, with one entry for each 813 recipient that was refused. Each entry contains a tuple of the SMTP 814 error code and the accompanying error message sent by the server. 815 816 This method may raise the following exceptions: 817 818 SMTPHeloError The server didn't reply properly to 819 the helo greeting. 820 SMTPRecipientsRefused The server rejected ALL recipients 821 (no mail was sent). 822 SMTPSenderRefused The server didn't accept the from_addr. 823 SMTPDataError The server replied with an unexpected 824 error code (other than a refusal of 825 a recipient). 826 SMTPNotSupportedError The mail_options parameter includes 'SMTPUTF8' 827 but the SMTPUTF8 extension is not supported by 828 the server. 829 830 Note: the connection will be open even after an exception is raised. 831 832 Example: 833 834 >>> import smtplib 835 >>> s=smtplib.SMTP("localhost") 836 >>> tolist=["one@one.org","two@two.org","three@three.org","four@four.org"] 837 >>> msg = '''\\ 838 ... From: Me@my.org 839 ... Subject: testin'... 840 ... 841 ... This is a test ''' 842 >>> s.sendmail("me@my.org",tolist,msg) 843 { "three@three.org" : ( 550 ,"User unknown" ) } 844 >>> s.quit() 845 846 In the above example, the message was accepted for delivery to three 847 of the four addresses, and one was rejected, with the error code 848 550. If all addresses are accepted, then the method will return an 849 empty dictionary. 850 851 """ 852 self.ehlo_or_helo_if_needed() 853 esmtp_opts = [] 854 if isinstance(msg, str): 855 msg = _fix_eols(msg).encode('ascii') 856 if self.does_esmtp: 857 if self.has_extn('size'): 858 esmtp_opts.append("size=%d" % len(msg)) 859 for option in mail_options: 860 esmtp_opts.append(option) 861 (code, resp) = self.mail(from_addr, esmtp_opts) 862 if code != 250: 863 if code == 421: 864 self.close() 865 else: 866 self._rset() 867 raise SMTPSenderRefused(code, resp, from_addr) 868 senderrs = {} 869 if isinstance(to_addrs, str): 870 to_addrs = [to_addrs] 871 for each in to_addrs: 872 (code, resp) = self.rcpt(each, rcpt_options) 873 if (code != 250) and (code != 251): 874 senderrs[each] = (code, resp) 875 if code == 421: 876 self.close() 877 raise SMTPRecipientsRefused(senderrs) 878 if len(senderrs) == len(to_addrs): 879 # the server refused all our recipients 880 self._rset() 881 raise SMTPRecipientsRefused(senderrs) 882 (code, resp) = self.data(msg) 883 if code != 250: 884 if code == 421: 885 self.close() 886 else: 887 self._rset() 888 raise SMTPDataError(code, resp) 889 #if we got here then somebody got our mail 890 return senderrs 891 892 def send_message(self, msg, from_addr=None, to_addrs=None, 893 mail_options=(), rcpt_options=()): 894 """Converts message to a bytestring and passes it to sendmail. 895 896 The arguments are as for sendmail, except that msg is an 897 email.message.Message object. If from_addr is None or to_addrs is 898 None, these arguments are taken from the headers of the Message as 899 described in RFC 2822 (a ValueError is raised if there is more than 900 one set of 'Resent-' headers). Regardless of the values of from_addr and 901 to_addr, any Bcc field (or Resent-Bcc field, when the Message is a 902 resent) of the Message object won't be transmitted. The Message 903 object is then serialized using email.generator.BytesGenerator and 904 sendmail is called to transmit the message. If the sender or any of 905 the recipient addresses contain non-ASCII and the server advertises the 906 SMTPUTF8 capability, the policy is cloned with utf8 set to True for the 907 serialization, and SMTPUTF8 and BODY=8BITMIME are asserted on the send. 908 If the server does not support SMTPUTF8, an SMTPNotSupported error is 909 raised. Otherwise the generator is called without modifying the 910 policy. 911 912 """ 913 # 'Resent-Date' is a mandatory field if the Message is resent (RFC 2822 914 # Section 3.6.6). In such a case, we use the 'Resent-*' fields. However, 915 # if there is more than one 'Resent-' block there's no way to 916 # unambiguously determine which one is the most recent in all cases, 917 # so rather than guess we raise a ValueError in that case. 918 # 919 # TODO implement heuristics to guess the correct Resent-* block with an 920 # option allowing the user to enable the heuristics. (It should be 921 # possible to guess correctly almost all of the time.) 922 923 self.ehlo_or_helo_if_needed() 924 resent = msg.get_all('Resent-Date') 925 if resent is None: 926 header_prefix = '' 927 elif len(resent) == 1: 928 header_prefix = 'Resent-' 929 else: 930 raise ValueError("message has more than one 'Resent-' header block") 931 if from_addr is None: 932 # Prefer the sender field per RFC 2822:3.6.2. 933 from_addr = (msg[header_prefix + 'Sender'] 934 if (header_prefix + 'Sender') in msg 935 else msg[header_prefix + 'From']) 936 from_addr = email.utils.getaddresses([from_addr])[0][1] 937 if to_addrs is None: 938 addr_fields = [f for f in (msg[header_prefix + 'To'], 939 msg[header_prefix + 'Bcc'], 940 msg[header_prefix + 'Cc']) 941 if f is not None] 942 to_addrs = [a[1] for a in email.utils.getaddresses(addr_fields)] 943 # Make a local copy so we can delete the bcc headers. 944 msg_copy = copy.copy(msg) 945 del msg_copy['Bcc'] 946 del msg_copy['Resent-Bcc'] 947 international = False 948 try: 949 ''.join([from_addr, *to_addrs]).encode('ascii') 950 except UnicodeEncodeError: 951 if not self.has_extn('smtputf8'): 952 raise SMTPNotSupportedError( 953 "One or more source or delivery addresses require" 954 " internationalized email support, but the server" 955 " does not advertise the required SMTPUTF8 capability") 956 international = True 957 with io.BytesIO() as bytesmsg: 958 if international: 959 g = email.generator.BytesGenerator( 960 bytesmsg, policy=msg.policy.clone(utf8=True)) 961 mail_options = (*mail_options, 'SMTPUTF8', 'BODY=8BITMIME') 962 else: 963 g = email.generator.BytesGenerator(bytesmsg) 964 g.flatten(msg_copy, linesep='\r\n') 965 flatmsg = bytesmsg.getvalue() 966 return self.sendmail(from_addr, to_addrs, flatmsg, mail_options, 967 rcpt_options) 968 969 def close(self): 970 """Close the connection to the SMTP server.""" 971 try: 972 file = self.file 973 self.file = None 974 if file: 975 file.close() 976 finally: 977 sock = self.sock 978 self.sock = None 979 if sock: 980 sock.close() 981 982 def quit(self): 983 """Terminate the SMTP session.""" 984 res = self.docmd("quit") 985 # A new EHLO is required after reconnecting with connect() 986 self.ehlo_resp = self.helo_resp = None 987 self.esmtp_features = {} 988 self.does_esmtp = False 989 self.close() 990 return res 991 992if _have_ssl: 993 994 class SMTP_SSL(SMTP): 995 """ This is a subclass derived from SMTP that connects over an SSL 996 encrypted socket (to use this class you need a socket module that was 997 compiled with SSL support). If host is not specified, '' (the local 998 host) is used. If port is omitted, the standard SMTP-over-SSL port 999 (465) is used. local_hostname and source_address have the same meaning 1000 as they do in the SMTP class. keyfile and certfile are also optional - 1001 they can contain a PEM formatted private key and certificate chain file 1002 for the SSL connection. context also optional, can contain a 1003 SSLContext, and is an alternative to keyfile and certfile; If it is 1004 specified both keyfile and certfile must be None. 1005 1006 """ 1007 1008 default_port = SMTP_SSL_PORT 1009 1010 def __init__(self, host='', port=0, local_hostname=None, 1011 keyfile=None, certfile=None, 1012 timeout=socket._GLOBAL_DEFAULT_TIMEOUT, 1013 source_address=None, context=None): 1014 if context is not None and keyfile is not None: 1015 raise ValueError("context and keyfile arguments are mutually " 1016 "exclusive") 1017 if context is not None and certfile is not None: 1018 raise ValueError("context and certfile arguments are mutually " 1019 "exclusive") 1020 if keyfile is not None or certfile is not None: 1021 import warnings 1022 warnings.warn("keyfile and certfile are deprecated, use a " 1023 "custom context instead", DeprecationWarning, 2) 1024 self.keyfile = keyfile 1025 self.certfile = certfile 1026 if context is None: 1027 context = ssl._create_stdlib_context(certfile=certfile, 1028 keyfile=keyfile) 1029 self.context = context 1030 SMTP.__init__(self, host, port, local_hostname, timeout, 1031 source_address) 1032 1033 def _get_socket(self, host, port, timeout): 1034 if self.debuglevel > 0: 1035 self._print_debug('connect:', (host, port)) 1036 new_socket = socket.create_connection((host, port), timeout, 1037 self.source_address) 1038 new_socket = self.context.wrap_socket(new_socket, 1039 server_hostname=self._host) 1040 return new_socket 1041 1042 __all__.append("SMTP_SSL") 1043 1044# 1045# LMTP extension 1046# 1047LMTP_PORT = 2003 1048 1049class LMTP(SMTP): 1050 """LMTP - Local Mail Transfer Protocol 1051 1052 The LMTP protocol, which is very similar to ESMTP, is heavily based 1053 on the standard SMTP client. It's common to use Unix sockets for 1054 LMTP, so our connect() method must support that as well as a regular 1055 host:port server. local_hostname and source_address have the same 1056 meaning as they do in the SMTP class. To specify a Unix socket, 1057 you must use an absolute path as the host, starting with a '/'. 1058 1059 Authentication is supported, using the regular SMTP mechanism. When 1060 using a Unix socket, LMTP generally don't support or require any 1061 authentication, but your mileage might vary.""" 1062 1063 ehlo_msg = "lhlo" 1064 1065 def __init__(self, host='', port=LMTP_PORT, local_hostname=None, 1066 source_address=None): 1067 """Initialize a new instance.""" 1068 SMTP.__init__(self, host, port, local_hostname=local_hostname, 1069 source_address=source_address) 1070 1071 def connect(self, host='localhost', port=0, source_address=None): 1072 """Connect to the LMTP daemon, on either a Unix or a TCP socket.""" 1073 if host[0] != '/': 1074 return SMTP.connect(self, host, port, source_address=source_address) 1075 1076 # Handle Unix-domain sockets. 1077 try: 1078 self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 1079 self.file = None 1080 self.sock.connect(host) 1081 except OSError: 1082 if self.debuglevel > 0: 1083 self._print_debug('connect fail:', host) 1084 if self.sock: 1085 self.sock.close() 1086 self.sock = None 1087 raise 1088 (code, msg) = self.getreply() 1089 if self.debuglevel > 0: 1090 self._print_debug('connect:', msg) 1091 return (code, msg) 1092 1093 1094# Test the sendmail method, which tests most of the others. 1095# Note: This always sends to localhost. 1096if __name__ == '__main__': 1097 def prompt(prompt): 1098 sys.stdout.write(prompt + ": ") 1099 sys.stdout.flush() 1100 return sys.stdin.readline().strip() 1101 1102 fromaddr = prompt("From") 1103 toaddrs = prompt("To").split(',') 1104 print("Enter message, end with ^D:") 1105 msg = '' 1106 while 1: 1107 line = sys.stdin.readline() 1108 if not line: 1109 break 1110 msg = msg + line 1111 print("Message length is %d" % len(msg)) 1112 1113 server = SMTP('localhost') 1114 server.set_debuglevel(1) 1115 server.sendmail(fromaddr, toaddrs, msg) 1116 server.quit() 1117