#! /usr/bin/python # -*- coding: utf-8 -*- # Utility to generate email replies according to a template # # Copyright © 2008 Tanguy Ortolo # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # I may want to translate this program later, so let me define a fonction # to translate strings. def _(s) : return s def main() : from sys import stdin from email.Parser import HeaderParser, Parser from email.Message import Message from email.Charset import Charset, QP from email.Header import Header, decode_header, make_header from email.Utils import parseaddr, formatdate from optparse import OptionParser ################################# # Handle command line arguments # ################################# usage = _("""Usage: %(prog)s -t TEMPLATE [OPTIONS] [MESSAGE] Read the headers of the message provided in FILE, or the standard input, and generate a reply on its standard output, according to a template. example: %(prog)s < {received_message|received_message_headers} | sendmail -t %(prog)s was designed to ease address changes, allowing you to automatically notify those who write you using an address you want to drop. You may call it from a mail filtering agent such as procmail(1).""") \ % {"prog" : "%prog"} opt_parser = OptionParser(usage = usage) opt_parser.add_option('-t', "--template", metavar = _("TEMPLATE"), help = _("a file containing a template for the generated message (mandatory): a message file, that should contain From, a Subject and a Content-Type headers, and may contains variables $(name), $(address) and $(subject) in its body")) (options, args) = opt_parser.parse_args() template_filename = options.template if not template_filename : opt_parser.error(_("you must specify a template file")) template_file = file(template_filename) if len(args) > 1 : opt_parser.error(_("too many arguments, you can only specify one input message file")) if len(args) == 1 : o_file = file(args[0]) else : o_file = stdin ######################################## # Parse the original message's headers # ######################################## header_parser = HeaderParser() o_message = header_parser.parse(o_file) o_file.close() o_from = o_message["From"] o_subject = o_message["Subject"] o_replyto = o_message["Reply-To"] o_messageid = o_message["Message-Id"] # Extract the name, the address and to reply address. # Name and address are in Unicode, reply address is taken as it is. if not(o_from and o_subject) : return 1 o_name, o_address = parseaddr(unicode(make_header(decode_header(o_from)))) if not o_name : o_name = o_address if o_replyto : r_to = o_replyto else : r_to = o_from # Decode the fields that may be used to replace the template's variables, # to Unicode strings o_from = unicode(make_header(decode_header(o_from))) o_subject = unicode(make_header(decode_header(o_subject))) # State of all these variables: # - o_from, o_subject, o_name, o_address: unicode, # - o_replyto, o_messageid, r_to: MIME-encoded strings (i.e. raw headers). ############################## # Parse the templace message # ############################## parser = Parser() r_message = parser.parse(template_file) template_file.close() if r_message.is_multipart() : opt_parser.error("multipart templates are not supported") r_encoding = r_message.get_content_charset("utf-8") ########################### # Build the reply message # ########################### r_charset = Charset(r_encoding) r_charset.body_encoding = QP r_body = r_message.get_payload(decode = True) r_body = r_body.replace("$(name)", o_name.encode(r_encoding)) r_body = r_body.replace("$(address)", o_address.encode(r_encoding)) r_body = r_body.replace("$(subject)", o_subject.encode(r_encoding)) r_message.set_payload(r_body, r_charset) for key in r_message.keys() : try : # If the header is MIME-encoded r_header = unicode(make_header(decode_header(r_message[key]))) except UnicodeDecodeError : # If the header is 8bit-encoded with r_encoding r_header = unicode(r_message[key], r_encoding) r_header.replace("$(name)", o_name) r_header.replace("$(address)", o_address) r_header.replace("$(subject)", o_subject) r_message.replace_header(key, str(Header(r_header, r_encoding))) r_message["To"] = r_to r_message["In-Reply-To"] = o_messageid r_message["References"] = o_messageid r_message["User-Agent"] = "autoreply 1.0" r_message["Date"] = formatdate() print r_message.as_string() return 0 if __name__ == "__main__" : from sys import exit exit(main())