diff --git a/src/caosadvancedtools/serverside/helper.py b/src/caosadvancedtools/serverside/helper.py index 7a22891aecf5eed803a8566a2a483b4c584bbd0c..5b3adba395b9d3a1d2d3bc81fa0f46c1d019c9c6 100644 --- a/src/caosadvancedtools/serverside/helper.py +++ b/src/caosadvancedtools/serverside/helper.py @@ -316,7 +316,8 @@ def get_shared_filename(filename): return filename, filepath -def send_mail(from_addr, to, subject, body, cc=None, bcc=None): +def send_mail(from_addr, to, subject, body, cc=None, bcc=None, + send_mail_bin=None): """ Send an email via the configured send_mail client. The relevant options in the pycaosdb.ini are: @@ -324,8 +325,8 @@ def send_mail(from_addr, to, subject, body, cc=None, bcc=None): [Misc] sendmail = ... - Parameters: - ----------- + Parameters + ---------- from_addr : str The sender's email address. to : str or list of str @@ -338,10 +339,19 @@ def send_mail(from_addr, to, subject, body, cc=None, bcc=None): Single or list of cc-recipients. Defaults to None. bcc : str or list of str (optional) Single or list of bcc-recipients. Defaults to None. + send_mail_bin : str (optional) + Path of sendmail client. Defaults to config["Misc"]["sendmail"]. + + Raises + ------ + subprocess.CalledProcessError + If the sendmail client returned with a non-zero code. + caosdb.ConfigurationException + If the caosdb configuration has no `Misc.sendmail` configured while the + `send_mail_bin` parameter is None. """ - caosdb_config = db.configuration.get_config() - sendmail = caosdb_config["Misc"]["sendmail"] + # construct the mail mail = message.EmailMessage(policy=policy.SMTP) mail.set_content(body) mail["From"] = from_addr @@ -354,6 +364,44 @@ def send_mail(from_addr, to, subject, body, cc=None, bcc=None): if bcc is not None: mail["BCC"] = bcc if isinstance(cc, str) else ", ".join(cc) - p = subprocess.Popen([sendmail, "-t", "-oi"], - stdin=subprocess.PIPE) - p.communicate(mail.as_bytes()) + # construct the call + if send_mail_bin is not None: + sendmail = send_mail_bin + else: + caosdb_config = db.configuration.get_config() + if not "Misc" in caosdb_config or not "sendmail" in caosdb_config["Misc"]: + err_msg = ("No sendmail executable configured. " + "Please configure `Misc.sendmail` " + "in your pycaosdb.ini.") + raise db.ConfigurationException(err_msg) + sendmail = caosdb_config["Misc"]["sendmail"] + + # construct sendmail command + # options explained (from `man sendmail`): + # -t Read message for recipients. To:, Cc:, and Bcc: lines will be + # scanned for recipient addresses. The Bcc: line will be deleted + # before transmission. + # -i Ignore dots alone on lines by themselves in incoming messages. This + # should be set if you are reading data from a file. + # -f Sets the name of the ''from'' person (i.e., the envelope sender of + # the mail). This address may also be used in the From: header if + # that header is missing during initial submission. The envelope + # sender address is used as the recipient for delivery status + # notifications and may also appear in a Return-Path: header. -f + # should only be used by ''trusted'' users (normally root, daemon, + # and network) or if the person you are trying to become is the same + # as the person you are. Otherwise, an X-Authentication-Warning + # header will be added to the message. + command = [sendmail, "-t", "-i", "-f", from_addr] + + # execute and handle return code + p = subprocess.Popen(command, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + stdout, stderr = p.communicate(mail.as_bytes()) + return_code = p.wait() + if return_code != 0: + raise subprocess.CalledProcessError(return_code, command, + output=stdout.decode("utf8"), + stderr=stderr.decode("utf8")) diff --git a/unittests/test_sss_helper.py b/unittests/test_sss_helper.py index ca53f0c8172f53d913733c5dbde93ac04bb02c45..e457735918a09835df35a401b0fc1df245a7ac54 100644 --- a/unittests/test_sss_helper.py +++ b/unittests/test_sss_helper.py @@ -1,7 +1,8 @@ from os.path import abspath, dirname, join, isfile, exists from os import listdir, remove +import subprocess from email import message_from_file, policy -from pytest import mark +from pytest import mark, raises from caosdb import configure_connection, RecordType, get_config from caosdb.connection.mockup import (MockUpServerConnection, MockUpResponse) from caosadvancedtools.serverside.helper import (parse_arguments, get_data, @@ -77,3 +78,9 @@ def test_send_mail(): assert msg["To"] == "you@example.com" assert msg["Subject"] == "the subject" assert msg.get_content() == "hello!\n" + + +def test_send_mail_error(): + with raises(subprocess.CalledProcessError): + send_mail("me@example.com", "you@example.com", "the subject", "hello!", + send_mail_bin="/bin/cat")