# Copyright (C) 1998,1999,2000,2001,2002 by the Free Software Foundation, Inc.
#
# 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 2
# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

"""Decorate a message by sticking the header and footer around it.
"""

from types import ListType
from email.MIMEText import MIMEText

from Mailman import mm_cfg
from Mailman import Utils
from Mailman import Errors
from Mailman.i18n import _
from Mailman.SafeDict import SafeDict
from Mailman.Logging.Syslog import syslog



def process(mlist, msg, msgdata):
    # Digests and Mailman-craft messages should not get additional headers
    if msgdata.get('isdigest') or msgdata.get('nodecorate'):
        return
    d = {}
    if msgdata.get('personalize'):
        # Calculate the extra personalization dictionary.  Note that the
        # length of the recips list better be exactly 1.
        recips = msgdata.get('recips')
        assert type(recips) == ListType and len(recips) == 1
        member = recips[0].lower()
        d['user_address'] = member
        try:
            d['user_delivered_to'] = mlist.getMemberCPAddress(member)
            # BAW: Hmm, should we allow this?
            d['user_password'] = mlist.getMemberPassword(member)
            d['user_language'] = mlist.getMemberLanguage(member)
            d['user_name'] = mlist.getMemberName(member) or _('not available')
            d['user_optionsurl'] = mlist.GetOptionsURL(member)
        except Errors.NotAMemberError:
            pass
    header = decorate(mlist, mlist.msg_header, _('non-digest header'), d)
    footer = decorate(mlist, mlist.msg_footer, _('non-digest footer'), d)
    # Be MIME smart here.  We only attach the header and footer by
    # concatenation when the message is a non-multipart of type text/plain.
    # Otherwise, if it is not a multipart, we make it a multipart, and then we
    # add the header and footer as text/plain parts.
    #
    # BJG: In addition, only add the footer if the message's character set
    # matches the charset of the list's preferred language.  This is a
    # suboptimal solution, and should be solved by allowing a list to have
    # multiple headers/footers, for each language the list supports.
    #
    # Also, if the list's preferred charset is us-ascii, we can always
    # safely add the header/footer to a plain text message since all
    # charsets Mailman supports are strict supersets of us-ascii --
    # no, UTF-16 emails are not supported yet.
    mcset = msg.get_param('charset', 'us-ascii').lower()
    lcset = Utils.GetCharSet(mlist.preferred_language)
    msgtype = msg.get_type('text/plain')
    # BAW: If the charsets don't match, should we add the header and footer by
    # MIME multipart chroming the message?
    if not msg.is_multipart() and msgtype == 'text/plain' and \
           msg.get('content-transfer-encoding', '').lower() <> 'base64' and \
           (lcset == 'us-ascii' or mcset == lcset):
        oldpayload = msg.get_payload()
        frontsep = endsep = ''
        if header and not header.endswith('\n'):
            frontsep = '\n'
        if footer and not oldpayload.endswith('\n'):
            endsep = '\n'
        payload = header + frontsep + oldpayload + endsep + footer
        msg.set_payload(payload)
    elif msg.get_type() == 'multipart/mixed':
        # The next easiest thing to do is just prepend the header and append
        # the footer as additional subparts
        mimehdr = MIMEText(header)
        mimeftr = MIMEText(footer)
        payload = msg.get_payload()
        if not isinstance(payload, ListType):
            payload = [payload]
        if footer:
            payload.append(mimeftr)
        if header:
            payload.insert(0, mimehdr)
        msg.set_payload(payload)
    elif msg.get_main_type() <> 'multipart':
        # Okay, we've got some 'image/*' or 'audio/*' -like type.  For now, we
        # simply refuse to add headers and footers to this message.  BAW:
        # still trying to decide what the Right Thing To Do is.
        pass
    else:
        # Now we've got some multipart/* that's not a multipart/mixed.  I'm
        # even less sure about what to do here, so once again, let's not add
        # headers or footers for now.
        pass



def decorate(mlist, template, what, extradict={}):
    # `what' is just a descriptive phrase
    #
    # BAW: We've found too many situations where Python can be fooled into
    # interpolating too much revealing data into a format string.  For
    # example, a footer of "% silly %(real_name)s" would give a header
    # containing all list attributes.  While we've previously removed such
    # really bad ones like `password' and `passwords', it's much better to
    # provide a whitelist of known good attributes, then to try to remove a
    # blacklist of known bad ones.
    d = SafeDict({'real_name'     : mlist.real_name,
                  'list_name'     : mlist.internal_name(),
                  # For backwards compatibility
                  '_internal_name': mlist.internal_name(),
                  'host_name'     : mlist.host_name,
                  'web_page_url'  : mlist.web_page_url,
                  'description'   : mlist.description,
                  'info'          : mlist.info,
                  'cgiext'        : mm_cfg.CGIEXT,
                  })
    d.update(extradict)
    # Using $-strings?
    if getattr(mlist, 'use_dollar_strings', 0):
        template = Utils.to_percent(template)
    # Interpolate into the template
    try:
        text = (template % d).replace('\r\n', '\n')
    except (ValueError, TypeError), e:
        syslog('error', 'Exception while calculating %s:\n%s', what, e)
        what = what.upper()
        text = template
    return text
