a1f2f3eec82e4c0a2998adef9fa2da0671155502
[plcapi.git] / PLC / sendmail.py
1 import os
2 import sys
3 import pprint
4 from email.mime.text import MIMEText
5 from email.header import Header
6 from smtplib import SMTP
7
8 from PLC.Logger import logger
9 from PLC.Faults import *
10
11 def sendmail(api, To, Subject, Body, From = None, Cc = None, Bcc = None):
12     """
13     Uses sendmail (must be installed and running locally) to send a
14     message to the specified recipients. If the API is running under
15     mod_python, the apache user must be listed in e.g.,
16     /etc/mail/trusted-users.
17
18     To, Cc, and Bcc may be addresses or lists of addresses. Each
19     address may be either a plain text address or a tuple of (name,
20     address).
21     """
22
23     # Fix up defaults
24     if not isinstance(To, list):
25         To = [To]
26     if Cc is not None and not isinstance(Cc, list):
27         Cc = [Cc]
28     if Bcc is not None and not isinstance(Bcc, list):
29         Bcc = [Bcc]
30     if From is None:
31         From = ("%s Support" % api.config.PLC_NAME,
32                 api.config.PLC_MAIL_SUPPORT_ADDRESS)
33
34     # Create a MIME-encoded UTF-8 message
35     msg = MIMEText(Body)
36
37     # Unicode subject headers are automatically encoded correctly
38     msg['Subject'] = Subject
39
40     def encode_addresses(addresses, header_name = None):
41         """
42         Unicode address headers are automatically encoded by
43         email.Header, but not correctly. The correct way is to put the
44         textual name inside quotes and the address inside brackets:
45
46         To: "=?utf-8?b?encoded" <recipient@domain>
47
48         Each address in addrs may be a tuple of (name, address) or
49         just an address. Returns a tuple of (header, addrlist)
50         representing the encoded header text and the list of plain
51         text addresses.
52         """
53
54         header = []
55         addrs = []
56
57         for addr in addresses:
58             if isinstance(addr, tuple):
59                 (name, addr) = addr
60                 h = Header(name, charset = api.encoding, header_name = header_name)
61                 header.append('"%s" <%s>' % (h, addr))
62             else:
63                 header.append(addr)
64             addrs.append(addr)
65
66         return (", ".join(header), addrs)
67
68     (msg['From'], from_addrs) = encode_addresses([From], 'From')
69     (msg['To'], to_addrs) = encode_addresses(To, 'To')
70
71     if Cc is not None:
72         (msg['Cc'], cc_addrs) = encode_addresses(Cc, 'Cc')
73         to_addrs += cc_addrs
74
75     if Bcc is not None:
76         (unused, bcc_addrs) = encode_addresses(Bcc, 'Bcc')
77         to_addrs += bcc_addrs
78
79     # Needed to pass some spam filters
80     msg['Reply-To'] = msg['From']
81     msg['X-Mailer'] = "Python/" + sys.version.split(" ")[0]
82
83     if not api.config.PLC_MAIL_ENABLED:
84         logger.info("PLC_MAIL_ENABLED not set")
85         logger.info("From: %(From)s, To: %(To)s, Subject: %(Subject)s" % msg)
86         return
87
88     s = SMTP()
89     s.connect()
90     rejected = s.sendmail(from_addrs[0], to_addrs, msg.as_string(), rcpt_options = ["NOTIFY=NEVER"])
91     s.close()
92
93     if rejected:
94         raise PLCAPIError("Error sending message to " + ", ".join(list(rejected.keys())))