[CalendarServer-changes] [9793] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Sat Sep 8 14:45:32 PDT 2012
Revision: 9793
http://trac.macosforge.org/projects/calendarserver/changeset/9793
Author: cdaboo at apple.com
Date: 2012-09-08 14:45:32 -0700 (Sat, 08 Sep 2012)
Log Message:
-----------
Remove minidom use and replace with ElementTree.
Modified Paths:
--------------
CalendarServer/trunk/twext/web2/dav/test/test_xattrprops.py
CalendarServer/trunk/twistedcaldav/directory/xmlaccountsparser.py
CalendarServer/trunk/twistedcaldav/scheduling/ischeduleservers.py
CalendarServer/trunk/txdav/xml/__init__.py
CalendarServer/trunk/txdav/xml/parser.py
Added Paths:
-----------
CalendarServer/trunk/twistedcaldav/scheduling/test/test_ischeduleservers.py
CalendarServer/trunk/txdav/xml/parser_etree.py
CalendarServer/trunk/txdav/xml/rfc6578.py
Removed Paths:
-------------
CalendarServer/trunk/txdav/xml/draft_sync.py
Modified: CalendarServer/trunk/twext/web2/dav/test/test_xattrprops.py
===================================================================
--- CalendarServer/trunk/twext/web2/dav/test/test_xattrprops.py 2012-09-08 21:41:55 UTC (rev 9792)
+++ CalendarServer/trunk/twext/web2/dav/test/test_xattrprops.py 2012-09-08 21:45:32 UTC (rev 9793)
@@ -201,7 +201,7 @@
self._setValue(document, originalValue)
self._checkValue(document)
self.assertEquals(
- decompress(self._getValue(document)), originalValue)
+ decompress(self._getValue(document)), document.root_element.toxml(pretty=False))
def test_getUpgradeCompressedPickle(self):
@@ -214,7 +214,7 @@
self._setValue(document, compress(dumps(document)))
self._checkValue(document)
self.assertEquals(
- decompress(self._getValue(document)), document.toxml())
+ decompress(self._getValue(document)), document.root_element.toxml(pretty=False))
def test_getInvalid(self):
@@ -243,7 +243,7 @@
document = self._makeValue()
self.propertyStore.set(document.root_element)
self.assertEquals(
- decompress(self._getValue(document)), document.toxml())
+ decompress(self._getValue(document)), document.root_element.toxml(pretty=False))
def test_delete(self):
@@ -396,7 +396,7 @@
document = self._makeValue(uid)
self.propertyStore.set(document.root_element, uid=uid)
self.assertEquals(
- decompress(self._getValue(document, uid)), document.toxml())
+ decompress(self._getValue(document, uid)), document.root_element.toxml(pretty=False))
def test_delete_uids(self):
"""
@@ -415,7 +415,7 @@
continue
document = self._makeValue(uid)
self.assertEquals(
- decompress(self._getValue(document, uid)), document.toxml())
+ decompress(self._getValue(document, uid)), document.root_element.toxml(pretty=False))
def test_contains_uids(self):
"""
Modified: CalendarServer/trunk/twistedcaldav/directory/xmlaccountsparser.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/xmlaccountsparser.py 2012-09-08 21:41:55 UTC (rev 9792)
+++ CalendarServer/trunk/twistedcaldav/directory/xmlaccountsparser.py 2012-09-08 21:45:32 UTC (rev 9793)
@@ -23,14 +23,13 @@
"XMLAccountsParser",
]
-import xml.dom.minidom
-
from twext.python.filepath import CachingFilePath as FilePath
from twext.python.log import Logger
from twistedcaldav.directory.directory import DirectoryService
from twistedcaldav.directory.util import normalizeUUID
+from twistedcaldav.xmlutil import readXML
import re
import hashlib
@@ -88,15 +87,10 @@
self.items[recordType] = {}
# Read in XML
- fd = open(self.xmlFile.path, "r")
- doc = xml.dom.minidom.parse(fd)
- fd.close()
-
- # Verify that top-level element is correct
- accounts_node = doc._get_documentElement()
- if accounts_node._get_localName() != ELEMENT_ACCOUNTS:
- log.error("Ignoring file %r because it is not a repository builder file" % (self.xmlFile,))
- return
+ try:
+ _ignore_tree, accounts_node = readXML(self.xmlFile.path, ELEMENT_ACCOUNTS)
+ except ValueError, e:
+ log.error("XML parse error for '%s' because: %s" % (self.xmlFile, e,), raiseException=RuntimeError)
self._parseXML(accounts_node)
def _parseXML(self, node):
@@ -104,8 +98,7 @@
Parse the XML root node from the accounts configuration document.
@param node: the L{Node} to parse.
"""
- if node.hasAttribute(ATTRIBUTE_REALM):
- self.realm = node.getAttribute(ATTRIBUTE_REALM).encode("utf-8")
+ self.realm = node.get(ATTRIBUTE_REALM, "").encode("utf-8")
def updateMembership(group):
# Update group membership
@@ -114,20 +107,13 @@
if item is not None:
item.groups.add(group.shortNames[0])
- for child in node._get_childNodes():
- child_name = child._get_localName()
- if child_name is None:
- continue
-
+ for child in node.getchildren():
try:
- recordType = RECORD_TYPES[child_name]
+ recordType = RECORD_TYPES[child.tag]
except KeyError:
- raise RuntimeError("Unknown account type: %s" % (child_name,))
+ raise RuntimeError("Unknown account type: %s" % (child.tag,))
- if child.hasAttribute(ATTRIBUTE_REPEAT):
- repeat = int(child.getAttribute(ATTRIBUTE_REPEAT))
- else:
- repeat = 0
+ repeat = int(child.get(ATTRIBUTE_REPEAT, 0))
principal = XMLAccountRecord(recordType)
principal.parseXML(child)
@@ -249,58 +235,39 @@
return result
def parseXML(self, node):
- for child in node._get_childNodes():
- child_name = child._get_localName()
- if child_name is None:
- continue
- elif child_name == ELEMENT_SHORTNAME:
- if child.firstChild is not None:
- self.shortNames.append(child.firstChild.data.encode("utf-8"))
- elif child_name == ELEMENT_GUID:
- if child.firstChild is not None:
- self.guid = normalizeUUID(
- child.firstChild.data.encode("utf-8")
- )
- if len(self.guid) < 4:
- self.guid += "?" * (4 - len(self.guid))
- elif child_name == ELEMENT_PASSWORD:
- if child.firstChild is not None:
- self.password = child.firstChild.data.encode("utf-8")
- elif child_name == ELEMENT_NAME:
- if child.firstChild is not None:
- self.fullName = child.firstChild.data.encode("utf-8")
- elif child_name == ELEMENT_FIRST_NAME:
- if child.firstChild is not None:
- self.firstName = child.firstChild.data.encode("utf-8")
- elif child_name == ELEMENT_LAST_NAME:
- if child.firstChild is not None:
- self.lastName = child.firstChild.data.encode("utf-8")
- elif child_name == ELEMENT_EMAIL_ADDRESS:
- if child.firstChild is not None:
- self.emailAddresses.add(child.firstChild.data.encode("utf-8").lower())
- elif child_name == ELEMENT_MEMBERS:
+ for child in node.getchildren():
+ if child.tag == ELEMENT_SHORTNAME:
+ self.shortNames.append(child.text.encode("utf-8"))
+ elif child.tag == ELEMENT_GUID:
+ self.guid = normalizeUUID(child.text.encode("utf-8"))
+ if len(self.guid) < 4:
+ self.guid += "?" * (4 - len(self.guid))
+ elif child.tag == ELEMENT_PASSWORD:
+ self.password = child.text.encode("utf-8")
+ elif child.tag == ELEMENT_NAME:
+ self.fullName = child.text.encode("utf-8")
+ elif child.tag == ELEMENT_FIRST_NAME:
+ self.firstName = child.text.encode("utf-8")
+ elif child.tag == ELEMENT_LAST_NAME:
+ self.lastName = child.text.encode("utf-8")
+ elif child.tag == ELEMENT_EMAIL_ADDRESS:
+ self.emailAddresses.add(child.text.encode("utf-8").lower())
+ elif child.tag == ELEMENT_MEMBERS:
self._parseMembers(child, self.members)
- elif child_name == ELEMENT_EXTRAS:
+ elif child.tag == ELEMENT_EXTRAS:
self._parseExtras(child, self.extras)
else:
- raise RuntimeError("Unknown account attribute: %s" % (child_name,))
+ raise RuntimeError("Unknown account attribute: %s" % (child.tag,))
if not self.shortNames:
self.shortNames.append(self.guid)
def _parseMembers(self, node, addto):
- for child in node._get_childNodes():
- if child._get_localName() == ELEMENT_MEMBER:
- if child.hasAttribute(ATTRIBUTE_RECORDTYPE):
- recordType = child.getAttribute(ATTRIBUTE_RECORDTYPE).encode("utf-8")
- else:
- recordType = DirectoryService.recordType_users
- if child.firstChild is not None:
- addto.add((recordType, child.firstChild.data.encode("utf-8")))
+ for child in node.getchildren():
+ if child.tag == ELEMENT_MEMBER:
+ recordType = child.get(ATTRIBUTE_RECORDTYPE, DirectoryService.recordType_users)
+ addto.add((recordType, child.text.encode("utf-8")))
def _parseExtras(self, node, addto):
- for child in node._get_childNodes():
- key = child._get_localName()
- if key:
- value = child.firstChild.data.encode("utf-8")
- addto[key.encode("utf-8")] = value
+ for child in node.getchildren():
+ addto[child.tag] = child.text.encode("utf-8")
Modified: CalendarServer/trunk/twistedcaldav/scheduling/ischeduleservers.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/ischeduleservers.py 2012-09-08 21:41:55 UTC (rev 9792)
+++ CalendarServer/trunk/twistedcaldav/scheduling/ischeduleservers.py 2012-09-08 21:45:32 UTC (rev 9793)
@@ -20,9 +20,8 @@
from twistedcaldav.config import config, fullServerPath
from twistedcaldav.scheduling.delivery import DeliveryService
+from twistedcaldav.xmlutil import readXML
-import xml.dom.minidom
-
"""
XML based iSchedule configuration file handling.
"""
@@ -100,15 +99,11 @@
self.servers = []
# Read in XML
- fd = open(xmlFile.path, "r")
- doc = xml.dom.minidom.parse(fd)
- fd.close()
+ try:
+ _ignore_tree, servers_node = readXML(xmlFile.path, ELEMENT_SERVERS)
+ except ValueError:
+ log.error("Ignoring file %r because it is not a server-to-server config file" % (xmlFile,))
- # Verify that top-level element is correct
- servers_node = doc._get_documentElement()
- if servers_node._get_localName() != ELEMENT_SERVERS:
- log.error("Ignoring file %r because it is not a server-to-server config file" % (self.xmlFile,))
- return
self._parseXML(servers_node)
def _parseXML(self, node):
@@ -116,12 +111,8 @@
Parse the XML root node from the server-to-server configuration document.
@param node: the L{Node} to parse.
"""
-
- for child in node._get_childNodes():
- child_name = child._get_localName()
- if child_name is None:
- continue
- elif child_name == ELEMENT_SERVER:
+ for child in node.getchildren():
+ if child.tag == ELEMENT_SERVER:
self.servers.append(IScheduleServerRecord())
self.servers[-1].parseXML(child)
@@ -147,49 +138,39 @@
self._parseDetails()
def parseXML(self, node):
- for child in node._get_childNodes():
- child_name = child._get_localName()
- if child_name is None:
- continue
- elif child_name == ELEMENT_URI:
- if child.firstChild is not None:
- self.uri = child.firstChild.data.encode("utf-8")
- elif child_name == ELEMENT_AUTHENTICATION:
+ for child in node.getchildren():
+ if child.tag == ELEMENT_URI:
+ self.uri = child.text
+ elif child.tag == ELEMENT_AUTHENTICATION:
self._parseAuthentication(child)
- elif child_name == ELEMENT_ALLOW_REQUESTS_FROM:
+ elif child.tag == ELEMENT_ALLOW_REQUESTS_FROM:
self.allow_from = True
- elif child_name == ELEMENT_ALLOW_REQUESTS_TO:
+ elif child.tag == ELEMENT_ALLOW_REQUESTS_TO:
self.allow_to = True
- elif child_name == ELEMENT_DOMAINS:
+ elif child.tag == ELEMENT_DOMAINS:
self._parseList(child, ELEMENT_DOMAIN, self.domains)
- elif child_name == ELEMENT_CLIENT_HOSTS:
+ elif child.tag == ELEMENT_CLIENT_HOSTS:
self._parseList(child, ELEMENT_HOST, self.client_hosts)
else:
- raise RuntimeError("[%s] Unknown attribute: %s" % (self.__class__, child_name,))
+ raise RuntimeError("[%s] Unknown attribute: %s" % (self.__class__, child.tag,))
self._parseDetails()
def _parseList(self, node, element_name, appendto):
- for child in node._get_childNodes():
- if child._get_localName() == element_name:
- if child.firstChild is not None:
- appendto.append(child.firstChild.data.encode("utf-8"))
+ for child in node.getchildren():
+ if child.tag == element_name:
+ appendto.append(child.text)
def _parseAuthentication(self, node):
- if node.hasAttribute(ATTRIBUTE_TYPE):
- atype = node.getAttribute(ATTRIBUTE_TYPE).encode("utf-8")
- if atype != ATTRIBUTE_BASICAUTH:
- return
- else:
+ atype = node.getAttribute(ATTRIBUTE_TYPE, "")
+ if atype != ATTRIBUTE_BASICAUTH:
return
- for child in node._get_childNodes():
- if child._get_localName() == ELEMENT_USER:
- if child.firstChild is not None:
- user = child.firstChild.data.encode("utf-8")
- elif child._get_localName() == ELEMENT_PASSWORD:
- if child.firstChild is not None:
- password = child.firstChild.data.encode("utf-8")
+ for child in node.getchildren():
+ if child.tag == ELEMENT_USER:
+ user = child.text
+ elif child.tag == ELEMENT_PASSWORD:
+ password = child.text
self.authentication = ("basic", user, password,)
Added: CalendarServer/trunk/twistedcaldav/scheduling/test/test_ischeduleservers.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/test/test_ischeduleservers.py (rev 0)
+++ CalendarServer/trunk/twistedcaldav/scheduling/test/test_ischeduleservers.py 2012-09-08 21:45:32 UTC (rev 9793)
@@ -0,0 +1,49 @@
+##
+# Copyright (c) 2012 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 twisted.python.filepath import FilePath
+from twistedcaldav.scheduling.ischeduleservers import IScheduleServersParser
+import twistedcaldav.test.util
+
+class Test_IScheduleServersParser(twistedcaldav.test.util.TestCase):
+ """
+ Test L{IScheduleServersParser} implementation.
+ """
+
+ def test_readXML(self):
+
+ fp = FilePath(self.mktemp())
+ fp.open("w").write(
+"""<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE servers SYSTEM "servertoserver.dtd">
+<servers>
+ <server>
+ <uri>https://localhost:8543/inbox</uri>
+ <allow-requests-from/>
+ <allow-requests-to/>
+ <domains>
+ <domain>example.org</domain>
+ </domains>
+ <hosts>
+ <host>127.0.0.1</host>
+ </hosts>
+ </server>
+</servers>
+"""
+)
+
+ parser = IScheduleServersParser(fp)
+ self.assertEqual(len(parser.servers), 1)
Modified: CalendarServer/trunk/txdav/xml/__init__.py
===================================================================
--- CalendarServer/trunk/txdav/xml/__init__.py 2012-09-08 21:41:55 UTC (rev 9792)
+++ CalendarServer/trunk/txdav/xml/__init__.py 2012-09-08 21:45:32 UTC (rev 9793)
@@ -33,6 +33,7 @@
See RFC 3744: http://www.ietf.org/rfc/rfc3744.txt (WebDAV ACLs)
See RFC 4331: http://www.ietf.org/rfc/rfc4331.txt (WebDAV Quota)
See RFC 5842: http://www.ietf.org/rfc/rfc5842.txt (WebDAV Bind)
+See RFC 6578: http://www.ietf.org/rfc/rfc6578.txt (WebDAV Sync)
"""
__all__ = [
@@ -45,10 +46,10 @@
import txdav.xml.rfc3253
import txdav.xml.rfc3744
import txdav.xml.rfc4331
+import txdav.xml.rfc5397
import txdav.xml.rfc5842
-import txdav.xml.rfc5397
import txdav.xml.rfc5995
-import txdav.xml.draft_sync
+import txdav.xml.rfc6578
import txdav.xml.extensions
txdav # Shhh pyflakes
Deleted: CalendarServer/trunk/txdav/xml/draft_sync.py
===================================================================
--- CalendarServer/trunk/txdav/xml/draft_sync.py 2012-09-08 21:41:55 UTC (rev 9792)
+++ CalendarServer/trunk/txdav/xml/draft_sync.py 2012-09-08 21:45:32 UTC (rev 9793)
@@ -1,103 +0,0 @@
-##
-# Copyright (c) 2009-2012 Apple Computer, Inc. All rights reserved.
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-# SOFTWARE.
-##
-
-"""
-draft-daboo-webdav-sync (Collection Synchronization for WebDAV) XML
-Elements
-
-This module provides XML element definitions for use with WebDAV
-Synchronization.
-
-See draft-daboo-webdav-sync: http://tools.ietf.org/html/draft-daboo-webdav-sync
-
-Last draft referenced: -08
-"""
-
-__all__ = []
-
-
-from txdav.xml.base import WebDAVElement, WebDAVTextElement, dav_namespace
-from txdav.xml.element import registerElement, registerElementClass
-from txdav.xml.rfc2518 import MultiStatus
-
-
- at registerElement
- at registerElementClass
-class SyncCollection (WebDAVElement):
- """
- DAV report used to retrieve specific calendar component items via
- their URIs.
- """
- name = "sync-collection"
-
- # To allow for an empty element in a supported-report-set property we need
- # to relax the child restrictions
- allowed_children = {
- (dav_namespace, "sync-token"): (0, 1), # When used in the REPORT this is required
- (dav_namespace, "sync-level"): (0, 1), # When used in the REPORT this is required
- (dav_namespace, "prop" ): (0, 1),
- }
-
- def __init__(self, *children, **attributes):
- super(SyncCollection, self).__init__(*children, **attributes)
-
- self.property = None
- self.sync_token = None
- self.sync_level = None
-
- for child in self.children:
- qname = child.qname()
-
- if qname == (dav_namespace, "sync-token"):
- self.sync_token = str(child)
-
- elif qname == (dav_namespace, "sync-level"):
- self.sync_level = str(child)
-
- elif qname == (dav_namespace, "prop"):
- if self.property is not None:
- raise ValueError("Only one of DAV:prop allowed")
- self.property = child
-
-
- at registerElement
- at registerElementClass
-class SyncToken (WebDAVTextElement):
- """
- Synchronization token used in report and as a property.
- """
- name = "sync-token"
- hidden = True
- protected = True
-
-
- at registerElement
- at registerElementClass
-class SyncLevel (WebDAVTextElement):
- """
- Synchronization level used in report.
- """
- name = "sync-level"
-
-
-# Extend MultiStatus, to add sync-token
-MultiStatus.allowed_children[(dav_namespace, "sync-token")] = (0, 1)
Modified: CalendarServer/trunk/txdav/xml/parser.py
===================================================================
--- CalendarServer/trunk/txdav/xml/parser.py 2012-09-08 21:41:55 UTC (rev 9792)
+++ CalendarServer/trunk/txdav/xml/parser.py 2012-09-08 21:45:32 UTC (rev 9793)
@@ -32,4 +32,4 @@
"WebDAVDocument",
]
-from txdav.xml.parser_sax import WebDAVDocument
+from txdav.xml.parser_etree import WebDAVDocument
Added: CalendarServer/trunk/txdav/xml/parser_etree.py
===================================================================
--- CalendarServer/trunk/txdav/xml/parser_etree.py (rev 0)
+++ CalendarServer/trunk/txdav/xml/parser_etree.py 2012-09-08 21:45:32 UTC (rev 9793)
@@ -0,0 +1,155 @@
+##
+# Copyright (c) 2012 Apple Computer, Inc. All rights reserved.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+##
+
+"""
+ElementTree implementation of XML parser/generator for WebDAV documents.
+"""
+
+__all__ = [
+ "WebDAVDocument",
+]
+
+from xml.etree.ElementTree import TreeBuilder, XMLParser, ParseError,\
+ _namespace_map
+from txdav.xml.base import WebDAVUnknownElement, PCDATAElement
+from txdav.xml.base import _elements_by_qname
+from txdav.xml.parser_base import AbstractWebDAVDocument
+
+def QNameSplit(qname):
+ return tuple(qname[1:].split("}", 1)) if "}" in qname else ("", qname,)
+
+class WebDAVContentHandler (TreeBuilder):
+
+ def __init__(self):
+ super(WebDAVContentHandler, self).__init__()
+ self._characterBuffer = None
+
+ self.startDocument()
+
+ def startDocument(self):
+ self.stack = [{
+ "name" : None,
+ "class" : None,
+ "attributes" : None,
+ "children" : [],
+ }]
+
+ # Keep a cache of the subclasses we create for unknown XML
+ # elements, so that we don't create multiple classes for the
+ # same element; it's fairly typical for elements to appear
+ # multiple times in a document.
+ self.unknownElementClasses = {}
+
+ def close(self):
+ top = self.stack[-1]
+
+ assert top["name"] is None
+ assert top["class"] is None
+ assert top["attributes"] is None
+ assert len(top["children"]) is 1, "Must have exactly one root element, got %d" % len(top["children"])
+
+ self.dom = WebDAVDocument(top["children"][0])
+ del(self.unknownElementClasses)
+ return self.dom
+
+ def data(self, data):
+ # Stash character data away in a list that we will "".join() when done
+ if self._characterBuffer is None:
+ self._characterBuffer = []
+ self._characterBuffer.append(data)
+
+ def start(self, tag, attrs):
+ name = QNameSplit(tag)
+
+ if self._characterBuffer is not None:
+ pcdata = PCDATAElement("".join(self._characterBuffer))
+ self.stack[-1]["children"].append(pcdata)
+ self._characterBuffer = None
+
+ # Need to convert a "full" namespace in an attribute QName to the form
+ # "%s:%s".
+ attributes_dict = {}
+ for aname, avalue in attrs.items():
+ anamespace, aname = QNameSplit(aname)
+ if anamespace:
+ anamespace = _namespace_map.get(anamespace, anamespace)
+ aname = "%s:%s" % (anamespace, aname,)
+ attributes_dict[aname] = avalue
+
+ tag_namespace, tag_name = name
+
+ if name in _elements_by_qname:
+ element_class = _elements_by_qname[name]
+ elif name in self.unknownElementClasses:
+ element_class = self.unknownElementClasses[name]
+ else:
+ def element_class(*args, **kwargs):
+ element = WebDAVUnknownElement(*args, **kwargs)
+ element.namespace = tag_namespace
+ element.name = tag_name
+ return element
+ self.unknownElementClasses[name] = element_class
+
+ self.stack.append({
+ "name" : name,
+ "class" : element_class,
+ "attributes" : attributes_dict,
+ "children" : [],
+ })
+
+ def end(self, tag):
+ name = QNameSplit(tag)
+
+ if self._characterBuffer is not None:
+ pcdata = PCDATAElement("".join(self._characterBuffer))
+ self.stack[-1]["children"].append(pcdata)
+ self._characterBuffer = None
+
+ # Pop the current element from the stack...
+ top = self.stack[-1]
+ del(self.stack[-1])
+
+ assert top["name"] == name, "Last item on stack is %s while closing %s" % (top["name"], name)
+
+ # ...then instantiate the element and add it to the parent's list of
+ # children.
+ element = top["class"](*top["children"], **top["attributes"])
+
+ self.stack[-1]["children"].append(element)
+
+
+class WebDAVDocument(AbstractWebDAVDocument):
+ @classmethod
+ def fromStream(cls, source):
+ parser = XMLParser(target=WebDAVContentHandler())
+ try:
+ while 1:
+ data = source.read(65536)
+ if not data:
+ break
+ parser.feed(data)
+ except ParseError, e:
+ raise ValueError(e)
+ return parser.close()
+
+ def writeXML(self, output):
+ self.root_element.writeXML(output)
Copied: CalendarServer/trunk/txdav/xml/rfc6578.py (from rev 9752, CalendarServer/trunk/txdav/xml/draft_sync.py)
===================================================================
--- CalendarServer/trunk/txdav/xml/rfc6578.py (rev 0)
+++ CalendarServer/trunk/txdav/xml/rfc6578.py 2012-09-08 21:45:32 UTC (rev 9793)
@@ -0,0 +1,100 @@
+##
+# Copyright (c) 2009-2012 Apple Computer, Inc. All rights reserved.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+##
+
+"""
+RFC 6578 (Collection Synchronization for WebDAV) XML Elements
+
+This module provides XML element definitions for use with WebDAV
+Synchronization.
+
+See RFC 6578: http://www.ietf.org/rfc/rfc6578.txt
+"""
+
+__all__ = []
+
+
+from txdav.xml.base import WebDAVElement, WebDAVTextElement, dav_namespace
+from txdav.xml.element import registerElement, registerElementClass
+from txdav.xml.rfc2518 import MultiStatus
+
+
+ at registerElement
+ at registerElementClass
+class SyncCollection (WebDAVElement):
+ """
+ DAV report used to retrieve specific calendar component items via
+ their URIs.
+ """
+ name = "sync-collection"
+
+ # To allow for an empty element in a supported-report-set property we need
+ # to relax the child restrictions
+ allowed_children = {
+ (dav_namespace, "sync-token"): (0, 1), # When used in the REPORT this is required
+ (dav_namespace, "sync-level"): (0, 1), # When used in the REPORT this is required
+ (dav_namespace, "prop" ): (0, 1),
+ }
+
+ def __init__(self, *children, **attributes):
+ super(SyncCollection, self).__init__(*children, **attributes)
+
+ self.property = None
+ self.sync_token = None
+ self.sync_level = None
+
+ for child in self.children:
+ qname = child.qname()
+
+ if qname == (dav_namespace, "sync-token"):
+ self.sync_token = str(child)
+
+ elif qname == (dav_namespace, "sync-level"):
+ self.sync_level = str(child)
+
+ elif qname == (dav_namespace, "prop"):
+ if self.property is not None:
+ raise ValueError("Only one of DAV:prop allowed")
+ self.property = child
+
+
+ at registerElement
+ at registerElementClass
+class SyncToken (WebDAVTextElement):
+ """
+ Synchronization token used in report and as a property.
+ """
+ name = "sync-token"
+ hidden = True
+ protected = True
+
+
+ at registerElement
+ at registerElementClass
+class SyncLevel (WebDAVTextElement):
+ """
+ Synchronization level used in report.
+ """
+ name = "sync-level"
+
+
+# Extend MultiStatus, to add sync-token
+MultiStatus.allowed_children[(dav_namespace, "sync-token")] = (0, 1)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120908/0382bfb5/attachment-0001.html>
More information about the calendarserver-changes
mailing list