import os import sys import pprint from types import StringTypes from email.MIMEText import MIMEText from email.Header import Header from smtplib import SMTP from PLC.Logger import logger from PLC.Faults import * def sendmail(api, To, Subject, Body, From = None, Cc = None, Bcc = None): """ Uses sendmail (must be installed and running locally) to send a message to the specified recipients. If the API is running under mod_python, the apache user must be listed in e.g., /etc/mail/trusted-users. To, Cc, and Bcc may be addresses or lists of addresses. Each address may be either a plain text address or a tuple of (name, address). """ # Fix up defaults if not isinstance(To, list): To = [To] if Cc is not None and not isinstance(Cc, list): Cc = [Cc] if Bcc is not None and not isinstance(Bcc, list): Bcc = [Bcc] if From is None: From = ("%s Support" % api.config.PLC_NAME, api.config.PLC_MAIL_SUPPORT_ADDRESS) # Create a MIME-encoded UTF-8 message msg = MIMEText(Body.encode(api.encoding), _charset = api.encoding) # Unicode subject headers are automatically encoded correctly msg['Subject'] = Subject def encode_addresses(addresses, header_name = None): """ Unicode address headers are automatically encoded by email.Header, but not correctly. The correct way is to put the textual name inside quotes and the address inside brackets: To: "=?utf-8?b?encoded" Each address in addrs may be a tuple of (name, address) or just an address. Returns a tuple of (header, addrlist) representing the encoded header text and the list of plain text addresses. """ header = [] addrs = [] for addr in addresses: if isinstance(addr, tuple): (name, addr) = addr try: name = name.encode('ascii') header.append('%s <%s>' % (name, addr)) except: h = Header(name, charset = api.encoding, header_name = header_name) header.append('"%s" <%s>' % (h.encode(), addr)) else: header.append(addr) addrs.append(addr) return (", ".join(header), addrs) (msg['From'], from_addrs) = encode_addresses([From], 'From') (msg['To'], to_addrs) = encode_addresses(To, 'To') if Cc is not None: (msg['Cc'], cc_addrs) = encode_addresses(Cc, 'Cc') to_addrs += cc_addrs if Bcc is not None: (unused, bcc_addrs) = encode_addresses(Bcc, 'Bcc') to_addrs += bcc_addrs # Needed to pass some spam filters msg['Reply-To'] = msg['From'] msg['X-Mailer'] = "Python/" + sys.version.split(" ")[0] if not api.config.PLC_MAIL_ENABLED: logger.info("PLC_MAIL_ENABLED not set") logger.info("From: %(From)s, To: %(To)s, Subject: %(Subject)s" % msg) return s = SMTP() s.connect() rejected = s.sendmail(from_addrs[0], to_addrs, msg.as_string(), rcpt_options = ["NOTIFY=NEVER"]) s.close() if rejected: raise PLCAPIError, "Error sending message to " + ", ".join(rejected.keys())