comparison mercurial/mail.py @ 18885:cf1304fbc184

smtp: add the class to verify the certificate of the SMTP server for STARTTLS Original "smtplib.SMTP" has no route to pass "ca_certs" and "cert_reqs" arguments to underlying SSL socket creation. This causes that "getpeercert()" on SSL socket returns empty dict, so the peer certificate for STARTTLS can't be verified. This patch introduces the "STARTTLS" class derived from "smtplib.SMTP" to pass "ca_certs" and "cert_reqs" arguments to underlying SSL socket creation. Almost all code of "starttls()" in this class is imported from "smtplib.SMTP" of Python 2.7.3, but it differs from original code in points below: - "self.ehlo_or_helo_if_needed()" invocation is omitted, because: - "ehlo_or_helo_if_needed()" is available with Python 2.6 or later, and - "ehlo()" is explicitly invoked in "mercurial.mail._smtp()" - "if not _have_ssl:" check is omitted, because: - "_have_ssl" is available with Python 2.6 or later, and - same checking is done in "mercurial.sslutil.ssl_wrap_socket()" - "ssl.wrap_socket()" is replaced by "sslutil.ssl_wrap_socket()" for compatibility between Python versions - use "sock.recv()" also as "sock.read()", if "sock" doesn't have "read()" method with Python 2.5.x or earlier, "sslutil.ssl_wrap_socket()" returns "httplib.FakeSocket"-ed object, and it doesn't have "read()" method, which is invoked via "smtplib.SSLFakeFile".
author FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
date Tue, 26 Mar 2013 02:27:23 +0900
parents 72803c8edaa4
children 14a60a0f7122
comparison
equal deleted inserted replaced
18884:0615b22da148 18885:cf1304fbc184
4 # 4 #
5 # This software may be used and distributed according to the terms of the 5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version. 6 # GNU General Public License version 2 or any later version.
7 7
8 from i18n import _ 8 from i18n import _
9 import util, encoding 9 import util, encoding, sslutil
10 import os, smtplib, socket, quopri, time 10 import os, smtplib, socket, quopri, time
11 import email.Header, email.MIMEText, email.Utils 11 import email.Header, email.MIMEText, email.Utils
12 12
13 _oldheaderinit = email.Header.Header.__init__ 13 _oldheaderinit = email.Header.Header.__init__
14 def _unifiedheaderinit(self, *args, **kw): 14 def _unifiedheaderinit(self, *args, **kw):
27 # override continuation_ws 27 # override continuation_ws
28 kw['continuation_ws'] = ' ' 28 kw['continuation_ws'] = ' '
29 _oldheaderinit(self, *args, **kw) 29 _oldheaderinit(self, *args, **kw)
30 30
31 email.Header.Header.__dict__['__init__'] = _unifiedheaderinit 31 email.Header.Header.__dict__['__init__'] = _unifiedheaderinit
32
33 class STARTTLS(smtplib.SMTP):
34 '''Derived class to verify the peer certificate for STARTTLS.
35
36 This class allows to pass any keyword arguments to SSL socket creation.
37 '''
38 def __init__(self, sslkwargs, **kwargs):
39 smtplib.SMTP.__init__(self, **kwargs)
40 self._sslkwargs = sslkwargs
41
42 def starttls(self, keyfile=None, certfile=None):
43 if not self.has_extn("starttls"):
44 msg = "STARTTLS extension not supported by server"
45 raise smtplib.SMTPException(msg)
46 (resp, reply) = self.docmd("STARTTLS")
47 if resp == 220:
48 self.sock = sslutil.ssl_wrap_socket(self.sock, keyfile, certfile,
49 **self._sslkwargs)
50 if not util.safehasattr(self.sock, "read"):
51 # using httplib.FakeSocket with Python 2.5.x or earlier
52 self.sock.read = self.sock.recv
53 self.file = smtplib.SSLFakeFile(self.sock)
54 self.helo_resp = None
55 self.ehlo_resp = None
56 self.esmtp_features = {}
57 self.does_esmtp = 0
58 return (resp, reply)
32 59
33 def _smtp(ui): 60 def _smtp(ui):
34 '''build an smtp connection and return a function to send mail''' 61 '''build an smtp connection and return a function to send mail'''
35 local_hostname = ui.config('smtp', 'local_hostname') 62 local_hostname = ui.config('smtp', 'local_hostname')
36 tls = ui.config('smtp', 'tls', 'none') 63 tls = ui.config('smtp', 'tls', 'none')