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 <konstantin@linuxfoundation.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
This commit is contained in:
Thomas Gleixner 2019-11-01 13:30:25 +01:00
parent 266221e151
commit 323b8cda64
6 changed files with 171 additions and 22 deletions

View file

@ -70,6 +70,7 @@ Usage guidelines (man pages)
:maxdepth: 3 :maxdepth: 3
man1/remail_daemon.rst man1/remail_daemon.rst
man1/remail_pipe.rst
man1/remail_chkcfg.rst man1/remail_chkcfg.rst
man5/remail.config.rst man5/remail.config.rst

View file

@ -14,7 +14,7 @@ Description
----------- -----------
:program:`remail_daemon`, The daemon for running an encrypted mailing :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 Options
@ -36,8 +36,8 @@ Options
Configuration file Configuration file
------------------ ------------------
remail_daemon reads the configuration file which was handed in on the remail_daemon reads the configuration file which was handed in as command
command line. The configuration file is a simple yaml file. Non-mandatory line argument. The configuration file is a simple yaml file. Non-mandatory
configuration options which are not in the configuration file are set to configuration options which are not in the configuration file are set to
the default values. the default values.

View file

@ -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)`

View file

@ -38,7 +38,7 @@ Configuration directory structure
The configuration directory structure is fixed and looks like this:: The configuration directory structure is fixed and looks like this::
├── .certs ├── .certs
│   ├── cacert.pem │   ├── cacert.pem
│   ├── list1@your.domain.key │   ├── list1@your.domain.key
│   ├── list2@your.domain.key │   ├── list2@your.domain.key
@ -427,4 +427,5 @@ See also
-------- --------
:manpage:`remail_daemon(1)` :manpage:`remail_daemon(1)`
:manpage:`remail_chkcfg(1)` :manpage:`remail_chkcfg(1)`
:manpage:`remail_pipe(1)`

View file

@ -7,7 +7,7 @@ from remail.config import RemailConfigException
from remail.config import main_config from remail.config import main_config
from remail.maillist import maillist 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 email.policy import EmailPolicy
from ruamel.yaml import YAML from ruamel.yaml import YAML
import pyinotify import pyinotify
@ -15,6 +15,7 @@ import mailbox
import pathlib import pathlib
import signal import signal
import fcntl import fcntl
import sys
import os import os
class EventHandler(pyinotify.ProcessEvent): class EventHandler(pyinotify.ProcessEvent):
@ -173,6 +174,25 @@ class remaild(object):
except: except:
pass 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 # The actual mail processing
def process_mail(self, queue): def process_mail(self, queue):
''' '''
@ -193,24 +213,9 @@ class remaild(object):
self.logger.log_exception(txt, ex) self.logger.log_exception(txt, ex)
continue continue
# Check whether one of the lists will take it res = self.process_msg(msg)
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
if processed: if res == 0:
os.unlink(mailfile) os.unlink(mailfile)
else: else:
self.move_frozen(mailfile) self.move_frozen(mailfile)
@ -360,6 +365,17 @@ class remaild(object):
return self.should_stop() 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 # The runner
def run(self): def run(self):

57
remail_pipe Executable file
View file

@ -0,0 +1,57 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-only
# Copyright Thomas Gleixner <tglx@linutronix.de>
#
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)