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