symbian-qemu-0.9.1-12/python-2.6.1/Lib/smtplib.py
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 #! /usr/bin/env python
       
     2 
       
     3 '''SMTP/ESMTP client class.
       
     4 
       
     5 This should follow RFC 821 (SMTP), RFC 1869 (ESMTP), RFC 2554 (SMTP
       
     6 Authentication) and RFC 2487 (Secure SMTP over TLS).
       
     7 
       
     8 Notes:
       
     9 
       
    10 Please remember, when doing ESMTP, that the names of the SMTP service
       
    11 extensions are NOT the same thing as the option keywords for the RCPT
       
    12 and MAIL commands!
       
    13 
       
    14 Example:
       
    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 
       
    44 import socket
       
    45 import re
       
    46 import email.utils
       
    47 import base64
       
    48 import hmac
       
    49 from email.base64mime import encode as encode_base64
       
    50 from sys import stderr
       
    51 
       
    52 __all__ = ["SMTPException","SMTPServerDisconnected","SMTPResponseException",
       
    53            "SMTPSenderRefused","SMTPRecipientsRefused","SMTPDataError",
       
    54            "SMTPConnectError","SMTPHeloError","SMTPAuthenticationError",
       
    55            "quoteaddr","quotedata","SMTP"]
       
    56 
       
    57 SMTP_PORT = 25
       
    58 SMTP_SSL_PORT = 465
       
    59 CRLF="\r\n"
       
    60 
       
    61 OLDSTYLE_AUTH = re.compile(r"auth=(.*)", re.I)
       
    62 
       
    63 # Exception classes used by this module.
       
    64 class SMTPException(Exception):
       
    65     """Base class for all exceptions raised by this module."""
       
    66 
       
    67 class SMTPServerDisconnected(SMTPException):
       
    68     """Not connected to any SMTP server.
       
    69 
       
    70     This exception is raised when the server unexpectedly disconnects,
       
    71     or when an attempt is made to use the SMTP instance before
       
    72     connecting it to a server.
       
    73     """
       
    74 
       
    75 class SMTPResponseException(SMTPException):
       
    76     """Base class for all exceptions that include an SMTP error code.
       
    77 
       
    78     These exceptions are generated in some instances when the SMTP
       
    79     server returns an error code.  The error code is stored in the
       
    80     `smtp_code' attribute of the error, and the `smtp_error' attribute
       
    81     is set to the error message.
       
    82     """
       
    83 
       
    84     def __init__(self, code, msg):
       
    85         self.smtp_code = code
       
    86         self.smtp_error = msg
       
    87         self.args = (code, msg)
       
    88 
       
    89 class SMTPSenderRefused(SMTPResponseException):
       
    90     """Sender address refused.
       
    91 
       
    92     In addition to the attributes set by on all SMTPResponseException
       
    93     exceptions, this sets `sender' to the string that the SMTP refused.
       
    94     """
       
    95 
       
    96     def __init__(self, code, msg, sender):
       
    97         self.smtp_code = code
       
    98         self.smtp_error = msg
       
    99         self.sender = sender
       
   100         self.args = (code, msg, sender)
       
   101 
       
   102 class SMTPRecipientsRefused(SMTPException):
       
   103     """All recipient addresses refused.
       
   104 
       
   105     The errors for each recipient are accessible through the attribute
       
   106     'recipients', which is a dictionary of exactly the same sort as
       
   107     SMTP.sendmail() returns.
       
   108     """
       
   109 
       
   110     def __init__(self, recipients):
       
   111         self.recipients = recipients
       
   112         self.args = ( recipients,)
       
   113 
       
   114 
       
   115 class SMTPDataError(SMTPResponseException):
       
   116     """The SMTP server didn't accept the data."""
       
   117 
       
   118 class SMTPConnectError(SMTPResponseException):
       
   119     """Error during connection establishment."""
       
   120 
       
   121 class SMTPHeloError(SMTPResponseException):
       
   122     """The server refused our HELO reply."""
       
   123 
       
   124 class SMTPAuthenticationError(SMTPResponseException):
       
   125     """Authentication error.
       
   126 
       
   127     Most probably the server didn't accept the username/password
       
   128     combination provided.
       
   129     """
       
   130 
       
   131 def quoteaddr(addr):
       
   132     """Quote a subset of the email addresses defined by RFC 821.
       
   133 
       
   134     Should be able to handle anything rfc822.parseaddr can handle.
       
   135     """
       
   136     m = (None, None)
       
   137     try:
       
   138         m = email.utils.parseaddr(addr)[1]
       
   139     except AttributeError:
       
   140         pass
       
   141     if m == (None, None): # Indicates parse failure or AttributeError
       
   142         # something weird here.. punt -ddm
       
   143         return "<%s>" % addr
       
   144     elif m is None:
       
   145         # the sender wants an empty return address
       
   146         return "<>"
       
   147     else:
       
   148         return "<%s>" % m
       
   149 
       
   150 def quotedata(data):
       
   151     """Quote data for email.
       
   152 
       
   153     Double leading '.', and change Unix newline '\\n', or Mac '\\r' into
       
   154     Internet CRLF end-of-line.
       
   155     """
       
   156     return re.sub(r'(?m)^\.', '..',
       
   157         re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data))
       
   158 
       
   159 
       
   160 try:
       
   161     import ssl
       
   162 except ImportError:
       
   163     _have_ssl = False
       
   164 else:
       
   165     class SSLFakeFile:
       
   166         """A fake file like object that really wraps a SSLObject.
       
   167 
       
   168         It only supports what is needed in smtplib.
       
   169         """
       
   170         def __init__(self, sslobj):
       
   171             self.sslobj = sslobj
       
   172 
       
   173         def readline(self):
       
   174             str = ""
       
   175             chr = None
       
   176             while chr != "\n":
       
   177                 chr = self.sslobj.read(1)
       
   178                 if not chr: break
       
   179                 str += chr
       
   180             return str
       
   181 
       
   182         def close(self):
       
   183             pass
       
   184 
       
   185     _have_ssl = True
       
   186 
       
   187 class SMTP:
       
   188     """This class manages a connection to an SMTP or ESMTP server.
       
   189     SMTP Objects:
       
   190         SMTP objects have the following attributes:
       
   191             helo_resp
       
   192                 This is the message given by the server in response to the
       
   193                 most recent HELO command.
       
   194 
       
   195             ehlo_resp
       
   196                 This is the message given by the server in response to the
       
   197                 most recent EHLO command. This is usually multiline.
       
   198 
       
   199             does_esmtp
       
   200                 This is a True value _after you do an EHLO command_, if the
       
   201                 server supports ESMTP.
       
   202 
       
   203             esmtp_features
       
   204                 This is a dictionary, which, if the server supports ESMTP,
       
   205                 will _after you do an EHLO command_, contain the names of the
       
   206                 SMTP service extensions this server supports, and their
       
   207                 parameters (if any).
       
   208 
       
   209                 Note, all extension names are mapped to lower case in the
       
   210                 dictionary.
       
   211 
       
   212         See each method's docstrings for details.  In general, there is a
       
   213         method of the same name to perform each SMTP command.  There is also a
       
   214         method called 'sendmail' that will do an entire mail transaction.
       
   215         """
       
   216     debuglevel = 0
       
   217     file = None
       
   218     helo_resp = None
       
   219     ehlo_msg = "ehlo"
       
   220     ehlo_resp = None
       
   221     does_esmtp = 0
       
   222 
       
   223     def __init__(self, host='', port=0, local_hostname=None,
       
   224                  timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
       
   225         """Initialize a new instance.
       
   226 
       
   227         If specified, `host' is the name of the remote host to which to
       
   228         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().
       
   233 
       
   234         """
       
   235         self.timeout = timeout
       
   236         self.esmtp_features = {}
       
   237         self.default_port = SMTP_PORT
       
   238         if host:
       
   239             (code, msg) = self.connect(host, port)
       
   240             if code != 220:
       
   241                 raise SMTPConnectError(code, msg)
       
   242         if local_hostname is not None:
       
   243             self.local_hostname = local_hostname
       
   244         else:
       
   245             # RFC 2821 says we should use the fqdn in the EHLO/HELO verb, and
       
   246             # if that can't be calculated, that we should use a domain literal
       
   247             # instead (essentially an encoded IP address like [A.B.C.D]).
       
   248             fqdn = socket.getfqdn()
       
   249             if '.' in fqdn:
       
   250                 self.local_hostname = fqdn
       
   251             else:
       
   252                 # We can't find an fqdn hostname, so use a domain literal
       
   253                 addr = '127.0.0.1'
       
   254                 try:
       
   255                     addr = socket.gethostbyname(socket.gethostname())
       
   256                 except socket.gaierror:
       
   257                     pass
       
   258                 self.local_hostname = '[%s]' % addr
       
   259 
       
   260     def set_debuglevel(self, debuglevel):
       
   261         """Set the debug output level.
       
   262 
       
   263         A non-false value results in debug messages for connection and for all
       
   264         messages sent to and received from the server.
       
   265 
       
   266         """
       
   267         self.debuglevel = debuglevel
       
   268 
       
   269     def _get_socket(self, port, host, timeout):
       
   270         # This makes it simpler for SMTP_SSL to use the SMTP connect code
       
   271         # 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):
       
   276         """Connect to a host on a given port.
       
   277 
       
   278         If the hostname ends with a colon (`:') followed by a number, and
       
   279         there is no port specified, that suffix will be stripped off and the
       
   280         number interpreted as the port number to use.
       
   281 
       
   282         Note: This method is automatically invoked by __init__, if a host is
       
   283         specified during instantiation.
       
   284 
       
   285         """
       
   286         if not port and (host.find(':') == host.rfind(':')):
       
   287             i = host.rfind(':')
       
   288             if i >= 0:
       
   289                 host, port = host[:i], host[i+1:]
       
   290                 try: port = int(port)
       
   291                 except ValueError:
       
   292                     raise socket.error, "nonnumeric port"
       
   293         if not port: port = self.default_port
       
   294         if self.debuglevel > 0: print>>stderr, 'connect:', (host, port)
       
   295         self.sock = self._get_socket(host, port, self.timeout)
       
   296         (code, msg) = self.getreply()
       
   297         if self.debuglevel > 0: print>>stderr, "connect:", msg
       
   298         return (code, msg)
       
   299 
       
   300     def send(self, str):
       
   301         """Send `str' to the server."""
       
   302         if self.debuglevel > 0: print>>stderr, 'send:', repr(str)
       
   303         if hasattr(self, 'sock') and self.sock:
       
   304             try:
       
   305                 self.sock.sendall(str)
       
   306             except socket.error:
       
   307                 self.close()
       
   308                 raise SMTPServerDisconnected('Server not connected')
       
   309         else:
       
   310             raise SMTPServerDisconnected('please run connect() first')
       
   311 
       
   312     def putcmd(self, cmd, args=""):
       
   313         """Send a command to the server."""
       
   314         if args == "":
       
   315             str = '%s%s' % (cmd, CRLF)
       
   316         else:
       
   317             str = '%s %s%s' % (cmd, args, CRLF)
       
   318         self.send(str)
       
   319 
       
   320     def getreply(self):
       
   321         """Get a reply from the server.
       
   322 
       
   323         Returns a tuple consisting of:
       
   324 
       
   325           - server response code (e.g. '250', or such, if all goes well)
       
   326             Note: returns -1 if it can't read response code.
       
   327 
       
   328           - server response string corresponding to response code (multiline
       
   329             responses are converted to a single, multiline string).
       
   330 
       
   331         Raises SMTPServerDisconnected if end-of-file is reached.
       
   332         """
       
   333         resp=[]
       
   334         if self.file is None:
       
   335             self.file = self.sock.makefile('rb')
       
   336         while 1:
       
   337             line = self.file.readline()
       
   338             if line == '':
       
   339                 self.close()
       
   340                 raise SMTPServerDisconnected("Connection unexpectedly closed")
       
   341             if self.debuglevel > 0: print>>stderr, 'reply:', repr(line)
       
   342             resp.append(line[4:].strip())
       
   343             code=line[:3]
       
   344             # Check that the error code is syntactically correct.
       
   345             # Don't attempt to read a continuation line if it is broken.
       
   346             try:
       
   347                 errcode = int(code)
       
   348             except ValueError:
       
   349                 errcode = -1
       
   350                 break
       
   351             # Check if multiline response.
       
   352             if line[3:4]!="-":
       
   353                 break
       
   354 
       
   355         errmsg = "\n".join(resp)
       
   356         if self.debuglevel > 0:
       
   357             print>>stderr, 'reply: retcode (%s); Msg: %s' % (errcode,errmsg)
       
   358         return errcode, errmsg
       
   359 
       
   360     def docmd(self, cmd, args=""):
       
   361         """Send a command, and return its response code."""
       
   362         self.putcmd(cmd,args)
       
   363         return self.getreply()
       
   364 
       
   365     # std smtp commands
       
   366     def helo(self, name=''):
       
   367         """SMTP 'helo' command.
       
   368         Hostname to send for this command defaults to the FQDN of the local
       
   369         host.
       
   370         """
       
   371         self.putcmd("helo", name or self.local_hostname)
       
   372         (code,msg)=self.getreply()
       
   373         self.helo_resp=msg
       
   374         return (code,msg)
       
   375 
       
   376     def ehlo(self, name=''):
       
   377         """ SMTP 'ehlo' command.
       
   378         Hostname to send for this command defaults to the FQDN of the local
       
   379         host.
       
   380         """
       
   381         self.esmtp_features = {}
       
   382         self.putcmd(self.ehlo_msg, name or self.local_hostname)
       
   383         (code,msg)=self.getreply()
       
   384         # According to RFC1869 some (badly written)
       
   385         # MTA's will disconnect on an ehlo. Toss an exception if
       
   386         # that happens -ddm
       
   387         if code == -1 and len(msg) == 0:
       
   388             self.close()
       
   389             raise SMTPServerDisconnected("Server not connected")
       
   390         self.ehlo_resp=msg
       
   391         if code != 250:
       
   392             return (code,msg)
       
   393         self.does_esmtp=1
       
   394         #parse the ehlo response -ddm
       
   395         resp=self.ehlo_resp.split('\n')
       
   396         del resp[0]
       
   397         for each in resp:
       
   398             # To be able to communicate with as many SMTP servers as possible,
       
   399             # we have to take the old-style auth advertisement into account,
       
   400             # because:
       
   401             # 1) Else our SMTP feature parser gets confused.
       
   402             # 2) There are some servers that only advertise the auth methods we
       
   403             #    support using the old style.
       
   404             auth_match = OLDSTYLE_AUTH.match(each)
       
   405             if auth_match:
       
   406                 # This doesn't remove duplicates, but that's no problem
       
   407                 self.esmtp_features["auth"] = self.esmtp_features.get("auth", "") \
       
   408                         + " " + auth_match.groups(0)[0]
       
   409                 continue
       
   410 
       
   411             # RFC 1869 requires a space between ehlo keyword and parameters.
       
   412             # It's actually stricter, in that only spaces are allowed between
       
   413             # parameters, but were not going to check for that here.  Note
       
   414             # 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)
       
   416             if m:
       
   417                 feature=m.group("feature").lower()
       
   418                 params=m.string[m.end("feature"):].strip()
       
   419                 if feature == "auth":
       
   420                     self.esmtp_features[feature] = self.esmtp_features.get(feature, "") \
       
   421                             + " " + params
       
   422                 else:
       
   423                     self.esmtp_features[feature]=params
       
   424         return (code,msg)
       
   425 
       
   426     def has_extn(self, opt):
       
   427         """Does the server support a given SMTP service extension?"""
       
   428         return opt.lower() in self.esmtp_features
       
   429 
       
   430     def help(self, args=''):
       
   431         """SMTP 'help' command.
       
   432         Returns help text from server."""
       
   433         self.putcmd("help", args)
       
   434         return self.getreply()[1]
       
   435 
       
   436     def rset(self):
       
   437         """SMTP 'rset' command -- resets session."""
       
   438         return self.docmd("rset")
       
   439 
       
   440     def noop(self):
       
   441         """SMTP 'noop' command -- doesn't do anything :>"""
       
   442         return self.docmd("noop")
       
   443 
       
   444     def mail(self,sender,options=[]):
       
   445         """SMTP 'mail' command -- begins mail xfer session."""
       
   446         optionlist = ''
       
   447         if options and self.does_esmtp:
       
   448             optionlist = ' ' + ' '.join(options)
       
   449         self.putcmd("mail", "FROM:%s%s" % (quoteaddr(sender) ,optionlist))
       
   450         return self.getreply()
       
   451 
       
   452     def rcpt(self,recip,options=[]):
       
   453         """SMTP 'rcpt' command -- indicates 1 recipient for this mail."""
       
   454         optionlist = ''
       
   455         if options and self.does_esmtp:
       
   456             optionlist = ' ' + ' '.join(options)
       
   457         self.putcmd("rcpt","TO:%s%s" % (quoteaddr(recip),optionlist))
       
   458         return self.getreply()
       
   459 
       
   460     def data(self,msg):
       
   461         """SMTP 'DATA' command -- sends message data to server.
       
   462 
       
   463         Automatically quotes lines beginning with a period per rfc821.
       
   464         Raises SMTPDataError if there is an unexpected reply to the
       
   465         DATA command; the return value from this method is the final
       
   466         response code received when the all data is sent.
       
   467         """
       
   468         self.putcmd("data")
       
   469         (code,repl)=self.getreply()
       
   470         if self.debuglevel >0 : print>>stderr, "data:", (code,repl)
       
   471         if code != 354:
       
   472             raise SMTPDataError(code,repl)
       
   473         else:
       
   474             q = quotedata(msg)
       
   475             if q[-2:] != CRLF:
       
   476                 q = q + CRLF
       
   477             q = q + "." + CRLF
       
   478             self.send(q)
       
   479             (code,msg)=self.getreply()
       
   480             if self.debuglevel >0 : print>>stderr, "data:", (code,msg)
       
   481             return (code,msg)
       
   482 
       
   483     def verify(self, address):
       
   484         """SMTP 'verify' command -- checks for address validity."""
       
   485         self.putcmd("vrfy", quoteaddr(address))
       
   486         return self.getreply()
       
   487     # a.k.a.
       
   488     vrfy=verify
       
   489 
       
   490     def expn(self, address):
       
   491         """SMTP 'expn' command -- expands a mailing list."""
       
   492         self.putcmd("expn", quoteaddr(address))
       
   493         return self.getreply()
       
   494 
       
   495     # some useful methods
       
   496 
       
   497     def ehlo_or_helo_if_needed(self):
       
   498         """Call self.ehlo() and/or self.helo() if needed.
       
   499 
       
   500         If there has been no previous EHLO or HELO command this session, this
       
   501         method tries ESMTP EHLO first.
       
   502 
       
   503         This method may raise the following exceptions:
       
   504 
       
   505          SMTPHeloError            The server didn't reply properly to
       
   506                                   the helo greeting.
       
   507         """
       
   508         if self.helo_resp is None and self.ehlo_resp is None:
       
   509             if not (200 <= self.ehlo()[0] <= 299):
       
   510                 (code, resp) = self.helo()
       
   511                 if not (200 <= code <= 299):
       
   512                     raise SMTPHeloError(code, resp)
       
   513 
       
   514     def login(self, user, password):
       
   515         """Log in on an SMTP server that requires authentication.
       
   516 
       
   517         The arguments are:
       
   518             - user:     The user name to authenticate with.
       
   519             - password: The password for the authentication.
       
   520 
       
   521         If there has been no previous EHLO or HELO command this session, this
       
   522         method tries ESMTP EHLO first.
       
   523 
       
   524         This method will return normally if the authentication was successful.
       
   525 
       
   526         This method may raise the following exceptions:
       
   527 
       
   528          SMTPHeloError            The server didn't reply properly to
       
   529                                   the helo greeting.
       
   530          SMTPAuthenticationError  The server didn't accept the username/
       
   531                                   password combination.
       
   532          SMTPException            No suitable authentication method was
       
   533                                   found.
       
   534         """
       
   535 
       
   536         def encode_cram_md5(challenge, user, password):
       
   537             challenge = base64.decodestring(challenge)
       
   538             response = user + " " + hmac.HMAC(password, challenge).hexdigest()
       
   539             return encode_base64(response, eol="")
       
   540 
       
   541         def encode_plain(user, password):
       
   542             return encode_base64("\0%s\0%s" % (user, password), eol="")
       
   543 
       
   544 
       
   545         AUTH_PLAIN = "PLAIN"
       
   546         AUTH_CRAM_MD5 = "CRAM-MD5"
       
   547         AUTH_LOGIN = "LOGIN"
       
   548 
       
   549         self.ehlo_or_helo_if_needed()
       
   550 
       
   551         if not self.has_extn("auth"):
       
   552             raise SMTPException("SMTP AUTH extension not supported by server.")
       
   553 
       
   554         # Authentication methods the server supports:
       
   555         authlist = self.esmtp_features["auth"].split()
       
   556 
       
   557         # List of authentication methods we support: from preferred to
       
   558         # less preferred methods. Except for the purpose of testing the weaker
       
   559         # ones, we prefer stronger methods like CRAM-MD5:
       
   560         preferred_auths = [AUTH_CRAM_MD5, AUTH_PLAIN, AUTH_LOGIN]
       
   561 
       
   562         # Determine the authentication method we'll use
       
   563         authmethod = None
       
   564         for method in preferred_auths:
       
   565             if method in authlist:
       
   566                 authmethod = method
       
   567                 break
       
   568 
       
   569         if authmethod == AUTH_CRAM_MD5:
       
   570             (code, resp) = self.docmd("AUTH", AUTH_CRAM_MD5)
       
   571             if code == 503:
       
   572                 # 503 == 'Error: already authenticated'
       
   573                 return (code, resp)
       
   574             (code, resp) = self.docmd(encode_cram_md5(resp, user, password))
       
   575         elif authmethod == AUTH_PLAIN:
       
   576             (code, resp) = self.docmd("AUTH",
       
   577                 AUTH_PLAIN + " " + encode_plain(user, password))
       
   578         elif authmethod == AUTH_LOGIN:
       
   579             (code, resp) = self.docmd("AUTH",
       
   580                 "%s %s" % (AUTH_LOGIN, encode_base64(user, eol="")))
       
   581             if code != 334:
       
   582                 raise SMTPAuthenticationError(code, resp)
       
   583             (code, resp) = self.docmd(encode_base64(password, eol=""))
       
   584         elif authmethod is None:
       
   585             raise SMTPException("No suitable authentication method found.")
       
   586         if code not in (235, 503):
       
   587             # 235 == 'Authentication successful'
       
   588             # 503 == 'Error: already authenticated'
       
   589             raise SMTPAuthenticationError(code, resp)
       
   590         return (code, resp)
       
   591 
       
   592     def starttls(self, keyfile = None, certfile = None):
       
   593         """Puts the connection to the SMTP server into TLS mode.
       
   594 
       
   595         If there has been no previous EHLO or HELO command this session, this
       
   596         method tries ESMTP EHLO first.
       
   597 
       
   598         If the server supports TLS, this will encrypt the rest of the SMTP
       
   599         session. If you provide the keyfile and certfile parameters,
       
   600         the identity of the SMTP server and client can be checked. This,
       
   601         however, depends on whether the socket module really checks the
       
   602         certificates.
       
   603 
       
   604         This method may raise the following exceptions:
       
   605 
       
   606          SMTPHeloError            The server didn't reply properly to
       
   607                                   the helo greeting.
       
   608         """
       
   609         self.ehlo_or_helo_if_needed()
       
   610         if not self.has_extn("starttls"):
       
   611             raise SMTPException("STARTTLS extension not supported by server.")
       
   612         (resp, reply) = self.docmd("STARTTLS")
       
   613         if resp == 220:
       
   614             if not _have_ssl:
       
   615                 raise RuntimeError("No SSL support included in this Python")
       
   616             self.sock = ssl.wrap_socket(self.sock, keyfile, certfile)
       
   617             self.file = SSLFakeFile(self.sock)
       
   618             # RFC 3207:
       
   619             # The client MUST discard any knowledge obtained from
       
   620             # the server, such as the list of SMTP service extensions,
       
   621             # which was not obtained from the TLS negotiation itself.
       
   622             self.helo_resp = None
       
   623             self.ehlo_resp = None
       
   624             self.esmtp_features = {}
       
   625             self.does_esmtp = 0
       
   626         return (resp, reply)
       
   627 
       
   628     def sendmail(self, from_addr, to_addrs, msg, mail_options=[],
       
   629                  rcpt_options=[]):
       
   630         """This command performs an entire mail transaction.
       
   631 
       
   632         The arguments are:
       
   633             - from_addr    : The address sending this mail.
       
   634             - to_addrs     : A list of addresses to send this mail to.  A bare
       
   635                              string will be treated as a list with 1 address.
       
   636             - msg          : The message to send.
       
   637             - mail_options : List of ESMTP options (such as 8bitmime) for the
       
   638                              mail command.
       
   639             - rcpt_options : List of ESMTP options (such as DSN commands) for
       
   640                              all the rcpt commands.
       
   641 
       
   642         If there has been no previous EHLO or HELO command this session, this
       
   643         method tries ESMTP EHLO first.  If the server does ESMTP, message size
       
   644         and each of the specified options will be passed to it.  If EHLO
       
   645         fails, HELO will be tried and ESMTP options suppressed.
       
   646 
       
   647         This method will return normally if the mail is accepted for at least
       
   648         one recipient.  It returns a dictionary, with one entry for each
       
   649         recipient that was refused.  Each entry contains a tuple of the SMTP
       
   650         error code and the accompanying error message sent by the server.
       
   651 
       
   652         This method may raise the following exceptions:
       
   653 
       
   654          SMTPHeloError          The server didn't reply properly to
       
   655                                 the helo greeting.
       
   656          SMTPRecipientsRefused  The server rejected ALL recipients
       
   657                                 (no mail was sent).
       
   658          SMTPSenderRefused      The server didn't accept the from_addr.
       
   659          SMTPDataError          The server replied with an unexpected
       
   660                                 error code (other than a refusal of
       
   661                                 a recipient).
       
   662 
       
   663         Note: the connection will be open even after an exception is raised.
       
   664 
       
   665         Example:
       
   666 
       
   667          >>> import smtplib
       
   668          >>> s=smtplib.SMTP("localhost")
       
   669          >>> tolist=["one@one.org","two@two.org","three@three.org","four@four.org"]
       
   670          >>> msg = '''\\
       
   671          ... From: Me@my.org
       
   672          ... Subject: testin'...
       
   673          ...
       
   674          ... This is a test '''
       
   675          >>> s.sendmail("me@my.org",tolist,msg)
       
   676          { "three@three.org" : ( 550 ,"User unknown" ) }
       
   677          >>> s.quit()
       
   678 
       
   679         In the above example, the message was accepted for delivery to three
       
   680         of the four addresses, and one was rejected, with the error code
       
   681         550.  If all addresses are accepted, then the method will return an
       
   682         empty dictionary.
       
   683 
       
   684         """
       
   685         self.ehlo_or_helo_if_needed()
       
   686         esmtp_opts = []
       
   687         if self.does_esmtp:
       
   688             # Hmmm? what's this? -ddm
       
   689             # self.esmtp_features['7bit']=""
       
   690             if self.has_extn('size'):
       
   691                 esmtp_opts.append("size=%d" % len(msg))
       
   692             for option in mail_options:
       
   693                 esmtp_opts.append(option)
       
   694 
       
   695         (code,resp) = self.mail(from_addr, esmtp_opts)
       
   696         if code != 250:
       
   697             self.rset()
       
   698             raise SMTPSenderRefused(code, resp, from_addr)
       
   699         senderrs={}
       
   700         if isinstance(to_addrs, basestring):
       
   701             to_addrs = [to_addrs]
       
   702         for each in to_addrs:
       
   703             (code,resp)=self.rcpt(each, rcpt_options)
       
   704             if (code != 250) and (code != 251):
       
   705                 senderrs[each]=(code,resp)
       
   706         if len(senderrs)==len(to_addrs):
       
   707             # the server refused all our recipients
       
   708             self.rset()
       
   709             raise SMTPRecipientsRefused(senderrs)
       
   710         (code,resp) = self.data(msg)
       
   711         if code != 250:
       
   712             self.rset()
       
   713             raise SMTPDataError(code, resp)
       
   714         #if we got here then somebody got our mail
       
   715         return senderrs
       
   716 
       
   717 
       
   718     def close(self):
       
   719         """Close the connection to the SMTP server."""
       
   720         if self.file:
       
   721             self.file.close()
       
   722         self.file = None
       
   723         if self.sock:
       
   724             self.sock.close()
       
   725         self.sock = None
       
   726 
       
   727 
       
   728     def quit(self):
       
   729         """Terminate the SMTP session."""
       
   730         res = self.docmd("quit")
       
   731         self.close()
       
   732         return res
       
   733 
       
   734 if _have_ssl:
       
   735 
       
   736     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         """
       
   744         def __init__(self, host='', port=0, local_hostname=None,
       
   745                      keyfile=None, certfile=None,
       
   746                      timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
       
   747             self.keyfile = keyfile
       
   748             self.certfile = certfile
       
   749             SMTP.__init__(self, host, port, local_hostname, timeout)
       
   750             self.default_port = SMTP_SSL_PORT
       
   751 
       
   752         def _get_socket(self, host, port, timeout):
       
   753             if self.debuglevel > 0: print>>stderr, 'connect:', (host, port)
       
   754             self.sock = socket.create_connection((host, port), timeout)
       
   755             self.sock = ssl.wrap_socket(self.sock, self.keyfile, self.certfile)
       
   756             self.file = SSLFakeFile(self.sock)
       
   757 
       
   758     __all__.append("SMTP_SSL")
       
   759 
       
   760 #
       
   761 # LMTP extension
       
   762 #
       
   763 LMTP_PORT = 2003
       
   764 
       
   765 class LMTP(SMTP):
       
   766     """LMTP - Local Mail Transfer Protocol
       
   767 
       
   768     The LMTP protocol, which is very similar to ESMTP, is heavily based
       
   769     on the standard SMTP client. It's common to use Unix sockets for LMTP,
       
   770     so our connect() method must support that as well as a regular
       
   771     host:port server. To specify a Unix socket, you must use an absolute
       
   772     path as the host, starting with a '/'.
       
   773 
       
   774     Authentication is supported, using the regular SMTP mechanism. When
       
   775     using a Unix socket, LMTP generally don't support or require any
       
   776     authentication, but your mileage might vary."""
       
   777 
       
   778     ehlo_msg = "lhlo"
       
   779 
       
   780     def __init__(self, host = '', port = LMTP_PORT, local_hostname = None):
       
   781         """Initialize a new instance."""
       
   782         SMTP.__init__(self, host, port, local_hostname)
       
   783 
       
   784     def connect(self, host = 'localhost', port = 0):
       
   785         """Connect to the LMTP daemon, on either a Unix or a TCP socket."""
       
   786         if host[0] != '/':
       
   787             return SMTP.connect(self, host, port)
       
   788 
       
   789         # Handle Unix-domain sockets.
       
   790         try:
       
   791             self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
       
   792             self.sock.connect(host)
       
   793         except socket.error, msg:
       
   794             if self.debuglevel > 0: print>>stderr, 'connect fail:', host
       
   795             if self.sock:
       
   796                 self.sock.close()
       
   797             self.sock = None
       
   798             raise socket.error, msg
       
   799         (code, msg) = self.getreply()
       
   800         if self.debuglevel > 0: print>>stderr, "connect:", msg
       
   801         return (code, msg)
       
   802 
       
   803 
       
   804 # Test the sendmail method, which tests most of the others.
       
   805 # Note: This always sends to localhost.
       
   806 if __name__ == '__main__':
       
   807     import sys
       
   808 
       
   809     def prompt(prompt):
       
   810         sys.stdout.write(prompt + ": ")
       
   811         return sys.stdin.readline().strip()
       
   812 
       
   813     fromaddr = prompt("From")
       
   814     toaddrs  = prompt("To").split(',')
       
   815     print "Enter message, end with ^D:"
       
   816     msg = ''
       
   817     while 1:
       
   818         line = sys.stdin.readline()
       
   819         if not line:
       
   820             break
       
   821         msg = msg + line
       
   822     print "Message length is %d" % len(msg)
       
   823 
       
   824     server = SMTP('localhost')
       
   825     server.set_debuglevel(1)
       
   826     server.sendmail(fromaddr, toaddrs, msg)
       
   827     server.quit()