--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+@author: Frank Brehm
+@contact: frank.brehm@pixelpark.com
+@copyright: © 2022 by Frank Brehm, Berlin
+@summary: A base module for application classes with mail sending support
+"""
+from __future__ import absolute_import
+
+# Standard modules
+import logging
+import copy
+
+from email.mime.text import MIMEText
+from email import charset
+
+import smtplib
+
+# Own modules
+from . import __version__ as GLOBAL_VERSION
+
+from fb_tools.common import pp
+
+from fb_tools.cfg_app import FbConfigApplication
+
+from fb_tools.errors import FbAppError
+
+from fb_tools.xlate import format_list
+
+from .xlate import XLATOR
+
+from .mail_config import MailConfigError, MailConfiguration
+from .mail_config import VALID_MAIL_METHODS, MAX_PORT_NUMBER
+
+__version__ = '0.1.1'
+LOG = logging.getLogger(__name__)
+
+_ = XLATOR.gettext
+ngettext = XLATOR.ngettext
+
+
+# =============================================================================
+class MailAppError(FbAppError):
+ """ Base exception class for all exceptions in all mail sending application classes."""
+ pass
+
+# =============================================================================
+class BaseMailApplication(FbConfigApplication):
+ """
+ Base class for all mail sending application classes.
+ """
+
+ charset.add_charset('utf-8', charset.SHORTEST, charset.QP)
+
+ # -------------------------------------------------------------------------
+ def __init__(
+ self, appname=None, verbose=0, version=GLOBAL_VERSION, base_dir=None,
+ cfg_class=MailConfiguration, initialized=False, usage=None, description=None,
+ argparse_epilog=None, argparse_prefix_chars='-', env_prefix=None):
+
+ super(BaseMailApplication, self).__init__(
+ appname=appname, verbose=verbose, version=version, base_dir=base_dir,
+ description=description, cfg_class=cfg_class, initialized=False,
+ )
+
+ # -------------------------------------------------------------------------
+ def post_init(self):
+ """
+ Method to execute before calling run(). Here could be done some
+ finishing actions after reading in commandline parameters,
+ configuration a.s.o.
+
+ This method could be overwritten by descendant classes, these
+ methhods should allways include a call to post_init() of the
+ parent class.
+
+ """
+
+ self.initialized = False
+
+ super(BaseMailApplication, self).post_init()
+
+ v = getattr(self.args, 'mail_method', None)
+ if v:
+ self.cfg.mail_method = v
+
+ v = getattr(self.args, 'mail_server', None)
+ if v:
+ self.cfg.mail_server = v
+
+ v = getattr(self.args, 'smtp_port', None)
+ if v is not None:
+ if v <= 0 or v > MAX_PORT_NUMBER:
+ msg = _("Got invalid SMTP port number {!r}.").format(v)
+ LOG.error(msg)
+ else:
+ self.cfg.smtp_port = v
+
+ self._perform_cmdline_mail_from()
+ self._perform_cmdline_mail_rcpt()
+ self._perform_cmdline_mail_cc()
+ self._perform_cmdline_reply_to()
+
+ # -------------------------------------------------------------------------
+ def _perform_cmdline_mail_from(self):
+
+ v = getattr(self.args, 'mail_from', None)
+ if not v:
+ return
+
+ if not MailAddress.valid_address(v):
+ msg = _("Got invalid mail from address {!r}.").format(v)
+ LOG.error(msg)
+ self.exit(1)
+
+ self.cfg.mail_from = v
+
+ # -------------------------------------------------------------------------
+ def _perform_cmdline_mail_rcpt(self):
+
+ v = getattr(self.args, 'mail_recipients', None)
+ if v not None:
+ return
+
+ recipients = []
+ bad_rcpts = []
+
+ for addr in v:
+ if MailAddress.valid_address(addr):
+ recipients.append(addr)
+ else:
+ bad_rcpts.append(addr)
+
+ if bad_rcpts:
+ msg = _("Got invalid recipient mail addresses:")
+ msg += " " + format_list(bad_rcpts, , do_repr=True)
+ LOG.error(msg)
+ self.exit(1)
+
+ self.cfg.mail_recipients = copy.copy(recipients)
+
+ if not self.cfg.mail_recipients:
+ msg = ("Did not found any valid recipient mail addresses.")
+ LOG.error(msg)
+
+ # -------------------------------------------------------------------------
+ def _perform_cmdline_mail_cc(self):
+
+ v = getattr(self.args, 'mail_cc', None)
+ if v is None:
+ return
+
+ cc = []
+ bad_cc = []
+
+ for addr in v:
+ if MailAddress.valid_address(addr):
+ cc.append(addr)
+ else:
+ bad_cc.append(addr)
+
+ if bad_cc:
+ msg = _("Got invalid cc mail addresses:")
+ msg += " " + format_list(bad_cc, , do_repr=True)
+ LOG.error(msg)
+ self.exit(1)
+
+ self.cfg.mail_cc = copy.copy(cc)
+
+ # -------------------------------------------------------------------------
+ def _perform_cmdline_reply_to(self):
+
+ v = getattr(self.args, 'mail_reply_to', None)
+ if not v:
+ return
+
+ if not MailAddress.valid_address(v):
+ msg = _("Got invalid reply mail address {!r}.").format(v)
+ LOG.error(msg)
+ self.exit(1)
+
+ self.cfg.reply_to = v
+
+ # -------------------------------------------------------------------------
+ def init_arg_parser(self):
+ """
+ Public available method to initiate the argument parser.
+ """
+
+ super(BaseMailApplication, self).init_arg_parser()
+
+ mail_group = self.arg_parser.add_argument_group(_('Mailing options'))
+
+ mail_group.add_argument(
+ '--from', '--mail-from',
+ metavar=_("ADDRESS"), dest="mail_from",
+ help=_(
+ "Sender mail address for mails generated by this script. "
+ "Default: {!r}").format(self.cfg.mail_from),
+ )
+
+ mail_group.add_argument(
+ '--recipients', '--mail-recipients',
+ metavar=_("ADDRESS"), nargs='+', dest="mail_recipients",
+ help=_("Mail addresses of all recipients for mails generated by this script.")
+ )
+
+ mail_group.add_argument(
+ '--cc', '--mail-cc',
+ metavar=_("ADDRESS"), nargs='*', dest="mail_cc",
+ help=_("Mail addresses of all CC recipients for mails generated by this script.")
+ )
+
+ mail_group.add_argument(
+ '--reply-to', '--mail-reply-to',
+ metavar=_("ADDRESS"), dest="mail_reply_to",
+ help=_("Reply mail address for mails generated by this script.")
+ )
+
+ method_list = format_list(VALID_MAIL_METHODS, do_repr=True)
+ mail_group.add_argument(
+ '--mail-method',
+ metavar=_("METHOD"), choices=VALID_MAIL_METHODS, dest="mail_method",
+ help=_(
+ "Method for sending the mails generated by this script. "
+ "Valid values: {v}, default: {d!r}.").format(
+ v=method_list, d=self.cfg.mail_method)
+ )
+
+ mail_group.add_argument(
+ '--mail-server',
+ metavar=_("SERVER"), dest="mail_server",
+ help=_(
+ "Mail server for submitting generated by this script if "
+ "the mail method of this script is 'smtp'. Default: {!r}.").format(
+ self.cfg.mail_server)
+ )
+
+ mail_group.add_argument(
+ '--smtp-port',
+ metavar=_("PORT"), type=int, dest='smtp_port',
+ help=_(
+ "The port to use for submitting generated by this script if "
+ "the mail method of this script is 'smtp'. Default: {}.").format(self.cfg.smtp_port)
+ )
+
+ # -------------------------------------------------------------------------
+ def perform_arg_parser(self):
+
+ if self.verbose > 2:
+ LOG.debug(_("Got command line arguments:") + '\n' + pp(self.args))
+
+
+# =============================================================================
+if __name__ == "__main__":
+
+ pass
+
+# =============================================================================
+
+# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 list