[CalendarServer-changes] [4040] CalendarServer/branches/users/sagen/resource-delegates-4038/bin/ caldav_utility.py
source_changes at macosforge.org
source_changes at macosforge.org
Sat Apr 18 17:08:45 PDT 2009
Revision: 4040
http://trac.macosforge.org/projects/calendarserver/changeset/4040
Author: sagen at apple.com
Date: 2009-04-18 17:08:45 -0700 (Sat, 18 Apr 2009)
Log Message:
-----------
command line tool for manipulating delegates
Modified Paths:
--------------
CalendarServer/branches/users/sagen/resource-delegates-4038/bin/caldav_utility.py
Modified: CalendarServer/branches/users/sagen/resource-delegates-4038/bin/caldav_utility.py
===================================================================
--- CalendarServer/branches/users/sagen/resource-delegates-4038/bin/caldav_utility.py 2009-04-18 20:22:39 UTC (rev 4039)
+++ CalendarServer/branches/users/sagen/resource-delegates-4038/bin/caldav_utility.py 2009-04-19 00:08:45 UTC (rev 4040)
@@ -18,6 +18,7 @@
from __future__ import with_statement
+
import sys
if "PYTHONPATH" in globals():
@@ -37,53 +38,278 @@
# sys.path.insert(0, "/usr/share/caldavd/lib/python")
-import os
-import itertools
-from code import interact
-
+from calendarserver.provision.root import RootResource
from getopt import getopt, GetoptError
-from os.path import dirname, abspath
-
from twisted.internet import reactor
+from twisted.internet.address import IPv4Address
from twisted.internet.defer import inlineCallbacks, returnValue, succeed
from twisted.python import log
from twisted.python.reflect import namedClass
from twisted.web2.dav import davxml
-# from twisted.web2.http import Request
-
-from twistedcaldav import ical
from twistedcaldav import caldavxml
-from twistedcaldav.resource import isPseudoCalendarCollectionResource
-from twistedcaldav.static import CalDAVFile, CalendarHomeFile, CalendarHomeProvisioningFile
+from twistedcaldav import memcachepool
from twistedcaldav.config import config, defaultConfigFile
+from twistedcaldav.customxml import calendarserver_namespace
from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord
from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource
+from twistedcaldav.notify import installNotificationClient
+from twistedcaldav.static import CalendarHomeProvisioningFile
+import itertools
+import os
-from calendarserver.provision.root import RootResource
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+def usage(e=None):
+ if e:
+ print e
+ print ""
-from twistedcaldav import memcachepool
-from twistedcaldav.notify import installNotificationClient
-from twisted.internet.address import IPv4Address
+ name = os.path.basename(sys.argv[0])
+ print "usage: %s [options]" % (name,)
+ print ""
+ print "options:"
+ print " -h --help: print this help and exit"
+ print " -f --config <path>: Specify caldavd.plist configuration path"
+ print " --resource <prinicpal-path>: path of the resource to use"
+ print " --read-property: namespace-qualified DAV property to read, e.g. 'DAV:#group-member-set'"
+ print " --list-read-delegates: list delegates with read-only access to the current resource"
+ print " --list-write-delegates: list delegates with read-write access to the current resource"
+ print " --add-read-delegate <prinicpal-path>: add argument as a read-only delegate to the current resource"
+ print " --add-write-delegate <prinicpal-path>: add argument as a read-write delegate to the current resource"
+ print " --remove-delegate <prinicpal-path>: strip argument of delegate status for the current resource"
-# This dictionary is a mapping of symbols that other modules might want
-# to use; it's populated by the @exportmethod decorator below.
-exportedSymbols = { }
+ if e:
+ sys.exit(64)
+ else:
+ sys.exit(0)
-def exportmethod(method):
- """ Add the method to exportedSymbols """
- global exportedSymbols
- exportedSymbols[method.func_name] = method
- return method
+class UsageError (StandardError):
+ pass
-def getExports(**kw):
- """ Return a copy of exportedSymbols, with kw included """
- exports = exportedSymbols.copy()
- exports.update(**kw)
- return exports
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+def main():
+ try:
+ (optargs, args) = getopt(
+ sys.argv[1:], "hf:r:s", [
+ "config=",
+ "help",
+ "resource=",
+ "read-property=",
+ "list-read-delegates",
+ "list-write-delegates",
+ "add-read-delegate=",
+ "add-write-delegate=",
+ "remove-delegate=",
+ ],
+ )
+ except GetoptError, e:
+ usage(e)
+ if args:
+ usage("Too many arguments: %s" % (" ".join(args),))
+ logFileName = "/dev/stdout"
+ observer = log.FileLogObserver(open(logFileName, "a"))
+ log.addObserver(observer.emit)
+
+ # First pass through the args:
+
+ configFileName = None
+
+ for opt, arg in optargs:
+ if opt in ("-h", "--help"):
+ usage()
+
+ elif opt in ("-f", "--config"):
+ configFileName = arg
+
+ loadConfig(configFileName)
+ directory, root = setup()
+ root = ResourceWrapper(root)
+
+ reactor.callLater(0, run, directory, root, optargs)
+ reactor.run()
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ at inlineCallbacks
+def run(directory, root, optargs):
+
+ resource = None
+
+ for opt, arg in optargs:
+
+ if opt in ("-r", "--resource",):
+ resource = root.getChild(arg)
+ if resource is not None:
+ print "Found resource %s at %s" % (resource.resource, arg)
+ else:
+ abort("Could not find resource at %s" % (arg,))
+
+ elif opt in ("--read-property",):
+ if resource is None: abort("No current resource.")
+
+ try:
+ namespace, name = arg.split("#")
+ except Exception, e:
+ abort("Can't parse --propertyToRead: %s" % (arg,))
+
+ result = (yield resource.readProperty((namespace, name)))
+ print result.toxml()
+
+ elif opt in ("--list-write-delegates",):
+ if resource is None: abort("No current resource.")
+
+ subPrincipal = resource.getChild("calendar-proxy-write")
+ if subPrincipal is None:
+ abort("No proxy subprincipal found for %s" % (resource.resource,))
+ namespace, name = davxml.dav_namespace, "group-member-set"
+ result = (yield subPrincipal.readProperty((namespace, name)))
+ print result.toxml()
+
+ elif opt in ("--add-write-delegate", "--add-read-delegate"):
+ if resource is None: abort("No current resource.")
+
+ delegate = root.getChild(arg)
+ if delegate is None:
+ abort("No delegate found for %s" % (arg,))
+
+ permission = "write" if "write" in opt else "read"
+ result = (yield resource.addDelegate(delegate, permission))
+
+ elif opt == "--remove-delegate":
+ if resource is None: abort("No current resource.")
+
+ delegate = root.getChild(arg)
+ if delegate is None:
+ abort("No delegate found for %s" % (arg,))
+
+ result = (yield resource.removeDelegate(delegate, "read"))
+ result = (yield resource.removeDelegate(delegate, "write"))
+
+
+ print "Stopping reactor"
+ reactor.stop()
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+class ResourceWrapper(object):
+
+ def __init__(self, resource):
+ self.resource = resource
+
+ def readProperty(self, prop):
+ # fake a request
+ request = FakeRequest()
+ return self.resource.readProperty(prop, request)
+
+ def writeProperty(self, prop):
+ # fake a request
+ request = FakeRequest()
+ return self.resource.writeProperty(prop, request)
+
+ def getChild(self, path):
+ resource = self.resource
+ segments = path.strip("/").split("/")
+ for segment in segments:
+ resource = resource.getChild(segment)
+ if resource is None:
+ return None
+ return ResourceWrapper(resource)
+
+ @inlineCallbacks
+ def removeDelegate(self, delegate, permission):
+ subPrincipalName = "calendar-proxy-%s" % (permission,)
+ subPrincipal = self.getChild(subPrincipalName)
+ if subPrincipal is None:
+ abort("No proxy subprincipal found for %s" % (self.resource,))
+
+ namespace, name = davxml.dav_namespace, "group-member-set"
+ prop = (yield subPrincipal.readProperty((namespace, name)))
+ newChildren = []
+ for child in prop.children:
+ if str(child) != delegate.url():
+ newChildren.append(child)
+
+ if len(prop.children) == len(newChildren):
+ # Nothing to do -- the delegate wasn't there
+ returnValue(False)
+
+ newProp = davxml.GroupMemberSet(*newChildren)
+ result = (yield subPrincipal.writeProperty(newProp))
+ returnValue(result)
+
+ @inlineCallbacks
+ def addDelegate(self, delegate, permission):
+
+ opposite = "read" if permission == "write" else "write"
+ result = (yield self.removeDelegate(delegate, opposite))
+
+ subPrincipalName = "calendar-proxy-%s" % (permission,)
+ subPrincipal = self.getChild(subPrincipalName)
+ if subPrincipal is None:
+ abort("No proxy subprincipal found for %s" % (self.resource,))
+
+ namespace, name = davxml.dav_namespace, "group-member-set"
+ prop = (yield subPrincipal.readProperty((namespace, name)))
+ for child in prop.children:
+ if str(child) == delegate.url():
+ # delegate is already in the group
+ break
+ else:
+ # delegate is not already in the group
+ newChildren = list(prop.children)
+ newChildren.append(davxml.HRef(delegate.url()))
+ newProp = davxml.GroupMemberSet(*newChildren)
+ result = (yield subPrincipal.writeProperty(newProp))
+ returnValue(result)
+
+ def url(self):
+ return self.resource.url()
+
+class FakeRequest(object):
+ pass
+
+def abort(msg, errno=1):
+ print "ERROR:", msg
+ print "Exiting"
+ reactor.stop()
+ sys.exit(errno)
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+def setup():
+
+ directory = getDirectory()
+ if config.Memcached["ClientEnabled"]:
+ memcachepool.installPool(
+ IPv4Address(
+ 'TCP',
+ config.Memcached["BindAddress"],
+ config.Memcached["Port"]
+ ),
+ config.Memcached["MaxClients"]
+ )
+ if config.Notifications["Enabled"]:
+ installNotificationClient(
+ config.Notifications["InternalNotificationHost"],
+ config.Notifications["InternalNotificationPort"],
+ )
+ principalCollection = directory.getPrincipalCollection()
+ root = RootResource(
+ config.DocumentRoot,
+ principalCollections=(principalCollection,),
+ )
+ root.putChild("principals", principalCollection)
+ calendarCollection = CalendarHomeProvisioningFile(
+ os.path.join(config.DocumentRoot, "calendars"),
+ directory, "/calendars/",
+ )
+ root.putChild("calendars", calendarCollection)
+
+ return (directory, root)
+
def loadConfig(configFileName):
if configFileName is None:
configFileName = defaultConfigFile
@@ -155,149 +381,7 @@
autoSchedule = False,
)
-def setup():
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- directory = getDirectory()
- if config.Memcached["ClientEnabled"]:
- memcachepool.installPool(
- IPv4Address(
- 'TCP',
- config.Memcached["BindAddress"],
- config.Memcached["Port"]
- ),
- config.Memcached["MaxClients"]
- )
- if config.Notifications["Enabled"]:
- installNotificationClient(
- config.Notifications["InternalNotificationHost"],
- config.Notifications["InternalNotificationPort"],
- )
- principalCollection = directory.getPrincipalCollection()
- root = RootResource(
- config.DocumentRoot,
- principalCollections=(principalCollection,),
- )
- root.putChild("principals", principalCollection)
- calendarCollection = CalendarHomeProvisioningFile(
- os.path.join(config.DocumentRoot, "calendars"),
- directory, "/calendars/",
- )
- root.putChild("calendars", calendarCollection)
-
- return (directory, root)
-
-
-
-class UsageError (StandardError):
- pass
-
-def usage(e=None):
- if e:
- print e
- print ""
-
- name = os.path.basename(sys.argv[0])
- print "usage: %s [options] [input_specifiers]" % (name,)
- print ""
- print "Change calendar user addresses"
- print __doc__
- print "options:"
- print " -h --help: print this help and exit"
- print " -f --config: Specify caldavd.plist configuration path"
- print ""
- print "input specifiers:"
- print " -c --changes: add all calendar homes"
- print " --dry-run: Don't actually change the data"
-
- if e:
- sys.exit(64)
- else:
- sys.exit(0)
-
-def loadChanges(fileName):
- addresses = {}
- with open(fileName) as input:
- count = 1
- for line in input:
- line = line.strip()
- if line and not line.startswith("#"):
- try:
- oldAddr, newAddr = line.split()
- addresses[oldAddr] = newAddr
- except Exception, e:
- print "Could not parse line %d: %s" % (count, line)
- sys.exit(2)
- count += 1
- return addresses
-
-
-
-
-class ResourceWrapper(object):
-
- def __init__(self, resource):
- self.resource = resource
-
- at exportmethod
-def byPath(root, path):
- resource = root
- segments = path.strip("/").split("/")
- for segment in segments:
- resource = resource.getChild(segment)
- return ResourceWrapper(resource)
-
-def main():
- try:
- (optargs, args) = getopt(
- sys.argv[1:], "hf:", [
- "config=",
- "help",
- "dry-run",
- ],
- )
- except GetoptError, e:
- usage(e)
-
- configFileName = None
- logFileName = "/dev/stdout"
- modifyData = True
- changesFile = None
-
- directory = None
- calendarHomePaths = set()
- calendarHomes = set()
-
- def checkExists(resource):
- if not resource.exists():
- sys.stderr.write("No such file: %s\n" % (resource.fp.path,))
- sys.exit(1)
-
- for opt, arg in optargs:
- if opt in ("-h", "--help"):
- usage()
-
- elif opt in ("-f", "--config"):
- configFileName = arg
-
- elif opt in ("--dry-run",):
- modifyData = False
-
- if args:
- usage("Too many arguments: %s" % (" ".join(args),))
-
- observer = log.FileLogObserver(open(logFileName, "a"))
- log.addObserver(observer.emit)
-
- loadConfig(configFileName)
-
- directory, root = setup()
- exportedSymbols['root'] = root
- exportedSymbols['directory'] = directory
-
- banner = "\nWelcome to calendar server\n"
- interact(banner, None, getExports(__name__="__console__", __doc__=None))
-
-
if __name__ == "__main__":
-
main()
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20090418/0b7a15b7/attachment-0001.html>
More information about the calendarserver-changes
mailing list