From 74174dc70bd1547f10a606bb1308642a61d36564 Mon Sep 17 00:00:00 2001
From: Jan Mach <jan.mach@cesnet.cz>
Date: Sat, 16 Oct 2021 20:58:49 +0200
Subject: [PATCH] Support for sending emails in development via SMTP.

(Redmine issue: #7041)
---
 conf/core/common.json.conf      |  3 +++
 conf/mentat-informant.py.conf   | 18 +++++++++++++++++
 conf/mentat-reporter.py.conf    | 18 +++++++++++++++++
 lib/mentat/plugin/app/mailer.py | 34 +++++++++++++++++++++++++++++++--
 4 files changed, 71 insertions(+), 2 deletions(-)

diff --git a/conf/core/common.json.conf b/conf/core/common.json.conf
index 4ff7a58f3..93493b904 100644
--- a/conf/core/common.json.conf
+++ b/conf/core/common.json.conf
@@ -23,6 +23,9 @@
     "mail_return_path": "root",
     "mail_admin": "root",
     "mail_test_mode": true,
+    "mail_dev_mode": false,
+    "mail_dev_server": "localhost",
+    "mail_dev_port": 1025,
 
     #---------------------------------------------------------------------------
     # Common useful piper daemon configurations
diff --git a/conf/mentat-informant.py.conf b/conf/mentat-informant.py.conf
index f4fb91684..f79735735 100644
--- a/conf/mentat-informant.py.conf
+++ b/conf/mentat-informant.py.conf
@@ -98,6 +98,24 @@
     #
     #"mail_test_mode": false,
 
+    # Send emails in development mode (flag). Will use localhost:1025 as mail server.
+    #   default: false (set in core/common.json.conf)
+    #   type:    bool
+    #
+    #"mail_dev_mode": true,
+
+    # Hostname for development SMTP server.
+    #   default: "localhost" (set in core/common.json.conf)
+    #   type:    string
+    #
+    #"mail_dev_server": "localhost",
+
+    # Port for development SMTP server.
+    #   default: 1025 (set in core/common.json.conf)
+    #   type:    integer
+    #
+    #"mail_dev_port": 1025,
+
     #---------------------------------------------------------------------------
     # Common script configurations
     #---------------------------------------------------------------------------
diff --git a/conf/mentat-reporter.py.conf b/conf/mentat-reporter.py.conf
index cdf350fea..800b04989 100644
--- a/conf/mentat-reporter.py.conf
+++ b/conf/mentat-reporter.py.conf
@@ -132,6 +132,24 @@
     #
     #"mail_test_mode": false,
 
+    # Send emails in development mode (flag). Will use localhost:1025 as mail server.
+    #   default: false (set in core/common.json.conf)
+    #   type:    bool
+    #
+    #"mail_dev_mode": true,
+
+    # Hostname for development SMTP server.
+    #   default: "localhost" (set in core/common.json.conf)
+    #   type:    string
+    #
+    #"mail_dev_server": "localhost",
+
+    # Port for development SMTP server.
+    #   default: 1025 (set in core/common.json.conf)
+    #   type:    integer
+    #
+    #"mail_dev_port": 1025,
+
     #---------------------------------------------------------------------------
     # Common script configurations
     #---------------------------------------------------------------------------
diff --git a/lib/mentat/plugin/app/mailer.py b/lib/mentat/plugin/app/mailer.py
index a5406e5ed..57ad0f548 100644
--- a/lib/mentat/plugin/app/mailer.py
+++ b/lib/mentat/plugin/app/mailer.py
@@ -32,6 +32,7 @@ __author__  = "Jan Mach <jan.mach@cesnet.cz>"
 __credits__ = "Pavel Kácha <pavel.kacha@cesnet.cz>, Andrea Kropáčová <andrea.kropacova@cesnet.cz>"
 
 
+import smtplib
 import weakref
 from subprocess import Popen, PIPE
 
@@ -61,6 +62,9 @@ class MailerPlugin(pyzenkit.baseapp.ZenAppPlugin):
     CONFIG_MAIL_RETURN_PATH = 'mail_return_path'
     CONFIG_MAIL_ADMIN       = 'mail_admin'
     CONFIG_MAIL_TEST_MODE   = 'mail_test_mode'
+    CONFIG_MAIL_DEV_MODE    = 'mail_dev_mode'
+    CONFIG_MAIL_DEV_SERVER  = 'mail_dev_server'
+    CONFIG_MAIL_DEV_PORT    = 'mail_dev_port'
 
     def __init__(self, settings = None):
         """
@@ -93,8 +97,11 @@ class MailerPlugin(pyzenkit.baseapp.ZenAppPlugin):
         arggroup_plugin.add_argument('--mail-reply-to',    type = str, default = None, help = 'reply to email address (repeatable)', action='append')
         arggroup_plugin.add_argument('--mail-return-path', type = str, default = None, help = 'return address for undeliverable emails')
         arggroup_plugin.add_argument('--mail-admin',       type = str, default = None, help = 'admin email address (repeatable)', action='append')
+        arggroup_plugin.add_argument('--mail-dev-server',  type = str, default = None, help = 'development SMTP server hostname')
+        arggroup_plugin.add_argument('--mail-dev-port',    type = int, default = None, help = 'development SMTP server port')
 
         arggroup_plugin.add_argument('--mail-test-mode', help = 'send emails in test mode (flag)', action = 'store_true', default = None)
+        arggroup_plugin.add_argument('--mail-dev-mode',  help = 'send emails in development mode (flag)', action = 'store_true', default = None)
 
         return argparser
 
@@ -111,7 +118,10 @@ class MailerPlugin(pyzenkit.baseapp.ZenAppPlugin):
             self.CONFIG_MAIL_REPLY_TO:    None,
             self.CONFIG_MAIL_RETURN_PATH: None,
             self.CONFIG_MAIL_ADMIN:       'root',
-            self.CONFIG_MAIL_TEST_MODE:   False
+            self.CONFIG_MAIL_TEST_MODE:   False,
+            self.CONFIG_MAIL_DEV_MODE:    False,
+            self.CONFIG_MAIL_DEV_SERVER:  None,
+            self.CONFIG_MAIL_DEV_PORT:    None
         })
         return config
 
@@ -155,6 +165,22 @@ class MailerPlugin(pyzenkit.baseapp.ZenAppPlugin):
         with Popen(["/usr/sbin/sendmail", "-t", "-oi", "-f", envelope_from], stdin=PIPE) as proc:
             proc.communicate(bytes(email.as_string(), 'UTF-8'))
 
+    @staticmethod
+    def mail_smtplib(email):
+        """
+        Send given email via smtplib.
+
+        :param mentat.emails.base.BaseEmail email: Email object.
+        """
+        envelope_from = email.get_header(
+            'return-path',
+            email.get_header('from')
+        )
+        s = self.get_application().c(self.CONFIG_MAIL_DEV_SERVER)
+        p = self.get_application().c(self.CONFIG_MAIL_DEV_PORT)
+        with smtplib.SMTP(s, p) as smtp:
+            smtp.send_message(email, from_addr = envelope_from)
+
     #---------------------------------------------------------------------------
 
     def email_send(self, email_class, email_headers, email_params, flag_redirect = False):
@@ -214,6 +240,10 @@ class MailerPlugin(pyzenkit.baseapp.ZenAppPlugin):
 
         self.get_application().logger.info("Sending email: '{}'".format(str(email_headers)))
         msg = email_class(email_headers, **email_params)
-        self.mail_sendmail(msg)
+
+        if self.get_application().c(self.CONFIG_MAIL_DEV_MODE):
+            self.mail_smtplib(msg)
+        else
+            self.mail_sendmail(msg)
 
         return msg
-- 
GitLab