#!/usr/bin/env python
#
# Copyright (C) 2001-2007 Jason R. Mastaler <jason@mastaler.com>
#
# This file is part of TMDA.
#
# TMDA 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.  A copy of this license should
# be included in the file COPYING.
#
# TMDA 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 TMDA; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA


from optparse import OptionGroup, OptionParser

import os
import sys
import socket

try:
    import paths
except ImportError:
    # Prepend /usr/lib/python2.x/site-packages/TMDA/pythonlib
    sitedir = os.path.join(sys.prefix, 'lib', 'python'+sys.version[:3],
                           'site-packages', 'TMDA', 'pythonlib')
    sys.path.insert(0, sitedir)

import email

from TMDA import Version

# option parsing

# We need to subclass OptionParser here in order to print text after
# the help message.  An 'epilogue' attribute to OptionParser was added
# in Python 2.5, so in the future, when we require Python >= 2.5, we
# can use that instead.

class MyOptionParser(OptionParser):
    def format_help(self, *args, **kwargs):
        result = OptionParser.format_help(self, *args, **kwargs)
        if hasattr(self, 'trailer'):
            return "%s\n%s\n" % (result, self.trailer)
        else:
            return result

opt_usage = "%prog [OPTIONS] [MESSAGES ... | - ]"

opt_desc = \
"""A TMDA pending queue manipulation tool. If one or more MESSAGES are
provided as arguments, operate just on them.  If `-' is specified,
operate on a list of messages provided by standard input. Otherwise,
operate on all messages in the pending queue."""

parser = MyOptionParser(version=Version.TMDA, usage=opt_usage, description=opt_desc)

parser.trailer = \
"""examples:

  (interactively operate on all pending messages)
  $ tmda-pending

  (get a summary listing of all pending messages)
  $ tmda-pending -T -b

  (same as above, but use /usr/local/bin/vhome as the vhome-script)
  $ tmda-pending -T -b --vhome-script=/usr/local/bin/vhome

  (interactively operate on just these messages)
  $ tmda-pending 1012182077.5803 1012939546.7870

  (immediately release these messages from the pending queue)
  $ tmda-pending -b -r 1012182077.5803 1012939546.7870

  (immediately release any messages with `foobar' in them)
  $ tmda-pending -b -T | grep foobar | awk '{print $1}' | $ tmda-pending -b -r -

  (immediately delete all messages from the pending queue)
  $ tmda-pending -b -d

  (silently and immediately delete all messages older than 30 days)
  $ tmda-pending -q -b -d -O 30d
    
  (mail a summary report of all new pending messages)
  $ tmda-pending -C -b -s | mail -s 'TMDA pending summary' jason
"""

parser.add_option("-V", 
		  action="store_true", default=False, dest="full_version",
		  help="show full TMDA version information and exit.")

# option groups
gengroup = OptionGroup(parser, "General")
actngroup = OptionGroup(parser, "Actions")
msggroup = OptionGroup(parser, "Messages")

# general
gengroup.add_option("--vhome-script",
	             metavar="SCRIPT", dest="vhomescript",
        	     help= \
"""Full pathname of SCRIPT that prints a virtual email user's home
directory on standard output.  tmda-pending will read that path and
set $HOME to that path so that '~' expansion works properly for
virtual users.  The script takes two arguments, the user name and
the domain, on its command line.  This option is for use with the
VPopMail and VMailMgr add-ons to qmail and with other systems that
manage virtual email addresses.  The user name is obtained from the
USER and LOGNAME environment variables, in that order, or by the
value of the '--vhome-user' option, if defined. The domain name is
obtained by the '--vhome-domain' option, if defined, and if not,
an attempt to figure it out automatically.""")

gengroup.add_option("--vhome-user",
	             metavar="USER", dest="vhomeuser",
        	     help= \
"""The user name to optionally pass to the program specified
in the '--vhome-script' option, above.""")

gengroup.add_option("--vhome-domain",
	             metavar="DOMAIN", dest="vhomedomain",
        	     help= \
"""The domain name to optionally pass to the program specified
in the '--vhome-script' option, above.""")

gengroup.add_option("-c", "--config-file",
                    metavar="FILE", dest="config_file",
                    help= \
"""Specify a different configuration file other than ~/.tmda/config""")

gengroup.add_option("-v", "--verbose",
                    action="store_true", default=True, dest="verbose",
                    help="Display output (default).")

gengroup.add_option("-q", "--quiet",
                    action="store_false", dest="verbose",
                    help="Suppress output.  Not compatible with '--interactive.'")

gengroup.add_option("-p", "--pretend",
                    action="store_true", dest="pretend",
                    help= \
"""Don't actually operate on messages, just show what would have
happened.  Implies '--verbose'.""")

gengroup.add_option("-i", "--interactive",
                    action="store_true", default=True, dest="interactive",
                    help= \
"""Display a summary of each pending message and prompt for disposal
(pass, show, release, delete, quit).  Implies '--verbose'. (default)""")

