[CalendarServer-changes] [8463] CalDAVClientLibrary/trunk/caldavclientlibrary

source_changes at macosforge.org source_changes at macosforge.org
Thu Dec 15 19:42:39 PST 2011


Revision: 8463
          http://trac.macosforge.org/projects/calendarserver/changeset/8463
Author:   cdaboo at apple.com
Date:     2011-12-15 19:42:39 -0800 (Thu, 15 Dec 2011)
Log Message:
-----------
Fix principal discovery. Add some new commands, including sync report.

Modified Paths:
--------------
    CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/__init__.py
    CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/cat.py
    CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/principal.py
    CalDAVClientLibrary/trunk/caldavclientlibrary/browser/shell.py
    CalDAVClientLibrary/trunk/caldavclientlibrary/client/clientsession.py
    CalDAVClientLibrary/trunk/caldavclientlibrary/client/principal.py
    CalDAVClientLibrary/trunk/caldavclientlibrary/protocol/caldav/multiget.py
    CalDAVClientLibrary/trunk/caldavclientlibrary/protocol/webdav/definitions/davxml.py
    CalDAVClientLibrary/trunk/caldavclientlibrary/protocol/webdav/propfindparser.py
    CalDAVClientLibrary/trunk/caldavclientlibrary/protocol/webdav/report.py

Added Paths:
-----------
    CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/addressbooks.py
    CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/calendars.py
    CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/sync.py
    CalDAVClientLibrary/trunk/caldavclientlibrary/client/addressbook.py
    CalDAVClientLibrary/trunk/caldavclientlibrary/protocol/webdav/synccollection.py

Modified: CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/__init__.py
===================================================================
--- CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/__init__.py	2011-12-16 00:20:51 UTC (rev 8462)
+++ CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/__init__.py	2011-12-16 03:42:39 UTC (rev 8463)
@@ -16,8 +16,10 @@
 
 __all__ = [
     "acl",
+    "addressbooks",
     "cat",
     "cd",
+    "calendars",
     "help",
     "history",
     "import",
@@ -35,6 +37,7 @@
     "quota",
     "rm",
     "server",
+    "sync",
     "user",
     "whoami",
 ]

Added: CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/addressbooks.py
===================================================================
--- CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/addressbooks.py	                        (rev 0)
+++ CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/addressbooks.py	2011-12-16 03:42:39 UTC (rev 8463)
@@ -0,0 +1,67 @@
+##
+# Copyright (c) 2007-2011 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.
+##
+
+from caldavclientlibrary.browser.command import Command, CommandError
+from caldavclientlibrary.browser.command import WrongOptions
+from caldavclientlibrary.protocol.url import URL
+import getopt
+import shlex
+
+class Cmd(Command):
+    
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = ("addressbooks",)
+        
+    def execute(self, name, options):
+        opts, args = getopt.getopt(shlex.split(options), '')
+        if len(opts) != 0:
+            print self.usage(name)
+            raise WrongOptions
+
+        if len(args) > 1:
+            print "Wrong number of arguments: %d" % (len(args),)
+            print self.usage(name)
+            raise WrongOptions
+        ppath = URL(url=args[0]) if args else None
+        principal = self.shell.account.getPrincipal(ppath)
+        if principal is None:
+            print "No principal found for %s" % (ppath if ppath else "current principal")
+            raise CommandError
+
+        homeset = principal.adbkhomeset
+        if not homeset:
+            print "No address book home set found for %s" % (principal.principalPath,)
+            raise CommandError
+
+        newpath = homeset[0].path
+        result = self.shell.setWD(newpath)
+
+        if not result:
+            print "%s: No such directory" % (newpath,)
+            
+        return result
+
+    def complete(self, text):
+        return self.shell.wdcomplete(text)
+
+    def usage(self, name):
+        return """Usage: %s [PRINCIPAL]
+PRINCIPAL is a principal-URL.
+""" % (name,)
+
+    def helpDescription(self):
+        return "Change working directory to address book home for current or specified principal."

