imip-agent

imiptools/mail.py

1026:4a0226da2137
2016-01-29 Paul Boddie Make a scheduling package to potentially support multiple scheduling modules.
     1 #!/usr/bin/env python     2      3 """     4 Mail preparation support.     5      6 Copyright (C) 2014, 2015 Paul Boddie <paul@boddie.org.uk>     7      8 This program is free software; you can redistribute it and/or modify it under     9 the terms of the GNU General Public License as published by the Free Software    10 Foundation; either version 3 of the License, or (at your option) any later    11 version.    12     13 This program is distributed in the hope that it will be useful, but WITHOUT    14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS    15 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more    16 details.    17     18 You should have received a copy of the GNU General Public License along with    19 this program.  If not, see <http://www.gnu.org/licenses/>.    20 """    21     22 from imiptools.config import LOCAL_PREFIX, MESSAGE_SENDER, OUTGOING_PREFIX    23 from email.mime.message import MIMEMessage    24 from email.mime.multipart import MIMEMultipart    25 from email.mime.text import MIMEText    26 from smtplib import LMTP, SMTP    27     28 # Fake gettext function for strings to be translated later.    29     30 _ = lambda s: s    31     32 MESSAGE_SUBJECT = _("Calendar system message")    33     34 PREAMBLE_TEXT = _("""\    35 This message contains several different parts, one of which will contain    36 calendar information that will only be understood by a suitable program.    37 """)    38     39 class Messenger:    40     41     "Sending of outgoing messages."    42     43     def __init__(self, lmtp_socket=None, local_smtp=False, sender=None, subject=None, preamble_text=None):    44     45         """    46         Deliver to a local mail system using LMTP if 'lmtp_socket' is provided    47         or if 'local_smtp' is given as a true value.    48         """    49     50         self.lmtp_socket = lmtp_socket    51         self.local_smtp = local_smtp    52         self.sender = sender or MESSAGE_SENDER    53         self.subject = subject    54         self.preamble_text = preamble_text    55     56         # The translation method is set by the client once locale information is    57         # known.    58     59         self.gettext = None    60     61     def local_delivery(self):    62     63         "Return whether local delivery is performed using this messenger."    64     65         return self.lmtp_socket is not None or self.local_smtp    66     67     def sendmail(self, recipients, data, sender=None, outgoing_bcc=None):    68     69         """    70         Send a mail to the given 'recipients' consisting of the given 'data',    71         using the given 'sender' identity if indicated, indicating an    72         'outgoing_bcc' identity if indicated.    73     74         The 'outgoing_bcc' argument is required when sending on behalf of a user    75         from the calendar@domain address, since this will not be detected as a    76         valid participant and handled using the outgoing transport.    77         """    78     79         if self.lmtp_socket:    80             smtp = LMTP(self.lmtp_socket)    81         else:    82             smtp = SMTP("localhost")    83     84         if outgoing_bcc:    85             recipients = list(recipients) + ["%s+%s" % (OUTGOING_PREFIX, outgoing_bcc)]    86         elif self.local_smtp:    87             recipients = [self.make_local(recipient) for recipient in recipients]    88     89         smtp.sendmail(sender or self.sender, recipients, data)    90         smtp.quit()    91     92     def make_local(self, recipient):    93     94         """    95         Make the 'recipient' an address for local delivery. For this to function    96         correctly, a virtual alias or equivalent must be defined for addresses    97         of the following form:    98     99         local+NAME@DOMAIN   100    101         Such aliases should direct delivery to the local recipient.   102         """   103    104         parts = recipient.split("+", 1)   105         return "%s+%s" % (LOCAL_PREFIX, parts[-1])   106    107     def make_outgoing_message(self, parts, recipients, sender=None, outgoing_bcc=None):   108    109         """   110         Make a message from the given 'parts' for the given 'recipients', using   111         the given 'sender' identity if indicated, indicating an 'outgoing_bcc'   112         identity if indicated.   113         """   114    115         message = self._make_summary_for_parts(parts)   116    117         message["From"] = sender or self.sender   118         for recipient in recipients:   119             message["To"] = recipient   120         if outgoing_bcc:   121             message["Bcc"] = "%s+%s" % (OUTGOING_PREFIX, outgoing_bcc)   122         message["Subject"] = self.subject or \   123             self.gettext and self.gettext(MESSAGE_SUBJECT) or MESSAGE_SUBJECT   124    125         return message   126    127     def make_summary_message(self, msg, parts):   128    129         """   130         Return a simple summary using details from 'msg' and the given 'parts'.   131         """   132    133         message = self._make_summary_for_parts(parts)   134         self._copy_headers(message, msg)   135         return message   136    137     def wrap_message(self, msg, parts):   138    139         "Wrap 'msg' and provide the given 'parts' as the primary content."   140    141         message = self._make_container_for_parts(parts)   142         payload = message.get_payload()   143         payload.append(MIMEMessage(msg))   144         self._copy_headers(message, msg)   145         return message   146    147     def _make_summary_for_parts(self, parts):   148    149         "Return a simple summary for the given 'parts'."   150    151         if len(parts) == 1:   152             return parts[0]   153         else:   154             return self._make_container_for_parts(parts)   155    156     def _make_container_for_parts(self, parts):   157    158         "Return a container for the given 'parts'."   159    160         message = MIMEMultipart("mixed", _subparts=parts)   161         message.preamble = self.preamble_text or \   162             self.gettext and self.gettext(PREAMBLE_TEXT) or PREAMBLE_TEXT   163         return message   164    165     def _copy_headers(self, message, msg):   166         message["From"] = msg["From"]   167         message["To"] = msg["To"]   168         message["Subject"] = msg["Subject"]   169    170 # vim: tabstop=4 expandtab shiftwidth=4