#!/usr/bin/python
-import moira
+import errno
+import optparse
import sys
-cache = {}
+import moira
+
+try:
+ import hesiod
+except ImportError:
+ hesiod = None
def get_members_of_list(lst):
return moira.query('get_members_of_list', lst)
-def expand(lst):
- if lst in cache: return cache[lst]
- try:
- members = get_members_of_list(lst)
- except moira.MoiraException, e:
- code, message = e
- if message == "Insufficient permission to perform requested database access":
- cache[lst] = [{'member_type': 'ERR', 'member_name':'** Error: no permissions to look up this list **'}]
- return cache[lst]
- else: raise
- for member in members:
- if member['member_type'] == 'LIST':
- member['members'] = expand(member['member_name'])
- cache[lst] = members
- return members
-
-def print_tree(path, lst_tree):
- for member in lst_tree:
- if 'members' in member:
- path.append(member['member_name'])
- print_tree(path, member['members'])
- path.pop()
+class RBlanche(object):
+ def __init__(self, chpoboxing=False, deactivations=False):
+ self.reset_caches()
+ self.chpoboxing = chpoboxing
+ self.deactivations = deactivations
+
+ def reset_caches(self, ):
+ self.cache = {}
+ self.chpobox = {}
+ self.user_active = {}
+
+ def expand(self, lst):
+ if lst in self.cache: return self.cache[lst]
+ try:
+ members = get_members_of_list(lst)
+ except moira.MoiraException, e:
+ code, message = e
+ if message == "Insufficient permission to perform requested database access":
+ self.cache[lst] = [{'member_type': 'ERR', 'member_name':'** Error: no permissions to look up this list **'}]
+ return self.cache[lst]
+ else: raise
+ for member in members:
+ if member['member_type'] == 'LIST':
+ member['members'] = self.expand(member['member_name'])
+ self.cache[lst] = members
+ return members
+
+ def get_and_format_pobox(self, user):
+ try:
+ result = moira.query('get_pobox', user)[0]
+ except moira.MoiraException, e:
+ return "%8s ** moira error: %s **" % (user, e)
+ if result['type'] == 'SMTP':
+ fmt = '-> %s'
+ elif result['type'] == 'SPLIT':
+ fmt = '=> %s'
+ elif result['type'] in ('EXCHANGE', 'IMAP', 'POP', ''):
+ fmt = ' (%s)'
else:
- if member['member_type'] == "USER":
- text = member['member_name']
- elif member['member_type'] == "ERR":
- text = member['member_name']
+ raise ValueError("Unknown pobox type %s" % (result['type'], ))
+ text = ('%8s ' + fmt) % (result['login'], result['address'], )
+ return text
+
+ def check_deactivated(self, user):
+ try:
+ passwd = hesiod.PasswdLookup(user)
+ return ""
+ except IOError, e:
+ if e.errno == errno.ENOENT:
+ return " ** deactivated **"
else:
- text = "%s:%s" % (member['member_type'], member['member_name'], )
- print ": ".join(path+[text])
+ raise
+
+ def format_user(self, user):
+ text = user
+ if self.chpoboxing:
+ if user not in self.chpobox:
+ self.chpobox[user] = self.get_and_format_pobox(user)
+ text = self.chpobox[user]
+ if self.deactivations:
+ if user not in self.user_active:
+ self.user_active[user] = self.check_deactivated(user)
+ append = self.user_active[user]
+ else:
+ append = ""
+ return text + append
+
+ def print_tree(self, path, lst_tree):
+ for member in lst_tree:
+ if 'members' in member:
+ path.append(member['member_name'])
+ self.print_tree(path, member['members'])
+ path.pop()
+ else:
+ if member['member_type'] == "USER":
+ text = self.format_user(member['member_name'])
+ elif member['member_type'] == "ERR":
+ text = member['member_name']
+ else:
+ text = "%s:%s" % (member['member_type'], member['member_name'], )
+ print ": ".join(path+[text])
+
+ def rblanche(self, lst):
+ result = self.expand(lst)
+ self.print_tree([lst], result)
+
+def parse_args():
+ parser = optparse.OptionParser(usage='usage: %prog [--auth=yes|no|try] [-cd] list')
+ parser.add_option('--auth', dest='auth', default=None,
+ help='authenticate to the moira server? (yes, no, or try)'
+ )
+ parser.add_option('-c', '--chpobox',
+ dest='chpoboxing', action='store_true', default=False,
+ help='check mail forwarding for each user',
+ )
+ parser.add_option('-d', '--deactivated',
+ dest='deactivations', action='store_true', default=False,
+ help='check whether users are deactivated',
+ )
+ options, args = parser.parse_args()
+
+ if len(args) != 1:
+ parser.error("incorrect number of arguments")
+ if options.auth:
+ if options.auth == 'yes': options.auth = True
+ elif options.auth == 'try': options.auth = None
+ elif options.auth == 'no': options.auth = False
+ else:
+ parser.error("--auth takes yes, no, and try (default; auth if possible)")
+ if options.deactivations and not hesiod:
+ parser.error("deactivation checking requires python-hesiod, which is not available")
-def rblanche(lst):
- result = expand(lst)
- print_tree([lst], result)
+ return options, args
if __name__ == '__main__':
+ options, args = parse_args()
moira.connect()
- rblanche(sys.argv[1])
+ if options.auth:
+ moira.auth('rblnchpy')
+ elif options.auth is None:
+ try:
+ moira.auth('rblnchpy')
+ except moira.MoiraException:
+ print >>sys.stderr, "Warning: Failed to authenticate to moira"
+ rblanche = RBlanche(
+ chpoboxing=options.chpoboxing,
+ deactivations=options.deactivations,
+ )
+ rblanche.rblanche(args[0])