Added: CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/calendars.py
===================================================================
--- CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/calendars.py	                        (rev 0)
+++ CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/calendars.py	2011-12-16 03:42:39 UTC (rev 8463)
@@ -0,0 +1,67 @@
+##
+# Copyright (c) 2007-2011 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.
+##
+
+from caldavclientlibrary.browser.command import Command, CommandError
+from caldavclientlibrary.browser.command import WrongOptions
+from caldavclientlibrary.protocol.url import URL
+import getopt
+import shlex
+
+class Cmd(Command):
+    
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = ("calendars",)
+        
+    def execute(self, name, options):
+        opts, args = getopt.getopt(shlex.split(options), '')
+        if len(opts) != 0:
+            print self.usage(name)
+            raise WrongOptions
+
+        if len(args) > 1:
+            print "Wrong number of arguments: %d" % (len(args),)
+            print self.usage(name)
+            raise WrongOptions
+        ppath = URL(url=args[0]) if args else None
+        principal = self.shell.account.getPrincipal(ppath)
+        if principal is None:
+            print "No principal found for %s" % (ppath if ppath else "current principal")
+            raise CommandError
+
+        homeset = principal.homeset
+        if not homeset:
+            print "No calendar home set found for %s" % (principal.principalPath,)
+            raise CommandError
+
+        newpath = homeset[0].path
+        result = self.shell.setWD(newpath)
+
+        if not result:
+            print "%s: No such directory" % (newpath,)
+            
+        return result
+
+    def complete(self, text):
+        return self.shell.wdcomplete(text)
+
+    def usage(self, name):
+        return """Usage: %s [PRINCIPAL]
+PRINCIPAL is a principal-URL.
+""" % (name,)
+
+    def helpDescription(self):
+        return "Change working directory to calendar home for current or specified principal."

Modified: CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/cat.py
===================================================================
--- CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/cat.py	2011-12-16 00:20:51 UTC (rev 8462)
+++ CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/cat.py	2011-12-16 03:42:39 UTC (rev 8463)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2007-2010 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2011 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.
@@ -25,7 +25,7 @@
     
     def __init__(self):
         super(Command, self).__init__()
-        self.cmds = ("cat",)
+        self.cmds = ("cat", "more",)
         
     def execute(self, name, options):
         opts, args = getopt.getopt(shlex.split(options), '')

Modified: CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/principal.py
===================================================================
--- CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/principal.py	2011-12-16 00:20:51 UTC (rev 8462)
+++ CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/principal.py	2011-12-16 03:42:39 UTC (rev 8463)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2007-2010 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2011 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.
@@ -69,6 +69,7 @@
     Outbox URL        : %s
     Inbox URL         : %s
     Calendar Addresses: %s
+    Address Book Homes: %s
 """ % (
           principal.principalPath,
           principal.getSmartDisplayName(),
@@ -80,11 +81,14 @@
           principal.outboxURL,
           principal.inboxURL,
           utils.printList(principal.cuaddrs),
+          utils.printList(principal.adbkhomeset),
       ),
 
         if print_proxies:
             utils.printProxyPrincipals(self.shell.account, principal, True, True, resolve, False, refresh)
 
+        print ""
+
         return True
 
     def usage(self, name):
@@ -98,4 +102,4 @@
 """ % (name,)
 
     def helpDescription(self):
-        return "Displays the current server login id."
+        return "Get details on principals."

