#!/usr/bin/env python # # Copyright (C) 2001-2007 Jason R. Mastaler # # 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()