class CustomHTTPSConnection(httplib.HTTPSConnection): def __init__(self, *args, **kwargs): httplib.HTTPSConnection.__init__(self, *args, **kwargs) self.context = None if HAS_SSLCONTEXT: self.context = create_default_context() elif HAS_URLLIB3_PYOPENSSLCONTEXT: self.context = PyOpenSSLContext(PROTOCOL) if self.context and self.cert_file: self.context.load_cert_chain(self.cert_file, self.key_file) def connect(self): "Connect to a host on a given (SSL) port." if hasattr(self, 'source_address'): sock = socket.create_connection((self.host, self.port), self.timeout, self.source_address) else: sock = socket.create_connection((self.host, self.port), self.timeout) server_hostname = self.host # Note: self._tunnel_host is not available on py < 2.6 but this code # isn't used on py < 2.6 (lack of create_connection) if self._tunnel_host: self.sock = sock self._tunnel() server_hostname = self._tunnel_host if HAS_SSLCONTEXT or HAS_URLLIB3_PYOPENSSLCONTEXT: self.sock = self.context.wrap_socket(sock, server_hostname=server_hostname) elif HAS_URLLIB3_SSL_WRAP_SOCKET: self.sock = ssl_wrap_socket(sock, keyfile=self.key_file, cert_reqs=ssl.CERT_NONE, certfile=self.cert_file, ssl_version=PROTOCOL, server_hostname=server_hostname) else: self.sock = ssl.wrap_socket(sock, keyfile=self.key_file, certfile=self.cert_file, ssl_version=PROTOCOL)
class CustomHTTPSConnection(httplib.HTTPSConnection): def __init__(self, *args, **kwargs): httplib.HTTPSConnection.__init__(self, *args, **kwargs) self.context = None if HAS_SSLCONTEXT: self.context = create_default_context() elif HAS_URLLIB3_PYOPENSSLCONTEXT: self.context = PyOpenSSLContext(PROTOCOL) if self.context and self.cert_file: self.context.load_cert_chain(self.cert_file, self.key_file) def connect(self): "Connect to a host on a given (SSL) port." if hasattr(self, 'source_address'): sock = socket.create_connection((self.host, self.port), self.timeout, self.source_address) else: sock = socket.create_connection((self.host, self.port), self.timeout) server_hostname = self.host # Note: self._tunnel_host is not available on py < 2.6 but this code # isn't used on py < 2.6 (lack of create_connection) if self._tunnel_host: self.sock = sock self._tunnel() server_hostname = self._tunnel_host if HAS_SSLCONTEXT or HAS_URLLIB3_PYOPENSSLCONTEXT: self.sock = self.context.wrap_socket(sock, server_hostname=server_hostname) elif HAS_URLLIB3_SSL_WRAP_SOCKET: self.sock = ssl_wrap_socket(sock, keyfile=self.key_file, cert_reqs=ssl.CERT_NONE, certfile=self.cert_file, ssl_version=PROTOCOL, server_hostname=server_hostname) else: self.sock = ssl.wrap_socket(sock, keyfile=self.key_file, certfile=self.cert_file, ssl_version=PROTOCOL)
def connect(self, host=None, port=None, user=None, password=None, encryption=None, smtp_from=None, ssl_certificate=None, ssl_private_key=None, smtp_debug=False, mail_server_id=None): """Returns a new SMTP connection to the given SMTP server. When running in test mode, this method does nothing and returns `None`. :param host: host or IP of SMTP server to connect to, if mail_server_id not passed :param int port: SMTP port to connect to :param user: optional username to authenticate with :param password: optional password to authenticate with :param string encryption: optional, ``'ssl'`` | ``'starttls'`` :param smtp_from: FROM SMTP envelop, used to find the best mail server :param ssl_certificate: filename of the SSL certificate used for authentication Used when no mail server is given and overwrite the odoo-bin argument "smtp_ssl_certificate" :param ssl_private_key: filename of the SSL private key used for authentication Used when no mail server is given and overwrite the odoo-bin argument "smtp_ssl_private_key" :param bool smtp_debug: toggle debugging of SMTP sessions (all i/o will be output in logs) :param mail_server_id: ID of specific mail server to use (overrides other parameters) """ # Do not actually connect while running in test mode if self._is_test_mode(): return mail_server = smtp_encryption = None if mail_server_id: mail_server = self.sudo().browse(mail_server_id) elif not host: mail_server, smtp_from = self.sudo()._find_mail_server(smtp_from) ssl_context = None if mail_server: smtp_server = mail_server.smtp_host smtp_port = mail_server.smtp_port if mail_server.smtp_authentication == "login": smtp_user = mail_server.smtp_user smtp_password = mail_server.smtp_pass else: smtp_user = None smtp_password = None smtp_encryption = mail_server.smtp_encryption smtp_debug = smtp_debug or mail_server.smtp_debug from_filter = mail_server.from_filter if (mail_server.smtp_authentication == "certificate" and mail_server.smtp_ssl_certificate and mail_server.smtp_ssl_private_key): try: ssl_context = PyOpenSSLContext(ssl.PROTOCOL_TLS) smtp_ssl_certificate = base64.b64decode( mail_server.smtp_ssl_certificate) certificate = SSLCrypto.load_certificate( FILETYPE_PEM, smtp_ssl_certificate) smtp_ssl_private_key = base64.b64decode( mail_server.smtp_ssl_private_key) private_key = SSLCrypto.load_privatekey( FILETYPE_PEM, smtp_ssl_private_key) ssl_context._ctx.use_certificate(certificate) ssl_context._ctx.use_privatekey(private_key) # Check that the private key match the certificate ssl_context._ctx.check_privatekey() except SSLCryptoError as e: raise UserError( _( 'The private key or the certificate is not a valid file. \n%s', str(e))) except SSLError as e: raise UserError( _( 'Could not load your certificate / private key. \n%s', str(e))) else: # we were passed individual smtp parameters or nothing and there is no default server smtp_server = host or tools.config.get('smtp_server') smtp_port = tools.config.get('smtp_port', 25) if port is None else port smtp_user = user or tools.config.get('smtp_user') smtp_password = password or tools.config.get('smtp_password') from_filter = self.env['ir.config_parameter'].sudo().get_param( 'mail.default.from_filter', tools.config.get('from_filter')) smtp_encryption = encryption if smtp_encryption is None and tools.config.get('smtp_ssl'): smtp_encryption = 'starttls' # smtp_ssl => STARTTLS as of v7 smtp_ssl_certificate_filename = ssl_certificate or tools.config.get( 'smtp_ssl_certificate_filename') smtp_ssl_private_key_filename = ssl_private_key or tools.config.get( 'smtp_ssl_private_key_filename') if smtp_ssl_certificate_filename and smtp_ssl_private_key_filename: try: ssl_context = PyOpenSSLContext(ssl.PROTOCOL_TLS) ssl_context.load_cert_chain( smtp_ssl_certificate_filename, keyfile=smtp_ssl_private_key_filename) # Check that the private key match the certificate ssl_context._ctx.check_privatekey() except SSLCryptoError as e: raise UserError( _( 'The private key or the certificate is not a valid file. \n%s', str(e))) except SSLError as e: raise UserError( _( 'Could not load your certificate / private key. \n%s', str(e))) if not smtp_server: raise UserError((_("Missing SMTP Server") + "\n" + _("Please define at least one SMTP server, " "or provide the SMTP parameters explicitly."))) if smtp_encryption == 'ssl': if 'SMTP_SSL' not in smtplib.__all__: raise UserError( _("Your Odoo Server does not support SMTP-over-SSL. " "You could use STARTTLS instead. " "If SSL is needed, an upgrade to Python 2.6 on the server-side " "should do the trick.")) connection = smtplib.SMTP_SSL(smtp_server, smtp_port, timeout=SMTP_TIMEOUT) else: connection = smtplib.SMTP(smtp_server, smtp_port, timeout=SMTP_TIMEOUT) connection.set_debuglevel(smtp_debug) if smtp_encryption == 'starttls': # starttls() will perform ehlo() if needed first # and will discard the previous list of services # after successfully performing STARTTLS command, # (as per RFC 3207) so for example any AUTH # capability that appears only on encrypted channels # will be correctly detected for next step connection.starttls(context=ssl_context) if smtp_user: # Attempt authentication - will raise if AUTH service not supported local, at, domain = smtp_user.rpartition('@') if at: smtp_user = local + at + idna.encode(domain).decode('ascii') connection.login(smtp_user, smtp_password or '') # Some methods of SMTP don't check whether EHLO/HELO was sent. # Anyway, as it may have been sent by login(), all subsequent usages should consider this command as sent. connection.ehlo_or_helo_if_needed() # Store the "from_filter" of the mail server / odoo-bin argument to know if we # need to change the FROM headers or not when we will prepare the mail message connection.from_filter = from_filter connection.smtp_from = smtp_from return connection