Added: CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/sync.py
===================================================================
--- CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/sync.py	                        (rev 0)
+++ CalDAVClientLibrary/trunk/caldavclientlibrary/browser/commands/sync.py	2011-12-16 03:42:39 UTC (rev 8463)
@@ -0,0 +1,86 @@
+##
+# Copyright (c) 2007-2011 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.
+##
+
+from caldavclientlibrary.browser.command import Command
+from caldavclientlibrary.browser.command import WrongOptions
+from caldavclientlibrary.protocol.url import URL
+import os
+import getopt
+import shlex
+
+synctokens = {}
+
+class Cmd(Command):
+    
+    def __init__(self):
+        super(Command, self).__init__()
+        self.cmds = ("sync",)
+        
+    def execute(self, name, options):
+        
+        force = False
+
+        opts, args = getopt.getopt(shlex.split(options), 'f')
+
+        for name, _ignore_value in opts:
+            
+            if name == "-f":
+                force = True
+            else:
+                print "Unknown option: %s" % (name,)
+                print self.usage(name)
+                raise WrongOptions
+        
+        if len(args) > 1:
+            print "Wrong number of arguments: %d" % (len(args),)
+            print self.usage(name)
+            raise WrongOptions
+        elif args:
+            path = args[0]
+            if not path.startswith("/"):
+                path = os.path.join(self.shell.wd, path)
+        else:
+            path = self.shell.wd
+        if not path.endswith("/"):
+            path += "/"
+        resource = URL(url=path)
+        synctoken = synctokens.get(path, "") if not force else ""
+        newsyctoken, changed, removed, other = self.shell.account.session.syncCollection(resource, synctoken)
+        synctokens[path] = newsyctoken
+        
+        for item in changed:
+            print "Changed: %s" % (item,)
+        for item in removed:
+            print "Removed: %s" % (item,)
+        for item in other:
+            print "Error: %s" % (item,)
+        print ""
+
+        return True
+
+    def complete(self, text):
+        return self.shell.wdcomplete(text)
+
+    def usage(self, name):
+        return """Usage: %s [OPTIONS] [PATH]
+PATH is a relative or absolute path.
+
+Options:
+-f   force full sync
+""" % (name,)
+
+    def helpDescription(self):
+        return "Sync the contents of a directory."

Modified: CalDAVClientLibrary/trunk/caldavclientlibrary/browser/shell.py
===================================================================
--- CalDAVClientLibrary/trunk/caldavclientlibrary/browser/shell.py	2011-12-16 00:20:51 UTC (rev 8462)
+++ CalDAVClientLibrary/trunk/caldavclientlibrary/browser/shell.py	2011-12-16 03:42:39 UTC (rev 8463)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2007-2010 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2011 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.
@@ -40,8 +40,7 @@
         # Create the account
         ssl = server.startswith("https://")
         server = server[8:] if ssl else server[7:]
-        paths = path
-        self.account = CalDAVAccount(server, ssl=ssl, user=self.user, pswd=self.pswd, root=paths, principal=paths, logging=logging)
+        self.account = CalDAVAccount(server, ssl=ssl, user=self.user, pswd=self.pswd, root=path, principal=None, logging=logging)
         
         atexit.register(self.saveHistory)
 

Added: CalDAVClientLibrary/trunk/caldavclientlibrary/client/addressbook.py
===================================================================
--- CalDAVClientLibrary/trunk/caldavclientlibrary/client/addressbook.py	                        (rev 0)
+++ CalDAVClientLibrary/trunk/caldavclientlibrary/client/addressbook.py	2011-12-16 03:42:39 UTC (rev 8463)
@@ -0,0 +1,65 @@
+##
+# Copyright (c) 2006-2011 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.
+##
+
+from caldavclientlibrary.protocol.carddav.definitions import carddavxml
+from caldavclientlibrary.protocol.url import URL
+from caldavclientlibrary.protocol.webdav.definitions import davxml
+
+class AddressBook(object):
+    
+    def __init__(self, path=None, session=None):
+        self.path = path
+        if not path.endswith("/"):
+            self.path += "/"
+        self.session = session
+        self.displayname = None
+        self.description = None
+
+    def __str__(self):
+        return "AddressBook: %s" % (self.path,)
+
+    def __repr__(self):
+        return "AddressBook: %s" % (self.path,)
+
+    def exists(self):
+        return self.session.testResource(URL(url=self.path))
+
+    def readAddressBook(self):
+        pass
+    def writeAddressBook(self, adbk):
+        pass
+
+    def readComponent(self, name=None, uid=None):
+        pass
+    def writeComponent(self, component, name=None):
+        pass
+
+    def getDisplayName(self):
+        if self.displayname is None and self.session:
+            self._getProperties()
+        return self.displayname
+
+    def getDescription(self):
+        if self.description is None and self.session:
+            self._getProperties()
+        return self.description
+    
+    def _getProperties(self):
+        assert(self.session is not None)
+        
+        results, _ignore_bad = self.session.getProperties(URL(url=self.path), (davxml.displayname, carddavxml.addressbook_description,))
+        self.displayname = results.get(davxml.displayname, "")
+        self.description = results.get(carddavxml.addressbook_description, "")

