Changeset 391 for python/trunk/Lib/smtplib.py
- Timestamp:
- Mar 19, 2014, 11:31:01 PM (11 years ago)
- Location:
- python/trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
python/trunk
-
Property svn:mergeinfo
set to
/python/vendor/Python-2.7.6 merged eligible /python/vendor/current merged eligible
-
Property svn:mergeinfo
set to
-
python/trunk/Lib/smtplib.py
r2 r391 50 50 from sys import stderr 51 51 52 __all__ = ["SMTPException", "SMTPServerDisconnected","SMTPResponseException",53 "SMTPSenderRefused", "SMTPRecipientsRefused","SMTPDataError",54 "SMTPConnectError", "SMTPHeloError","SMTPAuthenticationError",55 "quoteaddr", "quotedata","SMTP"]52 __all__ = ["SMTPException", "SMTPServerDisconnected", "SMTPResponseException", 53 "SMTPSenderRefused", "SMTPRecipientsRefused", "SMTPDataError", 54 "SMTPConnectError", "SMTPHeloError", "SMTPAuthenticationError", 55 "quoteaddr", "quotedata", "SMTP"] 56 56 57 57 SMTP_PORT = 25 58 58 SMTP_SSL_PORT = 465 59 CRLF ="\r\n"59 CRLF = "\r\n" 60 60 61 61 OLDSTYLE_AUTH = re.compile(r"auth=(.*)", re.I) 62 62 63 63 64 # Exception classes used by this module. … … 110 111 def __init__(self, recipients): 111 112 self.recipients = recipients 112 self.args = ( 113 self.args = (recipients,) 113 114 114 115 … … 128 129 combination provided. 129 130 """ 131 130 132 131 133 def quoteaddr(addr): … … 139 141 except AttributeError: 140 142 pass 141 if m == (None, None): # Indicates parse failure or AttributeError143 if m == (None, None): # Indicates parse failure or AttributeError 142 144 # something weird here.. punt -ddm 143 145 return "<%s>" % addr … … 148 150 return "<%s>" % m 149 151 152 def _addr_only(addrstring): 153 displayname, addr = email.utils.parseaddr(addrstring) 154 if (displayname, addr) == ('', ''): 155 # parseaddr couldn't parse it, so use it as is. 156 return addrstring 157 return addr 158 150 159 def quotedata(data): 151 160 """Quote data for email. … … 176 185 while chr != "\n": 177 186 chr = self.sslobj.read(1) 178 if not chr: break 187 if not chr: 188 break 179 189 str += chr 180 190 return str … … 220 230 ehlo_resp = None 221 231 does_esmtp = 0 232 default_port = SMTP_PORT 222 233 223 234 def __init__(self, host='', port=0, local_hostname=None, … … 227 238 If specified, `host' is the name of the remote host to which to 228 239 connect. If specified, `port' specifies the port to which to connect. 229 By default, smtplib.SMTP_PORT is used. An SMTPConnectError is raised 230 if the specified `host' doesn't respond correctly. If specified, 231 `local_hostname` is used as the FQDN of the local host. By default, 232 the local hostname is found using socket.getfqdn(). 240 By default, smtplib.SMTP_PORT is used. If a host is specified the 241 connect method is called, and if it returns anything other than a 242 success code an SMTPConnectError is raised. If specified, 243 `local_hostname` is used as the FQDN of the local host for the 244 HELO/EHLO command. Otherwise, the local hostname is found using 245 socket.getfqdn(). 233 246 234 247 """ 235 248 self.timeout = timeout 236 249 self.esmtp_features = {} 237 self.default_port = SMTP_PORT238 250 if host: 239 251 (code, msg) = self.connect(host, port) … … 267 279 self.debuglevel = debuglevel 268 280 269 def _get_socket(self, port, host, timeout):281 def _get_socket(self, host, port, timeout): 270 282 # This makes it simpler for SMTP_SSL to use the SMTP connect code 271 283 # and just alter the socket connection bit. 272 if self.debuglevel > 0: print>>stderr, 'connect:', (host, port) 273 return socket.create_connection((port, host), timeout) 274 275 def connect(self, host='localhost', port = 0): 284 if self.debuglevel > 0: 285 print>>stderr, 'connect:', (host, port) 286 return socket.create_connection((host, port), timeout) 287 288 def connect(self, host='localhost', port=0): 276 289 """Connect to a host on a given port. 277 290 … … 287 300 i = host.rfind(':') 288 301 if i >= 0: 289 host, port = host[:i], host[i+1:] 290 try: port = int(port) 302 host, port = host[:i], host[i + 1:] 303 try: 304 port = int(port) 291 305 except ValueError: 292 306 raise socket.error, "nonnumeric port" 293 if not port: port = self.default_port 294 if self.debuglevel > 0: print>>stderr, 'connect:', (host, port) 307 if not port: 308 port = self.default_port 309 if self.debuglevel > 0: 310 print>>stderr, 'connect:', (host, port) 295 311 self.sock = self._get_socket(host, port, self.timeout) 296 312 (code, msg) = self.getreply() 297 if self.debuglevel > 0: print>>stderr, "connect:", msg 313 if self.debuglevel > 0: 314 print>>stderr, "connect:", msg 298 315 return (code, msg) 299 316 300 317 def send(self, str): 301 318 """Send `str' to the server.""" 302 if self.debuglevel > 0: print>>stderr, 'send:', repr(str) 319 if self.debuglevel > 0: 320 print>>stderr, 'send:', repr(str) 303 321 if hasattr(self, 'sock') and self.sock: 304 322 try: … … 331 349 Raises SMTPServerDisconnected if end-of-file is reached. 332 350 """ 333 resp =[]351 resp = [] 334 352 if self.file is None: 335 353 self.file = self.sock.makefile('rb') 336 354 while 1: 337 line = self.file.readline() 355 try: 356 line = self.file.readline() 357 except socket.error as e: 358 self.close() 359 raise SMTPServerDisconnected("Connection unexpectedly closed: " 360 + str(e)) 338 361 if line == '': 339 362 self.close() 340 363 raise SMTPServerDisconnected("Connection unexpectedly closed") 341 if self.debuglevel > 0: print>>stderr, 'reply:', repr(line) 364 if self.debuglevel > 0: 365 print>>stderr, 'reply:', repr(line) 342 366 resp.append(line[4:].strip()) 343 code =line[:3]367 code = line[:3] 344 368 # Check that the error code is syntactically correct. 345 369 # Don't attempt to read a continuation line if it is broken. … … 350 374 break 351 375 # Check if multiline response. 352 if line[3:4] !="-":376 if line[3:4] != "-": 353 377 break 354 378 355 379 errmsg = "\n".join(resp) 356 380 if self.debuglevel > 0: 357 print>>stderr, 'reply: retcode (%s); Msg: %s' % (errcode, errmsg)381 print>>stderr, 'reply: retcode (%s); Msg: %s' % (errcode, errmsg) 358 382 return errcode, errmsg 359 383 360 384 def docmd(self, cmd, args=""): 361 385 """Send a command, and return its response code.""" 362 self.putcmd(cmd, args)386 self.putcmd(cmd, args) 363 387 return self.getreply() 364 388 … … 370 394 """ 371 395 self.putcmd("helo", name or self.local_hostname) 372 (code, msg)=self.getreply()373 self.helo_resp =msg374 return (code, msg)396 (code, msg) = self.getreply() 397 self.helo_resp = msg 398 return (code, msg) 375 399 376 400 def ehlo(self, name=''): … … 381 405 self.esmtp_features = {} 382 406 self.putcmd(self.ehlo_msg, name or self.local_hostname) 383 (code, msg)=self.getreply()407 (code, msg) = self.getreply() 384 408 # According to RFC1869 some (badly written) 385 409 # MTA's will disconnect on an ehlo. Toss an exception if … … 388 412 self.close() 389 413 raise SMTPServerDisconnected("Server not connected") 390 self.ehlo_resp =msg414 self.ehlo_resp = msg 391 415 if code != 250: 392 return (code, msg)393 self.does_esmtp =1416 return (code, msg) 417 self.does_esmtp = 1 394 418 #parse the ehlo response -ddm 395 resp =self.ehlo_resp.split('\n')419 resp = self.ehlo_resp.split('\n') 396 420 del resp[0] 397 421 for each in resp: … … 413 437 # parameters, but were not going to check for that here. Note 414 438 # that the space isn't present if there are no parameters. 415 m =re.match(r'(?P<feature>[A-Za-z0-9][A-Za-z0-9\-]*) ?',each)439 m = re.match(r'(?P<feature>[A-Za-z0-9][A-Za-z0-9\-]*) ?', each) 416 440 if m: 417 feature =m.group("feature").lower()418 params =m.string[m.end("feature"):].strip()441 feature = m.group("feature").lower() 442 params = m.string[m.end("feature"):].strip() 419 443 if feature == "auth": 420 444 self.esmtp_features[feature] = self.esmtp_features.get(feature, "") \ 421 445 + " " + params 422 446 else: 423 self.esmtp_features[feature] =params424 return (code, msg)447 self.esmtp_features[feature] = params 448 return (code, msg) 425 449 426 450 def has_extn(self, opt): … … 442 466 return self.docmd("noop") 443 467 444 def mail(self, sender,options=[]):468 def mail(self, sender, options=[]): 445 469 """SMTP 'mail' command -- begins mail xfer session.""" 446 470 optionlist = '' 447 471 if options and self.does_esmtp: 448 472 optionlist = ' ' + ' '.join(options) 449 self.putcmd("mail", "FROM:%s%s" % (quoteaddr(sender) ,optionlist))473 self.putcmd("mail", "FROM:%s%s" % (quoteaddr(sender), optionlist)) 450 474 return self.getreply() 451 475 452 def rcpt(self, recip,options=[]):476 def rcpt(self, recip, options=[]): 453 477 """SMTP 'rcpt' command -- indicates 1 recipient for this mail.""" 454 478 optionlist = '' 455 479 if options and self.does_esmtp: 456 480 optionlist = ' ' + ' '.join(options) 457 self.putcmd("rcpt", "TO:%s%s" % (quoteaddr(recip),optionlist))481 self.putcmd("rcpt", "TO:%s%s" % (quoteaddr(recip), optionlist)) 458 482 return self.getreply() 459 483 460 def data(self, msg):484 def data(self, msg): 461 485 """SMTP 'DATA' command -- sends message data to server. 462 486 … … 467 491 """ 468 492 self.putcmd("data") 469 (code,repl)=self.getreply() 470 if self.debuglevel >0 : print>>stderr, "data:", (code,repl) 493 (code, repl) = self.getreply() 494 if self.debuglevel > 0: 495 print>>stderr, "data:", (code, repl) 471 496 if code != 354: 472 raise SMTPDataError(code, repl)497 raise SMTPDataError(code, repl) 473 498 else: 474 499 q = quotedata(msg) … … 477 502 q = q + "." + CRLF 478 503 self.send(q) 479 (code,msg)=self.getreply() 480 if self.debuglevel >0 : print>>stderr, "data:", (code,msg) 481 return (code,msg) 504 (code, msg) = self.getreply() 505 if self.debuglevel > 0: 506 print>>stderr, "data:", (code, msg) 507 return (code, msg) 482 508 483 509 def verify(self, address): 484 510 """SMTP 'verify' command -- checks for address validity.""" 485 self.putcmd("vrfy", quoteaddr(address))511 self.putcmd("vrfy", _addr_only(address)) 486 512 return self.getreply() 487 513 # a.k.a. 488 vrfy =verify514 vrfy = verify 489 515 490 516 def expn(self, address): 491 517 """SMTP 'expn' command -- expands a mailing list.""" 492 self.putcmd("expn", quoteaddr(address))518 self.putcmd("expn", _addr_only(address)) 493 519 return self.getreply() 494 520 … … 590 616 return (code, resp) 591 617 592 def starttls(self, keyfile = None, certfile =None):618 def starttls(self, keyfile=None, certfile=None): 593 619 """Puts the connection to the SMTP server into TLS mode. 594 620 … … 693 719 esmtp_opts.append(option) 694 720 695 (code, resp) = self.mail(from_addr, esmtp_opts)721 (code, resp) = self.mail(from_addr, esmtp_opts) 696 722 if code != 250: 697 723 self.rset() 698 724 raise SMTPSenderRefused(code, resp, from_addr) 699 senderrs ={}725 senderrs = {} 700 726 if isinstance(to_addrs, basestring): 701 727 to_addrs = [to_addrs] 702 728 for each in to_addrs: 703 (code, resp)=self.rcpt(each, rcpt_options)729 (code, resp) = self.rcpt(each, rcpt_options) 704 730 if (code != 250) and (code != 251): 705 senderrs[each] =(code,resp)706 if len(senderrs) ==len(to_addrs):731 senderrs[each] = (code, resp) 732 if len(senderrs) == len(to_addrs): 707 733 # the server refused all our recipients 708 734 self.rset() 709 735 raise SMTPRecipientsRefused(senderrs) 710 (code, resp) = self.data(msg)736 (code, resp) = self.data(msg) 711 737 if code != 250: 712 738 self.rset() … … 735 761 736 762 class SMTP_SSL(SMTP): 737 """ This is a subclass derived from SMTP that connects over an SSL encrypted 738 socket (to use this class you need a socket module that was compiled with SSL 739 support). If host is not specified, '' (the local host) is used. If port is 740 omitted, the standard SMTP-over-SSL port (465) is used. keyfile and certfile 741 are also optional - they can contain a PEM formatted private key and 742 certificate chain file for the SSL connection. 743 """ 763 """ This is a subclass derived from SMTP that connects over an SSL 764 encrypted socket (to use this class you need a socket module that was 765 compiled with SSL support). If host is not specified, '' (the local 766 host) is used. If port is omitted, the standard SMTP-over-SSL port 767 (465) is used. local_hostname has the same meaning as it does in the 768 SMTP class. keyfile and certfile are also optional - they can contain 769 a PEM formatted private key and certificate chain file for the SSL 770 connection. 771 772 """ 773 774 default_port = SMTP_SSL_PORT 775 744 776 def __init__(self, host='', port=0, local_hostname=None, 745 777 keyfile=None, certfile=None, … … 748 780 self.certfile = certfile 749 781 SMTP.__init__(self, host, port, local_hostname, timeout) 750 self.default_port = SMTP_SSL_PORT751 782 752 783 def _get_socket(self, host, port, timeout): 753 if self.debuglevel > 0: print>>stderr, 'connect:', (host, port) 784 if self.debuglevel > 0: 785 print>>stderr, 'connect:', (host, port) 754 786 new_socket = socket.create_connection((host, port), timeout) 755 787 new_socket = ssl.wrap_socket(new_socket, self.keyfile, self.certfile) … … 768 800 769 801 The LMTP protocol, which is very similar to ESMTP, is heavily based 770 on the standard SMTP client. It's common to use Unix sockets for LMTP, 771 so our connect() method must support that as well as a regular 772 host:port server. To specify a Unix socket, you must use an absolute 802 on the standard SMTP client. It's common to use Unix sockets for 803 LMTP, so our connect() method must support that as well as a regular 804 host:port server. local_hostname has the same meaning as it does in 805 the SMTP class. To specify a Unix socket, you must use an absolute 773 806 path as the host, starting with a '/'. 774 807 … … 779 812 ehlo_msg = "lhlo" 780 813 781 def __init__(self, host = '', port = LMTP_PORT, local_hostname =None):814 def __init__(self, host='', port=LMTP_PORT, local_hostname=None): 782 815 """Initialize a new instance.""" 783 816 SMTP.__init__(self, host, port, local_hostname) 784 817 785 def connect(self, host = 'localhost', port =0):818 def connect(self, host='localhost', port=0): 786 819 """Connect to the LMTP daemon, on either a Unix or a TCP socket.""" 787 820 if host[0] != '/': … … 792 825 self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 793 826 self.sock.connect(host) 794 except socket.error, msg: 795 if self.debuglevel > 0: print>>stderr, 'connect fail:', host 827 except socket.error: 828 if self.debuglevel > 0: 829 print>>stderr, 'connect fail:', host 796 830 if self.sock: 797 831 self.sock.close() 798 832 self.sock = None 799 raise socket.error, msg833 raise 800 834 (code, msg) = self.getreply() 801 if self.debuglevel > 0: print>>stderr, "connect:", msg 835 if self.debuglevel > 0: 836 print>>stderr, "connect:", msg 802 837 return (code, msg) 803 838 … … 813 848 814 849 fromaddr = prompt("From") 815 toaddrs 850 toaddrs = prompt("To").split(',') 816 851 print "Enter message, end with ^D:" 817 852 msg = ''
Note:
See TracChangeset
for help on using the changeset viewer.