gengroup.add_option("-b", "--batch",
                    action="store_false", dest="interactive",
                    help= \
"""Operate non-interactively in batch mode. Use with caution.""")

gengroup.add_option("-C", "--cache",
                    action="store_true", dest="cache",
                    help= \
"""Operate only on messages which aren't stored in
 ~/.tmda/.pendingcache (i.e, haven't yet been read).  After operation,
 store the message in the cache file.  Useful primarily in conjunction
 with the '--summary' option.""")

# actions

actngroup.add_option("-r", "--release",
                     action="store_const", const="release", dest="dispose",
                     help="Release messages.")

actngroup.add_option("-R", "--recipient",
                     metavar="ADDRESS", dest="command_recipient",
                     help= \
"""Override the email address used to create the magic release address
by specifying ADDRESS.  Normally, this is determined by parsing the
`X-TMDA-Recipient' header which every pending message contains.""")

actngroup.add_option("-d", "--delete",
                     action="store_const", const="delete", dest="dispose",
                     help="Delete messages.")

actngroup.add_option("-B", "--blacklist",
                     action="store_const", const="blacklist", dest="dispose",
                     help= \
"""Append the sender of the message to the PENDING_BLACKLIST_APPEND file.""")

actngroup.add_option("-W", "--whitelist",
                     action="store_const", const="whitelist", dest="dispose",
                     help= \
"""Append the sender of the message to the PENDING_WHITELIST_APPEND file.""")

actngroup.add_option("-S", "--show",
                     action="store_const", const="show", dest="dispose",
                     help= \
"""Display the full contents of the given message with $PAGER (usually
the `more', or `less' program).""")

actngroup.add_option("-s", "--summary",
                     action="store_true", dest="summary",
                     help= \
"""Print a summary of pending messages along with a release address
link. Implies '--verbose'.""")

actngroup.add_option("-T", "--terse-summary",
                     action="store_true", dest="terse",
                     help= \
"""Print a terse (one-line per message) summary of pending messages.
Customize the display with TERSE_SUMMARY_HEADERS. Implies '--verbose'.""")

# messages

msggroup.add_option("-A", "--ascending",
                    action="store_false", dest="descending",
                    help="Operate on messages in ascending order (default).")

msggroup.add_option("-D", "--descending",
                    action="store_true", dest="descending",
                    help="Operate on messages in descending order.")

msggroup.add_option("-Y", "--younger",
                    metavar="INTERVAL", dest="younger",
                    help= \
"""Operate only on messages younger than the time INTERVAL given in
seconds (s), minutes (m), hours (h), days (d), weeks (w), months (M),
or years (Y).""")

msggroup.add_option("-O", "--older",
                    metavar="INTERVAL", dest="older",
                    help= \
"""Operate only on messages older than the time INTERVAL given in
seconds (s), minutes (m), hours (h), days (d), weeks (w), months (M),
or years (Y).""")

for g in (gengroup, actngroup, msggroup):
    parser.add_option_group(g)

(opts, args) = parser.parse_args()

if opts.full_version:
    print Version.ALL
    sys.exit()

if opts.config_file:
     os.environ['TMDARC'] = opts.config_file

if opts.pretend or opts.interactive:
    opts.verbose = True

if opts.younger:
    threshold = opts.younger
elif opts.older:
    threshold = opts.older
else:
    threshold = None

if opts.vhomescript:
    """Set $HOME to the recipient's (virtual user) home directory."""
    user = None
    if opts.vhomeuser:
	user = opts.vhomeuser
    else:
	try:
            user = os.environ['USER']
        except:
            try:
                user = os.environ['LOGNAME']
            except:
                user = None
    domain = None
    if opts.vhomedomain:
        domain = opts.vhomedomain
    else:
        try:
            domain = socket.gethostbyaddr(socket.gethostname())[0]
        except:
            domain = None
    message = ''
    if user is None:
        message += ' USER'
    if domain is None:
        message += ' DOMAIN'
    if message != '':
        sys.stdout.write('missing:%s\n' % message)
        sys.exit(1)
    cmd = "%s '%s' '%s'" % ( opts.vhomescript, user, domain )
    fpin = os.popen(cmd)
    vuserhomedir = fpin.read().strip()
    if fpin.close() is None:
        os.environ['HOME'] = vuserhomedir
        os.chdir(vuserhomedir)
    else:
        sys.stdout.write('invocation failed: %s\n' % ( cmd, ))
        sys.exit(1)

from TMDA import Pending
from TMDA import Errors


def main():
    if opts.interactive:
        QueueObject = Pending.InteractiveQueue
    else:
        QueueObject = Pending.Queue
    try:
        q = QueueObject(
            msgs = args,
            cache = opts.cache,
            command_recipient = opts.command_recipient,
            descending = opts.descending,
            dispose = opts.dispose,
            older = opts.older,
            summary = opts.summary,
            terse = opts.terse,
            threshold = threshold,
            verbose = opts.verbose,
            younger = opts.younger,
            pretend = opts.pretend
            ).initQueue()
        q.mainLoop()
    except Errors.QueueError, obj:
        print obj
        sys.exit(1)

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