Modified: CalDAVClientLibrary/trunk/caldavclientlibrary/client/clientsession.py
===================================================================
--- CalDAVClientLibrary/trunk/caldavclientlibrary/client/clientsession.py	2011-12-16 00:20:51 UTC (rev 8462)
+++ CalDAVClientLibrary/trunk/caldavclientlibrary/client/clientsession.py	2011-12-16 03:42:39 UTC (rev 8463)
@@ -20,6 +20,8 @@
 from caldavclientlibrary.protocol.carddav.makeaddressbook import MakeAddressBook
 from caldavclientlibrary.protocol.http.authentication.basic import Basic
 from caldavclientlibrary.protocol.http.authentication.digest import Digest
+from caldavclientlibrary.protocol.webdav.synccollection import SyncCollection
+from caldavclientlibrary.protocol.http.util import parseStatusLine
 try:
     from caldavclientlibrary.protocol.http.authentication.gssapi import Kerberos
 except ImportError:
@@ -65,7 +67,7 @@
 
         # Paths
         self.rootPath = URL(url=root)
-        self.principalPath = URL(url=principal)
+        self.principalPath = URL(url=principal) if principal else None
         
         self._initCalDAVState()
 
@@ -77,6 +79,13 @@
         
     def _discoverPrincipal(self):
         
+        current = self.getCurrentPrincipalResource(self.rootPath)
+        if current:
+            self.principalPath = current
+            if self.log:
+                self.log.write("Found current principal path: %s\n" % (self.principalPath.absoluteURL(),))
+            return
+            
         hrefs = self.getHrefListProperty(self.rootPath, davxml.principal_collection_set)
         if not hrefs:
             return
@@ -88,7 +97,7 @@
             if results:
                 self.principalPath = results[0]
                 if self.log:
-                    self.log.write("Found principal path: %s" % (self.principalPath.absoluteURL(),))
+                    self.log.write("Found principal path: %s\n" % (self.principalPath.absoluteURL(),))
                 return
     
     def setUserPswd(self, user, pswd):
@@ -318,7 +327,7 @@
         results = ()
 
         # Create WebDAV principal-match
-        request = PrincipalMatch(self, rurl.realtiveURL(), (davxml.principal_URL,))
+        request = PrincipalMatch(self, rurl.relativeURL(), (davxml.principal_URL,))
         result = ResponseDataString()
         request.setOutput(result)
     
@@ -336,7 +345,7 @@
 
                 # Get child element name (decode URL)
                 name = URL(url=item.getResource(), decode=True)
-                results += (name.path,)
+                results += (name,)
 
         else:
             self.handleHTTPError(request)
@@ -362,6 +371,14 @@
         
         return None
 
+    # Do current-user-principal property on the passed in url
+    def getCurrentPrincipalResource(self, rurl):
+        
+        assert(isinstance(rurl, URL))
+
+        hrefs = self.getHrefListProperty(rurl, davxml.current_user_principal)
+        return hrefs[0] if hrefs else None
+
     def setProperties(self, rurl, props):
 
         assert(isinstance(rurl, URL))
@@ -477,6 +494,53 @@
         if request.getStatusCode() not in (statuscodes.OK, statuscodes.Created, statuscodes.NoContent):
             self.handleHTTPError(request)
 
