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
man1/remail_daemon.rst
man1/remail_pipe.rst
man1/remail_chkcfg.rst
man5/remail.config.rst

View file

@ -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.

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::
├── .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)`

View file

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

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)