# -*- coding: utf-8 -*-
"""
+@summary: A base module for all DPX puppet tools application classes
@author: Frank Brehm
@contact: frank.brehm@pixelpark.com
@copyright: © 2023 by Frank Brehm, Berlin
-@summary: A base module for all DPX puppet tools application classes
"""
from __future__ import absolute_import
# Standard modules
import logging
import shutil
+import copy
+import pipes
+import os
+
+from email.mime.text import MIMEText
+from email import charset
+
+from subprocess import Popen, PIPE
+
+import smtplib
# Third party modules
from fb_tools.cfg_app import FbConfigApplication
from fb_tools.errors import FbAppError
-# from fb_tools.multi_config import BaseMultiConfig
+from fb_tools.xlate import format_list
+from fb_tools import MailAddress
# Own modules
from .. import __version__ as GLOBAL_VERSION
from .. import DEFAULT_CONFIG_DIR
from .. import DEFAULT_TERMINAL_WIDTH, DEFAULT_TERMINAL_HEIGHT
+from .. import pp
from ..xlate import XLATOR
+from ..argparse_actions import PortOptionAction
+
from ..config import DpxPuppetConfig
LOG = logging.getLogger(__name__)
_ = XLATOR.gettext
ngettext = XLATOR.ngettext
-__version__ = '0.2.0'
+__version__ = '0.3.0'
# =============================================================================
# show_force_option = False
show_simulate_option = True
+ charset.add_charset('utf-8', charset.SHORTEST, charset.QP)
+
# -------------------------------------------------------------------------
def __init__(
self, appname=None, verbose=0, version=GLOBAL_VERSION, base_dir=None, quiet=False,
else:
LOG.debug(_("Don't using a logfile."))
+ 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 is 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(BaseDPXPuppetApplication, self).init_arg_parser()
+
+ mail_group = self.arg_parser.add_argument_group(_('Mailing options'))
+
+ mail_from = DpxPuppetConfig.default_mail_from_complete
+ mail_method = DpxPuppetConfig.default_mail_method
+ mail_server = DpxPuppetConfig.default_mail_server
+ smtp_port = DpxPuppetConfig.default_smtp_port
+
+ if self.cfg:
+ mail_from = self.cfg.mail_from
+ mail_method = self.cfg.mail_method
+ mail_server = self.cfg.mail_server
+ smtp_port = self.cfg.smtp_port
+
+ 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(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.")
+ )
+
+ methods = DpxPuppetConfig.valid_mail_methods
+ method_list = format_list(methods, do_repr=True)
+ mail_group.add_argument(
+ '--mail-method',
+ metavar=_("METHOD"), choices=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=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(mail_server)
+ )
+
+ mail_group.add_argument(
+ '--smtp-port',
+ metavar=_("PORT"), type=int, dest='smtp_port', what="SMTP",
+ action=PortOptionAction,
+ help=_(
+ "The port to use for submitting generated by this script if "
+ "the mail method of this script is 'smtp'. Default: {}.").format(smtp_port)
+ )
+
+ # -------------------------------------------------------------------------
+ def perform_arg_parser(self):
+
+ if self.verbose > 2:
+ LOG.debug(_("Got command line arguments:") + '\n' + pp(self.args))
+
+ super(BaseDPXPuppetApplication, self).perform_arg_parser()
+
+ # -------------------------------------------------------------------------
+ def send_mail(self, subject, body):
+
+ mail = MIMEText(body, 'plain', 'utf-8')
+ mail['Subject'] = subject
+ mail['From'] = self.cfg.mail_from
+ mail['To'] = ', '.join(self.cfg.mail_recipients)
+ mail['Reply-To'] = self.cfg.reply_to
+ mail['X-Mailer'] = self.cfg.xmailer
+ if self.mail_cc:
+ mail['Cc'] = ', '.join(self.mail_cc)
+
+ if self.verbose > 1:
+ LOG.debug(_("Mail to send:") + '\n' + mail.as_string(unixfrom=True))
+
+ if self.mail_method == 'smtp':
+ self._send_mail_smtp(mail)
+ else:
+ self._send_mail_sendmail(mail)
+
+ # -------------------------------------------------------------------------
+ def _send_mail_smtp(self, mail):
+
+ with smtplib.SMTP(self.cfg.mail_server, self.cfg.smtp_port) as smtp:
+ if self.verbose > 2:
+ smtp.set_debuglevel(2)
+ elif self.verbose > 1:
+ smtp.set_debuglevel(1)
+
+ smtp.send_message(mail)
+
+ # -------------------------------------------------------------------------
+ def _send_mail_sendmail(self, mail):
+
+ # Searching for the location of sendmail ...
+ paths = (
+ '/usr/sbin/sendmail',
+ '/usr/lib/sendmail',
+ )
+ sendmail = None
+ for path in paths:
+ if os.path.isfile(path) and os.access(path, os.X_OK):
+ sendmail = path
+ break
+
+ if not sendmail:
+ msg = _("Did not found sendmail executable.")
+ LOG.error(msg)
+ return
+
+ cmd = [sendmail, "-t", "-oi"]
+ cmd_str = ' '.join(map(lambda x: pipes.quote(x), cmd))
+ LOG.debug(_("Executing: {}").format(cmd_str))
+
+ p = Popen(cmd, stdin=PIPE, universal_newlines=True)
+ p.communicate(mail.as_string())
+
# vim: ts=4 et list