"""User interface for mailto: handling.

This implementation supports the extended mailto: url scheme described in
ftp://ds.internic.net/internet-drafts/draft-hoffman-mailto-url-01.txt.  This
document is a work in progress, but reflects a generalization of common
practice in Web user agents.

This class is separated out from the protocols.mailtoAPI module to allow
user-defined handling of the mailto: scheme to subclass this dialog.
"""
__version__ = '$Revision: 2.5 $'

import cgi
import grailutil
import os
import rfc822
import string
import time
import tktools

from Tkinter import *
from urlparse import urlparse, urlunparse
from __main__ import GRAILVERSION
from Context import LAST_CONTEXT


COMMON_HEADERS = (
    (0, "to"),
    (1, "subject"),
    (3, "mime-version"),
    (4, "content-type"),
    (5, "content-transfer-encoding"),
    (20, "x-mailer"),
    (21, "x-url"),
    )

if os.sys.platform[:3] == 'sco': 
    # Use MMDF instead of sendmail
    SENDMAIL = "/usr/mmdf/bin/submit -mtlrxto,cc\'*\'s"
    # submit needs a Date: field or it will not include it
    COMMON_HEADERS = tuple(map(None, COMMON_HEADERS) + [(2, "date")])
    TEMPLATE ="""\
To: %(to)s
Date: %(date)s
Subject: %(subject)s
MIME-Version: 1.0
Content-Type: %(ctype)s
X-Mailer: %(mailer)s
X-URL: %(url)s
"""
else:
    SENDMAIL = "/usr/sbin/sendmail -t" # XXX
    TEMPLATE ="""\
To: %(to)s
Subject: %(subject)s
MIME-Version: 1.0
Content-Type: %(ctype)s
X-Mailer: %(mailer)s
X-URL: %(url)s
"""

DISALLOWED_HEADERS = ['from', 'appearantly-to', 'bcc', 'content-length',
                      'content-type', 'mime-version', 'to',
                      'content-transfer-encoding', 'x-mailer', 'x-url']


class MailDialog:

    template = TEMPLATE

    def __init__(self, master, address, data):
        # query semantics may be used to identify header field values
        scheme, netloc, path, params, query, fragment = urlparse(address)
        address = urlunparse((scheme, netloc, path, '', '', ''))
        headers = cgi.parse_qs(query)
        # create widgets
        self.master = master
        self.root = tktools.make_toplevel(self.master,
                                          title="Mail Dialog")
        self.root.protocol("WM_DELETE_WINDOW", self.cancel_command)
        self.root.bind("<Alt-w>", self.cancel_command)
        self.root.bind("<Alt-W>", self.cancel_command)
        fr, top, botframe = tktools.make_double_frame(self.root)
        self.text, fr = tktools.make_text_box(top, 80, 24)
        self.text.tag_config('SUSPICIOUS_HEADER', foreground='red')
        self.send_button = Button(botframe,
                                  text="Send",
                                  command=self.send_command)
        self.send_button.pack(side=LEFT)
        self.cancel_button = Button(botframe,
                                    text="Cancel",
                                    command=self.cancel_command)
        self.cancel_button.pack(side=RIGHT)
        tktools.unify_button_widths(self.send_button, self.cancel_button)
        hinfo = _make_sequence_dict(COMMON_HEADERS)
        variables = {
            'to':       address,
            'subject':  data and 'Form posted from Grail' or '',
            'mime-version': '1.0',
            'x-mailer': GRAILVERSION,
            'x-url':    LAST_CONTEXT and LAST_CONTEXT.get_baseurl() or ''
            }
        if data:
            variables["content-type"] = "application/x-www-form-urlencoded"
        else:
            variables["content-type"] = "text/plain; charset=us-ascii"
            variables["content-transfer-encoding"] = "7bit"
        # move default set of query'd headers into variables
        for header, vlist in headers.items():
            header = string.lower(header)
            if header != 'body':
                if header not in DISALLOWED_HEADERS:
                    variables[header] = vlist[0]        # toss duplicates
                    if not hinfo.has_key(header):
                        hinfo[header] = 15
                del headers[header]
        # insert user-specified extra headers
        variables = self.add_user_headers(variables)
        for header in variables.keys():
            if not hinfo.has_key(header):
                hinfo[header] = 19
        # write the headers into the buffer
        variables['date'] = time.ctime(time.time())
        hseq = _make_dict_sequence(hinfo)
        for x, header in hseq:
            if variables.has_key(header):
                s = "%s: %s\n" \
                    % (string.capwords(header, '-'), variables[header])
                self.text.insert(END, s)
        # insert newline
        self.text.insert(END, '\n', ())
        # insert data
        if data:
            self.text.insert(END, data)
        elif headers.has_key('body'):
            self.text.insert(END, headers['body'][0] + '\n')
        else:
            self.add_user_signature()
        self.text.focus_set()

    def add_user_headers(self, variables):
        # stuff already in `variables' overrides stuff from the file
        headers = self.load_user_headers()
        headers.update(variables)
        return headers

    def add_user_signature(self):
        fn = os.path.join(grailutil.getgraildir(), "mail-signature")
        if os.path.isfile(fn):
            index = self.text.index('end - 1 char')
            self.text.insert(END, open(fn).read())
            self.text.mark_set('insert', index)

    def load_user_headers(self):
        fn = os.path.join(grailutil.getgraildir(), "mail-headers")
        d = {}
        if os.path.isfile(fn):
            msg = rfc822.Message(open(fn))
            for k, v in msg.items():
                d[k] = v
        return d

    def send_command(self):
        message = self.text.get("1.0", END)
        if message:
            self.root['cursor'] = 'watch'
            self.text['cursor'] = 'watch'
            self.root.update_idletasks()
            if message[-1] != '\n': message = message + '\n'
            p = os.popen(SENDMAIL, 'w')
            p.write(message)
            sts = p.close()
            if sts:
                print "*** Sendmail exit status", sts, "***"
        self.root.destroy()

    def cancel_command(self, event=None):
        self.root.destroy()


def _make_sequence_dict(seq):
    dict = {}
    for v, k in seq:
        dict[k] = v
    return dict

def _make_dict_sequence(dict):
    stuff = []
    for k, v in dict.items():
        stuff.append((v, k))
    stuff.sort()
    return stuff