+    def syncCollection(self, rurl, synctoken, props=()):
+
+        assert(isinstance(rurl, URL))
+
+        newsynctoken = ""
+        changed = set()
+        removed = set()
+        other = set()
+
+        # Create WebDAV sync REPORT
+        request = SyncCollection(self, rurl.relativeURL(), headers.Depth1, synctoken, props)
+        result = ResponseDataString()
+        request.setOutput(result)
+    
+        # Process it
+        self.runSession(request)
+    
+        # If its a 207 we want to parse the XML
+        if request.getStatusCode() == statuscodes.MultiStatus:
+
+            parser = PropFindParser()
+            parser.parseData(result.getData())
+    
+            # Look at each propfind result
+            for item in parser.getResults().itervalues():
+
+                # Get child element name (decode URL)
+                name = URL(url=item.getResource(), decode=True)
+                status = parseStatusLine(item.status)
+                if status == 404:
+                    removed.add(name)
+                elif status / 100 != 2:
+                    other.add(name)
+                else:
+                    changed.add(name)
+
+            # Get the new token
+            for node in parser.getOthers():
+                if node.tag == davxml.sync_token:
+                    newsynctoken = node.text
+                    break
+            
+        else:
+            self.handleHTTPError(request)
+    
+        return (newsynctoken, changed, removed, other,)
+
     def deleteResource(self, rurl):
         
         assert(isinstance(rurl, URL))

Modified: CalDAVClientLibrary/trunk/caldavclientlibrary/client/principal.py
===================================================================
--- CalDAVClientLibrary/trunk/caldavclientlibrary/client/principal.py	2011-12-16 00:20:51 UTC (rev 8462)
+++ CalDAVClientLibrary/trunk/caldavclientlibrary/client/principal.py	2011-12-16 03:42:39 UTC (rev 8463)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2007-2009 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2011 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.
@@ -14,11 +14,13 @@
 # limitations under the License.
 ##
 
+from caldavclientlibrary.client.addressbook import AddressBook
+from caldavclientlibrary.client.calendar import Calendar
 from caldavclientlibrary.protocol.caldav.definitions import caldavxml
-from caldavclientlibrary.client.calendar import Calendar
+from caldavclientlibrary.protocol.caldav.definitions import headers
+from caldavclientlibrary.protocol.carddav.definitions import carddavxml
 from caldavclientlibrary.protocol.url import URL
 from caldavclientlibrary.protocol.webdav.definitions import davxml
-from caldavclientlibrary.protocol.caldav.definitions import headers
 
 class PrincipalCache(object):
     
@@ -63,6 +65,7 @@
     Outbox URL        : %s
     Inbox URL         : %s
     Calendar Addresses: %s
