#!/usr/bin/env python ## # Copyright (c) 2008 Apple Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ## import commands import getopt import os import sys from twext.python.plistlib import readPlist try: import opendirectory import dsattributes except ImportError: sys.path.append("/usr/share/caldavd/lib/python") import opendirectory import dsattributes def usage(e=None): if e: print e print "" name = os.path.basename(sys.argv[0]) print "usage: %s [-c] [-f FILE]" % (name,) print "" print "Tool to remove invalid OD record GUIDs from the proxy DB." print "" print "options:" print " -h: print this help" print " -c: change sqlite DB - without this just report what would happen" print " -f: caldavd.plist file to read (default: /etc/caldavd/caldavd.plist)" if e: sys.exit(64) else: sys.exit(0) def extractPlistPieces(plistdbpath): plist = readPlist(plistdbpath) try: dsnode = plist["DirectoryService"]["params"]["node"] except KeyError: raise ValueError("Unable to read DirectoryService/params/node key from plist: %s" % (plistdbpath,)) try: dataroot = plist["DataRoot"] except KeyError: raise ValueError("Unable to read DataRoot key from plist: %s" % (plistdbpath,)) # Find the appropriate sqlite db proxydbpath_data = os.path.join(dataroot, "calendaruserproxy.sqlite") if not os.path.exists(proxydbpath_data): try: docroot = plist["DocumentRoot"] except KeyError: "Unable to read DocumentRoot key from plist: %s" % (plistdbpath,) raise proxydbpath_doc = os.path.join(docroot, "principals", ".db.calendaruserproxy") if not os.path.exists(proxydbpath_doc): raise("Unable to find proxy db at '%s' or '%s'" % (proxydbpath_data, proxydbpath_doc)) else: proxydbpath = proxydbpath_doc else: proxydbpath = proxydbpath_data print "" print "Parsed: %s" % (plistdbpath,) print "Found DS Node: %s" % (dsnode,) print "Found proxy DB path: %s" % (proxydbpath) print "" return dsnode, proxydbpath def loadUserRecords(dsnode): print "Loading /Users records from OD: %s" % (dsnode,) od = opendirectory.odInit(dsnode) results = opendirectory.listAllRecordsWithAttributes( od, dsattributes.kDSStdRecordTypeUsers, [dsattributes.kDS1AttrGeneratedUID,] ) result = set() for record in results.itervalues(): guid = record.get(dsattributes.kDS1AttrGeneratedUID, None) if guid: result.add(guid) print "Found %d /Users records" % (len(result),) print "" return result def cleanProxies(existing_guids, proxydbpath, do_changes): # Get proxy entries print "Reading proxy DB: %s" % (proxydbpath,) result = commands.getoutput("sqlite3 %s \"select * from GROUPS\"" % (proxydbpath,)) if not result: print "Proxy DB empty. Nothing to do." return lines = result.split("\n") print "Found %d proxy DB records" % (len(lines),) proxying = set() proxying_total = 0 users = set() users_total = 0 for line in lines: proxy, user = line.split("|") if proxy.split("#")[0] not in existing_guids: proxying.add(proxy) proxying_total += 1 if user not in existing_guids: users.add(user) users_total += 1 if do_changes: print "Changes will be made to the proxy DB" else: print "Changes will not be made to the proxy DB" print "" print "Found %d invalid group names (total records %d)" % (len(proxying), proxying_total,) print "============================" for key in sorted(proxying): print key if do_changes: commands.getoutput("sqlite3 %s \"delete from GROUPS where GROUPNAME = '%s'\"" % (proxydbpath, key,)) print "============================" print "" print "Found %d invalid members (total records %d)" % (len(users), users_total,) print "============================" for key in sorted(users): print key if do_changes: commands.getoutput("sqlite3 %s \"delete from GROUPS where MEMBER = '%s'\"" % (proxydbpath, key,)) print "============================" print "" print "Done." if __name__ == "__main__": try: (optargs, args) = getopt.getopt(sys.argv[1:], "cf:h", ["help"]) except getopt.GetoptError, e: usage(e) if len(args) != 0: usage("Wrong number of arguments.") plistdbpath = "/etc/caldavd/caldavd.plist" do_changes = False for opt, arg in optargs: if opt in ("-h", "--help"): usage() elif opt == "-c": do_changes = True elif opt == "-f": plistdbpath = arg try: print "CalendarServer proxy DB clean-up tool" print "=====================================" if not os.path.exists(plistdbpath): raise ValueError("caldavd.plist file does not exist: %s" % (plistdbpath,)) dsnode, proxydbpath = extractPlistPieces(plistdbpath) guids = loadUserRecords(dsnode) cleanProxies(guids, proxydbpath, do_changes) sys.exit(0) except ValueError, e: print "" print "Failed: %s" % (str(e),)