From 06b50f3ac680dba68b525bdecd2357c3c3286189 Mon Sep 17 00:00:00 2001 From: Frank Brehm Date: Sun, 27 Mar 2022 15:26:53 +0200 Subject: [PATCH] Adding lib/pp_admintools/mail_app.py for class BaseMailApplication --- lib/pp_admintools/mail_app.py | 261 ++++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 lib/pp_admintools/mail_app.py diff --git a/lib/pp_admintools/mail_app.py b/lib/pp_admintools/mail_app.py new file mode 100644 index 0000000..d618437 --- /dev/null +++ b/lib/pp_admintools/mail_app.py @@ -0,0 +1,261 @@ +# -*- 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 -- 2.39.5