+    Address Book Homes: %s
 """ % (
           self.principalPath,
           self.displayname,
@@ -73,7 +76,8 @@
           self.homeset,
           self.outboxURL,
           self.inboxURL,
-          self.cuaddrs
+          self.cuaddrs,
+          self.adbkhomeset,
       )
 
     def _initFields(self):
@@ -88,6 +92,7 @@
         self.outboxURL = ""
         self.inboxURL = ""
         self.cuaddrs = ()
+        self.adbkhomeset = ()
         
         self.proxyFor = None
         self.proxyreadURL = ""
@@ -111,6 +116,7 @@
                 caldavxml.schedule_outbox_URL,
                 caldavxml.schedule_inbox_URL,
                 caldavxml.calendar_user_address_set,
+                carddavxml.addressbook_home_set,
             ),
         )
         if results:
@@ -133,6 +139,7 @@
                 self.outboxURL = results.get(caldavxml.schedule_outbox_URL, None)
                 self.inboxURL = results.get(caldavxml.schedule_inbox_URL, None)
                 self.cuaddrs = make_tuple(results.get(caldavxml.calendar_user_address_set, None))
+                self.adbkhomeset = make_tuple(results.get(carddavxml.addressbook_home_set, None))
 
         # Get proxy resource details if proxy support is available
         if self.session.hasDAVVersion(headers.calendar_proxy) and not self.proxyFor:
@@ -227,3 +234,17 @@
         
         self.session.setProperties(self.proxywriteURL, ((davxml.group_member_set, principals),))            
     
+    def listAddressBooks(self, root=None):
+        adbks = []
+        home = self.adbkhomeset[0]
+        if not home.path.endswith("/"):
+            home.path += "/"
+
+        results = self.session.getPropertiesOnHierarchy(home, (davxml.resourcetype,))
+        for path, items in results.iteritems():
+            if davxml.resourcetype in items:
+                rtype = items[davxml.resourcetype]
+                if rtype.find(str(davxml.collection)) is not None and rtype.find(str(carddavxml.addressbook)) is not None:
+                    adbks.append(AddressBook(path=path, session=self.session))
+        return adbks
+

Modified: CalDAVClientLibrary/trunk/caldavclientlibrary/protocol/caldav/multiget.py
===================================================================
--- CalDAVClientLibrary/trunk/caldavclientlibrary/protocol/caldav/multiget.py	2011-12-16 00:20:51 UTC (rev 8462)
+++ CalDAVClientLibrary/trunk/caldavclientlibrary/protocol/caldav/multiget.py	2011-12-16 03:42:39 UTC (rev 8463)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2011 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.
@@ -14,14 +14,14 @@
 # limitations under the License.
 ##
 
-from caldavclientlibrary.protocol.webdav.report import Report
 from StringIO import StringIO
-from caldavclientlibrary.protocol.http.data.string import RequestDataString
 from caldavclientlibrary.protocol.caldav.definitions import caldavxml
+from caldavclientlibrary.protocol.http.data.string import RequestDataString
+from caldavclientlibrary.protocol.utils.xmlhelpers import BetterElementTree
+from caldavclientlibrary.protocol.webdav.definitions import davxml
+from caldavclientlibrary.protocol.webdav.report import Report
 from xml.etree.ElementTree import Element
 from xml.etree.ElementTree import SubElement
-from caldavclientlibrary.protocol.webdav.definitions import davxml
-from caldavclientlibrary.protocol.utils.xmlhelpers import BetterElementTree
 
 class Multiget(Report):
 
@@ -29,14 +29,12 @@
         super(Multiget, self).__init__(session, url)
         self.props = props
         self.hrefs = hrefs
-        pass
 
     def initRequestData(self):
-        if self.displayname or self.description or self.timezone:
-            # Write XML info to a string
-            os = StringIO()
-            self.generateXML(os)
-            self.request_data = RequestDataString(os.getvalue(), "text/xml charset=utf-8")
+        # Write XML info to a string
+        os = StringIO()
+        self.generateXML(os)
+        self.request_data = RequestDataString(os.getvalue(), "text/xml charset=utf-8")
     
     def generateXML(self, os):
         # Structure of document is:

Modified: CalDAVClientLibrary/trunk/caldavclientlibrary/protocol/webdav/definitions/davxml.py
===================================================================
--- CalDAVClientLibrary/trunk/caldavclientlibrary/protocol/webdav/definitions/davxml.py	2011-12-16 00:20:51 UTC (rev 8462)
+++ CalDAVClientLibrary/trunk/caldavclientlibrary/protocol/webdav/definitions/davxml.py	2011-12-16 03:42:39 UTC (rev 8463)
@@ -94,3 +94,6 @@
 
 mkcol          = QName(DAVNamespace, "mkcol")
 mkcol_response = QName(DAVNamespace, "mkcol-response")
+
+sync_collection = QName(DAVNamespace, "sync-collection")
+sync_token      = QName(DAVNamespace, "sync-token")

Modified: CalDAVClientLibrary/trunk/caldavclientlibrary/protocol/webdav/propfindparser.py
===================================================================
--- CalDAVClientLibrary/trunk/caldavclientlibrary/protocol/webdav/propfindparser.py	2011-12-16 00:20:51 UTC (rev 8462)
+++ CalDAVClientLibrary/trunk/caldavclientlibrary/protocol/webdav/propfindparser.py	2011-12-16 03:42:39 UTC (rev 8463)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2007-2010 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2011 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.
@@ -67,14 +67,19 @@
 
     def __init__(self):
         self.results = {}
+        self.others = set()
     
     def getResults(self):
         return self.results
 
+    def getOthers(self):
+        return self.others
+
     # Parse the response element down to the properties
     def parseResponse(self, response):
         # Verify that the node is the correct element <DAV:response>
         if response.tag != davxml.response:
+            self.others.add(response)
             return
         
         # Node is the right type, so iterate over all child response nodes and process each one

Modified: CalDAVClientLibrary/trunk/caldavclientlibrary/protocol/webdav/report.py
===================================================================
--- CalDAVClientLibrary/trunk/caldavclientlibrary/protocol/webdav/report.py	2011-12-16 00:20:51 UTC (rev 8462)
+++ CalDAVClientLibrary/trunk/caldavclientlibrary/protocol/webdav/report.py	2011-12-16 03:42:39 UTC (rev 8463)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2011 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.
@@ -22,5 +22,5 @@
     def __init__(self, session, url):
         super(Report, self).__init__(session, methods.REPORT, url)
 
-    def _setOutput(self, response_data):
+    def setOutput(self, response_data):
         self.response_data = response_data;

Added: CalDAVClientLibrary/trunk/caldavclientlibrary/protocol/webdav/synccollection.py
===================================================================
--- CalDAVClientLibrary/trunk/caldavclientlibrary/protocol/webdav/synccollection.py	                        (rev 0)
+++ CalDAVClientLibrary/trunk/caldavclientlibrary/protocol/webdav/synccollection.py	2011-12-16 03:42:39 UTC (rev 8463)
@@ -0,0 +1,77 @@
+##
+# Copyright (c) 2007-2011 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.
+##
+
+from StringIO import StringIO
+from caldavclientlibrary.protocol.http.data.string import RequestDataString
+from caldavclientlibrary.protocol.utils.xmlhelpers import BetterElementTree
+from caldavclientlibrary.protocol.webdav.definitions import davxml, headers
+from caldavclientlibrary.protocol.webdav.report import Report
+from xml.etree.ElementTree import Element
+from xml.etree.ElementTree import SubElement
+
+class SyncCollection(Report):
+
+    def __init__(self, session, url, depth, synctoken, props=()):
+        assert(depth in (headers.Depth0, headers.Depth1, headers.DepthInfinity))
+
+        super(SyncCollection, self).__init__(session, url)
+        self.depth = depth
+        self.synctoken = synctoken
+        self.props = props
+        
+        self.initRequestData()
+
+    def initRequestData(self):
+        # Write XML info to a string
+        os = StringIO()
+        self.generateXML(os)
+        self.request_data = RequestDataString(os.getvalue(), "text/xml charset=utf-8")
+
+    def addHeaders(self, hdrs):
+        # Do default
+        super(SyncCollection, self).addHeaders(hdrs)
+        
+        # Add depth header
+        hdrs.append((headers.Depth, self.depth))
+    
+    def generateXML(self, os):
+        # Structure of document is:
+        #
+        # <DAV:sync-collection>
+        #   <DAV:sync-token>xxx</DAV:sync-token>
+        #   <DAV:prop>
+        #     <<names of each property as elements>>
+        #   </DAV:prop>
+        # </DAV:sync-collection>
+        
+        # <DAV:sync-collection> element
+        synccollection = Element(davxml.sync_collection)
+        
+        # Add sync-token element
+        SubElement(synccollection, davxml.sync_token).text = self.synctoken
+
+        if self.props:
+            # <DAV:prop> element
+            prop = SubElement(synccollection, davxml.prop)
+            
+            # Now add each property
+            for propname in self.props:
+                # Add property element taking namespace into account
+                SubElement(prop, propname)
+        
+        # Now we have the complete document, so write it out (no indentation)
+        xmldoc = BetterElementTree(synccollection)
+        xmldoc.writeUTF8(os)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20111215/55083a8c/attachment-0001.html>


More information about the calendarserver-changes mailing list