#! /usr/bin/python
# -*- coding: utf-8 -*-

# Utility to generate email replies according to a template
#
# Copyright © 2008 Tanguy Ortolo <tanguy@ortolo.eu>
#
# 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())
