Import mailman 2.1.9
[mspang/vmailman.git] / bin / list_members
1 #! @PYTHON@
2 #
3 # Copyright (C) 1998-2003 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 """List all the members of a mailing list.
20
21 Usage: %(PROGRAM)s [options] listname
22
23 Where:
24
25     --output file
26     -o file
27         Write output to specified file instead of standard out.
28
29     --regular / -r
30         Print just the regular (non-digest) members.
31
32     --digest[=kind] / -d [kind]
33         Print just the digest members.  Optional argument can be "mime" or
34         "plain" which prints just the digest members receiving that kind of
35         digest.
36
37     --nomail[=why] / -n [why]
38         Print the members that have delivery disabled.  Optional argument can
39         be "byadmin", "byuser", "bybounce", or "unknown" which prints just the
40         users who have delivery disabled for that reason.  It can also be
41         "enabled" which prints just those member for whom delivery is
42         enabled.
43
44     --fullnames / -f
45         Include the full names in the output.
46
47     --preserve / -p
48         Output member addresses case preserved the way they were added to the
49         list.  Otherwise, addresses are printed in all lowercase.
50
51     --invalid / -i
52         Print only the addresses in the membership list that are invalid.
53         Ignores -r, -d, -n.
54
55     --unicode / -u
56         Print addresses which are stored as Unicode objects instead of normal
57         string objects.  Ignores -r, -d, -n.
58
59     --help
60     -h
61         Print this help message and exit.
62
63     listname is the name of the mailing list to use.
64
65 Note that if neither -r or -d is supplied, both regular members are printed
66 first, followed by digest members, but no indication is given as to address
67 status.
68 """
69
70 import sys
71 from types import UnicodeType
72
73 import paths
74 from Mailman import mm_cfg
75 from Mailman import Utils
76 from Mailman import MailList
77 from Mailman import Errors
78 from Mailman import MemberAdaptor
79 from Mailman.i18n import _
80
81 from email.Utils import formataddr
82
83 PROGRAM = sys.argv[0]
84 ENC = sys.getdefaultencoding()
85 COMMASPACE = ', '
86
87 try:
88     True, False
89 except NameError:
90     True = 1
91     False = 0
92
93
94 WHYCHOICES = {'enabled' : MemberAdaptor.ENABLED,
95               'unknown' : MemberAdaptor.UNKNOWN,
96               'byuser'  : MemberAdaptor.BYUSER,
97               'byadmin' : MemberAdaptor.BYADMIN,
98               'bybounce': MemberAdaptor.BYBOUNCE,
99               }
100
101
102 def usage(code, msg=''):
103     if code:
104         fd = sys.stderr
105     else:
106         fd = sys.stdout
107     print >> fd, _(__doc__)
108     if msg:
109         print >> fd, msg
110     sys.exit(code)
111
112
113 \f
114 def safe(s):
115     if not s:
116         return ''
117     if isinstance(s, UnicodeType):
118         return s.encode(ENC, 'replace')
119     return unicode(s, ENC, 'replace').encode(ENC, 'replace')
120
121
122 def isinvalid(addr):
123     try:
124         Utils.ValidateEmail(addr)
125         return False
126     except Errors.EmailAddressError:
127         return True
128
129 def isunicode(addr):
130     return isinstance(addr, UnicodeType)
131
132
133 \f
134 def whymatches(mlist, addr, why):
135     # Return true if the `why' matches the reason the address is enabled, or
136     # in the case of why is None, that they are disabled for any reason
137     # (i.e. not enabled).
138     status = mlist.getDeliveryStatus(addr)
139     if why is None:
140         return status <> MemberAdaptor.ENABLED
141     return status == WHYCHOICES[why]
142
143
144 \f
145 def main():
146     # Because of the optional arguments, we can't use getopt. :(
147     outfile = None
148     regular = None
149     digest = None
150     preserve = None
151     nomail = None
152     why = None
153     kind = None
154     fullnames = False
155     invalidonly = False
156     unicodeonly = False
157
158     # Throw away the first (program) argument
159     args = sys.argv[1:]
160     if not args:
161         usage(0)
162
163     while True:
164         try:
165             opt = args.pop(0)
166         except IndexError:
167             usage(1)
168         if opt in ('-h', '--help'):
169             usage(0)
170         elif opt in ('-f', '--fullnames'):
171             fullnames = True
172         elif opt in ('-p', '--preserve'):
173             preserve = True
174         elif opt in ('-r', '--regular'):
175             regular = True
176         elif opt in ('-o', '--output'):
177             try:
178                 outfile = args.pop(0)
179             except IndexError:
180                 usage(1)
181         elif opt == '-n':
182             nomail = True
183             if args and args[0] in WHYCHOICES.keys():
184                 why = args.pop(0)
185         elif opt.startswith('--nomail'):
186             nomail = True
187             i = opt.find('=')
188             if i >= 0:
189                 why = opt[i+1:]
190                 if why not in WHYCHOICES.keys():
191                     usage(1, _('Bad --nomail option: %(why)s'))
192         elif opt == '-d':
193             digest = True
194             if args and args[0] in ('mime', 'plain'):
195                 kind = args.pop(0)
196         elif opt.startswith('--digest'):
197             digest = True
198             i = opt.find('=')
199             if i >= 0:
200                 kind = opt[i+1:]
201                 if kind not in ('mime', 'plain'):
202                     usage(1, _('Bad --digest option: %(kind)s'))
203         elif opt in ('-i', '--invalid'):
204             invalidonly = True
205         elif opt in ('-u', '--unicode'):
206             unicodeonly = True
207         else:
208             # No more options left, push the last one back on the list
209             args.insert(0, opt)
210             break
211
212     if len(args) <> 1:
213         usage(1)
214
215     listname = args[0].lower().strip()
216
217     if regular is None and digest is None:
218         regular = digest = True
219
220     if outfile:
221         try:
222             fp = open(outfile, 'w')
223         except IOError:
224             print >> sys.stderr, _('Could not open file for writing:'), outfile
225             sys.exit(1)
226     else:
227         fp = sys.stdout
228
229     try:
230         mlist = MailList.MailList(listname, lock=False)
231     except Errors.MMListError, e:
232         print >> sys.stderr, _('No such list: %(listname)s')
233         sys.exit(1)
234
235     # Get the lowercased member addresses
236     rmembers = mlist.getRegularMemberKeys()
237     dmembers = mlist.getDigestMemberKeys()
238
239     if preserve:
240         # Convert to the case preserved addresses
241         rmembers = mlist.getMemberCPAddresses(rmembers)
242         dmembers = mlist.getMemberCPAddresses(dmembers)
243
244     if invalidonly or unicodeonly:
245         all = rmembers + dmembers
246         all.sort()
247         for addr in all:
248             name = fullnames and mlist.getMemberName(addr) or ''
249             showit = False
250             if invalidonly and isinvalid(addr):
251                 showit = True
252             if unicodeonly and isunicode(addr):
253                 showit = True
254             if showit:
255                 print >> fp, formataddr((safe(name), addr))
256         return
257     if regular:
258         rmembers.sort()
259         for addr in rmembers:
260             name = fullnames and mlist.getMemberName(addr) or ''
261             # Filter out nomails
262             if nomail and not whymatches(mlist, addr, why):
263                 continue
264             print >> fp, formataddr((safe(name), addr))
265     if digest:
266         dmembers.sort()
267         for addr in dmembers:
268             name = fullnames and mlist.getMemberName(addr) or ''
269             # Filter out nomails
270             if nomail and not whymatches(mlist, addr, why):
271                 continue
272             # Filter out digest kinds
273             if mlist.getMemberOption(addr, mm_cfg.DisableMime):
274                 # They're getting plain text digests
275                 if kind == 'mime':
276                     continue
277             else:
278                 # They're getting MIME digests
279                 if kind == 'plain':
280                     continue
281             print >> fp, formataddr((safe(name), addr))
282
283
284 \f
285 if __name__ == '__main__':
286     main()