Apply 79_archiver_slash.patch
[mspang/vmailman.git] / bin / withlist
1 #! @PYTHON@
2 #
3 # Copyright (C) 1998-2004 by the Free Software Foundation, Inc.
4 #
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
19 """General framework for interacting with a mailing list object.
20
21 There are two ways to use this script: interactively or programmatically.
22 Using it interactively allows you to play with, examine and modify a MailList
23 object from Python's interactive interpreter.  When running interactively, a
24 MailList object called `m' will be available in the global namespace.  It also
25 loads the class MailList into the global namespace.
26
27 Programmatically, you can write a function to operate on a MailList object,
28 and this script will take care of the housekeeping (see below for examples).
29 In that case, the general usage syntax is:
30
31 %% bin/withlist [options] listname [args ...]
32
33 Options:
34
35     -l / --lock
36         Lock the list when opening.  Normally the list is opened unlocked
37         (e.g. for read-only operations).  You can always lock the file after
38         the fact by typing `m.Lock()'
39
40         Note that if you use this option, you should explicitly call m.Save()
41         before exiting, since the interpreter's clean up procedure will not
42         automatically save changes to the MailList object (but it will unlock
43         the list).
44
45     -i / --interactive
46         Leaves you at an interactive prompt after all other processing is
47         complete.  This is the default unless the -r option is given.
48
49     --run [module.]callable
50     -r [module.]callable
51         This can be used to run a script with the opened MailList object.
52         This works by attempting to import `module' (which must be in the
53         directory containing withlist, or already be accessible on your
54         sys.path), and then calling `callable' from the module.  callable can
55         be a class or function; it is called with the MailList object as the
56         first argument.  If additional args are given on the command line,
57         they are passed as subsequent positional args to the callable.
58
59         Note that `module.' is optional; if it is omitted then a module with
60         the name `callable' will be imported.
61
62         The global variable `r' will be set to the results of this call.
63
64     --all / -a
65         This option only works with the -r option.  Use this if you want to
66         execute the script on all mailing lists.  When you use -a you should
67         not include a listname argument on the command line.  The variable `r'
68         will be a list of all the results.
69
70     --quiet / -q
71         Suppress all status messages.
72
73     --help / -h
74         Print this message and exit
75
76
77 Here's an example of how to use the -r option.  Say you have a file in the
78 Mailman installation directory called `listaddr.py', with the following
79 two functions:
80
81 def listaddr(mlist):
82     print mlist.GetListEmail()
83
84 def requestaddr(mlist):
85     print mlist.GetRequestEmail()
86
87 Now, from the command line you can print the list's posting address by running
88 the following from the command line:
89
90 %% bin/withlist -r listaddr mylist
91 Loading list: mylist (unlocked)
92 Importing listaddr ...
93 Running listaddr.listaddr() ...
94 mylist@myhost.com
95
96 And you can print the list's request address by running:
97
98 %% bin/withlist -r listaddr.requestaddr mylist
99 Loading list: mylist (unlocked)
100 Importing listaddr ...
101 Running listaddr.requestaddr() ...
102 mylist-request@myhost.com
103
104 As another example, say you wanted to change the password for a particular
105 user on a particular list.  You could put the following function in a file
106 called `changepw.py':
107
108 from Mailman.Errors import NotAMemberError
109
110 def changepw(mlist, addr, newpasswd):
111     try:
112         mlist.setMemberPassword(addr, newpasswd)
113         mlist.Save()
114     except NotAMemberError:
115         print 'No address matched:', addr
116
117 and run this from the command line:
118  %% bin/withlist -l -r changepw mylist somebody@somewhere.org foobar
119 """
120
121 import os
122 import sys
123 import code
124 import getopt
125
126 import paths
127 from Mailman import Errors
128 from Mailman import MailList
129 from Mailman import Utils
130 from Mailman.i18n import _
131
132 try:
133     True, False
134 except NameError:
135     True = 1
136     False = 0
137
138
139 # `m' will be the MailList object and `r' will be the results of the callable
140 m = None
141 r = None
142 VERBOSE = True
143 LOCK = False
144
145
146 # Put the bin directory on sys.path -- last
147 sys.path.append(os.path.dirname(sys.argv[0]))
148
149
150 \f
151 def usage(code, msg=''):
152     if code:
153         fd = sys.stderr
154     else:
155         fd = sys.stdout
156     print >> fd, _(__doc__)
157     if msg:
158         print >> fd, msg
159     sys.exit(code)
160
161
162 def atexit():
163     """Unlock a locked list, but do not implicitly Save() it.
164
165     This does not get run if the interpreter exits because of a signal, or if
166     os._exit() is called.  It will get called if an exception occurs though.
167     """
168     global m
169     if not m:
170         return
171     if m.Locked():
172         if VERBOSE:
173             listname = m.internal_name()
174             print >> sys.stderr, _(
175                 'Unlocking (but not saving) list: %(listname)s')
176         m.Unlock()
177     if VERBOSE:
178         print >> sys.stderr, _('Finalizing')
179     del m
180
181
182 \f
183 def do_list(listname, args, func):
184     global m
185     # first try to open mailing list
186     if VERBOSE:
187         print >> sys.stderr, _('Loading list %(listname)s'),
188         if LOCK:
189             print >> sys.stderr, _('(locked)')
190         else:
191             print >> sys.stderr, _('(unlocked)')
192
193     try:
194         m = MailList.MailList(listname, lock=LOCK)
195     except Errors.MMUnknownListError:
196         print >> sys.stderr, _('Unknown list: %(listname)s')
197         m = None
198
199     # try to import the module and run the callable
200     if func:
201         return func(m, *args)
202     return None
203
204
205 \f
206 def main():
207     global VERBOSE
208     global LOCK
209     global r
210     try:
211         opts, args = getopt.getopt(
212             sys.argv[1:], 'hlr:qia',
213             ['help', 'lock', 'run=', 'quiet', 'interactive', 'all'])
214     except getopt.error, msg:
215         usage(1, msg)
216
217     run = None
218     interact = None
219     all = False
220     dolist = True
221     for opt, arg in opts:
222         if opt in ('-h', '--help'):
223             usage(0)
224         elif opt in ('-l', '--lock'):
225             LOCK = True
226         elif opt in ('-r', '--run'):
227             run = arg
228         elif opt in ('-q', '--quiet'):
229             VERBOSE = False
230         elif opt in ('-i', '--interactive'):
231             interact = True
232         elif opt in ('-a', '--all'):
233             all = True
234
235     if len(args) < 1 and not all:
236         warning = _('No list name supplied.')
237         if interact:
238             # Let them keep going
239             print warning
240             dolist = False
241         else:
242             usage(1, warning)
243
244     if all and not run:
245         usage(1, _('--all requires --run'))
246
247     # The default for interact is 1 unless -r was given
248     if interact is None:
249         if run is None:
250             interact = True
251         else:
252             interact = False
253
254     # try to import the module for the callable
255     func = None
256     if run:
257         i = run.find('.')
258         if i < 0:
259             module = run
260             callable = run
261         else:
262             module = run[:i]
263             callable = run[i+1:]
264         if VERBOSE:
265             print >> sys.stderr, _('Importing %(module)s...')
266         mod = __import__(module)
267         if VERBOSE:
268             print >> sys.stderr, _('Running %(module)s.%(callable)s()...')
269         func = getattr(mod, callable)
270
271     if all:
272         r = [do_list(listname, args, func) for listname in Utils.list_names()]
273     elif dolist:
274         listname = args.pop(0).lower().strip()
275         r = do_list(listname, args, func)
276
277     # Now go to interactive mode, perhaps
278     if interact:
279         # Attempt to import the readline module, so we emulate the interactive
280         # console as closely as possible.  Don't worry if it doesn't import.
281         # readline works by side-effect.
282         try:
283             import readline
284         except ImportError:
285             pass
286         namespace = globals().copy()
287         namespace.update(locals())
288         if dolist:
289             ban = _("The variable `m' is the %(listname)s MailList instance")
290         else:
291             ban = None
292         code.InteractiveConsole(namespace).interact(ban)
293
294
295 \f
296 sys.exitfunc = atexit
297 main()