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