From 323b8cda64cf52315fa8bfee97fa03b3b8d7a096 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 1 Nov 2019 13:30:25 +0100 Subject: [PATCH] remail: Add remail_pipe script Add a handle pipe function to the remailer and a pipe script for handling mail in a MTA delivery path. Requested-by: Konstantin Ryabitsev Signed-off-by: Thomas Gleixner --- Documentation/index.rst | 1 + Documentation/man1/remail_daemon.rst | 6 +-- Documentation/man1/remail_pipe.rst | 74 ++++++++++++++++++++++++++++ Documentation/man5/remail.config.rst | 3 +- remail/remaild.py | 52 ++++++++++++------- remail_pipe | 57 +++++++++++++++++++++ 6 files changed, 171 insertions(+), 22 deletions(-) create mode 100644 Documentation/man1/remail_pipe.rst create mode 100755 remail_pipe diff --git a/Documentation/index.rst b/Documentation/index.rst index 643fec6..8500360 100644 --- a/Documentation/index.rst +++ b/Documentation/index.rst @@ -70,6 +70,7 @@ Usage guidelines (man pages) :maxdepth: 3 man1/remail_daemon.rst + man1/remail_pipe.rst man1/remail_chkcfg.rst man5/remail.config.rst diff --git a/Documentation/man1/remail_daemon.rst b/Documentation/man1/remail_daemon.rst index 92f3114..8730d2b 100644 --- a/Documentation/man1/remail_daemon.rst +++ b/Documentation/man1/remail_daemon.rst @@ -14,7 +14,7 @@ Description ----------- :program:`remail_daemon`, The daemon for running an encrypted mailing -list. It reads the configuration file from the command line. +list. The configuration file name is handed in as command line argument. Options @@ -36,8 +36,8 @@ Options Configuration file ------------------ -remail_daemon reads the configuration file which was handed in on the -command line. The configuration file is a simple yaml file. Non-mandatory +remail_daemon reads the configuration file which was handed in as command +line argument. The configuration file is a simple yaml file. Non-mandatory configuration options which are not in the configuration file are set to the default values. diff --git a/Documentation/man1/remail_pipe.rst b/Documentation/man1/remail_pipe.rst new file mode 100644 index 0000000..1c91604 --- /dev/null +++ b/Documentation/man1/remail_pipe.rst @@ -0,0 +1,74 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. _remail_pipe_man: + +remail_pipe manual page +========================= + +Synopsis +-------- + +**remail_pipe** [*options*] config_file + +Description +----------- + +:program:`remail_pipe`, The pipe script for decrypting incoming mail and +sending it re-encrypted to the subscribers of an encrypted mailing +list. The incoming mail is read from stdin. + + +Options +------- + +-h, --help + Show this help message and exit + +-s syslog, --syslog + Use syslog for logging. Default is stderr + +-v, --verbose + Enable verbose logging. + +-V, --version + Display version information + + +Configuration file +------------------ + +remail_pipe reads the configuration file which was handed in as command +line argument. The configuration file is a simple yaml file. Non-mandatory +configuration options which are not in the configuration file are set to +the default values. + +See the configuration file man page for detailed information. + + +Work directory +-------------- + +remail pipe assumes that the configuration file is in the work directory +which has a defined layout and content. The directory structure is +documented in the full remail documentation along with hints how to manage +encrypted mailing lists. + +Exit codes +---------- + +.. list-table:: + + * - 0 + - Mail was successfully delivered + * - 1 + - No enabled mailinglist found for delivery + * - 2 + - Mail processing incomplete. See log output + * - 11 + - Configuration error + * - 12 + - Fatal exception + +See also +-------- +:manpage:`remail.config(5)` diff --git a/Documentation/man5/remail.config.rst b/Documentation/man5/remail.config.rst index ebe3959..8ff71fc 100644 --- a/Documentation/man5/remail.config.rst +++ b/Documentation/man5/remail.config.rst @@ -38,7 +38,7 @@ Configuration directory structure The configuration directory structure is fixed and looks like this:: - ├── .certs + ├── .certs │   ├── cacert.pem │   ├── list1@your.domain.key │   ├── list2@your.domain.key @@ -427,4 +427,5 @@ See also -------- :manpage:`remail_daemon(1)` :manpage:`remail_chkcfg(1)` +:manpage:`remail_pipe(1)` diff --git a/remail/remaild.py b/remail/remaild.py index b087f4b..6912741 100644 --- a/remail/remaild.py +++ b/remail/remaild.py @@ -7,7 +7,7 @@ from remail.config import RemailConfigException from remail.config import main_config from remail.maillist import maillist -from email import message_from_binary_file +from email import message_from_binary_file, message_from_file from email.policy import EmailPolicy from ruamel.yaml import YAML import pyinotify @@ -15,6 +15,7 @@ import mailbox import pathlib import signal import fcntl +import sys import os class EventHandler(pyinotify.ProcessEvent): @@ -173,6 +174,25 @@ class remaild(object): except: pass + + def process_msg(self, msg): + # Check whether one of the lists will take it + for ml in self.mailinglists: + if not ml.enabled: + continue + dest = ml.get_destination(msg) + if not dest: + continue + try: + if ml.process_mail(msg, dest): + return 0 + return 2 + except Exception as ex: + txt = 'Failed to process mail file %s\n' %(mailfile) + self.logger.log_exception(txt, ex) + break + return 1 + # The actual mail processing def process_mail(self, queue): ''' @@ -193,24 +213,9 @@ class remaild(object): self.logger.log_exception(txt, ex) continue - # Check whether one of the lists will take it - processed = False - for ml in self.mailinglists: - if not ml.enabled: - continue - dest = ml.get_destination(msg) - if not dest: - continue - try: - processed = ml.process_mail(msg, dest) - break - except Exception as ex: - self.failedmails.append(mailfile) - txt = 'Failed to process mail file %s\n' %(mailfile) - self.logger.log_exception(txt, ex) - break + res = self.process_msg(msg) - if processed: + if res == 0: os.unlink(mailfile) else: self.move_frozen(mailfile) @@ -360,6 +365,17 @@ class remaild(object): return self.should_stop() + # The pipe handling interface + def handle_pipe(self): + self._should_reload = True + self.reconfigure() + + if not self.enabled: + return 1 + + msg = message_from_file(sys.stdin) + return self.process_msg(msg) + # The runner def run(self): diff --git a/remail_pipe b/remail_pipe new file mode 100755 index 0000000..4e4c1b4 --- /dev/null +++ b/remail_pipe @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +# Copyright Thomas Gleixner +# + +from remail.utils import logger +from remail.version import __version__ +from remail.remaild import remaild + +from argparse import ArgumentParser + +import sys +import os + +def exit_daemon(logger, res): + logger.log('Stopped\n') + sys.exit(res) + +if __name__ == '__main__': + parser = ArgumentParser(description='remail pipe') + parser.add_argument('config', help='Config file') + parser.add_argument('--syslog', '-s', dest='syslog', action='store_true', + help='Use syslog for logging. Default is stderr') + parser.add_argument('--verbose', '-v', dest='verbose', action='store_true', + help='Verbose logging') + parser.add_argument('--version', '-V', action='version', + version='%(prog)s {version}'.format(version=__version__)) + args = parser.parse_args() + + logger = logger(use_syslog=args.syslog, verbose=args.verbose) + logger.log("Started\n") + + # Change into the directory in which the config file resides + wdir = os.path.dirname(args.config) + if len(wdir): + os.chdir(wdir) + args.config = os.path.basename(args.config) + + try: + rd = remaild(args.config, logger) + except Exception as ex: + ''' + Exceptions which reach here are fatal + ''' + logger.log_exception('', ex, verbose=True) + exit_daemon(logger, 11) + + try: + res = rd.handle_pipe() + except Exception as ex: + ''' + Exceptions which reach here are fatal + ''' + logger.log_exception('', ex, verbose=True) + res = 12 + + exit_daemon(logger, res)