source: titan/mediathek/localhoster/lib/python2.7/smtplib.py @ 40114

Last change on this file since 40114 was 40094, checked in by obi, 5 years ago

tithek add yoztube-dl support

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