#!/usr/bin/env python

######################
# Sender (client) mode
######################

"""Tag and send outgoing messages.

Usage:  %(program)s [OPTIONS] [ recip ... ]

OPTIONS:
	-h
	--help
	   Print this help message and exit.

	-c <file>
	--config-file <file>
	   Specify a different configuration file other than ~/.tmdarc.

        -f <sender>
           This option is silently ignored currently.

        -q
        --qfilter
           Return 99 to qfilter so it will not run qmail-queue itself.

        -O <file>
        --filter-outgoing-file <file>
           Full pathname to your outgoing filter file.  Overrides
           FILTER_OUTGOING in ~/.tmdarc.

        -M <recipient>
        --filter-match <recipient>
           Check whether the given e-mail address matches a line in your outgoing
           filter and then exit.  The address given should be that of the message
           recipient.  This option will also check for parsing errors in the
           filter file.
           
	recip
	   If one or more recipients are provided, send the message to
	   all recip arguments.  If none are provided, send the message
	   to all header recipient addresses.
"""

import getopt
import os
import sys

# The contents of these two lists will be used to tag the message.
envelope_sender_address = []
sender_full_name = []

filter_match = None
qfilter = None
program = sys.argv[0]

def usage(code, msg=''):
    print __doc__ % globals()
    if msg:
        print msg
    sys.exit(code)
    
try:
    opts, args = getopt.getopt(sys.argv[1:],
                               'c:O:M:qhf:', ['config-file=',
                                              'filter-outgoing-file=',
                                              'filter-match=',
                                              'qfilter',
                                              'help'])
except getopt.error, msg:
    usage(1, msg)

for opt, arg in opts:
    if opt in ('-h', '--help'):
        usage(0)
    elif opt in ('-M', '--filter-match'):
	filter_match = 1
    elif opt in ('-O', '--filter-outgoing-file'):
	os.environ['TMDA_FILTER_OUTGOING'] = arg
    elif opt in ('-c', '--config-file'):
        os.environ['TMDARC'] = arg
    elif opt in ('-q', '--qfilter'):
        qfilter = 99
    elif opt == '-f':
        pass


try:
    import paths
except ImportError:
    pass

from TMDA import Defaults
from TMDA import Cookie
from TMDA import FilterParser
from TMDA import Util
from TMDA import Version

import popen2
import rfc822
import string


# Just check Defaults.FILTER_OUTGOING for syntax errors and possible
# matches, and then exit.
if filter_match:
    recip = sys.argv[-1]
    Util.filter_match(Defaults.FILTER_OUTGOING, recip)
    sys.exit()

message = sys.stdin
message_headers = rfc822.Message(message)


def inject_message(resending,
                   to_address,
                   from_address,
                   message_headers,
                   message_body,
                   cookie_type,
                   cookie_option=None):
    """Hand the message off to sendmail."""
    if cookie_type == 'bare':
        # Send a message with an untagged address.
        envelope_sender_address.append(from_address)
        # Optionally append the recipient address to a file.
        if (cookie_option and string.lower(cookie_option) == 'append'
            and Defaults.BARE_APPEND):
            Util.append_to_file(to_address, Defaults.BARE_APPEND)
    elif cookie_type == 'dated':
        # Send a message with a tagged (dated) address.
        if cookie_option:               # check for timeout override
            os.environ['TMDA_TIMEOUT'] =  cookie_option
        envelope_sender_address.append(Cookie.make_dated_address(from_address))
    elif cookie_type == 'sender':
        # Send a message with a tagged (sender) address
        if cookie_option:               # base sender cookie on this address
            sender_cookie_address = cookie_option
        else:
            sender_cookie_address = to_address
        envelope_sender_address.append(Cookie.make_sender_address
                                       (from_address, sender_cookie_address))
    elif cookie_type in ('as','exp','explicit') and cookie_option:
        # Send a message with an explicitly defined address.
        envelope_sender_address.append(cookie_option)
    elif cookie_type in ('ext','extension') and cookie_option:
        # Send a message with a tagged (extension added) address.
        (username, hostname) = string.split(from_address,'@')
        envelope_sender_address.append(username + Defaults.RECIPIENT_DELIMITER
                                       + cookie_option + '@' + hostname)
    elif cookie_type in ('kw','keyword') and cookie_option:
        # Send a message with a tagged (keyword) address.
        envelope_sender_address.append(Cookie.make_keyword_address
                                       (from_address, cookie_option))
    else:
        # If cookie_type is invalid, punt and use an untagged address.
        envelope_sender_address.append(from_address)
    
    final_sender_address = envelope_sender_address[-1:][0]
    final_full_name = sender_full_name[0]
    # "angles" is the default MESSAGE_FROM_STYLE
    if Defaults.MESSAGE_FROM_STYLE == 'parens':
        message_from_format = '%s (%s)' % (final_sender_address,final_full_name)
    elif Defaults.MESSAGE_FROM_STYLE == 'address':
        message_from_format = '%s' % (final_sender_address)
    elif Defaults.MESSAGE_FROM_STYLE == 'unquoted':
        message_from_format = '%s <%s>' % (final_full_name,final_sender_address)
    else:
        message_from_format = '"%s" <%s>' % (final_full_name,final_sender_address)
    # Set From: or Resent-From: to match the envelope sender address.
    if resending:
        message_headers['Resent-From'] = message_from_format
    else:
        message_headers['From'] = message_from_format
    # If the MUA has added a `Mail-Followup-To' header that contains
    # the untagged address, we need to tag that as well.
    if message_headers.has_key('mail-followup-to'):
        mft_list = message_headers.getaddrlist('mail-followup-to')
        new_mft_list = []
        for a in mft_list:
            emaddy = a[1]
            if emaddy == from_address:
                new_mft_list.append(final_sender_address)
            else:
                new_mft_list.append(emaddy)
        message_headers['Mail-Followup-To'] = string.join(new_mft_list, ',\n\t')
    # Add `Date' and `Message-ID' headers only if they don't already exist.
    if not message_headers.has_key('date'):
        message_headers['Date'] = Util.make_date()
    if not message_headers.has_key('message-id'):
        message_headers['Message-ID'] = Util.make_msgid()
    # Add `X-Delivery-Agent' header.
    message_headers['X-Delivery-Agent'] = Version.ALL
    # Optionally, add an `X-TMDA-Fingerprint' header.
    if Defaults.FINGERPRINT:
        hdrlist = []
        for hdr in Defaults.FINGERPRINT:
            hdrval = message_headers.getheader(hdr, None)
            if hdrval:
                hdrlist.append(hdrval)
        if hdrlist:
            message_headers['X-TMDA-Fingerprint'] = (
                Cookie.make_fingerprint(hdrlist))
    # Optionally, remove some headers.
    if Defaults.PURGED_HEADERS:
        for hdr in Defaults.PURGED_HEADERS:
            del message_headers[hdr]
    # Inject the message.
    inject = []
    inject.append(Defaults.SENDMAIL)
    inject.append('-f')
    inject.append(final_sender_address)
    inject.append(to_address)
    pipeline = popen2.popen2(inject)[1]
    pipeline.write(str(message_headers))
    pipeline.write('\n')
    pipeline.write(message_body)
    pipeline.close()


