From 86ae4b90a30c23a478bb5c08a3e613ec1ddb1574 Mon Sep 17 00:00:00 2001
From: Olivier Dony <odo@odoo.com>
Date: Mon, 14 May 2018 12:02:34 +0200
Subject: [PATCH] [IMP] mail: make connection test attempt to send an email

The "connection test" was only attempting to establish a socket to the
SMTP server and then trying to login, if credentials were setup.

This would catch basic config errors but would fail to detect setups
were the SMTP server would forbid relaying, because of missing
credentials, missing STARTSSL mode, and other more complex cases.

By actually beginning a real SMTP session and sending the MAIL FROM,
RCPT TO and DATA command, we make sure that the server appeas willing to
relay emails from the current user address to an arbitrary recipient
address. Then we close the session before actually sending the message.

In order to have a valid destination domain with a valid MX server, we
use an odoo.com recipient address for the test, which should prove that
the destination server will relay to arbitrary addresses (unless the
destination server is an Odoo.com MX server ;-))
---
 odoo/addons/base/models/ir_mail_server.py | 27 ++++++++++++++++++++++-
 1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/odoo/addons/base/models/ir_mail_server.py b/odoo/addons/base/models/ir_mail_server.py
index 75d7f082196d..97f100fb94bc 100644
--- a/odoo/addons/base/models/ir_mail_server.py
+++ b/odoo/addons/base/models/ir_mail_server.py
@@ -156,12 +156,37 @@ class IrMailServer(models.Model):
             smtp = False
             try:
                 smtp = self.connect(mail_server_id=server.id)
+                # simulate sending an email from current user's address - without sending it!
+                email_from, email_to = self.env.user.email, 'noreply@odoo.com'
+                if not email_from:
+                    raise UserError(_('Please configure an email on the current user to simulate '
+                                      'sending an email message via this outgoing server'))
+                # Testing the MAIL FROM step should detect sender filter problems
+                (code, repl) = smtp.mail(email_from)
+                if code != 250:
+                    raise UserError(_('The server refused the sender address (%(email_from)s) '
+                                      'with error %(repl)s') % locals())
+                # Testing the RCPT TO step should detect most relaying problems
+                (code, repl) = smtp.rcpt(email_to)
+                if code not in (250, 251):
+                    raise UserError(_('The server refused the test recipient (%(email_to)s) '
+                                      'with error %(repl)s') % locals())
+                # Beginning the DATA step should detect some deferred rejections
+                # Can't use self.data() as it would actually send the mail!
+                smtp.putcmd("data")
+                (code, repl) = smtp.getreply()
+                if code != 354:
+                    raise UserError(_('The server refused the test connection '
+                                      'with error %(repl)s') % locals())
+            except UserError as e:
+                # let UserErrors (messages) bubble up
+                raise e
             except Exception as e:
                 raise UserError(_("Connection Test Failed! Here is what we got instead:\n %s") % ustr(e))
             finally:
                 try:
                     if smtp:
-                        smtp.quit()
+                        smtp.close()
                 except Exception:
                     # ignored, just a consequence of the previous exception
                     pass
-- 
GitLab