######
# Main
######

def main():

    x_tmda_over = None
    action = None
    action_option = None

    if message_headers.has_key('resent-from'):
        # We must be resending (bouncing) the message.
        (fullname, from_address) = message_headers.getaddr('resent-from')
        resending = 1
    else:
        # Use the existing From: header if possible.
        (fullname, from_address) = message_headers.getaddr("from")
        resending = None
    if fullname:
        sender_full_name.append(fullname)
    else:
        sender_full_name.append(Defaults.FULLNAME)
    if not from_address or len(string.split(from_address,'@')) != 2:
        from_address = Defaults.USERNAME + '@' + Defaults.HOSTNAME
    
    header_to = message_headers.getaddrlist("to")
    header_cc = message_headers.getaddrlist("cc")
    header_bcc = message_headers.getaddrlist("bcc")
    # Read in the message body.
    message_body = message.read()
    
    # If recipients were provided as args, use them.
    if args:
        address_list = args
    # If running through qfilter, get recipient list from QMAILRCPTS.
    elif os.environ.has_key('QMAILRCPTS'):
        address_list = string.split(string.lower
                                    (os.environ['QMAILRCPTS']),'\n')[:-1]
    else:
        # Combine To:, Cc:, and Bcc: addresses into one list.
	address_list = []
	for list in header_to,header_cc,header_bcc:
	    for a in list:
                address = a[1]
                address_list.append(address)
               
    # Check for the `X-TMDA' override header.
    if message_headers.has_key('x-tmda'):
        x_tmda_over = 1
        x_tmda = message_headers.getheader('x-tmda')
        # X-TMDA should only have one field.
        if len(string.split(x_tmda)) == 1:
            fields = string.split(x_tmda,'=')
            action = string.strip(string.lower(fields[0]))
            # Test for an action option.
            try:
                action_option = string.strip(fields[1])
            except IndexError:
                pass
        # Delete `X-TMDA' before sending.
        del message_headers['x-tmda']
    else:
        # Without `X-TMDA', we need to parse the outgoing filter file.
        outfilter = FilterParser.FilterParser()
        outfilter.read(Defaults.FILTER_OUTGOING)

    # If the address matches a line in the filter file, it is tagged
    # accordingly, otherwise it is tagged with the default cookie
    # type.
    for address in address_list:
        # If `X-TMDA' is present we are done here.
        if x_tmda_over:
            pass                        
        else:
            (action, action_option, matching_line) = outfilter.firstmatch(address)
        if not action:action = string.lower(Defaults.ACTION_OUTGOING)
        # The message is sent to each recipient separately so that
        # everyone gets the correct tag.  Make sure your MUA
        # generates its own Message-ID: and Date: headers so they
        # match on multiple recipient messages.
        inject_message(resending,
                       address,
                       from_address,
                       message_headers,
                       message_body,
                       action,
                       action_option)
    if qfilter:
        sys.exit(qfilter)
    else:
        sys.exit(Defaults.EX_OK)


# This is the end my friend.
if __name__ == '__main__':
    main()
