[CalendarServer-changes] [4130] CalendarServer/branches/exarkun/update-twisted-3816-3

source_changes at macosforge.org source_changes at macosforge.org
Fri May 1 10:19:57 PDT 2009


Revision: 4130
          http://trac.macosforge.org/projects/calendarserver/changeset/4130
Author:   exarkun at twistedmatrix.com
Date:     2009-05-01 10:19:57 -0700 (Fri, 01 May 2009)
Log Message:
-----------
Merge forward from exarkun/update-twisted-3816-2 so I can integrate r4116, r4053, and r4024 properly

Modified Paths:
--------------
    CalendarServer/branches/exarkun/update-twisted-3816-3/run
    CalendarServer/branches/exarkun/update-twisted-3816-3/twistedcaldav/notify.py
    CalendarServer/branches/exarkun/update-twisted-3816-3/twistedcaldav/test/test_index.py
    CalendarServer/branches/exarkun/update-twisted-3816-3/twistedcaldav/test/test_upgrade.py
    CalendarServer/branches/exarkun/update-twisted-3816-3/twistedcaldav/test/util.py

Removed Paths:
-------------
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.application.app.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.conch.test.test_keys.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.internet._sslverify.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.internet.defer.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.mail.imap4.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.mail.pop3client.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.persisted.sob.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.plugins.__init__.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.python.filepath.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.runner.procmon.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.spread.pb.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.test.test_plugin.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.test.test_tcp.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web.test.test_webclient.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.auth.basic.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.auth.digest.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.auth.interfaces.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.auth.wrapper.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.__init__.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.auth.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.davxml.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.__init__.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.base.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.extensions.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.parser.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.rfc2518.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.rfc3744.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.rfc4331.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.fileop.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.http.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.idav.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.__init__.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.copymove.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.delete.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.prop_common.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.propfind.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.put.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.put_common.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.report.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.report_acl_principal_prop_set.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.report_expand.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.report_principal_match.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.report_principal_property_search.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.resource.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.static.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.stream.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.data.quota_100.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_acl.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_copy.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_pipeline.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_prop.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_quota.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_resource.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_static.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_stream.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_xml.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.tworequest_client.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.util.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.util.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.xattrprops.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.http.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.log.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.server.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.static.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.test.test_http.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.test.test_httpauth.patch
    CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.words.protocols.jabber.sasl_mechanisms.patch

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.application.app.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.application.app.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.application.app.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,13 +0,0 @@
-Index: twisted/application/app.py
-===================================================================
---- twisted/application/app.py	(revision 19773)
-+++ twisted/application/app.py	(working copy)
-@@ -18,7 +18,7 @@
- def runWithProfiler(reactor, config):
-     """Run reactor under standard profiler."""
-     try:
--        import profile
-+        import cProfile as profile
-     except ImportError, e:
-         s = "Failed to import module profile: %s" % e
-         s += """

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.conch.test.test_keys.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.conch.test.test_keys.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.conch.test.test_keys.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,16 +0,0 @@
-Index: twisted/conch/test/test_keys.py
-===================================================================
---- twisted/conch/test/test_keys.py	(revision 19773)
-+++ twisted/conch/test/test_keys.py	(working copy)
-@@ -4,10 +4,10 @@
- 
- try:
-     import Crypto
-+    from twisted.conch.ssh import keys
- except ImportError:
-     Crypto = None
- 
--from twisted.conch.ssh import keys
- from twisted.trial import unittest
- 
- publicRSA_openssh = "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAGEArzJx8OYOnJmzf4tfBEvLi8DVPrJ3/c9k2I/Az64fxjHf9imyRJbixtQhlH9lfNjUIx+4LmrJH5QNRsFporcHDKOTwTTYLh5KmRpslkYHRivcJSkbh/C+BR3utDS555mV comment"

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.internet._sslverify.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.internet._sslverify.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.internet._sslverify.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,35 +0,0 @@
-Index: twisted/internet/_sslverify.py
-===================================================================
---- twisted/internet/_sslverify.py	(revision 19773)
-+++ twisted/internet/_sslverify.py	(working copy)
-@@ -1,7 +1,11 @@
- # -*- test-case-name: twisted.test.test_sslverify -*-
- # Copyright 2005 Divmod, Inc.  See LICENSE file for details
- 
--import itertools, md5
-+import itertools
-+try:
-+    from hashlib import md5
-+except ImportError:
-+    from md5 import new as md5
- from OpenSSL import SSL, crypto
- 
- from twisted.python import reflect, util
-@@ -666,7 +670,7 @@
-         MD5 hex digest of signature on an empty certificate request with this
-         key.
-         """
--        return md5.md5(self._emptyReq).hexdigest()
-+        return md5(self._emptyReq).hexdigest()
- 
- 
-     def inspect(self):
-@@ -942,7 +946,7 @@
-             ctx.set_options(self._OP_ALL)
- 
-         if self.enableSessions:
--            sessionName = md5.md5("%s-%d" % (reflect.qual(self.__class__), _sessionCounter())).hexdigest()
-+            sessionName = md5("%s-%d" % (reflect.qual(self.__class__), _sessionCounter())).hexdigest()
-             ctx.set_session_id(sessionName)
- 
-         return ctx

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.internet.defer.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.internet.defer.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.internet.defer.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,12 +0,0 @@
-Index: twisted/internet/defer.py
-===================================================================
---- twisted/internet/defer.py	(revision 19773)
-+++ twisted/internet/defer.py	(working copy)
-@@ -998,6 +998,6 @@
- __all__ = ["Deferred", "DeferredList", "succeed", "fail", "FAILURE", "SUCCESS",
-            "AlreadyCalledError", "TimeoutError", "gatherResults",
-            "maybeDeferred",
--           "waitForDeferred", "deferredGenerator", "inlineCallbacks",
-+           "waitForDeferred", "deferredGenerator", "returnValue", "inlineCallbacks",
-            "DeferredLock", "DeferredSemaphore", "DeferredQueue",
-           ]

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.mail.imap4.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.mail.imap4.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.mail.imap4.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,65 +0,0 @@
-Index: twisted/mail/imap4.py
-===================================================================
---- twisted/mail/imap4.py	(revision 19773)
-+++ twisted/mail/imap4.py	(working copy)
-@@ -363,16 +363,11 @@
-         for L in self.lines:
-             names = parseNestedParens(L)
-             N = len(names)
-+            # This section is patched as described in http://twistedmatrix.com/trac/ticket/1105
-             if (N >= 1 and names[0] in self._1_RESPONSES or
-+                N >= 2 and names[1] in self._2_RESPONSES or
-                 N >= 2 and names[0] == 'OK' and isinstance(names[1], types.ListType) and names[1][0] in self._OK_RESPONSES):
-                 send.append(L)
--            elif N >= 3 and names[1] in self._2_RESPONSES:
--                if isinstance(names[2], list) and len(names[2]) >= 1 and names[2][0] == 'FLAGS' and 'FLAGS' not in self.args:
--                    unuse.append(L)
--                else:
--                    send.append(L)
--            elif N >= 2 and names[1] in self._2_RESPONSES:
--                send.append(L)
-             else:
-                 unuse.append(L)
-         d, self.defer = self.defer, None
-@@ -2245,10 +2240,12 @@
-                     for f in fetched.get('FLAGS', []):
-                         sum.append(f)
-                     flags.setdefault(mId, []).extend(sum)
-+            elif L.find('BYE LOGOUT') != -1:
-+                pass
-             else:
-                 log.msg('Unhandled unsolicited response: ' + repr(L))
--        if flags:
--            self.flagsChanged(flags)
-+        #if flags:
-+        #    self.flagsChanged(flags)
-         if recent is not None or exists is not None:
-             self.newMessages(exists, recent)
- 
-@@ -3336,6 +3333,8 @@
-                             if len(data) < 2:
-                                 raise IllegalServerResponse("Not enough arguments", data)
-                             flags.setdefault(id, {})[data[0]] = data[1]
-+                            if data[0] == 'FLAGS':
-+                                self.flagsChanged({id: data[1]})
-                             del data[:2]
-                 else:
-                     print '(2)Ignoring ', parts
-@@ -3431,7 +3430,16 @@
-                     except ValueError:
-                         raise IllegalServerResponse, line
-                     else:
--                        info[id] = parseNestedParens(parts[2])
-+                        data = parseNestedParens(parts[2])[0]
-+                        # This section is patched as described in http://twistedmatrix.com/trac/ticket/1105
-+                        # XXX this will fail if 'FLAGS' is a retrieved part
-+                        for i in range(len(data) -1):
-+                            if data[i] == 'FLAGS':
-+                                self.flagsChanged({id: data[i+1]})
-+                                del data[i:i+2]
-+                                break
-+                        if data:
-+                            info.setdefault(id, []).append(data)
-         return info
- 
-     def _fetch(self, messages, useUID=0, **terms):

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.mail.pop3client.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.mail.pop3client.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.mail.pop3client.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,28 +0,0 @@
-Index: twisted/mail/pop3client.py
-===================================================================
---- twisted/mail/pop3client.py	(revision 19773)
-+++ twisted/mail/pop3client.py	(working copy)
-@@ -11,8 +11,13 @@
- API Stability: Unstable
- """
- 
--import re, md5
-+import re
- 
-+try:
-+    from hashlib import md5
-+except ImportError:
-+    from md5 import new as md5
-+
- from twisted.python import log
- from twisted.internet import defer
- from twisted.protocols import basic
-@@ -486,7 +491,7 @@
-     def _apop(self, username, password, challenge):
-         # Internal helper.  Computes and sends an APOP response.  Returns
-         # a Deferred that fires when the server responds to the response.
--        digest = md5.new(challenge + password).hexdigest()
-+        digest = md5(challenge + password).hexdigest()
-         return self.apop(username, digest)
- 
-     def apop(self, username, digest):

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.persisted.sob.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.persisted.sob.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.persisted.sob.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,32 +0,0 @@
-Index: twisted/persisted/sob.py
-===================================================================
---- twisted/persisted/sob.py	(revision 19773)
-+++ twisted/persisted/sob.py	(working copy)
-@@ -10,8 +10,12 @@
- Maintainer: U{Moshe Zadka<mailto:moshez at twistedmatrix.com>}
- """
- 
--import os, md5, sys
-+import os, sys
- try:
-+    from hashlib import md5
-+except ImportError:
-+    from md5 import new as md5
-+try:
-     import cPickle as pickle
- except ImportError:
-     import pickle
-@@ -32,11 +36,11 @@
-     leftover = len(data) % cipher.block_size
-     if leftover:
-         data += ' '*(cipher.block_size - leftover)
--    return cipher.new(md5.new(passphrase).digest()[:16]).encrypt(data)
-+    return cipher.new(md5(passphrase).digest()[:16]).encrypt(data)
- 
- def _decrypt(passphrase, data):
-     from Crypto.Cipher import AES
--    return AES.new(md5.new(passphrase).digest()[:16]).decrypt(data)
-+    return AES.new(md5(passphrase).digest()[:16]).decrypt(data)
- 
- 
- class IPersistable(Interface):

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.plugins.__init__.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.plugins.__init__.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.plugins.__init__.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,12 +0,0 @@
-Index: twisted/plugins/__init__.py
-===================================================================
---- twisted/plugins/__init__.py	(revision 19773)
-+++ twisted/plugins/__init__.py	(working copy)
-@@ -12,6 +12,6 @@
- """
- 
- import os, sys
--__path__ = [os.path.abspath(os.path.join(x, 'twisted', 'plugins')) for x in sys.path]
-+__path__ = [os.path.abspath(os.path.join(x, 'twisted', 'plugins')) for x in sys.path if not x.startswith('/System')]
- 
- __all__ = []                    # nothing to see here, move along, move along

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.python.filepath.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.python.filepath.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.python.filepath.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,28 +0,0 @@
-Index: twisted/python/filepath.py
-===================================================================
---- twisted/python/filepath.py	(revision 19773)
-+++ twisted/python/filepath.py	(working copy)
-@@ -9,9 +9,13 @@
- import os
- import errno
- import random
--import sha
- import base64
- 
-+try:
-+    from hashlib import sha1
-+except ImportError:
-+    from sha import new as sha1
-+
- from os.path import isabs, exists, normpath, abspath, splitext
- from os.path import basename, dirname
- from os.path import join as joinpath
-@@ -109,7 +113,7 @@
-     """
-     Create a pseudorandom, 16-character string for use in secure filenames.
-     """
--    return armor(sha.new(randomBytes(64)).digest())[:16]
-+    return armor(sha1(randomBytes(64)).digest())[:16]
- 
- class _PathHelper:
-     """

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.runner.procmon.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.runner.procmon.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.runner.procmon.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,39 +0,0 @@
-Index: twisted/runner/procmon.py
-===================================================================
---- twisted/runner/procmon.py	(revision 19773)
-+++ twisted/runner/procmon.py	(working copy)
-@@ -59,6 +59,9 @@
- 
-     disconnecting = 0
- 
-+    def loseConnection(self):
-+        pass
-+
- transport = DummyTransport() 
- 
- class LineLogger(basic.LineReceiver):
-@@ -130,10 +133,10 @@
-         self.consistency = reactor.callLater(self.consistencyDelay,
-                                              self._checkConsistency)
- 
--    def addProcess(self, name, args, uid=None, gid=None):
-+    def addProcess(self, name, args, uid=None, gid=None, env={}):
-         if self.processes.has_key(name):
-             raise KeyError("remove %s first" % name)
--        self.processes[name] = args, uid, gid
-+        self.processes[name] = args, uid, gid, env
-         if self.active:
-             self.startProcess(name)
- 
-@@ -175,9 +178,9 @@
-         p = self.protocols[name] = LoggingProtocol()
-         p.service = self
-         p.name = name
--        args, uid, gid = self.processes[name]
-+        args, uid, gid, env = self.processes[name]
-         self.timeStarted[name] = time.time()
--        reactor.spawnProcess(p, args[0], args, uid=uid, gid=gid)
-+        reactor.spawnProcess(p, args[0], args, uid=uid, gid=gid, env=env)
- 
-     def _forceStopProcess(self, proc):
-         try:

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.spread.pb.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.spread.pb.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.spread.pb.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,53 +0,0 @@
-Index: twisted/spread/pb.py
-===================================================================
---- twisted/spread/pb.py	(revision 19773)
-+++ twisted/spread/pb.py	(working copy)
-@@ -64,7 +64,11 @@
- except ImportError:
-     import StringIO
- 
--import md5
-+try:
-+    from hashlib import md5
-+except ImportError:
-+    from md5 import new as md5
-+
- import random
- import new
- import types
-@@ -1003,10 +1007,10 @@
- 
-     This is useful for challenge/response authentication.
-     """
--    m = md5.new()
-+    m = md5()
-     m.update(password)
-     hashedPassword = m.digest()
--    m = md5.new()
-+    m = md5()
-     m.update(hashedPassword)
-     m.update(challenge)
-     doubleHashedPassword = m.digest()
-@@ -1017,7 +1021,7 @@
-     crap = ''
-     for x in range(random.randrange(15,25)):
-         crap = crap + chr(random.randint(65,90))
--    crap = md5.new(crap).digest()
-+    crap = md5(crap).digest()
-     return crap
- 
- 
-@@ -1226,11 +1230,11 @@
- 
-     # IUsernameHashedPassword:
-     def checkPassword(self, password):
--        return self.checkMD5Password(md5.md5(password).digest())
-+        return self.checkMD5Password(md5(password).digest())
- 
-     # IUsernameMD5Password
-     def checkMD5Password(self, md5Password):
--        md = md5.new()
-+        md = md5()
-         md.update(md5Password)
-         md.update(self.challenge)
-         correct = md.digest()

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.test.test_plugin.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.test.test_plugin.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.test.test_plugin.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,11 +0,0 @@
-Index: twisted/test/test_plugin.py
-===================================================================
---- twisted/test/test_plugin.py	(revision 19773)
-+++ twisted/test/test_plugin.py	(working copy)
-@@ -518,3 +518,6 @@
-         self.assertEqual(len(self.flushLoggedErrors()), 0)
-         self.assertIn('one', self.getAllPlugins())
-         self.assertEqual(len(self.flushLoggedErrors()), 1)
-+
-+    
-+    test_newPluginsOnReadOnlyPath.skip = "Seems not to work on OS X 10.4 buildbot machine."

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.test.test_tcp.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.test.test_tcp.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.test.test_tcp.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,13 +0,0 @@
-Index: twisted/test/test_tcp.py
-===================================================================
---- twisted/test/test_tcp.py	(revision 19773)
-+++ twisted/test/test_tcp.py	(working copy)
-@@ -1294,6 +1294,8 @@
-             self.client.transport.loseConnection()
-             log.flushErrors(RuntimeError)
-         return d.addCallback(check)
-+
-+    testReadNotificationRaises.todo = "self.f.protocol is None"
-     
-     def testWriteNotificationRaises(self):
-         self.client.writeConnectionLost = self.aBug

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web.test.test_webclient.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web.test.test_webclient.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web.test.test_webclient.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,13 +0,0 @@
-Index: twisted/web/test/test_webclient.py
-===================================================================
---- twisted/web/test/test_webclient.py	(revision 19773)
-+++ twisted/web/test/test_webclient.py	(working copy)
-@@ -206,6 +206,8 @@
-         d.addBoth(self._cleanupDownloadPageError3)
-         return d
- 
-+    testDownloadPageError3.skip = "Seems not to work on OS X."
-+
-     def _cleanupDownloadPageError3(self, ignored):
-         os.chmod("unwritable", 0700)
-         os.unlink("unwritable")

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.auth.basic.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.auth.basic.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.auth.basic.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,29 +0,0 @@
-Index: twisted/web2/auth/basic.py
-===================================================================
---- twisted/web2/auth/basic.py	(revision 19773)
-+++ twisted/web2/auth/basic.py	(working copy)
-@@ -1,6 +1,7 @@
- # -*- test-case-name: twisted.web2.test.test_httpauth -*-
- 
- from twisted.cred import credentials, error
-+from twisted.internet.defer import succeed
- from twisted.web2.auth.interfaces import ICredentialFactory
- 
- from zope.interface import implements
-@@ -18,7 +19,7 @@
-         self.realm = realm
- 
-     def getChallenge(self, peer):
--        return {'realm': self.realm}
-+        return succeed({'realm': self.realm})
- 
-     def decode(self, response, request):
-         try:
-@@ -28,6 +29,6 @@
- 
-         creds = creds.split(':', 1)
-         if len(creds) == 2:
--            return credentials.UsernamePassword(*creds)
-+            return succeed(credentials.UsernamePassword(*creds))
-         else:
-             raise error.LoginFailed('Invalid credentials')

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.auth.digest.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.auth.digest.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.auth.digest.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,135 +0,0 @@
-Index: twisted/web2/auth/digest.py
-===================================================================
---- twisted/web2/auth/digest.py	(revision 19773)
-+++ twisted/web2/auth/digest.py	(working copy)
-@@ -8,19 +8,28 @@
- import time
- 
- from twisted.cred import credentials, error
-+from twisted.internet.defer import succeed
- from zope.interface import implements, Interface
- 
- from twisted.web2.auth.interfaces import ICredentialFactory
-+from twisted.web2.http_headers import tokenize
-+from twisted.web2.http_headers import Token
-+from twisted.web2.http_headers import split
-+from twisted.web2.http_headers import parseKeyValue
- 
--import md5, sha
-+try:
-+    from hashlib import md5, sha1
-+except ImportError:
-+    from md5 import new as md5
-+    from sha import new as sha1
- import random, sys
- 
- # The digest math
- 
- algorithms = {
--    'md5': md5.new,
--    'md5-sess': md5.new,
--    'sha': sha.new,
-+    'md5': md5,
-+    'md5-sess': md5,
-+    'sha': sha1,
- }
- 
- # DigestCalcHA1
-@@ -153,7 +162,18 @@
-             calcHA1(algo, self.username, self.realm, password, nonce, cnonce),
-             algo, nonce, nc, cnonce, qop, self.method, uri, None
-         )
-+        
-+        if expected == response:
-+            return True
- 
-+        # IE7 sends cnonce and nc values, but auth fails if they are used.
-+        # So try again without them...
-+        # They can be omitted for backwards compatibility [RFC 2069].
-+        expected = calcResponse(
-+            calcHA1(algo, self.username, self.realm, password, nonce, cnonce),
-+            algo, nonce, None, None, qop, self.method, uri, None
-+        )
-+
-         return expected == response
- 
-     def checkHash(self, digestHash):
-@@ -228,9 +248,9 @@
-         # Now, what we do is encode the nonce, client ip and a timestamp
-         # in the opaque value with a suitable digest
-         key = "%s,%s,%s" % (nonce, clientip, str(int(self._getTime())))
--        digest = md5.new(key + self.privateKey).hexdigest()
-+        digest = md5(key + self.privateKey).hexdigest()
-         ekey = key.encode('base64')
--        return "%s-%s" % (digest, ekey.strip('\n'))
-+        return "%s-%s" % (digest, ekey.replace('\n', ''))
- 
-     def verifyOpaque(self, opaque, nonce, clientip):
-         """
-@@ -274,7 +294,7 @@
-                 'Invalid response, incompatible opaque/nonce too old')
- 
-         # Verify the digest
--        digest = md5.new(key + self.privateKey).hexdigest()
-+        digest = md5(key + self.privateKey).hexdigest()
-         if digest != opaqueParts[0]:
-             raise error.LoginFailed('Invalid response, invalid opaque value')
- 
-@@ -293,11 +313,12 @@
-         c = self.generateNonce()
-         o = self.generateOpaque(c, peer.host)
- 
--        return {'nonce': c,
--                'opaque': o,
--                'qop': 'auth',
--                'algorithm': self.algorithm,
--                'realm': self.realm}
-+        return succeed({'nonce': c,
-+            'opaque': o,
-+            'qop': 'auth',
-+            'algorithm': self.algorithm,
-+            'realm': self.realm,
-+        })
- 
-     def decode(self, response, request):
-         """
-@@ -315,18 +336,18 @@
-         @raise: L{error.LoginFailed} if the response does not contain a
-             username, a nonce, an opaque, or if the opaque is invalid.
-         """
--        def unq(s):
--            if s[0] == s[-1] == '"':
--                return s[1:-1]
--            return s
-         response = ' '.join(response.splitlines())
--        parts = response.split(',')
--
--        auth = {}
--
--        for (k, v) in [p.split('=', 1) for p in parts]:
--            auth[k.strip()] = unq(v.strip())
--
-+        
-+        try:
-+            parts = split(tokenize((response,), foldCase=False), Token(","))
-+    
-+            auth = {}
-+    
-+            for (k, v) in [parseKeyValue(p) for p in parts]:
-+                auth[k.strip()] = v.strip()
-+        except ValueError:
-+            raise error.LoginFailed('Invalid response.')
-+            
-         username = auth.get('username')
-         if not username:
-             raise error.LoginFailed('Invalid response, no username given.')
-@@ -342,7 +363,7 @@
-                              auth.get('nonce'),
-                              request.remoteAddr.host):
- 
--            return DigestedCredentials(username,
-+            return succeed(DigestedCredentials(username,
-                                        request.method,
-                                        self.realm,
--                                       auth)
-+                                       auth))

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.auth.interfaces.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.auth.interfaces.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.auth.interfaces.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,22 +0,0 @@
-Index: twisted/web2/auth/interfaces.py
-===================================================================
---- twisted/web2/auth/interfaces.py	(revision 19773)
-+++ twisted/web2/auth/interfaces.py	(working copy)
-@@ -18,7 +18,7 @@
-         @param peer: The client's address
- 
-         @rtype: C{dict}
--        @return: dictionary of challenge arguments
-+        @return: deferred returning dictionary of challenge arguments
-         """
- 
-     def decode(response, request):
-@@ -32,7 +32,7 @@
-         @type request: L{twisted.web2.server.Request}
-         @param request: the request being processed
- 
--        @return: ICredentials
-+        @return: deferred returning ICredentials
-         """
- 
- 

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.auth.wrapper.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.auth.wrapper.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.auth.wrapper.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,205 +0,0 @@
-Index: twisted/web2/auth/wrapper.py
-===================================================================
---- twisted/web2/auth/wrapper.py	(revision 19773)
-+++ twisted/web2/auth/wrapper.py	(working copy)
-@@ -5,32 +5,45 @@
- """
- from zope.interface import implements, directlyProvides
- from twisted.cred import error, credentials
--from twisted.python import failure
- from twisted.web2 import responsecode
- from twisted.web2 import http
- from twisted.web2 import iweb
- from twisted.web2.auth.interfaces import IAuthenticatedRequest
-+from twisted.internet.defer import inlineCallbacks, returnValue
- 
- class UnauthorizedResponse(http.StatusResponse):
-     """A specialized response class for generating www-authenticate headers
-     from the given L{CredentialFactory} instances
-     """
- 
--    def __init__(self, factories, remoteAddr=None):
-+    @staticmethod
-+    def makeResponse(factories, remoteAddr=None):
-+        
-+        response = UnauthorizedResponse()
-+        d = response.generateHeaders(factories, remoteAddr)
-+        d.addCallback(lambda _:response)
-+        return d
-+
-+    def __init__(self):
-+
-+        super(UnauthorizedResponse, self).__init__(
-+            responsecode.UNAUTHORIZED,
-+            "You are not authorized to access this resource."
-+    )
-+
-+    @inlineCallbacks
-+    def generateHeaders(self, factories, remoteAddr=None):
-         """
-         @param factories: A L{dict} of {'scheme': ICredentialFactory}
- 
-         @param remoteAddr: An L{IAddress} for the connecting client.
-         """
- 
--        super(UnauthorizedResponse, self).__init__(
--            responsecode.UNAUTHORIZED,
--            "You are not authorized to access this resource.")
--
-         authHeaders = []
-         for factory in factories.itervalues():
--            authHeaders.append((factory.scheme,
--                                factory.getChallenge(remoteAddr)))
-+            scheme = factory.scheme
-+            challenge = (yield factory.getChallenge(remoteAddr))
-+            authHeaders.append((scheme, challenge,))
- 
-         self.headers.setHeader('www-authenticate', authHeaders)
- 
-@@ -71,8 +84,6 @@
- 
-     def _loginSucceeded(self, avatar, request):
-         """
--        Callback for successful login.
--
-         @param avatar: A tuple of the form (interface, avatar) as
-             returned by your realm.
- 
-@@ -85,6 +96,7 @@
- 
-         directlyProvides(request, IAuthenticatedRequest)
- 
-+        @inlineCallbacks
-         def _addAuthenticateHeaders(request, response):
-             """
-             A response filter that adds www-authenticate headers
-@@ -93,14 +105,16 @@
-             """
-             if response.code == responsecode.UNAUTHORIZED:
-                 if not response.headers.hasHeader('www-authenticate'):
--                    newResp = UnauthorizedResponse(self.credentialFactories,
--                                                   request.remoteAddr)
-+                    newResp = (yield UnauthorizedResponse.makeResponse(
-+                        self.credentialFactories,
-+                        request.remoteAddr
-+                    ))
- 
-                     response.headers.setHeader(
-                         'www-authenticate',
-                         newResp.headers.getHeader('www-authenticate'))
- 
--            return response
-+            returnValue(response)
- 
-         _addAuthenticateHeaders.handleErrors = True
- 
-@@ -108,27 +122,22 @@
- 
-         return self.wrappedResource
- 
--    def _loginFailed(self, result, request):
-+    @inlineCallbacks
-+    def _loginFailed(self, request):
-         """
--        Errback for failed login.
--
--        @param result: L{Failure} returned by portal.login
--
-         @param request: L{IRequest} that encapsulates this auth
-             attempt.
- 
--        @return: A L{Failure} containing an L{HTTPError} containing the
--            L{UnauthorizedResponse} if C{result} is an L{UnauthorizedLogin}
--            or L{UnhandledCredentials} error
-+        @raise: always rais HTTPError
-         """
--        result.trap(error.UnauthorizedLogin, error.UnhandledCredentials)
- 
--        return failure.Failure(
--            http.HTTPError(
--                UnauthorizedResponse(
--                self.credentialFactories,
--                request.remoteAddr)))
-+        response = (yield UnauthorizedResponse.makeResponse(
-+            self.credentialFactories,
-+            request.remoteAddr
-+        ))
-+        raise http.HTTPError(response)
- 
-+    @inlineCallbacks
-     def login(self, factory, response, request):
-         """
-         @param factory: An L{ICredentialFactory} that understands the given
-@@ -142,50 +151,48 @@
-             or a failure containing an L{UnauthorizedResponse}
-         """
-         try:
--            creds = factory.decode(response, request)
-+            creds = (yield factory.decode(response, request))
-         except error.LoginFailed:
--            raise http.HTTPError(UnauthorizedResponse(
--                                    self.credentialFactories,
--                                    request.remoteAddr))
-+            yield self._loginFailed(request)
- 
-+        try:
-+            avatar = (yield self.portal.login(creds, None, *self.interfaces))
-+        except (error.UnauthorizedLogin, error.UnhandledCredentials):
-+            yield self._loginFailed(request)
-+        resource = self._loginSucceeded(avatar, request)
-+        returnValue(resource)
- 
--        return self.portal.login(creds, None, *self.interfaces
--                                ).addCallbacks(self._loginSucceeded,
--                                               self._loginFailed,
--                                               (request,), None,
--                                               (request,), None)
--
-+    @inlineCallbacks
-     def authenticate(self, request):
-         """
--        Attempt to authenticate the givin request
-+        Attempt to authenticate the giving request
- 
-         @param request: An L{IRequest} to be authenticated.
-         """
-         authHeader = request.headers.getHeader('authorization')
- 
-         if authHeader is None:
--            return self.portal.login(credentials.Anonymous(),
--                                     None,
--                                     *self.interfaces
--                                     ).addCallbacks(self._loginSucceeded,
--                                                    self._loginFailed,
--                                                    (request,), None,
--                                                    (request,), None)
-+            try:
-+                avatar = (yield self.portal.login(credentials.Anonymous(), None, *self.interfaces))
-+            except:
-+                yield self._loginFailed(request)
-+            resource = self._loginSucceeded(avatar, request)
-+            returnValue(resource)
- 
-         elif authHeader[0] not in self.credentialFactories:
--            raise http.HTTPError(UnauthorizedResponse(
--                                    self.credentialFactories,
--                                    request.remoteAddr))
-+            yield self._loginFailed(request)
-         else:
--            return self.login(self.credentialFactories[authHeader[0]],
--                              authHeader[1], request)
-+            result = (yield self.login(self.credentialFactories[authHeader[0]], authHeader[1], request))
-+            returnValue(result)
- 
-     def locateChild(self, request, seg):
-         """
-         Authenticate the request then return the C{self.wrappedResource}
-         and the unmodified segments.
-         """
--        return self.authenticate(request), seg
-+        d = self.authenticate(request)
-+        d.addCallback(lambda result:(result, seg,))
-+        return d
- 
-     def renderHTTP(self, request):
-         """

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.__init__.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.__init__.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.__init__.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,12 +0,0 @@
-Index: twisted/web2/dav/__init__.py
-===================================================================
---- twisted/web2/dav/__init__.py	(revision 19773)
-+++ twisted/web2/dav/__init__.py	(working copy)
-@@ -45,6 +45,7 @@
-     "noneprops",
-     "resource",
-     "static",
-+    "stream",
-     "util",
-     "xattrprops",
- ]

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.auth.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.auth.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.auth.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,83 +0,0 @@
-Index: twisted/web2/dav/auth.py
-===================================================================
---- twisted/web2/dav/auth.py	(revision 19773)
-+++ twisted/web2/dav/auth.py	(working copy)
-@@ -5,7 +5,13 @@
- from twisted.web2.dav import davxml
- from twisted.web2.dav.davxml import twisted_private_namespace
- 
--__all__ = ["PrincipalCredentials", "AuthenticationWrapper"]
-+__all__ = [
-+    "IPrincipal",
-+    "DavRealm",
-+    "IPrincipalCredentials",
-+    "PrincipalCredentials",
-+    "AuthenticationWrapper",
-+]
- 
- class AuthenticationWrapper(WrapperResource):
-     def __init__(self, resource, portal, credentialFactories, loginInterfaces):
-@@ -40,7 +46,7 @@
- 
-     def requestAvatar(self, avatarId, mind, *interfaces):
-         if IPrincipal in interfaces:
--            return IPrincipal, davxml.Principal(davxml.HRef(avatarId))
-+            return IPrincipal, davxml.Principal(davxml.HRef(avatarId[0])), davxml.Principal(davxml.HRef(avatarId[1]))
-         
-         raise NotImplementedError("Only IPrincipal interface is supported")
- 
-@@ -52,33 +58,44 @@
- class PrincipalCredentials(object):
-     implements(IPrincipalCredentials)
- 
--    def __init__(self, principal, principalURI, credentials):
--        self.principal = principal
--        self.principalURI = principalURI
-+    def __init__(self, authnPrincipal, authzPrincipal, credentials):
-+        """
-+        Initialize with both authentication and authorization values. Note that in most cases theses will be the same
-+        since HTTP auth makes no distinction between the two - but we may be layering some addition auth on top of this
-+        (.e.g.. proxy auth, cookies, forms etc) that make result in authentication and authorization being different.
-+
-+        @param authnPrincipal: L{IDAVPrincipalResource} for the authenticated principal.
-+        @param authnURI: C{str} containing the URI of the authenticated principal.
-+        @param authzPrincipal: L{IDAVPrincipalResource} for the authorized principal.
-+        @param authzURI: C{str} containing the URI of the authorized principal.
-+        @param credentials: L{ICredentials} for the authentication credentials.
-+        """
-+        self.authnPrincipal = authnPrincipal
-+        self.authzPrincipal = authzPrincipal
-         self.credentials = credentials
- 
-     def checkPassword(self, password):
-         return self.credentials.checkPassword(password)
- 
- 
--class TwistedPropertyChecker:
-+class TwistedPropertyChecker(object):
-     implements(checkers.ICredentialsChecker)
- 
-     credentialInterfaces = (IPrincipalCredentials,)
- 
--    def _cbPasswordMatch(self, matched, principalURI):
-+    def _cbPasswordMatch(self, matched, principalURIs):
-         if matched:
--            return principalURI
-+            # We return both URIs
-+            return principalURIs
-         else:
--            raise error.UnauthorizedLogin(
--                "Bad credentials for: %s" % (principalURI,))
-+            raise error.UnauthorizedLogin("Bad credentials for: %s" % (principalURIs[0],))
- 
-     def requestAvatarId(self, credentials):
-         pcreds = IPrincipalCredentials(credentials)
--        pswd = str(pcreds.principal.readDeadProperty(TwistedPasswordProperty))
-+        pswd = str(pcreds.authnPrincipal.readDeadProperty(TwistedPasswordProperty))
- 
-         d = defer.maybeDeferred(credentials.checkPassword, pswd)
--        d.addCallback(self._cbPasswordMatch, pcreds.principalURI)
-+        d.addCallback(self._cbPasswordMatch, (pcreds.authnPrincipal.principalURL(), pcreds.authzPrincipal.principalURL()))
-         return d
- 
- ##

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.davxml.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.davxml.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.davxml.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,30 +0,0 @@
-Index: twisted/web2/dav/davxml.py
-===================================================================
---- twisted/web2/dav/davxml.py	(revision 19773)
-+++ twisted/web2/dav/davxml.py	(working copy)
-@@ -45,6 +45,8 @@
- from twisted.web2.dav.element.rfc2518 import *
- from twisted.web2.dav.element.rfc3253 import *
- from twisted.web2.dav.element.rfc3744 import *
-+from twisted.web2.dav.element.rfc4331 import *
-+from twisted.web2.dav.element.extensions import *
- 
- #
- # Register all XML elements with the parser
-@@ -56,11 +58,15 @@
- import twisted.web2.dav.element.rfc2518
- import twisted.web2.dav.element.rfc3253
- import twisted.web2.dav.element.rfc3744
-+import twisted.web2.dav.element.rfc4331
-+import twisted.web2.dav.element.extensions
- 
- __all__ = (
-     registerElements(twisted.web2.dav.element.base   ) +
-     registerElements(twisted.web2.dav.element.parser ) +
-     registerElements(twisted.web2.dav.element.rfc2518) +
-     registerElements(twisted.web2.dav.element.rfc3253) +
--    registerElements(twisted.web2.dav.element.rfc3744)
-+    registerElements(twisted.web2.dav.element.rfc3744) +
-+    registerElements(twisted.web2.dav.element.rfc4331) +
-+    registerElements(twisted.web2.dav.element.extensions)
- )

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.__init__.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.__init__.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.__init__.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,11 +0,0 @@
-Index: twisted/web2/dav/element/__init__.py
-===================================================================
---- twisted/web2/dav/element/__init__.py	(revision 19773)
-+++ twisted/web2/dav/element/__init__.py	(working copy)
-@@ -35,4 +35,6 @@
-     "rfc2518",
-     "rfc3253",
-     "rfc3744",
-+    "rfc4331",
-+    "extensions",
- ]

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.base.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.base.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.base.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,267 +0,0 @@
-Index: twisted/web2/dav/element/base.py
-===================================================================
---- twisted/web2/dav/element/base.py	(revision 19773)
-+++ twisted/web2/dav/element/base.py	(working copy)
-@@ -45,7 +45,7 @@
- ]
- 
- import string
--import StringIO
-+import cStringIO as StringIO
- import xml.dom.minidom
- 
- import datetime
-@@ -90,6 +90,35 @@
-             raise NotImplementedError("WebDAVElement subclass %s is not implemented."
-                                       % (self.__class__.__name__,))
- 
-+        my_children = []
-+
-+        allowPCDATA = self.allowed_children.has_key(PCDATAElement)
-+
-+        for child in children:
-+            if child is None:
-+                continue
-+
-+            if isinstance(child, (str, unicode)):
-+                child = PCDATAElement(child)
-+
-+            if isinstance(child, PCDATAElement) and not allowPCDATA:
-+                continue
-+
-+            my_children.append(child)
-+
-+        self.children = tuple(my_children)
-+
-+        self.attributes = attributes
-+
-+    def validate(self):
-+
-+        children = self.children
-+        attributes = self.attributes
-+
-+        if self.allowed_children is None:
-+            raise NotImplementedError("WebDAVElement subclass %s is not implemented."
-+                                      % (self.__class__.__name__,))
-+
-         #
-         # Validate that children are of acceptable types
-         #
-@@ -102,13 +131,10 @@
-         my_children = []
- 
-         for child in children:
--            if child is None:
--                continue
- 
--            if isinstance(child, (str, unicode)):
--                child = PCDATAElement(child)
--
-             assert isinstance(child, (WebDAVElement, PCDATAElement)), "Not an element: %r" % (child,)
-+            
-+            child.validate()
- 
-             for allowed, (min, max) in allowed_children.items():
-                 if type(allowed) == type and isinstance(child, allowed):
-@@ -145,24 +171,26 @@
- 
-         if self.allowed_attributes:
-             for name in attributes:
--                if name in self.allowed_attributes:
--                    my_attributes[name] = attributes[name]
--                else:
--                    log.msg("Attribute %s is unexpected and therefore ignored in %s element"
--                            % (name, self.sname()))
-+                if name not in self.allowed_attributes:
-+                    log.msg("Attribute %s is unexpected in %s element" % (name, self.sname()))
-+                my_attributes[name] = attributes[name]
-     
-             for name, required in self.allowed_attributes.items():
-                 if required and name not in my_attributes:
-                     raise ValueError("Attribute %s is required in %s element"
-                                      % (name, self.sname()))
- 
--        elif not isinstance(self, WebDAVUnknownElement):
--            if attributes:
--                log.msg("Attributes %s are unexpected and therefore ignored in %s element"
-+        else:
-+            if not isinstance(self, WebDAVUnknownElement) and attributes:
-+                log.msg("Attributes %s are unexpected in %s element"
-                         % (attributes.keys(), self.sname()))
-+            my_attributes.update(attributes)
- 
-         self.attributes = my_attributes
- 
-+    def emptyCopy(self):
-+        return self.__class__()
-+
-     def __str__(self):
-         return self.sname()
- 
-@@ -190,14 +218,93 @@
-         return child in self.children
- 
-     def writeXML(self, output):
--        document = xml.dom.minidom.Document()
--        self.addToDOM(document, None)
--        PrintXML(document, stream=output)
-+        # FIXME: Now have a 'fast' write implementation as well as previous PyXML-based one.
-+        # For now the fast one is the default and we will test to see if its good enough.
-+        
-+        usePyXML = False
-+        if usePyXML:
-+            document = xml.dom.minidom.Document()
-+            self.addToDOM(document, None)
-+            PrintXML(document, stream=output)
-+        else:
-+            output.write("<?xml version='1.0' encoding='UTF-8'?>\r\n")
-+            self.writeToStream(output, "", 0, True)
-+            output.write("\r\n")
-+    
-+    def writeToStream(self, output, ns, level, pretty):
-+        """
-+        Fast XML output.
- 
-+        @param output: C{stream} to write to.
-+        @param ns: C{str} containing the namespace of the enclosing element.
-+        @param level: C{int} containing the element nesting level (starts at 0).
-+        @param pretty: C{bool} whether to use 'pretty' formatted output or not.
-+        """
-+        
-+        # Do pretty indent
-+        if pretty and level:
-+            output.write("  " * level)
-+        
-+        # Check for empty element (one with either no children or a single PCDATA that is itself empty)
-+        if (len(self.children) == 0 or
-+            (len(self.children) == 1 and isinstance(self.children[0], PCDATAElement) and len(str(self.children[0])) == 0)):
-+
-+            # Write out any attributes or the namespace if difference from enclosing element.
-+            if self.attributes or (ns != self.namespace):
-+                output.write("<%s" % (self.name,))
-+                for name, value in self.attributes.iteritems():
-+                    self.writeAttributeToStream(output, name, value)
-+                if ns != self.namespace:
-+                    output.write(" xmlns='%s'" % (self.namespace,))
-+                output.write("/>")
-+            else:
-+                output.write("<%s/>" % (self.name,))
-+        else:
-+            # Write out any attributes or the namespace if difference from enclosing element.
-+            if self.attributes or (ns != self.namespace):
-+                output.write("<%s" % (self.name,))
-+                for name, value in self.attributes.iteritems():
-+                    self.writeAttributeToStream(output, name, value)
-+                if ns != self.namespace:
-+                    output.write(" xmlns='%s'" % (self.namespace,))
-+                    ns = self.namespace
-+                output.write(">")
-+            else:
-+                output.write("<%s>" % (self.name,))
-+                
-+            # Determine nature of children when doing pretty print: we do
-+            # not want to insert CRLFs or any other whitespace in PCDATA.
-+            hasPCDATA = False
-+            for child in self.children:
-+                if isinstance(child, PCDATAElement):
-+                    hasPCDATA = True
-+                    break
-+            
-+            # Write out the children.
-+            if pretty and not hasPCDATA:
-+                output.write("\r\n")
-+            for child in self.children:
-+                child.writeToStream(output, ns, level+1, pretty)
-+                
-+            # Close the element.
-+            if pretty and not hasPCDATA and level:
-+                output.write("  " * level)
-+            output.write("</%s>" % (self.name,))
-+
-+        if pretty and level:
-+            output.write("\r\n")
-+
-+    def writeAttributeToStream(self, output, name, value):
-+        
-+        # Quote any single quotes. We do not need to be any smarter than this.
-+        value = value.replace("'", "&apos;")
-+
-+        output.write(" %s='%s'" % (name, value,))  
-+      
-     def toxml(self):
-         output = StringIO.StringIO()
-         self.writeXML(output)
--        return output.getvalue()
-+        return str(output.getvalue())
- 
-     def element(self, document):
-         element = document.createElementNS(self.namespace, self.name)
-@@ -285,6 +392,9 @@
- 
-         self.data = data
- 
-+    def validate(self):
-+        pass
-+
-     def __str__(self):
-         return str(self.data)
- 
-@@ -324,6 +434,22 @@
-             log.err("Invalid PCDATA: %r" % (self.data,))
-             raise
- 
-+    def writeToStream(self, output, ns, level, pretty):
-+        # Do escaping/CDATA behavior
-+        if "\r" in self.data or "\n" in self.data:
-+            # Do CDATA
-+            cdata = "<![CDATA[%s]]>" % (self.data.replace("]]>", "]]&gt;"),)
-+        else:
-+            cdata = self.data
-+            if "&" in cdata:
-+                cdata = cdata.replace("&", "&amp;")
-+            if "<" in cdata:
-+                cdata = cdata.replace("<", "&lt;")
-+            if ">" in cdata:
-+                cdata = cdata.replace(">", "&gt;")
-+
-+        output.write(cdata)
-+
- class WebDAVOneShotElement (WebDAVElement):
-     """
-     Element with exactly one WebDAVEmptyElement child and no attributes.
-@@ -364,6 +490,18 @@
-         PCDATAElement: (0, None),
-     }
- 
-+    def qname(self):
-+        return (self.namespace, self.name)
-+
-+    def sname(self):
-+        return "{%s}%s" % (self.namespace, self.name)
-+
-+    def emptyCopy(self):
-+        copied = self.__class__()
-+        copied.name = self.name
-+        copied.namespace = self.namespace
-+        return copied
-+
- class WebDAVEmptyElement (WebDAVElement):
-     """
-     WebDAV element with no contents.
-@@ -388,6 +526,7 @@
-     """
-     WebDAV element containing PCDATA.
-     """
-+    @classmethod
-     def fromString(clazz, string):
-         if string is None:
-             return clazz()
-@@ -396,8 +535,6 @@
-         else:
-             return clazz(PCDATAElement(str(string)))
- 
--    fromString = classmethod(fromString)
--
-     allowed_children = { PCDATAElement: (0, None) }
- 
-     def __str__(self):

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.extensions.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.extensions.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.extensions.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,44 +0,0 @@
-Index: twisted/web2/dav/element/extensions.py
-===================================================================
---- twisted/web2/dav/element/extensions.py	(revision 0)
-+++ twisted/web2/dav/element/extensions.py	(revision 0)
-@@ -0,0 +1,39 @@
-+##
-+# Copyright (c) 2005 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.
-+#
-+##
-+
-+from twisted.web2.dav.element.base import *
-+
-+##
-+# draft-sanchez-webdav-current-principal
-+##
-+
-+class CurrentUserPrincipal(WebDAVElement):
-+    """
-+    Current principal information
-+    """
-+
-+    name = "current-user-principal"
-+    allowed_children = {
-+        (dav_namespace, "href" )                : (0, 1),
-+        (dav_namespace, "unauthenticated" )     : (0, 1),
-+    }

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.parser.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.parser.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.parser.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,81 +0,0 @@
-Index: twisted/web2/dav/element/parser.py
-===================================================================
---- twisted/web2/dav/element/parser.py	(revision 19773)
-+++ twisted/web2/dav/element/parser.py	(working copy)
-@@ -37,7 +37,7 @@
-     "WebDAVDocument",
- ]
- 
--import StringIO
-+import cStringIO as StringIO
- import xml.dom.minidom
- import xml.sax
- 
-@@ -106,6 +106,12 @@
-             "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 endDocument(self):
-         top = self.stack[-1]
- 
-@@ -115,6 +121,7 @@
-         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)
- 
-     def startElementNS(self, name, qname, attributes):
-         attributes_dict = {}
-@@ -125,13 +132,17 @@
- 
-         tag_namespace, tag_name = name
- 
--        if (name not in elements_by_tag_name):
--            class UnknownElement (WebDAVUnknownElement):
--                namespace = tag_namespace
--                name      = tag_name
--            element_class = UnknownElement
-+        if name in elements_by_tag_name:
-+            element_class = elements_by_tag_name[name]
-+        elif name in self.unknownElementClasses:
-+            element_class = self.unknownElementClasses[name]
-         else:
--            element_class = elements_by_tag_name[name]
-+            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,
-@@ -158,7 +169,12 @@
-         self.stack[-1]["children"].append(element)
- 
-     def characters(self, content):
--        self.stack[-1]["children"].append(PCDATAElement(content))
-+        # Coalesce adjacent PCDATAElements
-+        pcdata = PCDATAElement(content)
-+        if len(self.stack[-1]["children"]) and isinstance(self.stack[-1]["children"][-1], PCDATAElement):
-+            self.stack[-1]["children"][-1] = self.stack[-1]["children"][-1] + pcdata
-+        else:
-+            self.stack[-1]["children"].append(pcdata)
- 
-     def ignorableWhitespace(self, whitespace):
-         self.characters(self, whitespace)
-@@ -194,6 +210,8 @@
-             except xml.sax.SAXParseException, e:
-                 raise ValueError(e)
- 
-+            #handler.dom.root_element.validate()
-+
-             return handler.dom
- 
-         return parse

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.rfc2518.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.rfc2518.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.rfc2518.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,37 +0,0 @@
-Index: twisted/web2/dav/element/rfc2518.py
-===================================================================
---- twisted/web2/dav/element/rfc2518.py	(revision 19773)
-+++ twisted/web2/dav/element/rfc2518.py	(working copy)
-@@ -59,8 +59,8 @@
-     """
-     name = "depth"
- 
--    def __init__(self, *children, **attributes):
--        super(Depth, self).__init__(*children, **attributes)
-+    def validate(self):
-+        super(Depth, self).validate()
- 
-         depth = str(self)
-         if depth not in ("0", "1", "infinity"):
-@@ -382,8 +382,8 @@
-         PCDATAElement: (0, 1),
-     }
- 
--    def __init__(self, *children, **attributes):
--        super(KeepAlive, self).__init__(*children, **attributes)
-+    def validate(self):
-+        super(KeepAlive, self).validate()
- 
-         type = None
- 
-@@ -450,8 +450,8 @@
-         (dav_namespace, "prop"    ): (0, 1),
-     }
- 
--    def __init__(self, *children, **attributes):
--        super(PropertyFind, self).__init__(*children, **attributes)
-+    def validate(self):
-+        super(PropertyFind, self).validate()
- 
-         if len(self.children) != 1:
-             raise ValueError(

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.rfc3744.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.rfc3744.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.rfc3744.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,102 +0,0 @@
-Index: twisted/web2/dav/element/rfc3744.py
-===================================================================
---- twisted/web2/dav/element/rfc3744.py	(revision 19773)
-+++ twisted/web2/dav/element/rfc3744.py	(working copy)
-@@ -131,8 +131,8 @@
-         (dav_namespace, "self"           ): (0, 1),
-     }
- 
--    def __init__(self, *children, **attributes):
--        super(Principal, self).__init__(*children, **attributes)
-+    def validate(self):
-+        super(Principal, self).validate()
- 
-         if len(self.children) > 1:
-             raise ValueError(
-@@ -385,9 +385,14 @@
-         self.inherited  = None
-         self.protected  = False
- 
-+        my_children = []
-+
-         for child in self.children:
-             namespace, name = child.qname()
- 
-+            if isinstance(child, PCDATAElement):
-+                continue
-+
-             if (namespace == dav_namespace):
-                 if name in ("principal", "invert"):
-                     if self.principal is not None:
-@@ -417,6 +422,10 @@
-                 elif name == "protected":
-                     self.protected = True
- 
-+            my_children.append(child)
-+
-+        self.children = tuple(my_children)
-+
-         if self.principal is None:
-             raise ValueError(
-                 "One of DAV:principal or DAV:invert is required in %s, got: %s"
-@@ -456,7 +465,7 @@
-     """
-     name = "self"
- 
--class Invert (WebDAVEmptyElement):
-+class Invert (WebDAVElement):
-     """
-     Principal which matches a user if the user does not match the principal
-     contained by this principal. (RFC 3744, section 5.5.1)
-@@ -551,8 +560,8 @@
-         (dav_namespace, "property"       ): (0, None),
-     }
- 
--    def __init__(self, *children, **attributes):
--        super(RequiredPrincipal, self).__init__(*children, **attributes)
-+    def validate(self):
-+        super(RequiredPrincipal, self).validate()
- 
-         type = None
- 
-@@ -628,8 +637,8 @@
- 
-     allowed_children = { WebDAVElement: (0, None) }
- 
--    def __init__(self, *children, **attributes):
--        super(ACLPrincipalPropSet, self).__init__(*children, **attributes)
-+    def validate(self):
-+        super(ACLPrincipalPropSet, self).validate()
- 
-         prop = False
-         
-@@ -656,8 +665,8 @@
-         (dav_namespace, "prop"              ): (0, 1),
-     }
- 
--    def __init__(self, *children, **attributes):
--        super(PrincipalMatch, self).__init__(*children, **attributes)
-+    def validate(self):
-+        super(PrincipalMatch, self).validate()
- 
-         # This element can be empty when uses in supported-report-set
-         if not len(self.children):
-@@ -705,6 +714,7 @@
-         (dav_namespace, "prop"                             ): (0, 1),
-         (dav_namespace, "apply-to-principal-collection-set"): (0, 1),
-     }
-+    allowed_attributes = { "test": False }
- 
- class PropertySearch (WebDAVElement):
-     """
-@@ -745,4 +755,10 @@
-         (dav_namespace, "description"): (1, 1),
-     }
- 
-+class NumberOfMatchesWithinLimits (WebDAVEmptyElement):
-+    """
-+    Error which indicates too many results
-+    """
-+    name = "number-of-matches-within-limits"
-+
- # For DAV:description element (RFC 3744, section 9.5) see Description class above.

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.rfc4331.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.rfc4331.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.element.rfc4331.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,60 +0,0 @@
-Index: twisted/web2/dav/element/rfc4331.py
-===================================================================
---- twisted/web2/dav/element/rfc4331.py	(revision 0)
-+++ twisted/web2/dav/element/rfc4331.py	(revision 0)
-@@ -0,0 +1,55 @@
-+##
-+# Copyright (c) 2005-2007 Apple 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.
-+#
-+# DRI: Cyrus Daboo, cdaboo at apple.com
-+##
-+
-+"""
-+RFC 4331 (Quota and Size Properties for WebDAV Collections) XML Elements
-+
-+This module provides XML element definitions for use with WebDAV.
-+
-+See RFC 4331: http://www.ietf.org/rfc/rfc4331.txt
-+"""
-+
-+from twisted.web2.dav.element.base import WebDAVTextElement
-+
-+##
-+# Section 3 & 4 (Quota Properties)
-+##
-+
-+class QuotaAvailableBytes (WebDAVTextElement):
-+    """
-+    Property which contains the the number of bytes available under the
-+    current quota to store data in a collection (RFC 4331, section 3)
-+    """
-+    name = "quota-available-bytes"
-+    hidden = True
-+    protected = True
-+
-+class QuotaUsedBytes (WebDAVTextElement):
-+    """
-+    Property which contains the the number of bytes used under the
-+    current quota to store data in a collection (RFC 4331, section 4)
-+    """
-+    name = "quota-used-bytes"
-+    hidden = True
-+    protected = True

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.fileop.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.fileop.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.fileop.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,31 +0,0 @@
-Index: twisted/web2/dav/fileop.py
-===================================================================
---- twisted/web2/dav/fileop.py	(revision 19773)
-+++ twisted/web2/dav/fileop.py	(working copy)
-@@ -35,6 +35,7 @@
-     "move",
-     "put",
-     "mkcollection",
-+    "rmdir",
- ]
- 
- import os
-@@ -287,7 +288,7 @@
-                     response = waitForDeferred(copy(FilePath(source_path), FilePath(destination_path), destination_uri, depth))
-                     yield response
-                     response = response.getResult()
--                    checkResponse(response, "copy", responsecode.NO_CONTENT)
-+                    checkResponse(response, "copy", responsecode.CREATED, responsecode.NO_CONTENT)
- 
-             for subdir in subdirs:
-                 source_path, destination_path = paths(dir, subdir)
-@@ -509,7 +510,5 @@
-     os.rmdir(dirname)
- 
- def checkResponse(response, method, *codes):
--    assert (
--        response in codes,
--        "%s() should have raised, but returned one of %r instead" % (method, codes)
--    )
-+    assert  response in codes, \
-+        "%s() returned %r, but should have returned one of %r instead" % (method, response, codes)

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.http.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.http.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.http.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,41 +0,0 @@
-Index: twisted/web2/dav/http.py
-===================================================================
---- twisted/web2/dav/http.py	(revision 19773)
-+++ twisted/web2/dav/http.py	(working copy)
-@@ -28,10 +28,13 @@
- 
- __all__ = [
-     "ErrorResponse",
-+    "NeedPrivilegesResponse",
-     "MultiStatusResponse",
-     "ResponseQueue",
-     "PropertyStatusResponseQueue",
-     "statusForFailure",
-+    "errorForFailure",
-+    "messageForFailure",
- ]
- 
- import errno
-@@ -69,10 +72,9 @@
-         """
-         if type(error) is tuple:
-             xml_namespace, xml_name = error
--            class EmptyError (davxml.WebDAVEmptyElement):
--                namespace = xml_namespace
--                name      = xml_name
--            error = EmptyError()
-+            error = davxml.WebDAVUnknownElement()
-+            error.namespace = xml_namespace
-+            error.name = xml_name
- 
-         output = davxml.Error(error).toxml()
- 
-@@ -227,7 +229,7 @@
- 
-         if len(property.children) > 0:
-             # Re-instantiate as empty element.
--            property = property.__class__()
-+            property = property.emptyCopy()
- 
-         if code > 400: # Error codes only
-             log.err("Error during %s for %s: %s" % (self.method, property, message))

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.idav.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.idav.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.idav.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,157 +0,0 @@
-Index: twisted/web2/dav/idav.py
-===================================================================
---- twisted/web2/dav/idav.py	(revision 19773)
-+++ twisted/web2/dav/idav.py	(working copy)
-@@ -26,7 +26,7 @@
- web2.dav interfaces.
- """
- 
--__all__ = [ "IDAVResource", "IDAVPrincipalResource" ]
-+__all__ = [ "IDAVResource", "IDAVPrincipalResource", "IDAVPrincipalCollectionResource", ]
- 
- from twisted.web2.iweb import IResource
- 
-@@ -41,7 +41,7 @@
-             otherwise.
-         """
- 
--    def findChildren(depth, request, callback, privileges):
-+    def findChildren(depth, request, callback, privileges, inherited_aces):
-         """
-         Returns an iterable of child resources for the given depth.
-         Because resources do not know their request URIs, chidren are returned
-@@ -52,6 +52,8 @@
-         @param callback: C{callable} that will be called for each child found
-         @param privileges: the list of L{Privilege}s to test for.  This should 
-             default to None.
-+        @param inherited_aces: a list of L{Privilege}s for aces being inherited from
-+            the parent collection used to bypass inheritance lookup.
-         @return: An L{Deferred} that fires when all the children have been found
-         """
- 
-@@ -125,15 +127,10 @@
-             L{responsecode.UNAUTHORIZED}) if not authorized.
-         """
- 
--    def principalCollections(request):
-+    def principalCollections():
-         """
--        Provides the DAV:HRef's of collection resources which contain principal
--        resources which may be used in access control entries on this resource.
--        (RFC 3744, section 5.8)
--        @param request: the request being processed.
--        @return: a deferred sequence of L{davxml.HRef}s referring to
--            collection resources which implement the
--            C{DAV:principal-property-search} C{REPORT}.
-+        @return: an interable of L{IDAVPrincipalCollectionResource}s which
-+            contain principals used in ACLs for this resource.
-         """
- 
-     def setAccessControlList(acl):
-@@ -180,6 +177,80 @@
-             the specified principal.
-         """
- 
-+    ##
-+    # Quota
-+    ##
-+    
-+    def quota(request):
-+        """
-+        Get current available & used quota values for this resource's quota root
-+        collection.
-+
-+        @return: a C{tuple} containing two C{int}'s the first is 
-+            quota-available-bytes, the second is quota-used-bytes, or
-+            C{None} if quota is not defined on the resource.
-+        """
-+    
-+    def hasQuota(request):
-+        """
-+        Check whether this resource is undre quota control by checking each parent to see if
-+        it has a quota root.
-+        
-+        @return: C{True} if under quota control, C{False} if not.
-+        """
-+        
-+    def hasQuotaRoot(request):
-+        """
-+        Determine whether the resource has a quota root.
-+
-+        @return: a C{True} if this resource has quota root, C{False} otherwise.
-+        """
-+    
-+
-+    def quotaRoot(request):
-+        """
-+        Get the quota root (max. allowed bytes) value for this collection.
-+
-+        @return: a C{int} containing the maximum allowed bytes if this collection
-+            is quota-controlled, or C{None} if not quota controlled.
-+        """
-+    
-+    def setQuotaRoot(request, maxsize):
-+        """
-+        Set the quota root (max. allowed bytes) value for this collection.
-+
-+        @param maxsize: a C{int} containing the maximum allowed bytes for the contents
-+            of this collection.
-+        """
-+    
-+    def quotaSize(request):
-+        """
-+        Get the size of this resource (if its a collection get total for all children as well).
-+        TODO: Take into account size of dead-properties.
-+
-+        @return: a L{Deferred} with a C{int} result containing the size of the resource.
-+        """
-+        
-+    def currentQuotaUse(request):
-+        """
-+        Get the cached quota use value, or if not present (or invalid) determine
-+        quota use by brute force.
-+
-+        @return: an L{Deferred} with a C{int} result containing the current used byte count if
-+            this collection is quota-controlled, or C{None} if not quota controlled.
-+        """
-+        
-+    def updateQuotaUse(request, adjust):
-+        """
-+        Adjust current quota use on this all all parent collections that also
-+        have quota roots.
-+
-+        @param adjust: a C{int} containing the number of bytes added (positive) or
-+        removed (negative) that should be used to adjust the cached total.
-+        @return: an L{Deferred} with a C{int} result containing the current used byte if this collection
-+            is quota-controlled, or C{None} if not quota controlled.
-+        """
-+
- class IDAVPrincipalResource (IDAVResource):
-     """
-     WebDAV principal resource.  (RFC 3744, section 2)
-@@ -203,12 +274,23 @@
-         """
-         Provides the principal URLs of principals that are direct members of
-         this (group) principal.  (RFC 3744, section 4.3)
--        @return: a iterable of principal URLs.
-+        @return: a deferred returning an iterable of principal URLs.
-         """
- 
-     def groupMemberships():
-         """
-         Provides the URLs of the group principals in which the principal is
-         directly a member.  (RFC 3744, section 4.4)
--        @return: a iterable of group principal URLs.
-+        @return: a deferred containing an iterable of group principal URLs.
-         """
-+
-+class IDAVPrincipalCollectionResource (IDAVResource):
-+    """
-+    WebDAV principal collection resource.  (RFC 3744, section 5.8)
-+    """
-+    def principalCollectionURL():
-+        """
-+        Provides a URL for this resource which may be used to identify this
-+        resource in ACL requests.  (RFC 3744, section 5.8)
-+        @return: a URL.
-+        """

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.__init__.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.__init__.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.__init__.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,12 +0,0 @@
-Index: twisted/web2/dav/method/__init__.py
-===================================================================
---- twisted/web2/dav/method/__init__.py	(revision 19773)
-+++ twisted/web2/dav/method/__init__.py	(working copy)
-@@ -40,6 +40,7 @@
-     "proppatch",
-     "prop_common",
-     "put",
-+    "put_common",
-     "report",
-     "report_acl_principal_prop_set",
-     "report_expand",

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.copymove.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.copymove.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.copymove.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,76 +0,0 @@
-Index: twisted/web2/dav/method/copymove.py
-===================================================================
---- twisted/web2/dav/method/copymove.py	(revision 19773)
-+++ twisted/web2/dav/method/copymove.py	(working copy)
-@@ -34,11 +34,12 @@
- from twisted.python import log
- from twisted.internet.defer import waitForDeferred, deferredGenerator
- from twisted.web2 import responsecode
-+from twisted.web2.dav.fileop import move
- from twisted.web2.http import HTTPError, StatusResponse
- from twisted.web2.filter.location import addLocation
- from twisted.web2.dav import davxml
- from twisted.web2.dav.idav import IDAVResource
--from twisted.web2.dav.fileop import copy, move
-+from twisted.web2.dav.method import put_common
- from twisted.web2.dav.util import parentForURL
- 
- # FIXME: This is circular
-@@ -81,7 +82,15 @@
-         # May need to add a location header
-         addLocation(request, destination_uri)
- 
--    x = waitForDeferred(copy(self.fp, destination.fp, destination_uri, depth))
-+    #x = waitForDeferred(copy(self.fp, destination.fp, destination_uri, depth))
-+    x = waitForDeferred(put_common.storeResource(request,
-+                                                 source=self,
-+                                                 source_uri=request.uri,
-+                                                 destination=destination,
-+                                                 destination_uri=destination_uri,
-+                                                 deletesource=False,
-+                                                 depth=depth
-+                                                 ))
-     yield x
-     yield x.getResult()
- 
-@@ -100,7 +109,8 @@
-     #
-     # Check authentication and access controls
-     #
--    parent = waitForDeferred(request.locateResource(parentForURL(request.uri)))
-+    parentURL = parentForURL(request.uri)
-+    parent = waitForDeferred(request.locateResource(parentURL))
-     yield parent
-     parent = parent.getResult()
- 
-@@ -117,7 +127,8 @@
-         yield x
-         x.getResult()
-     else:
--        destparent = waitForDeferred(request.locateResource(parentForURL(destination_uri)))
-+        destparentURL = parentForURL(destination_uri)
-+        destparent = waitForDeferred(request.locateResource(destparentURL))
-         yield destparent
-         destparent = destparent.getResult()
- 
-@@ -144,7 +155,19 @@
-         log.err(msg)
-         raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))
- 
--    x = waitForDeferred(move(self.fp, request.uri, destination.fp, destination_uri, depth))
-+    # Lets optimise a move within the same directory to a new resource as a simple move
-+    # rather than using the full transaction based storeResource api. This allows simple
-+    # "rename" operations to work quickly.
-+    if (not destination.exists()) and destparent == parent:
-+        x = waitForDeferred(move(self.fp, request.uri, destination.fp, destination_uri, depth))
-+    else:
-+        x = waitForDeferred(put_common.storeResource(request,
-+                                                     source=self,
-+                                                     source_uri=request.uri,
-+                                                     destination=destination,
-+                                                     destination_uri=destination_uri,
-+                                                     deletesource=True,
-+                                                     depth=depth))
-     yield x
-     yield x.getResult()
- 

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.delete.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.delete.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.delete.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,34 +0,0 @@
-Index: twisted/web2/dav/method/delete.py
-===================================================================
---- twisted/web2/dav/method/delete.py	(revision 19773)
-+++ twisted/web2/dav/method/delete.py	(working copy)
-@@ -58,8 +58,28 @@
-     yield x
-     x.getResult()
- 
-+    # Do quota checks before we start deleting things
-+    myquota = waitForDeferred(self.quota(request))
-+    yield myquota
-+    myquota = myquota.getResult()
-+    if myquota is not None:
-+        old_size = waitForDeferred(self.quotaSize(request))
-+        yield old_size
-+        old_size = old_size.getResult()
-+    else:
-+        old_size = 0
-+
-+    # Do delete
-     x = waitForDeferred(delete(request.uri, self.fp, depth))
-     yield x
--    yield x.getResult()
-+    result = x.getResult()
- 
-+    # Adjust quota
-+    if myquota is not None:
-+        d = waitForDeferred(self.quotaSizeAdjust(request, -old_size))
-+        yield d
-+        d.getResult()
-+    
-+    yield result
-+
- http_DELETE = deferredGenerator(http_DELETE)

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.prop_common.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.prop_common.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.prop_common.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,36 +0,0 @@
-Index: twisted/web2/dav/method/prop_common.py
-===================================================================
---- twisted/web2/dav/method/prop_common.py	(revision 19773)
-+++ twisted/web2/dav/method/prop_common.py	(working copy)
-@@ -23,19 +23,21 @@
-         properties_by_status = waitForDeferred(propertiesForResource(request, propertyreq, resource))
-         yield properties_by_status
-         properties_by_status = properties_by_status.getResult()
--        
-+
-+        propstats = []
-+
-         for status in properties_by_status:
-             properties = properties_by_status[status]
-             if properties:
--                responses.append(
--                    davxml.PropertyStatusResponse(
--                        href,
--                        davxml.PropertyStatus(
--                            davxml.PropertyContainer(*properties),
--                            davxml.Status.fromResponseCode(status)
--                        )
--                    )
--                )
-+                xml_status = davxml.Status.fromResponseCode(status)
-+                xml_container = davxml.PropertyContainer(*properties)
-+                xml_propstat = davxml.PropertyStatus(xml_container, xml_status)
-+
-+                propstats.append(xml_propstat)
-+
-+        if propstats:
-+            responses.append(davxml.PropertyStatusResponse(href, *propstats))
-+
-     else:
-         responses.append(
-             davxml.StatusResponse(

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.propfind.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.propfind.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.propfind.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,28 +0,0 @@
-Index: twisted/web2/dav/method/propfind.py
-===================================================================
---- twisted/web2/dav/method/propfind.py	(revision 19773)
-+++ twisted/web2/dav/method/propfind.py	(working copy)
-@@ -27,7 +27,10 @@
- WebDAV PROPFIND method
- """
- 
--__all__ = ["http_PROPFIND"]
-+__all__ = [
-+    "http_PROPFIND",
-+    "propertyName",
-+]
- 
- from twisted.python import log
- from twisted.python.failure import Failure
-@@ -200,7 +203,7 @@
- 
- def propertyName(name):
-     property_namespace, property_name = name
--    class PropertyName (davxml.WebDAVEmptyElement):
--        namespace = property_namespace
--        name = property_name
--    return PropertyName()
-+    pname = davxml.WebDAVUnknownElement()
-+    pname.namespace = property_namespace
-+    pname.name = property_name
-+    return pname

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.put.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.put.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.put.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,20 +0,0 @@
-Index: twisted/web2/dav/method/put.py
-===================================================================
---- twisted/web2/dav/method/put.py	(revision 19773)
-+++ twisted/web2/dav/method/put.py	(working copy)
-@@ -34,7 +34,7 @@
- from twisted.web2 import responsecode
- from twisted.web2.http import HTTPError, StatusResponse
- from twisted.web2.dav import davxml
--from twisted.web2.dav.fileop import put
-+from twisted.web2.dav.method import put_common
- from twisted.web2.dav.util import parentForURL
- 
- def preconditions_PUT(self, request):
-@@ -107,4 +107,5 @@
-     # to return a MULTI_STATUS response, which is WebDAV-specific (and PUT is
-     # not).
-     #
--    return put(request.stream, self.fp)
-+    #return put(request.stream, self.fp)
-+    return put_common.storeResource(request, destination=self, destination_uri=request.uri)

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.put_common.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.put_common.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.put_common.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,270 +0,0 @@
-Index: twisted/web2/dav/method/put_common.py
-===================================================================
---- twisted/web2/dav/method/put_common.py	(revision 0)
-+++ twisted/web2/dav/method/put_common.py	(revision 0)
-@@ -0,0 +1,265 @@
-+##
-+# Copyright (c) 2005-2007 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.
-+#
-+# DRI: Cyrus Daboo, cdaboo at apple.com
-+##
-+
-+"""
-+PUT/COPY/MOVE common behavior.
-+"""
-+
-+__version__ = "0.0"
-+
-+__all__ = ["storeCalendarObjectResource"]
-+
-+from twisted.internet.defer import deferredGenerator, maybeDeferred, waitForDeferred
-+from twisted.python import failure, log
-+from twisted.python.filepath import FilePath
-+from twisted.web2 import responsecode
-+from twisted.web2.dav import davxml
-+from twisted.web2.dav.element.base import dav_namespace
-+from twisted.web2.dav.fileop import copy, delete, put
-+from twisted.web2.dav.http import ErrorResponse
-+from twisted.web2.dav.resource import TwistedGETContentMD5
-+from twisted.web2.dav.stream import MD5StreamWrapper
-+from twisted.web2.http import HTTPError
-+from twisted.web2.http_headers import generateContentType
-+from twisted.web2.iweb import IResponse
-+from twisted.web2.stream import MemoryStream
-+
-+def storeResource(
-+    request,
-+    source=None, source_uri=None, data=None,
-+    destination=None, destination_uri=None,
-+    deletesource=False,
-+    depth="0"
-+):
-+    """
-+    Function that does common PUT/COPY/MOVE behaviour.
-+    
-+    @param request:           the L{twisted.web2.server.Request} for the current HTTP request.
-+    @param source:            the L{DAVFile} for the source resource to copy from, or None if source data
-+                              is to be read from the request.
-+    @param source_uri:        the URI for the source resource.
-+    @param data:              a C{str} to copy data from instead of the request stream.
-+    @param destination:       the L{DAVFile} for the destination resource to copy into.
-+    @param destination_uri:   the URI for the destination resource.
-+    @param deletesource:      True if the source resource is to be deleted on successful completion, False otherwise.
-+    @param depth:             a C{str} containing the COPY/MOVE Depth header value.
-+    @return:                  status response.
-+    """
-+    
-+    try:
-+        assert request is not None and destination is not None and destination_uri is not None
-+        assert (source is None) or (source is not None and source_uri is not None)
-+        assert not deletesource or (deletesource and source is not None)
-+    except AssertionError:
-+        log.err("Invalid arguments to storeResource():")
-+        log.err("request=%s\n" % (request,))
-+        log.err("source=%s\n" % (source,))
-+        log.err("source_uri=%s\n" % (source_uri,))
-+        log.err("data=%s\n" % (data,))
-+        log.err("destination=%s\n" % (destination,))
-+        log.err("destination_uri=%s\n" % (destination_uri,))
-+        log.err("deletesource=%s\n" % (deletesource,))
-+        log.err("depth=%s\n" % (depth,))
-+        raise
-+
-+    class RollbackState(object):
-+        """
-+        This class encapsulates the state needed to rollback the entire PUT/COPY/MOVE
-+        transaction, leaving the server state the same as it was before the request was
-+        processed. The DoRollback method will actually execute the rollback operations.
-+        """
-+        
-+        def __init__(self):
-+            self.active = True
-+            self.source_copy = None
-+            self.destination_copy = None
-+            self.destination_created = False
-+            self.source_deleted = False
-+        
-+        def Rollback(self):
-+            """
-+            Rollback the server state. Do not allow this to raise another exception. If
-+            rollback fails then we are going to be left in an awkward state that will need
-+            to be cleaned up eventually.
-+            """
-+            if self.active:
-+                self.active = False
-+                log.err("Rollback: rollback")
-+                try:
-+                    if self.source_copy and self.source_deleted:
-+                        self.source_copy.moveTo(source.fp)
-+                        log.err("Rollback: source restored %s to %s" % (self.source_copy.path, source.fp.path))
-+                        self.source_copy = None
-+                        self.source_deleted = False
-+                    if self.destination_copy:
-+                        destination.fp.remove()
-+                        log.err("Rollback: destination restored %s to %s" % (self.destination_copy.path, destination.fp.path))
-+                        self.destination_copy.moveTo(destination.fp)
-+                        self.destination_copy = None
-+                    elif self.destination_created:
-+                        destination.fp.remove()
-+                        log.err("Rollback: destination removed %s" % (destination.fp.path,))
-+                        self.destination_created = False
-+                except:
-+                    log.err("Rollback: exception caught and not handled: %s" % failure.Failure())
-+
-+        def Commit(self):
-+            """
-+            Commit the resource changes by wiping the rollback state.
-+            """
-+            if self.active:
-+                log.err("Rollback: commit")
-+                self.active = False
-+                if self.source_copy:
-+                    self.source_copy.remove()
-+                    log.err("Rollback: removed source backup %s" % (self.source_copy.path,))
-+                    self.source_copy = None
-+                if self.destination_copy:
-+                    self.destination_copy.remove()
-+                    log.err("Rollback: removed destination backup %s" % (self.destination_copy.path,))
-+                    self.destination_copy = None
-+                self.destination_created = False
-+                self.source_deleted = False
-+    
-+    rollback = RollbackState()
-+
-+    try:
-+        """
-+        Handle validation operations here.
-+        """
-+
-+        """
-+        Handle rollback setup here.
-+        """
-+
-+        # Do quota checks on destination and source before we start messing with adding other files
-+        destquota = waitForDeferred(destination.quota(request))
-+        yield destquota
-+        destquota = destquota.getResult()
-+        if destquota is not None and destination.exists():
-+            old_dest_size = waitForDeferred(destination.quotaSize(request))
-+            yield old_dest_size
-+            old_dest_size = old_dest_size.getResult()
-+        else:
-+            old_dest_size = 0
-+            
-+        if source is not None:
-+            sourcequota = waitForDeferred(source.quota(request))
-+            yield sourcequota
-+            sourcequota = sourcequota.getResult()
-+            if sourcequota is not None and source.exists():
-+                old_source_size = waitForDeferred(source.quotaSize(request))
-+                yield old_source_size
-+                old_source_size = old_source_size.getResult()
-+            else:
-+                old_source_size = 0
-+        else:
-+            sourcequota = None
-+            old_source_size = 0
-+
-+        # We may need to restore the original resource data if the PUT/COPY/MOVE fails,
-+        # so rename the original file in case we need to rollback.
-+        overwrite = destination.exists()
-+        if overwrite:
-+            rollback.destination_copy = FilePath(destination.fp.path)
-+            rollback.destination_copy.path += ".rollback"
-+            destination.fp.copyTo(rollback.destination_copy)
-+        else:
-+            rollback.destination_created = True
-+
-+        if deletesource:
-+            rollback.source_copy = FilePath(source.fp.path)
-+            rollback.source_copy.path += ".rollback"
-+            source.fp.copyTo(rollback.source_copy)
-+    
-+        """
-+        Handle actual store operations here.
-+        """
-+
-+        # Do put or copy based on whether source exists
-+        if source is not None:
-+            response = maybeDeferred(copy, source.fp, destination.fp, destination_uri, depth)
-+        else:
-+            datastream = request.stream
-+            if data is not None:
-+                datastream = MemoryStream(data)
-+            md5 = MD5StreamWrapper(datastream)
-+            response = maybeDeferred(put, md5, destination.fp)
-+
-+        response = waitForDeferred(response)
-+        yield response
-+        response = response.getResult()
-+
-+        # Update the MD5 value on the resource
-+        if source is not None:
-+            # Copy MD5 value from source to destination
-+            if source.hasDeadProperty(TwistedGETContentMD5):
-+                md5 = source.readDeadProperty(TwistedGETContentMD5)
-+                destination.writeDeadProperty(md5)
-+        else:
-+            # Finish MD5 calc and write dead property
-+            md5.close()
-+            md5 = md5.getMD5()
-+            destination.writeDeadProperty(TwistedGETContentMD5.fromString(md5))
-+
-+        # Update the content-type value on the resource if it is not been copied or moved
-+        if source is None:
-+            content_type = request.headers.getHeader("content-type")
-+            if content_type is not None:
-+                destination.writeDeadProperty(davxml.GETContentType.fromString(generateContentType(content_type)))
-+
-+        response = IResponse(response)
-+        
-+        # Do quota check on destination
-+        if destquota is not None:
-+            # Get size of new/old resources
-+            new_dest_size = waitForDeferred(destination.quotaSize(request))
-+            yield new_dest_size
-+            new_dest_size = new_dest_size.getResult()
-+            diff_size = new_dest_size - old_dest_size
-+            if diff_size >= destquota[0]:
-+                log.err("Over quota: available %d, need %d" % (destquota[0], diff_size))
-+                raise HTTPError(ErrorResponse(responsecode.INSUFFICIENT_STORAGE_SPACE, (dav_namespace, "quota-not-exceeded")))
-+            d = waitForDeferred(destination.quotaSizeAdjust(request, diff_size))
-+            yield d
-+            d.getResult()
-+
-+        if deletesource:
-+            # Delete the source resource
-+            if sourcequota is not None:
-+                delete_size = 0 - old_source_size
-+                d = waitForDeferred(source.quotaSizeAdjust(request, delete_size))
-+                yield d
-+                d.getResult()
-+
-+            delete(source_uri, source.fp, depth)
-+            rollback.source_deleted = True
-+
-+        # Can now commit changes and forget the rollback details
-+        rollback.Commit()
-+
-+        yield response
-+        return
-+        
-+    except:
-+        # Roll back changes to original server state. Note this may do nothing
-+        # if the rollback has already ocurred or changes already committed.
-+        rollback.Rollback()
-+        raise
-+
-+storeResource = deferredGenerator(storeResource)

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.report.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.report.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.report.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,33 +0,0 @@
-Index: twisted/web2/dav/method/report.py
-===================================================================
---- twisted/web2/dav/method/report.py	(revision 19773)
-+++ twisted/web2/dav/method/report.py	(working copy)
-@@ -27,7 +27,11 @@
- WebDAV REPORT method
- """
- 
--__all__ = ["http_REPORT"]
-+__all__ = [
-+    "max_number_of_matches",
-+    "NumberOfMatchesWithinLimits",
-+    "http_REPORT",
-+]
- 
- import string
- 
-@@ -43,7 +47,14 @@
- max_number_of_matches = 500
- 
- class NumberOfMatchesWithinLimits(Exception):
--    pass
-+    
-+    def __init__(self, limit):
-+        
-+        super(NumberOfMatchesWithinLimits, self).__init__()
-+        self.limit = limit
-+        
-+    def maxLimit(self):
-+        return self.limit
- 
- def http_REPORT(self, request):
-     """

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.report_acl_principal_prop_set.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.report_acl_principal_prop_set.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.report_acl_principal_prop_set.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,22 +0,0 @@
-Index: twisted/web2/dav/method/report_acl_principal_prop_set.py
-===================================================================
---- twisted/web2/dav/method/report_acl_principal_prop_set.py	(revision 19773)
-+++ twisted/web2/dav/method/report_acl_principal_prop_set.py	(working copy)
-@@ -103,7 +103,7 @@
-             # Check size of results is within limit
-             matchcount += 1
-             if matchcount > max_number_of_matches:
--                raise NumberOfMatchesWithinLimits
-+                raise NumberOfMatchesWithinLimits(max_number_of_matches)
- 
-             resource = waitForDeferred(request.locateResource(str(principal)))
-             yield resource
-@@ -144,7 +144,7 @@
-         log.err("Too many matching components")
-         raise HTTPError(ErrorResponse(
-             responsecode.FORBIDDEN,
--            (dav_namespace, "number-of-matches-within-limits")
-+            davxml.NumberOfMatchesWithinLimits()
-         ))
- 
-     yield MultiStatusResponse(responses)

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.report_expand.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.report_expand.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.report_expand.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,214 +0,0 @@
-Index: twisted/web2/dav/method/report_expand.py
-===================================================================
---- twisted/web2/dav/method/report_expand.py	(revision 19773)
-+++ twisted/web2/dav/method/report_expand.py	(working copy)
-@@ -1,6 +1,6 @@
- # -*- test-case-name: twisted.web2.dav.test.test_report_expand -*-
- ##
--# Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
-+# Copyright (c) 2005-2008 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
-@@ -19,8 +19,6 @@
- # 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.
--#
--# DRI: Wilfredo Sanchez, wsanchez at apple.com
- ##
- 
- """
-@@ -29,86 +27,143 @@
- 
- __all__ = ["report_DAV__expand_property"]
- 
-+from twisted.internet.defer import inlineCallbacks, returnValue
- from twisted.python import log
- from twisted.python.failure import Failure
--from twisted.internet.defer import deferredGenerator, waitForDeferred
- from twisted.web2 import responsecode
- from twisted.web2.dav import davxml
--from twisted.web2.dav.http import statusForFailure
- from twisted.web2.dav.davxml import dav_namespace
-+from twisted.web2.dav.http import statusForFailure, MultiStatusResponse
-+from twisted.web2.dav.method import prop_common
-+from twisted.web2.dav.method.propfind import propertyName
-+from twisted.web2.dav.resource import AccessDeniedError
-+from twisted.web2.dav.util import parentForURL
-+from twisted.web2.http import HTTPError, StatusResponse
- 
-+ at inlineCallbacks
- def report_DAV__expand_property(self, request, expand_property):
-     """
-     Generate an expand-property REPORT. (RFC 3253, section 3.8)
-+    
-+    TODO: for simplicity we will only support one level of expansion.
-     """
--    # FIXME: Handle depth header
--
-+    # Verify root element
-     if not isinstance(expand_property, davxml.ExpandProperty):
-         raise ValueError("%s expected as root element, not %s."
-                          % (davxml.ExpandProperty.sname(), expand_property.sname()))
- 
-+    # Only handle Depth: 0
-+    depth = request.headers.getHeader("depth", "0")
-+    if depth != "0":
-+        log.err("Non-zero depth is not allowed: %s" % (depth,))
-+        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Depth %s not allowed" % (depth,)))
-+    
-     #
--    # Expand DAV:allprop
-+    # Get top level properties to expand and make sure we only have one level
-     #
-     properties = {}
- 
-     for property in expand_property.children:
--        namespace = property.getAttribute("namespace")
--        name      = property.getAttribute("name")
-+        namespace = property.attributes.get("namespace", dav_namespace)
-+        name      = property.attributes.get("name", "")
-+        
-+        # Make sure children have no children
-+        props_to_find = []
-+        for child in property.children:
-+            if child.children:
-+                log.err("expand-property REPORT only supports single level expansion")
-+                raise HTTPError(StatusResponse(
-+                    responsecode.NOT_IMPLEMENTED,
-+                    "expand-property REPORT only supports single level expansion"
-+                ))
-+            child_namespace = child.attributes.get("namespace", dav_namespace)
-+            child_name      = child.attributes.get("name", "")
-+            props_to_find.append((child_namespace, child_name))
- 
--        if not namespace: namespace = dav_namespace
-+        properties[(namespace, name)] = props_to_find
- 
--        if (namespace, name) == (dav_namespace, "allprop"):
--            all_properties = waitForDeferred(self.listAllProp(request))
--            yield all_properties
--            all_properties = all_properties.getResult()
--
--            for all_property in all_properties:
--                properties[all_property.qname()] = property
--        else:
--            properties[(namespace, name)] = property
--
-     #
--    # Look up the requested properties
-+    # Generate the expanded responses status for each top-level property
-     #
-     properties_by_status = {
-         responsecode.OK        : [],
-         responsecode.NOT_FOUND : [],
-     }
-+    
-+    filteredaces = None
-+    lastParent = None
- 
--    for property in properties:
--        my_properties = waitForDeferred(self.listProperties(request))
--        yield my_properties
--        my_properties = my_properties.getResult()
-+    for qname in properties.iterkeys():
-+        try:
-+            prop = (yield self.readProperty(qname, request))
-+            
-+            # Form the PROPFIND-style DAV:prop element we need later
-+            props_to_return = davxml.PropertyContainer(*properties[qname])
- 
--        if property in my_properties:
--            try:
--                value = waitForDeferred(self.readProperty(property, request))
--                yield value
--                value = value.getResult()
-+            # Now dereference any HRefs
-+            responses = []
-+            for href in prop.children:
-+                if isinstance(href, davxml.HRef):
-+                    
-+                    # Locate the Href resource and its parent
-+                    resource_uri = str(href)
-+                    child = (yield request.locateResource(resource_uri))
-+    
-+                    if not child or not child.exists():
-+                        responses.append(davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.NOT_FOUND)))
-+                        continue
-+                    parent = (yield request.locateResource(parentForURL(resource_uri)))
-+    
-+                    # Check privileges on parent - must have at least DAV:read
-+                    try:
-+                        yield parent.checkPrivileges(request, (davxml.Read(),))
-+                    except AccessDeniedError:
-+                        responses.append(davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.FORBIDDEN)))
-+                        continue
-+                    
-+                    # Cache the last parent's inherited aces for checkPrivileges optimization
-+                    if lastParent != parent:
-+                        lastParent = parent
-+                
-+                        # Do some optimisation of access control calculation by determining any inherited ACLs outside of
-+                        # the child resource loop and supply those to the checkPrivileges on each child.
-+                        filteredaces = (yield parent.inheritedACEsforChildren(request))
- 
--                if isinstance(value, davxml.HRef):
--                    raise NotImplementedError()
--                else:
--                    raise NotImplementedError()
--            except:
--                f = Failure()
-+                    # Check privileges - must have at least DAV:read
-+                    try:
-+                        yield child.checkPrivileges(request, (davxml.Read(),), inherited_aces=filteredaces)
-+                    except AccessDeniedError:
-+                        responses.append(davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.FORBIDDEN)))
-+                        continue
-+            
-+                    # Now retrieve all the requested properties on the HRef resource
-+                    yield prop_common.responseForHref(
-+                        request,
-+                        responses,
-+                        href,
-+                        child,
-+                        prop_common.propertyListForResource,
-+                        props_to_return,
-+                    )
-+            
-+            prop.children = responses
-+            properties_by_status[responsecode.OK].append(prop)
-+        except:
-+            f = Failure()
- 
--                log.err("Error reading property %r for resource %s: %s"
--                        % (property, self, f.value))
-+            log.err("Error reading property %r for resource %s: %s" % (qname, request.uri, f.value))
- 
--                status = statusForFailure(f, "getting property: %s" % (property,))
--                if status not in properties_by_status:
--                    properties_by_status[status] = []
-+            status = statusForFailure(f, "getting property: %s" % (qname,))
-+            if status not in properties_by_status: properties_by_status[status] = []
-+            properties_by_status[status].append(propertyName(qname))
- 
--                raise NotImplementedError()
-+    # Build the overall response
-+    propstats = [
-+        davxml.PropertyStatus(
-+            davxml.PropertyContainer(*properties_by_status[status]),
-+            davxml.Status.fromResponseCode(status)
-+        )
-+        for status in properties_by_status if properties_by_status[status]
-+    ]
- 
--                #properties_by_status[status].append(
--                #    ____propertyName(property)
--                #)
--        else:
--            properties_by_status[responsecode.NOT_FOUND].append(property)
--
--    raise NotImplementedError()
--
--report_DAV__expand_property = deferredGenerator(report_DAV__expand_property)
-+    returnValue(MultiStatusResponse((davxml.PropertyStatusResponse(davxml.HRef(request.uri), *propstats),)))

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.report_principal_match.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.report_principal_match.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.report_principal_match.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,150 +0,0 @@
-Index: twisted/web2/dav/method/report_principal_match.py
-===================================================================
---- twisted/web2/dav/method/report_principal_match.py	(revision 19773)
-+++ twisted/web2/dav/method/report_principal_match.py	(working copy)
-@@ -89,40 +89,64 @@
-         responses = []
-         matchcount = 0
- 
--        selfPrincipal = self.currentPrincipal(request).children[0]
-+        selfPrincipalURL = self.currentPrincipal(request).children[0]
- 
--        # Do some optimisation of access control calculation by determining any inherited ACLs outside of
--        # the child resource loop and supply those to the checkPrivileges on each child.
--        filteredaces = waitForDeferred(self.inheritedACEsforChildren(request))
--        yield filteredaces
--        filteredaces = filteredaces.getResult()
--    
--        children = []
--        d = waitForDeferred(self.findChildren("infinity", request, lambda x, y: children.append((x,y)),
--                                              privileges=(davxml.Read(),), inherited_aces=filteredaces))
--        yield d
--        d.getResult()
--
-         if lookForPrincipals:
- 
--            for child, uri in children:
--                if isPrincipalResource(child) and child.principalMatch(selfPrincipal):
--                    # Check size of results is within limit
--                    matchcount += 1
--                    if matchcount > max_number_of_matches:
--                        raise NumberOfMatchesWithinLimits
-+            # Find the set of principals that represent "self".
-+            
-+            # First add "self"
-+            principal = waitForDeferred(request.locateResource(str(selfPrincipalURL)))
-+            yield principal
-+            principal = principal.getResult()
-+            selfItems = [principal,]
-+            
-+            # Get group memberships for "self" and add each of those
-+            d = waitForDeferred(principal.groupMemberships())
-+            yield d
-+            memberships = d.getResult()
-+            selfItems.extend(memberships)
-+            
-+            # Now add each principal found to the response provided the principal resource is a child of
-+            # the current resource.
-+            for principal in selfItems:
-+                # Get all the URIs that point to the principal resource
-+                # FIXME: making the assumption that the principalURL() is the URL of the resource we found
-+                principal_uris = [principal.principalURL()]
-+                principal_uris.extend(principal.alternateURIs())
-+                
-+                # Compare each one to the request URI and return at most one that matches
-+                for uri in principal_uris:
-+                    if uri.startswith(request.uri):
-+                        # Check size of results is within limit
-+                        matchcount += 1
-+                        if matchcount > max_number_of_matches:
-+                            raise NumberOfMatchesWithinLimits(max_number_of_matches)
-+        
-+                        d = waitForDeferred(prop_common.responseForHref(
-+                            request,
-+                            responses,
-+                            davxml.HRef.fromString(uri),
-+                            principal,
-+                            propertiesForResource,
-+                            propElement
-+                        ))
-+                        yield d
-+                        d.getResult()
-+                        break
-+        else:
-+            # Do some optimisation of access control calculation by determining any inherited ACLs outside of
-+            # the child resource loop and supply those to the checkPrivileges on each child.
-+            filteredaces = waitForDeferred(self.inheritedACEsforChildren(request))
-+            yield filteredaces
-+            filteredaces = filteredaces.getResult()
-+        
-+            children = []
-+            d = waitForDeferred(self.findChildren("infinity", request, lambda x, y: children.append((x,y)),
-+                                                  privileges=(davxml.Read(),), inherited_aces=filteredaces))
-+            yield d
-+            d.getResult()
- 
--                    d = waitForDeferred(prop_common.responseForHref(
--                        request,
--                        responses,
--                        davxml.HRef.fromString(uri),
--                        child,
--                        propertiesForResource,
--                        propElement
--                    ))
--                    yield d
--                    d.getResult()
--        else:
-             for child, uri in children:
-                 # Try to read the requested property from this resource
-                 try:
-@@ -137,22 +161,26 @@
-                         yield principal
-                         principal = principal.getResult()
- 
--                        if principal and isPrincipalResource(principal) and principal.principalMatch(selfPrincipal):
--                            # Check size of results is within limit
--                            matchcount += 1
--                            if matchcount > max_number_of_matches:
--                                raise NumberOfMatchesWithinLimits
--
--                            d = waitForDeferred(prop_common.responseForHref(
--                                request,
--                                responses,
--                                davxml.HRef.fromString(uri),
--                                child,
--                                propertiesForResource,
--                                propElement
--                            ))
-+                        if principal and isPrincipalResource(principal):
-+                            d = waitForDeferred(principal.principalMatch(selfPrincipalURL))
-                             yield d
--                            d.getResult()
-+                            matched = d.getResult()
-+                            if matched:
-+                                # Check size of results is within limit
-+                                matchcount += 1
-+                                if matchcount > max_number_of_matches:
-+                                    raise NumberOfMatchesWithinLimits(max_number_of_matches)
-+    
-+                                d = waitForDeferred(prop_common.responseForHref(
-+                                    request,
-+                                    responses,
-+                                    davxml.HRef.fromString(uri),
-+                                    child,
-+                                    propertiesForResource,
-+                                    propElement
-+                                ))
-+                                yield d
-+                                d.getResult()
-                 except HTTPError:
-                     # Just ignore a failure to access the property. We treat this like a property that does not exist
-                     # or does not match the principal.
-@@ -162,7 +190,7 @@
-         log.err("Too many matching components in principal-match report")
-         raise HTTPError(ErrorResponse(
-             responsecode.FORBIDDEN,
--            (dav_namespace, "number-of-matches-within-limits")
-+            davxml.NumberOfMatchesWithinLimits()
-         ))
- 
-     yield MultiStatusResponse(responses)

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.report_principal_property_search.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.report_principal_property_search.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.method.report_principal_property_search.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,22 +0,0 @@
-Index: twisted/web2/dav/method/report_principal_property_search.py
-===================================================================
---- twisted/web2/dav/method/report_principal_property_search.py	(revision 19773)
-+++ twisted/web2/dav/method/report_principal_property_search.py	(working copy)
-@@ -166,7 +166,7 @@
-                         # Check size of results is within limit
-                         matchcount += 1
-                         if matchcount > max_number_of_matches:
--                            raise NumberOfMatchesWithinLimits
-+                            raise NumberOfMatchesWithinLimits(max_number_of_matches)
-     
-                         d = waitForDeferred(prop_common.responseForHref(
-                             request,
-@@ -183,7 +183,7 @@
-         log.err("Too many matching components in prinicpal-property-search report")
-         raise HTTPError(ErrorResponse(
-             responsecode.FORBIDDEN,
--            (dav_namespace, "number-of-matches-within-limits")
-+            davxml.NumberOfMatchesWithinLimits()
-         ))
- 
-     yield MultiStatusResponse(responses)

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.resource.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.resource.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.resource.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,1238 +0,0 @@
-Index: twisted/web2/dav/resource.py
-===================================================================
---- twisted/web2/dav/resource.py	(revision 19773)
-+++ twisted/web2/dav/resource.py	(working copy)
-@@ -31,20 +31,31 @@
-     "DAVResource",
-     "DAVLeafResource",
-     "DAVPrincipalResource",
-+    "DAVPrincipalCollectionResource",
-     "AccessDeniedError",
-     "isPrincipalResource",
-     "TwistedACLInheritable",
-+    "TwistedGETContentMD5",
-+    "TwistedQuotaRootProperty",
-     "allACL",
-     "readonlyACL",
-     "davPrivilegeSet",
-     "unauthenticatedPrincipal",
- ]
- 
- import urllib
-+import __builtin__
-+if not hasattr(__builtin__, "set"):
-+    import sets.Set as set
-+if not hasattr(__builtin__, "frozenset"):
-+    import sets.ImmutableSet as frozenset
- 
- from zope.interface import implements
- from twisted.python import log
--from twisted.internet.defer import Deferred, maybeDeferred, succeed
-+from twisted.python.failure import Failure
-+from twisted.cred.error import LoginFailed, UnauthorizedLogin
-+from twisted.internet.defer import Deferred, maybeDeferred, succeed,\
-+    inlineCallbacks, returnValue
- from twisted.internet.defer import waitForDeferred, deferredGenerator
- from twisted.internet import reactor
- from twisted.web2 import responsecode
-@@ -52,12 +62,13 @@
- from twisted.web2.http_headers import generateContentType
- from twisted.web2.iweb import IResponse
- from twisted.web2.resource import LeafResource
-+from twisted.web2.server import NoURLForResourceError
- from twisted.web2.static import MetaDataMixin, StaticRenderMixin
- from twisted.web2.auth.wrapper import UnauthorizedResponse
- from twisted.web2.dav import davxml
- from twisted.web2.dav.davxml import dav_namespace, lookupElement
- from twisted.web2.dav.davxml import twisted_dav_namespace, twisted_private_namespace
--from twisted.web2.dav.idav import IDAVResource, IDAVPrincipalResource
-+from twisted.web2.dav.idav import IDAVResource, IDAVPrincipalResource, IDAVPrincipalCollectionResource
- from twisted.web2.dav.http import NeedPrivilegesResponse
- from twisted.web2.dav.noneprops import NonePropertyStore
- from twisted.web2.dav.util import unimplemented, parentForURL, joinURL
-@@ -126,10 +137,13 @@
-        #(dav_namespace, "group"                     ), # RFC 3744, section 5.2
-         (dav_namespace, "supported-privilege-set"   ), # RFC 3744, section 5.3
-         (dav_namespace, "current-user-privilege-set"), # RFC 3744, section 5.4
-+        (dav_namespace, "current-user-principal"    ), # draft-sanchez-webdav-current-principal
-         (dav_namespace, "acl"                       ), # RFC 3744, section 5.5
-         (dav_namespace, "acl-restrictions"          ), # RFC 3744, section 5.6
-         (dav_namespace, "inherited-acl-set"         ), # RFC 3744, section 5.7
-         (dav_namespace, "principal-collection-set"  ), # RFC 3744, section 5.8
-+        (dav_namespace, "quota-available-bytes"     ), # RFC 4331, section 3
-+        (dav_namespace, "quota-used-bytes"          ), # RFC 4331, section 4
- 
-         (twisted_dav_namespace, "resource-class"),
-     )
-@@ -166,6 +180,14 @@
-         if qname[0] == twisted_private_namespace:
-             return succeed(False)
- 
-+        # Need to special case the dynamic live properties
-+        namespace, name = qname
-+        if namespace == dav_namespace:
-+            if name in ("quota-available-bytes", "quota-used-bytes"):
-+                d = self.hasQuota(request)
-+                d.addCallback(lambda result: result)
-+                return d
-+        
-         return succeed(qname in self.liveProperties or self.deadProperties().contains(qname))
- 
-     def readProperty(self, property, request):
-@@ -201,7 +223,6 @@
-                     mimeType = self.contentType()
-                     if mimeType is None:
-                         return None
--                    mimeType.params = None # WebDAV getcontenttype property does not include parameters
-                     return davxml.GETContentType(generateContentType(mimeType))
- 
-                 if name == "getcontentlength":
-@@ -239,8 +260,10 @@
-                     )
- 
-                 if name == "supported-report-set":
--                    supported = [davxml.SupportedReport(report,) for report in self.supportedReports()]
--                    return davxml.SupportedReportSet(*supported)
-+                    return davxml.SupportedReportSet(*[
-+                        davxml.SupportedReport(report,)
-+                        for report in self.supportedReports()
-+                    ])
- 
-                 if name == "supported-privilege-set":
-                     return self.supportedPrivileges(request)
-@@ -252,9 +275,10 @@
-                     return davxml.InheritedACLSet(*self.inheritedACLSet())
- 
-                 if name == "principal-collection-set":
--                    d = self.principalCollections(request)
--                    d.addCallback(lambda collections: davxml.PrincipalCollectionSet(*collections))
--                    return d
-+                    return davxml.PrincipalCollectionSet(*[
-+                        davxml.HRef(principalCollection.principalCollectionURL())
-+                        for principalCollection in self.principalCollections()
-+                    ])
- 
-                 def ifAllowed(privileges, callback):
-                     def onError(failure):
-@@ -286,7 +310,36 @@
-                         d.addCallback(gotACL)
-                         return d
-                     return ifAllowed((davxml.ReadACL(),), callback)
-+                
-+                if name == "current-user-principal":
-+                    return davxml.CurrentUserPrincipal(self.currentPrincipal(request).children[0])
- 
-+                if name == "quota-available-bytes":
-+                    def callback(qvalue):
-+                        if qvalue is None:
-+                            raise HTTPError(StatusResponse(
-+                                responsecode.NOT_FOUND,
-+                                "Property %s does not exist." % (sname,)
-+                            ))
-+                        else:
-+                            return davxml.QuotaAvailableBytes(str(qvalue[0]))
-+                    d = self.quota(request)
-+                    d.addCallback(callback)
-+                    return d
-+
-+                if name == "quota-used-bytes":
-+                    def callback(qvalue):
-+                        if qvalue is None:
-+                            raise HTTPError(StatusResponse(
-+                                responsecode.NOT_FOUND,
-+                                "Property %s does not exist." % (sname,)
-+                            ))
-+                        else:
-+                            return davxml.QuotaUsedBytes(str(qvalue[1]))
-+                    d = self.quota(request)
-+                    d.addCallback(callback)
-+                    return d
-+
-             elif namespace == twisted_dav_namespace:
-                 if name == "resource-class":
-                     class ResourceClass (davxml.WebDAVTextElement):
-@@ -309,10 +362,7 @@
-         """
-         See L{IDAVResource.writeProperty}.
-         """
--        assert (
--            isinstance(property, davxml.WebDAVElement),
--            "Not a property: %r" % (property,)
--        )
-+        assert isinstance(property, davxml.WebDAVElement), "Not a property: %r" % (property,)
- 
-         def defer():
-             if property.protected:
-@@ -363,15 +413,28 @@
-         """
-         See L{IDAVResource.listProperties}.
-         """
--        # FIXME: A set would be better here, that that's a python 2.4+ feature.
--        qnames = list(self.liveProperties)
-+        qnames = set(self.liveProperties)
- 
-+        # Add dynamic live properties that exist
-+        dynamicLiveProperties = (
-+            (dav_namespace, "quota-available-bytes"     ),
-+            (dav_namespace, "quota-used-bytes"          ),
-+        )
-+        for dqname in dynamicLiveProperties:
-+            has = waitForDeferred(self.hasProperty(dqname, request))
-+            yield has
-+            has = has.getResult()
-+            if not has:
-+                qnames.remove(dqname)
-+
-         for qname in self.deadProperties().list():
-             if (qname not in qnames) and (qname[0] != twisted_private_namespace):
--                qnames.append(qname)
-+                qnames.add(qname)
- 
--        return succeed(qnames)
-+        yield qnames
- 
-+    listProperties = deferredGenerator(listProperties)
-+
-     def listAllprop(self, request):
-         """
-         Some DAV properties should not be returned to a C{DAV:allprop} query.
-@@ -465,8 +528,22 @@
-             return super(DAVPropertyMixIn, self).displayName()
- 
- class DAVResource (DAVPropertyMixIn, StaticRenderMixin):
-+    """
-+    WebDAV resource.
-+    """
-     implements(IDAVResource)
- 
-+    def __init__(self, principalCollections=None):
-+        """
-+        @param principalCollections: an iterable of L{IDAVPrincipalCollectionResource}s
-+            which contain principals to be used in ACLs for this resource.
-+        """
-+        if principalCollections is not None:
-+            self._principalCollections = frozenset([
-+                IDAVPrincipalCollectionResource(principalCollection)
-+                for principalCollection in principalCollections
-+            ])
-+
-     ##
-     # DAV
-     ##
-@@ -553,69 +630,65 @@
-     def supportedReports(self):
-         """
-         See L{IDAVResource.supportedReports}.
--        This implementation lists the three main ACL reports.
-+        This implementation lists the three main ACL reports and expand-property.
-         """
-         result = []
-         result.append(davxml.Report(davxml.ACLPrincipalPropSet(),))
-         result.append(davxml.Report(davxml.PrincipalMatch(),))
-         result.append(davxml.Report(davxml.PrincipalPropertySearch(),))
-+        result.append(davxml.Report(davxml.ExpandProperty(),))
-         return result
- 
-     ##
-     # Authentication
-     ##
- 
-+    @inlineCallbacks
-     def authorize(self, request, privileges, recurse=False):
-         """
-         See L{IDAVResource.authorize}.
-         """
--        def onError(failure):
--            log.err("Invalid authentication details: %s" % (request,))
--            raise HTTPError(UnauthorizedResponse(
-+
-+        try:
-+            yield self.authenticate(request)
-+        except (UnauthorizedLogin, LoginFailed), e:
-+            log.msg("Authentication failed: %s" % (e,))
-+            response = (yield UnauthorizedResponse.makeResponse(
-                 request.credentialFactories,
-                 request.remoteAddr
-             ))
-+            raise HTTPError(response)
- 
--        def onAuth(result):
--            def onErrors(failure):
--                failure.trap(AccessDeniedError)
--                
--                # If we were unauthorized to start with (no Authorization header from client) then
--                # we should return an unauthorized response instead to force the client to login if it can
--                if request.user == davxml.Principal(davxml.Unauthenticated()):
--                    response = UnauthorizedResponse(request.credentialFactories,
--                                                    request.remoteAddr)
--                else:
--                    response = NeedPrivilegesResponse(request.uri,
--                                                      failure.value.errors)
--                #
--                # We're not adding the headers here because this response
--                # class is supposed to be a FORBIDDEN status code and
--                # "Authorization will not help" according to RFC2616
--                #
--                raise HTTPError(response)
-+        try:
-+            yield self.checkPrivileges(request, privileges, recurse)
-+        except AccessDeniedError, e:
-+            # If we were unauthenticated to start with (no Authorization header from client) then
-+            # we should return an unauthorized response instead to force the client to login if it can
-+            if request.authnUser == davxml.Principal(davxml.Unauthenticated()):
-+                response = (yield UnauthorizedResponse.makeResponse(
-+                    request.credentialFactories,
-+                    request.remoteAddr
-+                ))
-+            else:
-+                response = NeedPrivilegesResponse(request.uri, e.errors)
-+            #
-+            # We're not adding the headers here because this response
-+            # class is supposed to be a FORBIDDEN status code and
-+            # "Authorization will not help" according to RFC2616
-+            #
-+            raise HTTPError(response)
- 
--            d = self.checkPrivileges(request, privileges, recurse)
--            d.addErrback(onErrors)
--            return d
--
--        d = maybeDeferred(self.authenticate, request)
--        d.addCallbacks(onAuth, onError)
--
--        return d
--
-+    @inlineCallbacks
-     def authenticate(self, request):
--        def loginSuccess(result):
--            request.user = result[1]
--            return request.user
- 
-         if not (
-             hasattr(request, 'portal') and 
-             hasattr(request, 'credentialFactories') and
-             hasattr(request, 'loginInterfaces')
-         ):
--            request.user = davxml.Principal(davxml.Unauthenticated())
--            return request.user
-+            request.authnUser = davxml.Principal(davxml.Unauthenticated())
-+            request.authzUser = davxml.Principal(davxml.Unauthenticated())
-+            returnValue((request.authnUser, request.authzUser,))
- 
-         authHeader = request.headers.getHeader('authorization')
- 
-@@ -623,31 +696,32 @@
-             if authHeader[0] not in request.credentialFactories:
-                 log.err("Client authentication scheme %s is not provided by server %s"
-                         % (authHeader[0], request.credentialFactories.keys()))
--                raise HTTPError(responsecode.FORBIDDEN)
-+
-+                response = (yield UnauthorizedResponse.makeResponse(
-+                    request.credentialFactories,
-+                    request.remoteAddr
-+                ))
-+                raise HTTPError(response)
-             else:
-                 factory = request.credentialFactories[authHeader[0]]
- 
--                creds = factory.decode(authHeader[1], request)
-+                creds = (yield factory.decode(authHeader[1], request))
- 
-                 # Try to match principals in each principal collection on the resource
--                def gotDetails(details):
--                    principal = IDAVPrincipalResource(details[0])
--                    principalURI = details[1]
--                    return PrincipalCredentials(principal, principalURI, creds)
-+                authnPrincipal, authzPrincipal = (yield self.principalsForAuthID(request, creds.username))
-+                authnPrincipal = IDAVPrincipalResource(authnPrincipal)
-+                authzPrincipal = IDAVPrincipalResource(authzPrincipal)
- 
--                def login(pcreds):
--                    d = request.portal.login(pcreds, None, *request.loginInterfaces)
--                    d.addCallback(loginSuccess)
-+                pcreds = PrincipalCredentials(authnPrincipal, authzPrincipal, creds)
- 
--                    return d
--
--                d = self.findPrincipalForAuthID(request, creds.username)
--                d.addCallback(gotDetails).addCallback(login)
--
--                return d
-+                result = (yield request.portal.login(pcreds, None, *request.loginInterfaces))
-+                request.authnUser = result[1]
-+                request.authzUser = result[2]
-+                returnValue((request.authnUser, request.authzUser,))
-         else:
--            request.user = davxml.Principal(davxml.Unauthenticated())
--            return request.user
-+            request.authnUser = davxml.Principal(davxml.Unauthenticated())
-+            request.authzUser = davxml.Principal(davxml.Unauthenticated())
-+            returnValue((request.authnUser, request.authzUser,))
- 
-     ##
-     # ACL
-@@ -656,49 +730,23 @@
-     def currentPrincipal(self, request):
-         """
-         @param request: the request being processed.
--        @return: the current principal, as derived from the given request.
-+        @return: the current authorized principal, as derived from the given request.
-         """
--        if hasattr(request, "user"):
--            return request.user
-+        if hasattr(request, "authzUser"):
-+            return request.authzUser
-         else:
-             return unauthenticatedPrincipal
- 
--    def principalCollections(self, request):
-+    def principalCollections(self):
-         """
-         See L{IDAVResource.accessControlList}.
--
--        This implementation tries to read the L{davxml.PrincipalCollectionSet}
--        from the dead property store of this resource and uses that. If not
--        present on this resource, it tries to get it from the parent, unless it
--        is the root or has no parent.
-         """
--        try:
--            principalCollections = self.readDeadProperty(davxml.PrincipalCollectionSet).childrenOfType(davxml.HRef)
--        except HTTPError, e:
--            if e.response.code != responsecode.NOT_FOUND:
--                raise
-+        if hasattr(self, "_principalCollections"):
-+            return self._principalCollections
-+        else:
-+            return ()
- 
--            principalCollections = []
--
--            # Try the parent
--            myURL = request.urlForResource(self)
--            if myURL != "/":
--                parentURL = parentForURL(myURL)
--
--                parent = waitForDeferred(request.locateResource(parentURL))
--                yield parent
--                parent = parent.getResult()
--
--                if parent:
--                    principalCollections = waitForDeferred(parent.principalCollections(request))
--                    yield principalCollections
--                    principalCollections = principalCollections.getResult()
--
--        yield principalCollections
--
--    principalCollections = deferredGenerator(principalCollections)
--
--    def defaultAccessControlList(self):
-+    def defaultRootAccessControlList(self):
-         """
-         @return: the L{davxml.ACL} element containing the default access control
-             list for this resource.
-@@ -710,6 +758,17 @@
-         #
-         return readonlyACL
- 
-+    def defaultAccessControlList(self):
-+        """
-+        @return: the L{davxml.ACL} element containing the default access control
-+            list for this resource.
-+        """
-+        #
-+        # The default behaviour is no ACL; we should inherrit from the parent
-+        # collection.
-+        #
-+        return davxml.ACL()
-+
-     def setAccessControlList(self, acl):
-         """
-         See L{IDAVResource.setAccessControlList}.
-@@ -748,13 +807,16 @@
-         # 10. Verify that new acl is not in conflict with itself
-         # 11. Update acl on the resource
- 
--        old_acl = waitForDeferred(self.accessControlList(request))
-+        # Get the current access control list, preserving any private properties on the ACEs as
-+        # we will need to keep those when we change the ACL.
-+        old_acl = waitForDeferred(self.accessControlList(request, expanding=True))
-         yield old_acl
-         old_acl = old_acl.getResult()
- 
-         # Check disabled
-         if old_acl is None:
-             yield None
-+            return
- 
-         # Need to get list of supported privileges
-         supported = []
-@@ -773,10 +835,7 @@
-         yield supportedPrivs
-         supportedPrivs = supportedPrivs.getResult()
-         for item in supportedPrivs.children:
--            assert (
--                isinstance(item, davxml.SupportedPrivilege),
--                "Not a SupportedPrivilege: %r" % (item,)
--            )
-+            assert isinstance(item, davxml.SupportedPrivilege), "Not a SupportedPrivilege: %r" % (item,)
-             addSupportedPrivilege(item)
- 
-         # Steps 1 - 6
-@@ -910,8 +969,7 @@
-         supportedPrivs = supportedPrivs.getResult()
- 
-         # Other principals types don't make sense as actors.
--        assert (
--            principal.children[0].name in ("unauthenticated", "href"),
-+        assert principal.children[0].name in ("unauthenticated", "href"), (
-             "Principal is not an actor: %r" % (principal,)
-         )
- 
-@@ -1019,15 +1077,16 @@
-         def getMyURL():
-             url = request.urlForResource(self)
- 
--            assert url is not None, "urlForResource(self) returned None for resource %s" % (self,)
-+            assert url is not None, (
-+                "urlForResource(self) returned None for resource %s" % (self,)
-+            )
- 
-             return url
- 
-         try:
-             acl = self.readDeadProperty(davxml.ACL)
-         except HTTPError, e:
--            assert (
--                e.response.code == responsecode.NOT_FOUND,
-+            assert e.response.code == responsecode.NOT_FOUND, (
-                 "Expected %s response from readDeadProperty() exception, not %s"
-                 % (responsecode.NOT_FOUND, e.response.code)
-             )
-@@ -1038,9 +1097,9 @@
- 
-             if myURL == "/":
-                 # If we get to the root without any ACLs, then use the default.
-+                acl = self.defaultRootAccessControlList()
-+            else:
-                 acl = self.defaultAccessControlList()
--            else:
--                acl = davxml.ACL()
- 
-         # Dynamically update privileges for those ace's that are inherited.
-         if inheritance:
-@@ -1076,7 +1135,7 @@
-                                 # Adjust ACE for inherit on this resource
-                                 children = list(ace.children)
-                                 children.remove(TwistedACLInheritable())
--                                children.append(davxml.Inherited(davxml.HRef.fromString(parentURL)))
-+                                children.append(davxml.Inherited(davxml.HRef(parentURL)))
-                                 aces.append(davxml.ACE(*children))
-             else:
-                 aces.extend(inherited_aces)
-@@ -1105,8 +1164,7 @@
-         the child resource loop and supply those to the checkPrivileges on each child.
- 
-         @param request: the L{IRequest} for the request in progress.
--        @return:        a C{list} of L{Ace}s that child resources of this one will
--            inherit and which will match the currently authenticated principal.
-+        @return:        a C{list} of L{Ace}s that child resources of this one will inherit.
-         """
-         
-         # Get the parent ACLs with inheritance and preserve the <inheritable> element.
-@@ -1128,21 +1186,9 @@
-                 # Adjust ACE for inherit on this resource
-                 children = list(ace.children)
-                 children.remove(TwistedACLInheritable())
--                children.append(davxml.Inherited(davxml.HRef.fromString(request.urlForResource(self))))
-+                children.append(davxml.Inherited(davxml.HRef(request.urlForResource(self))))
-                 aces.append(davxml.ACE(*children))
--                
--        # Filter out those that do not have a principal match with the current principal
--        principal = self.currentPrincipal(request)
--        filteredaces = []
--        for ace in aces:
--            if self.matchPrincipal(principal, ace.principal, request):
--                if ace.invert:
--                    continue
--            else:
--                if not ace.invert:
--                    continue
--            filteredaces.append(ace)
--        yield filteredaces
-+        yield aces
- 
-     inheritedACEsforChildren = deferredGenerator(inheritedACEsforChildren)
- 
-@@ -1152,49 +1198,69 @@
- 
-         This implementation returns an empty set.
-         """
--
-         return []
- 
--    def findPrincipalForAuthID(self, request, authid):
-+    def principalsForAuthID(self, request, authid):
-         """
-+        Return authentication and authorization prinicipal identifiers for the
-+        authentication identifer passed in. In this implementation authn and authz
-+        principals are the same.
-+
-         @param request: the L{IRequest} for the request in progress.
-         @param authid: a string containing the
-             authentication/authorization identifier for the principal
-             to lookup.
--        @return: a deferred tuple of C{(principal, principalURI)}
--            where: C{principal} is the L{Principal} that is found;
--            C{principalURI} is the C{str} URI of the principal. 
-+        @return: a deferred tuple of two tuples. Each tuple is
-+            C{(principal, principalURI)} where: C{principal} is the L{Principal}
-+            that is found; {principalURI} is the C{str} URI of the principal.
-+            The first tuple corresponds to authentication identifiers,
-+            the second to authorization identifiers.
-             It will errback with an HTTPError(responsecode.FORBIDDEN) if
-             the principal isn't found.
-         """
--        # Try to match principals in each principal collection on the resource
--        collections = waitForDeferred(self.principalCollections(request))
--        yield collections
--        collections = collections.getResult()
-+        authnPrincipal = self.findPrincipalForAuthID(authid)
- 
--        for collection in collections:
--            principalURI = joinURL(str(collection), authid)
-+        if authnPrincipal is None:
-+            log.msg("Could not find the principal resource for user id: %s" % (authid,))
-+            raise HTTPError(responsecode.FORBIDDEN)
- 
--            principal = waitForDeferred(request.locateResource(principalURI))
--            yield principal
--            principal = principal.getResult()
-+        d = self.authorizationPrincipal(request, authid, authnPrincipal)
-+        d.addCallback(lambda authzPrincipal: (authnPrincipal, authzPrincipal))
-+        return d
- 
--            if isPrincipalResource(principal):
--                yield (principal, principalURI)
--                return
--        else:
--            principalCollections = waitForDeferred(self.principalCollections(request))
--            yield principalCollections
--            principalCollections = principalCollections.getResult()
-+    def findPrincipalForAuthID(self, authid):
-+        """
-+        Return authentication and authoirization prinicipal identifiers for the
-+        authentication identifer passed in. In this implementation authn and authz
-+        principals are the same.
- 
--            if len(principalCollections) == 0:
--                log.msg("DAV:principal-collection-set property cannot be found on the resource being authorized: %s" % self)
--            else:
--                log.msg("Could not find principal matching user id: %s" % authid)
--            raise HTTPError(responsecode.FORBIDDEN)
-+        @param authid: a string containing the
-+            authentication/authorization identifier for the principal
-+            to lookup.
-+        @return: a tuple of C{(principal, principalURI)} where: C{principal} is the L{Principal}
-+            that is found; {principalURI} is the C{str} URI of the principal.
-+            If not found return None.
-+        """
-+        for collection in self.principalCollections():
-+            principal = collection.principalForUser(authid)
-+            if principal is not None:
-+                return principal
-+        return None
- 
--    findPrincipalForAuthID = deferredGenerator(findPrincipalForAuthID)
--
-+    def authorizationPrincipal(self, request, authid, authnPrincipal):
-+        """
-+        Determine the authorization principal for the given request and authentication principal.
-+        This implementation simply uses aht authentication principalk as the authoization principal.
-+        
-+        @param request: the L{IRequest} for the request in progress.
-+        @param authid: a string containing the uthentication/authorization identifier
-+            for the principal to lookup.
-+        @param authnPrincipal: the L{IDAVPrincipal} for the authenticated principal
-+         @return: a deferred result C{tuple} of (L{IDAVPrincipal}, C{str}) containing the authorization principal
-+            resource and URI respectively.
-+        """
-+        return succeed(authnPrincipal)
-+        
-     def samePrincipal(self, principal1, principal2):
-         """
-         Check whether the two prinicpals are exactly the same in terms of
-@@ -1219,7 +1285,6 @@
-             return False
-                 
-     def matchPrincipal(self, principal1, principal2, request):
--
-         """
-         Check whether the principal1 is a principal in the set defined by
-         principal2.
-@@ -1244,6 +1309,9 @@
-             if isinstance(principal1, davxml.Unauthenticated):
-                 yield False
-                 return
-+            elif isinstance(principal1, davxml.All):
-+                yield False
-+                return
-             else:
-                 yield True
-                 return
-@@ -1260,10 +1328,7 @@
-             yield False
-             return
- 
--        assert (
--            isinstance(principal1, davxml.HRef),
--            "Not an HRef: %r" % (principal1,)
--        )
-+        assert isinstance(principal1, davxml.HRef), "Not an HRef: %r" % (principal1,)
- 
-         principal2 = waitForDeferred(self.resolvePrincipal(principal2, request))
-         yield principal2
-@@ -1271,7 +1336,6 @@
- 
-         assert principal2 is not None, "principal2 is None"
- 
--
-         # Compare two HRefs and do group membership test as well
-         if principal1 == principal2:
-             yield True
-@@ -1289,6 +1353,7 @@
- 
-     matchPrincipal = deferredGenerator(matchPrincipal)
- 
-+    @deferredGenerator
-     def principalIsGroupMember(self, principal1, principal2, request):
-         """
-         Check whether one principal is a group member of another.
-@@ -1299,18 +1364,21 @@
-         @return: L{Deferred} with result C{True} if principal1 is a member of principal2, C{False} otherwise
-         """
-         
--        def testGroup(group):
--            # Get principal resource for principal2
--            if group and isinstance(group, DAVPrincipalResource):
--                members = group.groupMembers()
--                if principal1 in members:
--                    return True
--                
--            return False
-+        d = waitForDeferred(request.locateResource(principal2))
-+        yield d
-+        group = d.getResult()
- 
--        d = request.locateResource(principal2)
--        d.addCallback(testGroup)
--        return d
-+        # Get principal resource for principal2
-+        if group and isinstance(group, DAVPrincipalResource):
-+            d = waitForDeferred(group.expandedGroupMembers())
-+            yield d
-+            members = d.getResult()
-+            for member in members:
-+                if member.principalURL() == principal1:
-+                    yield True
-+                    return
-+            
-+        yield False
-         
-     def validPrincipal(self, ace_principal, request):
-         """
-@@ -1351,11 +1419,16 @@
-         @return C{True} if C{href_principal} is valid, C{False} otherwise.
- 
-         This implementation tests for a href element that corresponds to
--        a principal resource.
-+        a principal resource and matches the principal-URL.
-         """
--        # Must have the principal resource type
-+
-+        # Must have the principal resource type and must match the principal-URL
-+        
-+        def _matchPrincipalURL(resource):
-+            return isPrincipalResource(resource) and resource.principalURL() == str(href_principal)
-+
-         d = request.locateResource(str(href_principal))
--        d.addCallback(isPrincipalResource)
-+        d.addCallback(_matchPrincipalURL)
-         return d
- 
-     def resolvePrincipal(self, principal, request):
-@@ -1404,8 +1477,7 @@
-             try:
-                 principal = principal.getResult()
-             except HTTPError, e:
--                assert (
--                    e.response.code == responsecode.NOT_FOUND,
-+                assert e.response.code == responsecode.NOT_FOUND, (
-                     "Expected %s response from readProperty() exception, not %s"
-                     % (responsecode.NOT_FOUND, e.response.code)
-                 )
-@@ -1432,15 +1504,15 @@
-                 log.err("DAV:self ACE is set on non-principal resource %r" % (self,))
-                 yield None
-                 return
--            principal = davxml.HRef.fromString(self.principalURL())
-+            principal = davxml.HRef(self.principalURL())
- 
-         if isinstance(principal, davxml.HRef):
-             yield principal
-+            return
-         else:
-             yield None
- 
--        assert (
--            isinstance(principal, (davxml.All, davxml.Authenticated, davxml.Unauthenticated)),
-+        assert isinstance(principal, (davxml.All, davxml.Authenticated, davxml.Unauthenticated)), (
-             "Not a meta-principal: %r" % (principal,)
-         )
- 
-@@ -1517,6 +1589,280 @@
-         return None
- 
-     ##
-+    # Quota
-+    ##
-+    
-+    """
-+    The basic policy here is to define a private 'quota-root' property on a collection.
-+    That property will contain the maximum allowed bytes for the collections and all
-+    its contents.
-+    
-+    In order to determine the quota property values on a resource, the server must look
-+    for the private property on that resource and any of its parents. If found on a parent,
-+    then that parent should be queried for quota information. If not found, no quota
-+    exists for the resource.
-+    
-+    To determine tha actual quota in use we will cache the used byte count on the quota-root
-+    collection in another private property. It is the servers responsibility to
-+    keep that property up to date by adjusting it after every PUT, DELETE, COPY,
-+    MOVE, MKCOL, PROPPATCH, ACL, POST or any other method that may affect the size of
-+    stored data. If the private property is not present, the server will fall back to
-+    getting the size by iterating over all resources (this is done in static.py).
-+    
-+    """
-+
-+    def quota(self, request):
-+        """
-+        Get current available & used quota values for this resource's quota root
-+        collection.
-+
-+        @return: an L{Defered} with result C{tuple} containing two C{int}'s the first is 
-+            quota-available-bytes, the second is quota-used-bytes, or
-+            C{None} if quota is not defined on the resource.
-+        """
-+        
-+        # See if already cached
-+        if not hasattr(request, "quota"):
-+            request.quota = {}
-+        if request.quota.has_key(self):
-+            yield request.quota[self]
-+            return
-+
-+        # Check this resource first
-+        if self.isCollection():
-+            qroot = self.quotaRoot(request)
-+            if qroot is not None:
-+                used = waitForDeferred(self.currentQuotaUse(request))
-+                yield used
-+                used = used.getResult()
-+                available = qroot - used
-+                if available < 0:
-+                    available = 0
-+                request.quota[self] = (available, used)
-+                yield request.quota[self]
-+                return
-+        
-+        # Check the next parent
-+        url = request.urlForResource(self)
-+        if url != "/":
-+            parent = waitForDeferred(request.locateResource(parentForURL(url)))
-+            yield parent
-+            parent = parent.getResult()
-+            d = waitForDeferred(parent.quota(request))
-+            yield d
-+            request.quota[self] = d.getResult()
-+        else:
-+            request.quota[self] = None
-+
-+        yield request.quota[self]
-+        return
-+    
-+    quota = deferredGenerator(quota)
-+
-+    def hasQuota(self, request):
-+        """
-+        Check whether this resource is undre quota control by checking each parent to see if
-+        it has a quota root.
-+        
-+        @return: C{True} if under quota control, C{False} if not.
-+        """
-+        
-+        # Check this one first
-+        if self.hasQuotaRoot(request):
-+            yield True
-+            return
-+        
-+        # Look at each parent
-+        try:
-+            url = request.urlForResource(self)
-+            if url != "/":
-+                parent = waitForDeferred(request.locateResource(parentForURL(url)))
-+                yield parent
-+                parent = parent.getResult()
-+                d = waitForDeferred(parent.hasQuota(request))
-+                yield d
-+                yield d.getResult()
-+            else:
-+                yield False
-+        except NoURLForResourceError:
-+            yield False
-+    
-+    hasQuota = deferredGenerator(hasQuota)
-+        
-+    def hasQuotaRoot(self, request):
-+        """
-+        @return: a C{True} if this resource has quota root, C{False} otherwise.
-+        """
-+        return self.hasDeadProperty(TwistedQuotaRootProperty)
-+    
-+    def quotaRoot(self, request):
-+        """
-+        @return: a C{int} containing the maximum allowed bytes if this collection
-+            is quota-controlled, or C{None} if not quota controlled.
-+        """
-+        if self.hasDeadProperty(TwistedQuotaRootProperty):
-+            return int(str(self.readDeadProperty(TwistedQuotaRootProperty)))
-+        else:
-+            return None
-+    
-+    def quotaRootParent(self, request):
-+        """
-+        Return the next quota root above this resource.
-+        
-+        @return: L{DAVResource} or C{None}
-+        """
-+
-+        # Check the next parent
-+        url = request.urlForResource(self)
-+        while (url != "/"):
-+            url = parentForURL(url)
-+            parent = waitForDeferred(request.locateResource(url))
-+            yield parent
-+            parent = parent.getResult()
-+            if parent.hasQuotaRoot(request):
-+                yield parent
-+                return
-+
-+        yield None
-+    
-+    quotaRootParent = deferredGenerator(quotaRootParent)
-+        
-+    def setQuotaRoot(self, request, maxsize):
-+        """
-+        @param maxsize: a C{int} containing the maximum allowed bytes for the contents
-+            of this collection, or C{None} tp remove quota restriction.
-+        """
-+        assert self.isCollection(), "Only collections can have a quota root"
-+        assert maxsize is None or isinstance(maxsize, int), "maxsize must be an int or None"
-+        
-+        if maxsize is not None:
-+            self.writeDeadProperty(TwistedQuotaRootProperty(str(maxsize)))
-+        else:
-+            # Remove both the root and the cached used value
-+            self.removeDeadProperty(TwistedQuotaRootProperty)
-+            self.removeDeadProperty(TwistedQuotaUsedProperty)
-+    
-+    def quotaSize(self, request):
-+        """
-+        Get the size of this resource (if its a collection get total for all children as well).
-+        TODO: Take into account size of dead-properties.
-+
-+        @return: a C{int} containing the size of the resource.
-+        """
-+        unimplemented(self)
-+
-+    def checkQuota(self, request, available):
-+        """
-+        Check to see whether all quota roots have sufficient available bytes.
-+        We currently do not use hierarchical quota checks - i.e. only the most
-+        immediate quota root parent is checked for quota.
-+        
-+        @param available: a C{int} containing the additional quota required.
-+        @return: C{True} if there is sufficient quota remaining on all quota roots,
-+            C{False} otherwise.
-+        """
-+        
-+        quotaroot = self
-+        while(quotaroot is not None):
-+            # Check quota on this root (if it has one)
-+            quota = quotaroot.quotaRoot(request)
-+            if quota is not None:
-+                if available > quota[0]:
-+                    yield False
-+                    return
-+
-+            # Check the next parent with a quota root
-+            quotaroot = waitForDeferred(quotaroot.quotaRootParent(request))
-+            yield quotaroot
-+            quotaroot = quotaroot.getResult()
-+
-+        yield True
-+
-+    checkQuota = deferredGenerator(checkQuota)
-+
-+    def quotaSizeAdjust(self, request, adjust):
-+        """
-+        Update the quota used value on all quota root parents of this resource.
-+
-+        @param adjust: a C{int} containing the number of bytes added (positive) or
-+            removed (negative) that should be used to adjust the cached total.
-+        """
-+        
-+        # Check this resource first
-+        if self.isCollection():
-+            if self.hasQuotaRoot(request):
-+                d = waitForDeferred(self.updateQuotaUse(request, adjust))
-+                yield d
-+                d.getResult()
-+                yield None
-+                return
-+        
-+        # Check the next parent
-+        url = request.urlForResource(self)
-+        if url != "/":
-+            parent = waitForDeferred(request.locateResource(parentForURL(url)))
-+            yield parent
-+            parent = parent.getResult()
-+            d = waitForDeferred(parent.quotaSizeAdjust(request, adjust))
-+            yield d
-+            d.getResult()
-+
-+        yield None
-+
-+    quotaSizeAdjust = deferredGenerator(quotaSizeAdjust)
-+
-+    def currentQuotaUse(self, request):
-+        """
-+        Get the cached quota use value, or if not present (or invalid) determine
-+        quota use by brute force.
-+
-+        @return: an L{Deferred} with a C{int} result containing the current used byte if this collection
-+            is quota-controlled, or C{None} if not quota controlled.
-+        """
-+        assert self.isCollection(), "Only collections can have a quota root"
-+        assert self.hasQuotaRoot(request), "Quota use only on quota root collection"
-+        
-+        # Try to get the cached value property
-+        if self.hasDeadProperty(TwistedQuotaUsedProperty):
-+            return succeed(int(str(self.readDeadProperty(TwistedQuotaUsedProperty))))
-+        else:
-+            # Do brute force size determination and cache the result in the private property
-+            def _defer(result):
-+                self.writeDeadProperty(TwistedQuotaUsedProperty(str(result)))
-+                return result
-+            d = self.quotaSize(request)
-+            d.addCallback(_defer)
-+            return d
-+
-+    def updateQuotaUse(self, request, adjust):
-+        """
-+        Update the quota used value on this resource.
-+
-+        @param adjust: a C{int} containing the number of bytes added (positive) or
-+        removed (negative) that should be used to adjust the cached total.
-+        @return: an L{Deferred} with a C{int} result containing the current used byte if this collection
-+            is quota-controlled, or C{None} if not quota controlled.
-+        """
-+        assert self.isCollection(), "Only collections can have a quota root"
-+        
-+        # Get current value
-+        def _defer(size):
-+            size += adjust
-+            
-+            # Sanity check the resulting size
-+            if size >= 0:
-+                self.writeDeadProperty(TwistedQuotaUsedProperty(str(size)))
-+            else:
-+                # Remove the dead property and re-read to do brute force quota calc
-+                log.msg("Attempt to set quota used to a negative value: %s (adjustment: %s)" % (size, adjust,))
-+                self.removeDeadProperty(TwistedQuotaUsedProperty)
-+                return self.currentQuotaUse(request)
-+
-+        d = self.currentQuotaUse(request)
-+        d.addCallback(_defer)
-+        return d
-+        
-+    ##
-     # HTTP
-     ##
- 
-@@ -1525,15 +1871,11 @@
-         #litmus = request.headers.getRawHeaders("x-litmus")
-         #if litmus: log.msg("*** Litmus test: %s ***" % (litmus,))
- 
--        # FIXME: Learn how to use twisted logging facility, wsanchez
--        protocol = "HTTP/%s.%s" % request.clientproto
--        log.msg("%s %s %s" % (request.method, urllib.unquote(request.uri), protocol))
--
-         #
-         # If this is a collection and the URI doesn't end in "/", redirect.
-         #
--        if self.isCollection() and request.uri[-1:] != "/":
--            return RedirectResponse(request.uri + "/")
-+        if self.isCollection() and request.path[-1:] != "/":
-+            return RedirectResponse(request.unparseURL(path=urllib.quote(request.path, safe=':/')+'/'))
- 
-         def setHeaders(response):
-             response = IResponse(response)
-@@ -1567,7 +1909,7 @@
-     def findChildren(self, depth, request, callback, privileges=None, inherited_aces=None):
-         return succeed(None)
- 
--class DAVPrincipalResource (DAVLeafResource):
-+class DAVPrincipalResource (DAVResource):
-     """
-     Resource representing a WebDAV principal.  (RFC 3744, section 2)
-     """
-@@ -1577,7 +1919,7 @@
-     # WebDAV
-     ##
- 
--    liveProperties = DAVLeafResource.liveProperties + (
-+    liveProperties = DAVResource.liveProperties + (
-         (dav_namespace, "alternate-URI-set"),
-         (dav_namespace, "principal-URL"    ),
-         (dav_namespace, "group-member-set" ),
-@@ -1585,14 +1927,11 @@
-     )
- 
-     def davComplianceClasses(self):
--        return ("1",)
-+        return ("1", "access-control",)
- 
-     def isCollection(self):
-         return False
- 
--    def findChildren(self, depth, request, callback, privileges=None, inherited_aces=None):
--        return succeed(None)
--
-     def readProperty(self, property, request):
-         def defer():
-             if type(property) is tuple:
-@@ -1610,10 +1949,20 @@
-                     return davxml.PrincipalURL(davxml.HRef(self.principalURL()))
- 
-                 if name == "group-member-set":
--                    return davxml.GroupMemberSet(*[davxml.HRef(p) for p in self.groupMembers()])
-+                    def callback(members):
-+                        return davxml.GroupMemberSet(*[davxml.HRef(p.principalURL()) for p in members])
-+                    
-+                    d = self.groupMembers()
-+                    d.addCallback(callback)
-+                    return d
- 
-                 if name == "group-membership":
--                    return davxml.GroupMembership(*[davxml.HRef(g) for g in self.groupMemberships()])
-+                    def callback(memberships):
-+                        return davxml.GroupMembership(*[davxml.HRef(g.principalURL()) for g in memberships])
-+                    
-+                    d = self.groupMemberships()
-+                    d.addCallback(callback)
-+                    return d
- 
-                 if name == "resourcetype":
-                     if self.isCollection():
-@@ -1655,7 +2004,7 @@
-         principals.  Subclasses should override this method to provide member
-         URLs for this resource if appropriate.
-         """
--        return ()
-+        return succeed(())
- 
-     def groupMemberships(self):
-         """
-@@ -1666,6 +2015,7 @@
-         """
-         unimplemented(self)
- 
-+    @deferredGenerator
-     def principalMatch(self, href):
-         """
-         Check whether the supplied principal matches this principal or is a
-@@ -1675,10 +2025,33 @@
-         """
-         uri = str(href)
-         if self.principalURL() == uri:
--            return True
-+            yield True
-+            return
-         else:
--            return uri in self.groupMembers()
-+            d = waitForDeferred(self.expandedGroupMembers())
-+            yield d
-+            members = d.getResult()
-+            member_uris = [member.principalURL() for member in members]
-+            yield uri in member_uris
- 
-+class DAVPrincipalCollectionResource (DAVResource):
-+    """
-+    WebDAV principal collection resource.  (RFC 3744, section 5.8)
-+    """
-+    implements(IDAVPrincipalCollectionResource)
-+
-+    def __init__(self, url, principalCollections=()):
-+        """
-+        @param url: This resource's URL.
-+        """
-+        DAVResource.__init__(self, principalCollections=principalCollections)
-+
-+        assert url.endswith("/"), "Collection URL must end in '/'"
-+        self._url = url
-+
-+    def principalCollectionURL(self):
-+        return self._url
-+
- class AccessDeniedError(Exception):
-     def __init__(self, errors):
-         """ 
-@@ -1718,6 +2091,37 @@
- davxml.registerElement(TwistedACLInheritable)
- davxml.ACE.allowed_children[(twisted_dav_namespace, "inheritable")] = (0, 1)
- 
-+class TwistedGETContentMD5 (davxml.WebDAVTextElement):
-+    """
-+    MD5 hash of the resource content.
-+    """
-+    namespace = twisted_dav_namespace
-+    name = "getcontentmd5"
-+
-+davxml.registerElement(TwistedGETContentMD5)
-+
-+"""
-+When set on a collection, this property indicates that the collection has a quota limit for
-+the size of all resources stored in the collection (and any associate meta-data such as properties).
-+The value is a number - the maximum size in bytes allowed.
-+"""
-+class TwistedQuotaRootProperty (davxml.WebDAVTextElement):
-+    namespace = twisted_private_namespace
-+    name = "quota-root"
-+
-+davxml.registerElement(TwistedQuotaRootProperty)
-+
-+"""
-+When set on a collection, this property contains the cached running total of the size of all
-+resources stored in the collection (and any associate meta-data such as properties).
-+The value is a number - the size in bytes used.
-+"""
-+class TwistedQuotaUsedProperty (davxml.WebDAVTextElement):
-+    namespace = twisted_private_namespace
-+    name = "quota-used"
-+
-+davxml.registerElement(TwistedQuotaUsedProperty)
-+
- allACL = davxml.ACL(
-     davxml.ACE(
-         davxml.Principal(davxml.All()),

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.static.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.static.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.static.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,161 +0,0 @@
-Index: twisted/web2/dav/static.py
-===================================================================
---- twisted/web2/dav/static.py	(revision 19773)
-+++ twisted/web2/dav/static.py	(working copy)
-@@ -28,16 +28,17 @@
- 
- __all__ = ["DAVFile"]
- 
--import os
--
-+from twisted.internet.defer import succeed, deferredGenerator, waitForDeferred
-+from twisted.python.filepath import InsecurePath
- from twisted.python import log
--from twisted.internet.defer import succeed, deferredGenerator, waitForDeferred
--from twisted.web2.static import File
-+from twisted.web2 import http_headers
- from twisted.web2 import responsecode, dirlist
--from twisted.web2.http import RedirectResponse
- from twisted.web2.dav import davxml
- from twisted.web2.dav.resource import DAVResource, davPrivilegeSet
-+from twisted.web2.dav.resource import TwistedGETContentMD5
- from twisted.web2.dav.util import bindMethods
-+from twisted.web2.http import HTTPError, StatusResponse, RedirectResponse
-+from twisted.web2.static import File
- 
- try:
-     from twisted.web2.dav.xattrprops import xattrPropertyStore as DeadPropertyStore
-@@ -52,9 +53,11 @@
- 
-     Extends twisted.web2.static.File to handle WebDAV methods.
-     """
--    def __init__(self, path,
--                 defaultType="text/plain",
--                 indexNames=None):
-+    def __init__(
-+        self, path,
-+        defaultType="text/plain", indexNames=None,
-+        principalCollections=()
-+    ):
-         """
-         @param path: the path of the file backing this resource.
-         @param defaultType: the default mime type (as a string) for this
-@@ -62,11 +65,14 @@
-         @param indexNames: a sequence of index file names.
-         @param acl: an L{IDAVAccessControlList} with the .
-         """
--        super(DAVFile, self).__init__(path,
--                                      defaultType = defaultType,
--                                      ignoredExts = (),
--                                      processors  = None,
--                                      indexNames  = indexNames)
-+        File.__init__(
-+            self, path,
-+            defaultType = defaultType,
-+            ignoredExts = (),
-+            processors = None,
-+            indexNames = indexNames,
-+        )
-+        DAVResource.__init__(self, principalCollections=principalCollections)
- 
-     def __repr__(self):
-         return "<%s: %s>" % (self.__class__.__name__, self.fp.path)
-@@ -75,6 +81,13 @@
-     # WebDAV
-     ##
- 
-+    def etag(self):
-+        if not self.fp.exists(): return None
-+        if self.hasDeadProperty(TwistedGETContentMD5):
-+            return http_headers.ETag(str(self.readDeadProperty(TwistedGETContentMD5)))
-+        else:
-+            return super(DAVFile, self).etag()
-+
-     def davComplianceClasses(self):
-         return ("1", "access-control") # Add "2" when we have locking
- 
-@@ -87,7 +100,6 @@
-         """
-         See L{IDAVResource.isCollection}.
-         """
--        for child in self.listChildren(): return True
-         return self.fp.isdir()
- 
-     ##
-@@ -98,6 +110,50 @@
-         return succeed(davPrivilegeSet)
- 
-     ##
-+    # Quota
-+    ##
-+
-+    def quotaSize(self, request):
-+        """
-+        Get the size of this resource.
-+        TODO: Take into account size of dead-properties. Does stat
-+            include xattrs size?
-+
-+        @return: an L{Deferred} with a C{int} result containing the size of the resource.
-+        """
-+        if self.isCollection():
-+            def walktree(top):
-+                """
-+                Recursively descend the directory tree rooted at top,
-+                calling the callback function for each regular file
-+                
-+                @param top: L{FilePath} for the directory to walk.
-+                """
-+            
-+                total = 0
-+                for f in top.listdir():
-+                    child = top.child(f)
-+                    if child.isdir():
-+                        # It's a directory, recurse into it
-+                        result = waitForDeferred(walktree(child))
-+                        yield result
-+                        total += result.getResult()
-+                    elif child.isfile():
-+                        # It's a file, call the callback function
-+                        total += child.getsize()
-+                    else:
-+                        # Unknown file type, print a message
-+                        pass
-+            
-+                yield total
-+            
-+            walktree = deferredGenerator(walktree)
-+    
-+            return walktree(self.fp)
-+        else:
-+            return succeed(self.fp.getsize())
-+
-+    ##
-     # Workarounds for issues with File
-     ##
- 
-@@ -112,8 +168,12 @@
-         See L{IResource}C{.locateChild}.
-         """
-         # If getChild() finds a child resource, return it
--        child = self.getChild(segments[0])
--        if child is not None: return (child, segments[1:])
-+        try:
-+            child = self.getChild(segments[0])
-+            if child is not None:
-+                return (child, segments[1:])
-+        except InsecurePath:
-+            raise HTTPError(StatusResponse(responsecode.FORBIDDEN, "Invalid URL path"))
-         
-         # If we're not backed by a directory, we have no children.
-         # But check for existance first; we might be a collection resource
-@@ -132,7 +192,9 @@
-         return (self.createSimilarFile(self.fp.child(path).path), segments[1:])
- 
-     def createSimilarFile(self, path):
--        return self.__class__(path, defaultType=self.defaultType, indexNames=self.indexNames[:])
-+        return self.__class__(
-+            path, defaultType=self.defaultType, indexNames=self.indexNames[:],
-+            principalCollections=self.principalCollections())
- 
- #
- # Attach method handlers to DAVFile

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.stream.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.stream.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.stream.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,84 +0,0 @@
-Index: twisted/web2/dav/stream.py
-===================================================================
---- twisted/web2/dav/stream.py	(revision 0)
-+++ twisted/web2/dav/stream.py	(revision 0)
-@@ -0,0 +1,79 @@
-+##
-+# Copyright (c) 2005-2007 Apple 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.
-+#
-+# DRI: Cyrus Daboo, cdaboo at apple.com
-+##
-+
-+"""
-+Class that implements a stream that calculates the MD5 hash of the data
-+as the data is read.
-+"""
-+
-+__all__ = ["MD5StreamWrapper"]
-+
-+from twisted.internet.defer import Deferred
-+from twisted.web2.stream import SimpleStream
-+
-+try:
-+    from hashlib import md5
-+except ImportError:
-+    from md5 import new as md5
-+
-+class MD5StreamWrapper(SimpleStream):
-+ 
-+    def __init__(self, wrap):
-+        
-+        assert wrap is not None, "Must have a stream to wrap."
-+
-+        self.stream = wrap
-+
-+        # Init MD5
-+        self.md5 = md5()
-+    
-+    def read(self):
-+        assert self.md5 is not None, "Cannot call read after close."
-+
-+        # Read from wrapped stream first
-+        b = self.stream.read()
-+        
-+        if isinstance(b, Deferred):
-+            def _gotData(data):
-+                if data is not None:
-+                    self.md5.update(data)
-+                return data
-+            b.addCallback(_gotData)
-+        else:
-+            # Update current MD5 state
-+            if b is not None:
-+                self.md5.update(str(b))
-+    
-+        return b
-+    
-+    def close(self):
-+        # Close out the MD5 hash
-+        self.md5value = self.md5.hexdigest()
-+        self.md5 = None
-+
-+        SimpleStream.close(self)
-+    
-+    def getMD5(self):
-+        assert hasattr(self, "md5value"), "Stream has to be closed first"
-+        return self.md5value

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.data.quota_100.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.data.quota_100.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.data.quota_100.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,15 +0,0 @@
-Index: twisted/web2/dav/test/data/quota_100.txt
-===================================================================
---- twisted/web2/dav/test/data/quota_100.txt	(revision 0)
-+++ twisted/web2/dav/test/data/quota_100.txt	(revision 0)
-@@ -0,0 +1,10 @@
-+123456789
-+123456789
-+123456789
-+123456789
-+123456789
-+123456789
-+123456789
-+123456789
-+123456789
-+123456789

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_acl.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_acl.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_acl.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,108 +0,0 @@
-Index: twisted/web2/dav/test/test_acl.py
-===================================================================
---- twisted/web2/dav/test/test_acl.py	(revision 19773)
-+++ twisted/web2/dav/test/test_acl.py	(working copy)
-@@ -30,6 +30,7 @@
- from twisted.web2.auth import basic
- from twisted.web2.stream import MemoryStream
- from twisted.web2.dav import davxml
-+from twisted.web2.dav.resource import DAVPrincipalCollectionResource
- from twisted.web2.dav.util import davXMLFromStream
- from twisted.web2.dav.auth import TwistedPasswordProperty, IPrincipal, DavRealm, TwistedPropertyChecker, AuthenticationWrapper
- 
-@@ -38,6 +39,25 @@
- from twisted.web2.dav.test.util import Site, serialize
- from twisted.web2.dav.test.test_resource import TestResource, TestDAVPrincipalResource
- 
-+class TestPrincipalsCollection(DAVPrincipalCollectionResource, TestResource):
-+    def __init__(self, url, children):
-+        DAVPrincipalCollectionResource.__init__(self, url)
-+        TestResource.__init__(self, url, children, principalCollections=(self,))
-+    
-+    def principalForUser(self, user):
-+        return self.principalForShortName('users', user)
-+
-+    def principalForAuthID(self, creds):
-+        return self.principalForShortName('users', creds.username)
-+
-+    def principalForShortName(self, type, shortName):
-+        typeResource = self.children.get(type, None)
-+        user = None
-+        if typeResource:
-+            user = typeResource.children.get(shortName, None)
-+
-+        return user
-+
- class ACL(twisted.web2.dav.test.util.TestCase):
-     """
-     RFC 3744 (WebDAV ACL) tests.
-@@ -46,8 +66,18 @@
-         if not hasattr(self, "docroot"):
-             self.docroot = self.mktemp()
-             os.mkdir(self.docroot)
--            rootresource = self.resource_class(self.docroot)
- 
-+            userResource = TestDAVPrincipalResource("/principals/users/user01")
-+            userResource.writeDeadProperty(TwistedPasswordProperty("user01"))
-+
-+            principalCollection = TestPrincipalsCollection(
-+                "/principals/", 
-+                children={"users": TestPrincipalsCollection(
-+                        "/principals/users/",
-+                        children={"user01": userResource})})
-+
-+            rootResource = self.resource_class(self.docroot, principalCollections=(principalCollection,))
-+
-             portal = Portal(DavRealm())
-             portal.registerChecker(TwistedPropertyChecker())
- 
-@@ -56,26 +86,14 @@
-             loginInterfaces = (IPrincipal,)
- 
-             self.site = Site(AuthenticationWrapper(
--                rootresource, 
-+                rootResource, 
-                 portal,
-                 credentialFactories,
-                 loginInterfaces
-             ))
- 
--            rootresource.setAccessControlList(self.grant(davxml.All()))
-+            rootResource.setAccessControlList(self.grant(davxml.All()))
- 
--            userresource = TestDAVPrincipalResource("/principals/user01")
--            userresource.writeDeadProperty(TwistedPasswordProperty.fromString("user01"))
--
--            rootresource.putChild(
--                "principals",
--                TestResource("/principals", {"user01": userresource})
--            )
--
--            rootresource.writeDeadProperty(
--                davxml.PrincipalCollectionSet(davxml.HRef("/principals/"))
--            )
--
-         for name, acl in (
-             ("none"       , self.grant()),
-             ("read"       , self.grant(davxml.Read())),
-@@ -361,9 +379,7 @@
-                 if method == "GET":
-                     ok = responsecode.OK
-                 elif method == "REPORT":
--                    # BAD_REQUEST in the allowed case, because we're not actually
--                    # including the required XML in the request body.
--                    ok = responsecode.BAD_REQUEST
-+                    ok = responsecode.MULTI_STATUS
-                 else:
-                     raise AssertionError("We shouldn't be here.  (method = %r)" % (method,))
- 
-@@ -377,6 +393,9 @@
-                     path = os.path.join(self.docroot, name)
- 
-                     request = SimpleRequest(self.site, method, "/" + name)
-+                    if method == "REPORT":
-+                        request.stream = MemoryStream(davxml.PrincipalPropertySearch().toxml())
-+
-                     _add_auth_header(request)
- 
-                     def test(response, code=code, path=path):

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_copy.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_copy.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_copy.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,24 +0,0 @@
-Index: twisted/web2/dav/test/test_copy.py
-===================================================================
---- twisted/web2/dav/test/test_copy.py	(revision 19773)
-+++ twisted/web2/dav/test/test_copy.py	(working copy)
-@@ -22,9 +22,9 @@
- # DRI: Wilfredo Sanchez, wsanchez at apple.com
- ##
- 
-+from hashlib import md5
- import os
- import urllib
--import md5
- 
- import twisted.web2.dav.test.util
- from twisted.web2 import responsecode
-@@ -161,7 +161,7 @@
-             yield (request, do_test)
- 
- def sumFile(path):
--    m = md5.new()
-+    m = md5()
- 
-     if os.path.isfile(path):
-         f = file(path)

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_pipeline.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_pipeline.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_pipeline.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,74 +0,0 @@
-Index: twisted/web2/dav/test/test_pipeline.py
-===================================================================
---- twisted/web2/dav/test/test_pipeline.py	(revision 0)
-+++ twisted/web2/dav/test/test_pipeline.py	(revision 0)
-@@ -0,0 +1,69 @@
-+##
-+# Copyright (c) 2005 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.
-+#
-+# DRI: Wilfredo Sanchez, wsanchez at apple.com
-+##
-+from twisted.internet import utils
-+from twisted.web2.test import test_server
-+from twisted.web2 import resource
-+from twisted.web2 import http
-+from twisted.web2.test import test_http
-+import sys
-+
-+from twisted.internet.defer import waitForDeferred, deferredGenerator
-+
-+from twisted.python import util
-+
-+class Pipeline(test_server.BaseCase):
-+    """
-+    Pipelined request
-+    """
-+    class TestResource(resource.LeafResource):
-+        def render(self, req):
-+            return http.Response(stream="Host:%s, Path:%s"%(req.host, req.path))
-+            
-+    def setUp(self):
-+        self.root = self.TestResource()
-+
-+    def chanrequest(self, root, uri, length, headers, method, version, prepath, content):
-+        self.cr = super(Pipeline, self).chanrequest(root, uri, length, headers, method, version, prepath, content)
-+        return self.cr
-+
-+    def test_root(self):
-+        
-+        def _testStreamRead(x):
-+            self.assertTrue(self.cr.request.stream.length == 0)
-+
-+        return self.assertResponse(
-+            (self.root, 'http://host/path', {"content-type":"text/plain",}, "PUT", None, '', "This is some text."),
-+            (405, {}, None)).addCallback(_testStreamRead)
-+
-+class SSLPipeline(test_http.SSLServerTest):
-+
-+    @deferredGenerator
-+    def testAdvancedWorkingness(self):
-+        args = ('-u', util.sibpath(__file__, "tworequest_client.py"), "basic",
-+                str(self.port), self.type)
-+        d = waitForDeferred(utils.getProcessOutputAndValue(sys.executable, args=args))
-+        yield d; out,err,code = d.getResult()
-+
-+        self.assertEquals(code, 0, "Error output:\n%s" % (err,))
-+        self.assertEquals(out, "HTTP/1.1 403 Forbidden\r\nContent-Length: 0\r\n\r\nHTTP/1.1 403 Forbidden\r\nContent-Length: 0\r\n\r\n")

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_prop.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_prop.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_prop.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,69 +0,0 @@
-Index: twisted/web2/dav/test/test_prop.py
-===================================================================
---- twisted/web2/dav/test/test_prop.py	(revision 19773)
-+++ twisted/web2/dav/test/test_prop.py	(working copy)
-@@ -21,6 +21,8 @@
- #
- # DRI: Wilfredo Sanchez, wsanchez at apple.com
- ##
-+from twisted.web2.dav.element.rfc4331 import QuotaUsedBytes
-+from twisted.web2.dav.element.rfc4331 import QuotaAvailableBytes
- 
- import random
- 
-@@ -37,8 +39,13 @@
- from twisted.web2.dav.test.util import serialize
- import twisted.web2.dav.test.util
- 
--live_properties = [lookupElement(qname)() for qname in DAVResource.liveProperties if qname[0] == dav_namespace]
-+# Remove dynamic live properties that exist
-+dynamicLiveProperties = (
-+    (dav_namespace, "quota-available-bytes"     ),
-+    (dav_namespace, "quota-used-bytes"          ),
-+)
- 
-+
- #
- # See whether dead properties are available
- #
-@@ -49,6 +56,10 @@
-     """
-     PROPFIND, PROPPATCH requests
-     """
-+
-+    def liveProperties(self):
-+        return [lookupElement(qname)() for qname in self.resource_class.liveProperties if (qname[0] == dav_namespace) and qname not in dynamicLiveProperties]
-+
-     def test_PROPFIND_basic(self):
-         """
-         PROPFIND request
-@@ -85,7 +96,7 @@
-                             self.fail("PROPFIND failed (status %s) to locate live properties: %s"
-                                       % (status.code, properties))
- 
--                        properties_to_find = [p.qname() for p in live_properties]
-+                        properties_to_find = [p.qname() for p in self.liveProperties()]
- 
-                         for property in properties:
-                             qname = property.qname()
-@@ -102,7 +113,7 @@
-             else:
-                 self.fail("No response for URI /")
- 
--        query = davxml.PropertyFind(davxml.PropertyContainer(*live_properties))
-+        query = davxml.PropertyFind(davxml.PropertyContainer(*self.liveProperties()))
- 
-         request = SimpleRequest(self.site, "PROPFIND", "/")
- 
-@@ -146,9 +157,9 @@
-                               % (status.code, properties))
- 
-                 if which.name == "allprop":
--                    properties_to_find = [p.qname() for p in live_properties if not p.hidden]
-+                    properties_to_find = [p.qname() for p in self.liveProperties() if not p.hidden]
-                 else:
--                    properties_to_find = [p.qname() for p in live_properties]
-+                    properties_to_find = [p.qname() for p in self.liveProperties()]
- 
-                 for property in properties:
-                     qname = property.qname()

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_quota.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_quota.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_quota.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,206 +0,0 @@
-Index: twisted/web2/dav/test/test_quota.py
-===================================================================
---- twisted/web2/dav/test/test_quota.py	(revision 0)
-+++ twisted/web2/dav/test/test_quota.py	(revision 0)
-@@ -0,0 +1,201 @@
-+##
-+# Copyright (c) 2005 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.
-+#
-+# DRI: Wilfredo Sanchez, wsanchez at apple.com
-+##
-+
-+from twisted.web2 import responsecode
-+from twisted.web2.iweb import IResponse
-+from twisted.web2.stream import FileStream
-+
-+import twisted.web2.dav.test.util
-+from twisted.web2.test.test_server import SimpleRequest
-+from twisted.web2.dav.test.util import Site
-+from twisted.web2.dav import davxml
-+import os
-+
-+class QuotaBase(twisted.web2.dav.test.util.TestCase):
-+
-+    def setUp(self):
-+        
-+        #super(Quota, self).setUp()
-+        self.docroot = self.mktemp()
-+        os.mkdir(self.docroot)
-+        rootresource = self.resource_class(self.docroot)
-+        rootresource.setAccessControlList(self.grantInherit(davxml.All()))
-+        self.site = Site(rootresource)
-+        self.site.resource.setQuotaRoot(None, 100000)
-+
-+    def checkQuota(self, value):
-+        def _defer(quota):
-+            self.assertEqual(quota, value)
-+            
-+        d = self.site.resource.currentQuotaUse(None)
-+        d.addCallback(_defer)
-+        return d
-+        
-+class QuotaEmpty(QuotaBase):
-+
-+    def test_Empty_Quota(self):
-+        
-+        return self.checkQuota(0)
-+
-+class QuotaPUT(QuotaBase):
-+
-+    def test_Quota_PUT(self):
-+        """
-+        Quota change on PUT
-+        """
-+        dst_uri = "/dst"
-+
-+        def checkResult(response):
-+            response = IResponse(response)
-+
-+            if response.code != responsecode.CREATED:
-+                self.fail("Incorrect response code for PUT (%s != %s)"
-+                          % (response.code, responsecode.CREATED))
-+                
-+            return self.checkQuota(100)
-+
-+        request = SimpleRequest(self.site, "PUT", dst_uri)
-+        request.stream = FileStream(file(os.path.join(os.path.dirname(__file__), "data", "quota_100.txt"), "rb"))        
-+        return self.send(request, checkResult)
-+
-+class QuotaDELETE(QuotaBase):
-+
-+    def test_Quota_DELETE(self):
-+        """
-+        Quota change on DELETE
-+        """
-+        dst_uri = "/dst"
-+
-+        def checkPUTResult(response):
-+            response = IResponse(response)
-+
-+            if response.code != responsecode.CREATED:
-+                self.fail("Incorrect response code for PUT (%s != %s)"
-+                          % (response.code, responsecode.CREATED))
-+            
-+            def doDelete(_ignore):
-+                def checkDELETEResult(response):
-+                    response = IResponse(response)
-+        
-+                    if response.code != responsecode.NO_CONTENT:
-+                        self.fail("Incorrect response code for PUT (%s != %s)"
-+                                  % (response.code, responsecode.NO_CONTENT))
-+
-+                    return self.checkQuota(0)
-+                    
-+                request = SimpleRequest(self.site, "DELETE", dst_uri)
-+                return self.send(request, checkDELETEResult)
-+                
-+            d = self.checkQuota(100)
-+            d.addCallback(doDelete)
-+            return d
-+
-+        request = SimpleRequest(self.site, "PUT", dst_uri)
-+        request.stream = FileStream(file(os.path.join(os.path.dirname(__file__), "data", "quota_100.txt"), "rb"))        
-+        return self.send(request, checkPUTResult)
-+
-+class OverQuotaPUT(QuotaBase):
-+
-+    def test_Quota_PUT(self):
-+        """
-+        Quota change on PUT
-+        """
-+        dst_uri = "/dst"
-+
-+        self.site.resource.setQuotaRoot(None, 90)
-+
-+        def checkResult(response):
-+            response = IResponse(response)
-+
-+            if response.code != responsecode.INSUFFICIENT_STORAGE_SPACE:
-+                self.fail("Incorrect response code for PUT (%s != %s)"
-+                          % (response.code, responsecode.INSUFFICIENT_STORAGE_SPACE))
-+                
-+            return self.checkQuota(0)
-+
-+        request = SimpleRequest(self.site, "PUT", dst_uri)
-+        request.stream = FileStream(file(os.path.join(os.path.dirname(__file__), "data", "quota_100.txt"), "rb"))        
-+        return self.send(request, checkResult)
-+
-+class QuotaOKAdjustment(QuotaBase):
-+
-+    def test_Quota_OK_Adjustment(self):
-+        """
-+        Quota adjustment OK
-+        """
-+        dst_uri = "/dst"
-+
-+        def checkPUTResult(response):
-+            response = IResponse(response)
-+
-+            if response.code != responsecode.CREATED:
-+                self.fail("Incorrect response code for PUT (%s != %s)"
-+                          % (response.code, responsecode.CREATED))
-+            
-+            def doOKAdjustment(_ignore):
-+                def checkAdjustmentResult(_ignore):
-+                    return self.checkQuota(10)
-+                
-+                d = self.site.resource.quotaSizeAdjust(None, -90)
-+                d.addCallback(checkAdjustmentResult)
-+                return d
-+                
-+            d = self.checkQuota(100)
-+            d.addCallback(doOKAdjustment)
-+            return d
-+
-+        request = SimpleRequest(self.site, "PUT", dst_uri)
-+        request.stream = FileStream(file(os.path.join(os.path.dirname(__file__), "data", "quota_100.txt"), "rb"))        
-+        return self.send(request, checkPUTResult)
-+
-+class QuotaBadAdjustment(QuotaBase):
-+
-+    def test_Quota_Bad_Adjustment(self):
-+        """
-+        Quota adjustment too much
-+        """
-+        dst_uri = "/dst"
-+
-+        def checkPUTResult(response):
-+            response = IResponse(response)
-+
-+            if response.code != responsecode.CREATED:
-+                self.fail("Incorrect response code for PUT (%s != %s)"
-+                          % (response.code, responsecode.CREATED))
-+            
-+            def doBadAdjustment(_ignore):
-+                def checkAdjustmentResult(_ignore):
-+                    return self.checkQuota(100)
-+                
-+                d = self.site.resource.quotaSizeAdjust(None, -200)
-+                d.addCallback(checkAdjustmentResult)
-+                return d
-+                
-+            d = self.checkQuota(100)
-+            d.addCallback(doBadAdjustment)
-+            return d
-+
-+        request = SimpleRequest(self.site, "PUT", dst_uri)
-+        request.stream = FileStream(file(os.path.join(os.path.dirname(__file__), "data", "quota_100.txt"), "rb"))        
-+        return self.send(request, checkPUTResult)

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_resource.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_resource.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_resource.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,102 +0,0 @@
-Index: twisted/web2/dav/test/test_resource.py
-===================================================================
---- twisted/web2/dav/test/test_resource.py	(revision 19773)
-+++ twisted/web2/dav/test/test_resource.py	(working copy)
-@@ -192,13 +192,10 @@
- class AccessTests(TestCase):
-     def setUp(self):
-         gooduser = TestDAVPrincipalResource('/users/gooduser')
-+        gooduser.writeDeadProperty(TwistedPasswordProperty('goodpass'))
- 
--        gooduser.writeDeadProperty(
--            TwistedPasswordProperty.fromString('goodpass'))
--
-         baduser = TestDAVPrincipalResource('/users/baduser')
--        baduser.writeDeadProperty(
--            TwistedPasswordProperty.fromString('badpass'))
-+        baduser.writeDeadProperty(TwistedPasswordProperty('badpass'))
- 
-         protected = TestResource('/protected')
-         protected.setAccessControlList(davxml.ACL(
-@@ -282,7 +279,8 @@
-         # Has auth; should allow
- 
-         request = SimpleRequest(site, "GET", "/")
--        request.user = davxml.Principal(davxml.HRef("/users/d00d"))
-+        request.authnUser = davxml.Principal(davxml.HRef("/users/d00d"))
-+        request.authzUser = davxml.Principal(davxml.HRef("/users/d00d"))
-         d = request.locateResource('/')
-         d.addCallback(_checkPrivileges)
-         d.addCallback(expectOK)
-@@ -301,6 +299,8 @@
-                       
-         return self.checkSecurity(request)
- 
-+    test_authorize.todo = "Needs refactoring"
-+
-     def test_badUsernameOrPassword(self):
-         request = SimpleRequest(self.site, 'GET', '/protected')
- 
-@@ -316,6 +316,8 @@
- 
-         return d
- 
-+    test_badUsernameOrPassword.todo = "Needs refactoring."
-+
-     def test_lacksPrivileges(self):
-         request = SimpleRequest(self.site, 'GET', '/protected')
- 
-@@ -348,12 +350,12 @@
-             davxml.Grant(davxml.Privilege(davxml.All())),
-             davxml.Protected()))
- 
--    def __init__(self, uri=None, children=None):
-+    def __init__(self, uri=None, children=None, principalCollections=()):
-         """
-         @param uri: A string respresenting the URI of the given resource
-         @param children: a dictionary of names to Resources
-         """
--
-+        DAVResource.__init__(self, principalCollections=principalCollections)
-         self.children = children
-         self.uri = uri
- 
-@@ -380,8 +382,8 @@
-         return succeed(davPrivilegeSet)
- 
-     def currentPrincipal(self, request):
--        if hasattr(request, "user"):
--            return request.user
-+        if hasattr(request, "authzUser"):
-+            return request.authzUser
-         else:
-             return davxml.Principal(davxml.Unauthenticated())
- 
-@@ -399,18 +401,21 @@
- 
-     def accessControlList(self, request, **kwargs):
-         return succeed(self.acl)
--    
- 
- class AuthAllResource (TestResource):
--    """Give Authenticated principals all privileges deny everything else
-     """
-+    Give Authenticated principals all privileges and deny everyone else.
-+    """
-     acl = davxml.ACL(
-         davxml.ACE(
-             davxml.Principal(davxml.Authenticated()),
-             davxml.Grant(davxml.Privilege(davxml.All())),
--            davxml.Protected()))
--
-+            davxml.Protected()
-+        )
-+    )
-     
- class TestDAVPrincipalResource(DAVPrincipalResource, TestResource):
--    """Get deadProperties from TestResource
--    """
-+    # Get dead properties from TestResource
-+
-+    def principalURL(self):
-+        return self.uri

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_static.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_static.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_static.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,10 +0,0 @@
-Index: twisted/web2/dav/test/test_static.py
-===================================================================
---- twisted/web2/dav/test/test_static.py	(revision 19773)
-+++ twisted/web2/dav/test/test_static.py	(working copy)
-@@ -60,3 +60,5 @@
-         d.addCallback(assertListing)
- 
-         return d
-+
-+    test_renderPrivileges.todo = "We changed the rules here; test needs an update"

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_stream.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_stream.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_stream.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,56 +0,0 @@
-Index: twisted/web2/dav/test/test_stream.py
-===================================================================
---- twisted/web2/dav/test/test_stream.py	(revision 0)
-+++ twisted/web2/dav/test/test_stream.py	(revision 0)
-@@ -0,0 +1,51 @@
-+##
-+# Copyright (c) 2005 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.
-+##
-+
-+from twisted.trial import unittest
-+from twisted.web2.stream import MemoryStream
-+from twisted.web2.dav.stream import MD5StreamWrapper
-+import md5
-+
-+class Stream(unittest.TestCase):
-+    """
-+    MD5StreamWrapper tests.
-+    """
-+    def test_simple(self):
-+        """
-+        Simple test
-+        """
-+        
-+        data = """I am sorry Dave, I can't do that.
-+--HAL 9000
-+"""
-+
-+        datastream = MemoryStream(data)
-+        stream = MD5StreamWrapper(datastream)
-+        
-+        while stream.read() is not None:
-+            pass
-+        stream.close()
-+
-+        m = md5.new()
-+        m.update(data)
-+        
-+        self.assertEqual(m.hexdigest(), stream.getMD5())

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_xml.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_xml.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.test_xml.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,88 +0,0 @@
-Index: twisted/web2/dav/test/test_xml.py
-===================================================================
---- twisted/web2/dav/test/test_xml.py	(revision 0)
-+++ twisted/web2/dav/test/test_xml.py	(revision 0)
-@@ -0,0 +1,83 @@
-+##
-+# Copyright (c) 2005 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.
-+##
-+
-+from twisted.trial import unittest
-+from twisted.web2.dav import davxml
-+
-+class XML(unittest.TestCase):
-+    """
-+    XML tests.
-+    """
-+    def test_parse(self):
-+        """
-+        Simple parsing
-+        """
-+        doc = davxml.WebDAVDocument.fromString(
-+            """<?xml version="1.0" encoding="utf-8" ?>"""
-+            """<D:multistatus xmlns:D="DAV:">"""
-+            """  <D:response>"""
-+            """    <D:href>http://webdav.sb.aol.com/webdav/secret</D:href>"""
-+            """    <D:status>HTTP/1.1 403 Forbidden</D:status>"""
-+            """  </D:response>"""
-+            """</D:multistatus>"""
-+        )
-+        self.assertEquals(
-+            doc,
-+            davxml.WebDAVDocument(
-+                davxml.MultiStatus(
-+                    davxml.Response(
-+                        davxml.HRef("http://webdav.sb.aol.com/webdav/secret"),
-+                        davxml.Status("HTTP/1.1 403 Forbidden"),
-+                    )
-+                )
-+            )
-+        )
-+
-+    def test_serialize_unserialize(self):
-+        """
-+        Serialization and unserialization results in equivalent document.
-+        """
-+        doc = davxml.WebDAVDocument(
-+            davxml.MultiStatus(
-+                davxml.Response(
-+                    davxml.HRef("http://webdav.sb.aol.com/webdav/secret"),
-+                    davxml.Status("HTTP/1.1 403 Forbidden"),
-+                )
-+            )
-+        )
-+        self.assertEquals(doc, davxml.WebDAVDocument.fromString(doc.toxml()))
-+
-+    def test_unknownElement(self):
-+        """
-+        Serialization and unserialization of unknown element.
-+        """
-+        doc = davxml.WebDAVDocument.fromString(
-+            """<?xml version="1.0" encoding="utf-8" ?>"""
-+            """<T:foo xmlns:T="http://twistedmatrix.com/"/>"""
-+        )
-+
-+        foo = davxml.WebDAVUnknownElement()
-+        foo.namespace = "http://twistedmatrix.com/"
-+        foo.name = "foo"
-+
-+        self.assertEquals(doc, davxml.WebDAVDocument(foo))
-+        self.assertEquals(doc, davxml.WebDAVDocument.fromString(doc.toxml()))

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.tworequest_client.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.tworequest_client.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.tworequest_client.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,52 +0,0 @@
-Index: twisted/web2/dav/test/tworequest_client.py
-===================================================================
---- twisted/web2/dav/test/tworequest_client.py	(revision 0)
-+++ twisted/web2/dav/test/tworequest_client.py	(revision 0)
-@@ -0,0 +1,47 @@
-+import socket, sys
-+
-+test_type = sys.argv[1]
-+port = int(sys.argv[2])
-+socket_type = sys.argv[3]
-+
-+s = socket.socket(socket.AF_INET)
-+s.connect(("127.0.0.1", port))
-+s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 40000)
-+
-+if socket_type == 'ssl':
-+    s2 = socket.ssl(s)
-+    send=s2.write
-+    recv=s2.read
-+else:
-+    send=s.send
-+    recv=s.recv
-+    
-+print >> sys.stderr, ">> Making %s request to port %d" % (socket_type, port)
-+
-+send("PUT /forbidden HTTP/1.1\r\n")
-+send("Host: localhost\r\n")
-+
-+print >> sys.stderr, ">> Sending lots of data"
-+send("Content-Length: 100\r\n\r\n")
-+send("X"*100)
-+
-+send("PUT /forbidden HTTP/1.1\r\n")
-+send("Host: localhost\r\n")
-+
-+print >> sys.stderr, ">> Sending lots of data"
-+send("Content-Length: 100\r\n\r\n")
-+send("X"*100)
-+
-+#import time
-+#time.sleep(5)
-+print >> sys.stderr, ">> Getting data"
-+data=''
-+while len(data) < 299999:
-+    try:
-+        x=recv(10000)
-+    except:
-+        break
-+    if x == '':
-+        break
-+    data+=x
-+sys.stdout.write(data)

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.util.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.util.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.test.util.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,20 +0,0 @@
-Index: twisted/web2/dav/test/util.py
-===================================================================
---- twisted/web2/dav/test/util.py	(revision 19773)
-+++ twisted/web2/dav/test/util.py	(working copy)
-@@ -184,10 +184,11 @@
-         d.addCallback(lambda resource: resource.renderHTTP(request))
-         d.addCallback(request._cbFinishRender)
- 
--        if type(callback) is tuple:
--            d.addCallbacks(*callback)
--        else:
--            d.addCallback(callback)
-+        if callback:
-+            if type(callback) is tuple:
-+                d.addCallbacks(*callback)
-+            else:
-+                d.addCallback(callback)
- 
-         return d
- 

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.util.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.util.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.util.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,23 +0,0 @@
-Index: twisted/web2/dav/util.py
-===================================================================
---- twisted/web2/dav/util.py	(revision 19773)
-+++ twisted/web2/dav/util.py	(working copy)
-@@ -37,6 +37,7 @@
-     "normalizeURL",
-     "joinURL",
-     "parentForURL",
-+    "unimplemented",
-     "bindMethods",
- ]
- 
-@@ -76,7 +77,9 @@
- 
-     def parse(xml):
-         try:
--            return davxml.WebDAVDocument.fromString(xml)
-+            doc = davxml.WebDAVDocument.fromString(xml)
-+            doc.root_element.validate()
-+            return doc
-         except ValueError:
-             log.err("Bad XML:\n%s" % (xml,))
-             raise

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.xattrprops.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.xattrprops.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.dav.xattrprops.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,169 +0,0 @@
-Index: twisted/web2/dav/xattrprops.py
-===================================================================
---- twisted/web2/dav/xattrprops.py	(revision 19773)
-+++ twisted/web2/dav/xattrprops.py	(working copy)
-@@ -33,15 +33,24 @@
- 
- import urllib
- import sys
-+import zlib
-+from time import sleep
-+from random import random
-+from errno import EAGAIN
-+from zlib import compress, decompress
-+from cPickle import loads as unpickle, PicklingError, UnpicklingError
- 
- import xattr
- 
- if getattr(xattr, 'xattr', None) is None:
-     raise ImportError("wrong xattr package imported")
- 
-+from twisted.python import log
-+from twisted.python.failure import Failure
- from twisted.web2 import responsecode
- from twisted.web2.http import HTTPError, StatusResponse
- from twisted.web2.dav import davxml
-+from twisted.web2.dav.http import statusForFailure
- 
- class xattrPropertyStore (object):
-     """
-@@ -66,16 +75,8 @@
-         deadPropertyXattrPrefix = "user."
- 
-     def _encode(clazz, name):
--        #
--        # FIXME: The xattr API in Mac OS 10.4.2 breaks if you have "/" in an
--        # attribute name (radar://4202440). We'll quote the strings to get rid
--        # of "/" characters for now.
--        #
--        result = list("{%s}%s" % name)
--        for i in range(len(result)):
--            c = result[i]
--            if c in "%/": result[i] = "%%%02X" % (ord(c),)
--        r = clazz.deadPropertyXattrPrefix + ''.join(result)
-+        result = urllib.quote("{%s}%s" % name, safe='{}:')
-+        r = clazz.deadPropertyXattrPrefix + result
-         return r
- 
-     def _decode(clazz, name):
-@@ -97,19 +98,83 @@
- 
-     def get(self, qname):
-         try:
--            value = self.attrs[self._encode(qname)]
-+            data = self.attrs[self._encode(qname)]
-         except KeyError:
-             raise HTTPError(StatusResponse(
-                 responsecode.NOT_FOUND,
-                 "No such property: {%s}%s" % qname
-             ))
-+        except Exception, e:
-+            raise HTTPError(StatusResponse(
-+                statusForFailure(Failure()),
-+                "Unable to read property: {%s}%s" % qname
-+            ))
- 
--        doc = davxml.WebDAVDocument.fromString(value)
-+        #
-+        # Serialize XML data to an xattr.  The storage format has
-+        # changed over time:
-+        #
-+        #  1- Started with XML
-+        #  2- Started compressing the XML due to limits on xattr size
-+        #  3- Switched to pickle which is faster, still compressing
-+        #  4- Back to compressed XML for interoperability, size
-+        #
-+        # We only write the current format, but we also read the old
-+        # ones for compatibility.
-+        #
-+        legacy = False
- 
--        return doc.root_element
-+        try:
-+            data = decompress(data)
-+        except zlib.error:
-+            legacy = True
- 
-+        try:
-+            doc = davxml.WebDAVDocument.fromString(data)
-+            result = doc.root_element
-+        except ValueError:
-+            legacy = True
-+
-+            try:
-+                result = unpickle(data)
-+            except UnpicklingError:
-+                msg = ("Invalid property value stored on server: {%s}%s %s"
-+                       % (qname[0], qname[1], data))
-+                log.err(msg)
-+                raise HTTPError(StatusResponse(responsecode.INTERNAL_SERVER_ERROR, msg))
-+
-+        if legacy:
-+            # Try to upgrade the property to the current format
-+            try:
-+                log.msg("Upgrading property %r on resource %r"
-+                        % (qname, self.resource.fp.path))
-+                self.set(result)
-+            except Exception, e:
-+                #
-+                # Hrm, that failed.  No need to re-raise here, since
-+                # we can do what we were asked to do, but let's
-+                # complain about it.
-+                #
-+                log.err("Error while upgrading property %r on resource %r: %s"
-+                        % (qname, self.resource.fp.path, e))
-+
-+        return result
-+
-     def set(self, property):
--        self.attrs[self._encode(property.qname())] = property.toxml()
-+        for n in range(20):
-+            data = compress(property.toxml())
-+            try:
-+                self.attrs[self._encode(property.qname())] = data
-+            except Exception, e:
-+                if e.errno == EAGAIN and n < 19:
-+                    sleep(random() / 10) # OMG Brutal Hax
-+                else:
-+                    raise HTTPError(StatusResponse(
-+                        statusForFailure(Failure()),
-+                        "Unable to write property: %s" % (property.sname(),)
-+                    ))
-+            else:
-+                break
- 
-         # Update the resource because we've modified it
-         self.resource.fp.restat()
-@@ -121,15 +186,31 @@
-             # RFC 2518 Section 12.13.1 says that removal of
-             # non-existing property is not an error.
-             pass
-+        except Exception, e:
-+            raise HTTPError(StatusResponse(
-+                statusForFailure(Failure()),
-+                "Unable to delete property: {%s}%s" % qname
-+            ))
- 
-     def contains(self, qname):
-         try:
-             return self._encode(qname) in self.attrs
-         except TypeError:
-             return False
-+        except Exception, e:
-+            raise HTTPError(StatusResponse(
-+                statusForFailure(Failure()),
-+                "Unable to read properties"
-+            ))
- 
-     def list(self):
-         prefix     = self.deadPropertyXattrPrefix
-         prefix_len = len(prefix)
- 
--        return [ self._decode(name) for name in self.attrs if name.startswith(prefix) ]
-+        try:
-+            return [ self._decode(name) for name in self.attrs if name.startswith(prefix) ]
-+        except Exception, e:
-+            raise HTTPError(StatusResponse(
-+                statusForFailure(Failure()),
-+                "Unable to list properties"
-+            ))

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.http.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.http.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.http.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,57 +0,0 @@
-Index: twisted/web2/http.py
-===================================================================
---- twisted/web2/http.py	(revision 19773)
-+++ twisted/web2/http.py	(working copy)
-@@ -26,7 +26,7 @@
- from twisted.web2 import http_headers
- from twisted.web2 import iweb
- from twisted.web2 import stream
--from twisted.web2.stream import IByteStream
-+from twisted.web2.stream import IByteStream, readAndDiscard
- 
- defaultPortForScheme = {'http': 80, 'https':443, 'ftp':21}
- 
-@@ -66,9 +66,9 @@
-             object.
-         @type codeOrResponse: C{int} or L{http.Response}
-         """
--        Exception.__init__(self)
--        self.response = iweb.IResponse(codeOrResponse)
--        self.args = str(self.response)
-+        response = iweb.IResponse(codeOrResponse)
-+        Exception.__init__(self, str(response))
-+        self.response = response
- 
-     def __repr__(self):
-         return "<%s %s>" % (self.__class__.__name__, self.response)
-@@ -408,9 +408,22 @@
-     def _sendContinue(self):
-         self.chanRequest.writeIntermediateResponse(responsecode.CONTINUE)
- 
--    def _finished(self, x):
-+    def _reallyFinished(self, x):
-         """We are finished writing data."""
-         self.chanRequest.finish()
-+        
-+    def _finished(self, x):
-+        """
-+        We are finished writing data.
-+        But we need to check that we have also finished reading all data as we
-+        might have sent a, for example, 401 response before we read any data.
-+        To make sure that the stream/producer sequencing works properly we need
-+        to discard the remaining data in the request.  
-+        """
-+        if self.stream.length != 0:
-+            return readAndDiscard(self.stream).addCallback(self._reallyFinished).addErrback(self._error)
-+        else:
-+            self._reallyFinished(x)
- 
-     def _error(self, reason):
-         if reason.check(error.ConnectionLost):
-@@ -471,5 +484,5 @@
- else:
-     components.registerAdapter(compat.OldResourceAdapter, resource.IResource, iweb.IOldNevowResource)
- 
--__all__ = ['HTTPError', 'NotModifiedResponse', 'Request', 'Response', 'checkIfRange', 'checkPreconditions', 'defaultPortForScheme', 'parseVersion', 'splitHostPort']
-+__all__ = ['HTTPError', 'NotModifiedResponse', 'Request', 'Response', 'StatusResponse', 'RedirectResponse', 'checkIfRange', 'checkPreconditions', 'defaultPortForScheme', 'parseVersion', 'splitHostPort']
- 

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.log.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.log.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.log.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,13 +0,0 @@
-Index: twisted/web2/log.py
-===================================================================
---- twisted/web2/log.py	(revision 19773)
-+++ twisted/web2/log.py	(working copy)
-@@ -88,7 +88,7 @@
- class LogWrapperResource(resource.WrapperResource):
-     def hook(self, request):
-         # Insert logger
--        request.addResponseFilter(logFilter, atEnd=True)
-+        request.addResponseFilter(logFilter, atEnd=True, onlyOnce=True)
- 
- monthname = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
-              'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.server.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.server.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.server.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,129 +0,0 @@
-Index: twisted/web2/server.py
-===================================================================
---- twisted/web2/server.py	(revision 19773)
-+++ twisted/web2/server.py	(working copy)
-@@ -26,6 +26,7 @@
- from twisted.web2 import http_headers
- from twisted.web2.filter.range import rangefilter
- from twisted.web2 import error
-+from twisted.web2.dav.util import joinURL
- 
- from twisted.web2 import version as web2_version
- from twisted import __version__ as twisted_version
-@@ -143,6 +144,9 @@
-                        error.defaultErrorHandler, defaultHeadersFilter]
-     
-     def __init__(self, *args, **kw):
-+        
-+        self.initTime = time.time()
-+
-         if kw.has_key('site'):
-             self.site = kw['site']
-             del kw['site']
-@@ -150,17 +154,36 @@
-             self._initialprepath = kw['prepathuri']
-             del kw['prepathuri']
- 
-+        self._resourcesByURL = {}
-+        self._urlsByResource = {}
-+
-         # Copy response filters from the class
-         self.responseFilters = self.responseFilters[:]
-         self.files = {}
-         self.resources = []
-         http.Request.__init__(self, *args, **kw)
-+        try:
-+            self.serverInstance = self.chanRequest.channel.transport.server.port
-+        except AttributeError:
-+            self.serverInstance = "Unknown"
- 
--    def addResponseFilter(self, f, atEnd=False):
-+    def addResponseFilter(self, filter, atEnd=False, onlyOnce=False):
-+        """
-+        Add a response filter to this request.
-+        Response filters are applied to the response to this request in order.
-+        @param filter: a callable which takes an response argument and returns
-+            a response object.
-+        @param atEnd: if C{True}, C{filter} is added at the end of the list of
-+            response filters; if C{False}, it is added to the beginning.
-+        @param onlyOnce: if C{True}, C{filter} is not added to the list of
-+            response filters if it already in the list.
-+        """
-+        if onlyOnce and filter in self.responseFilters:
-+            return
-         if atEnd:
--            self.responseFilters.append(f)
-+            self.responseFilters.append(filter)
-         else:
--            self.responseFilters.insert(0, f)
-+            self.responseFilters.insert(0, filter)
- 
-     def unparseURL(self, scheme=None, host=None, port=None,
-                    path=None, params=None, querystring=None, fragment=None):
-@@ -265,6 +288,7 @@
-         
-         d = defer.Deferred()
-         d.addCallback(self._getChild, self.site.resource, self.postpath)
-+        d.addCallback(self._rememberResource, "/" + "/".join(quote(s) for s in self.postpath))
-         d.addCallback(lambda res, req: res.renderHTTP(req), self)
-         d.addCallback(self._cbFinishRender)
-         d.addErrback(self._processingFailed)
-@@ -321,7 +345,6 @@
-         if newpath is StopTraversal:
-             # We need to rethink how to do this.
-             #if newres is res:
--                self._rememberResource(res, url)
-                 return res
-             #else:
-             #    raise ValueError("locateChild must not return StopTraversal with a resource other than self.")
-@@ -337,7 +360,6 @@
-                 self.prepath.append(self.postpath.pop(0))
- 
-         child = self._getChild(None, newres, newpath, updatepaths=updatepaths)
--        self._rememberResource(child, url)
- 
-         return child
- 
-@@ -347,6 +369,7 @@
-         """
-         Remember the URL of a visited resource.
-         """
-+        self._resourcesByURL[url] = resource
-         self._urlsByResource[resource] = url
-         return resource
- 
-@@ -386,7 +409,8 @@
-             The contained response will have a status code of
-             L{responsecode.BAD_REQUEST}.
-         """
--        if url is None: return None
-+        if url is None:
-+            return defer.succeed(None)
- 
-         #
-         # Parse the URL
-@@ -407,9 +431,13 @@
-                 "URL is not on this site (%s://%s/): %s" % (scheme, self.headers.getHeader("host"), url)
-             ))
- 
--        segments = path.split("/")
-+        # Look for cached value
-+        cached = self._resourcesByURL.get(path, None)
-+        if cached is not None:
-+            return defer.succeed(cached)
-+
-+        segments = unquote(path).split("/")
-         assert segments[0] == "", "URL path didn't begin with '/': %s" % (path,)
--        segments = map(unquote, segments[1:])
- 
-         def notFound(f):
-             f.trap(http.HTTPError)
-@@ -417,7 +445,7 @@
-                 return f
-             return None
- 
--        d = defer.maybeDeferred(self._getChild, None, self.site.resource, segments, updatepaths=False)
-+        d = defer.maybeDeferred(self._getChild, None, self.site.resource, segments[1:], updatepaths=False)
-         d.addCallback(self._rememberResource, path)
-         d.addErrback(notFound)
-         return d

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.static.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.static.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.static.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,33 +0,0 @@
-Index: twisted/web2/static.py
-===================================================================
---- twisted/web2/static.py	(revision 19773)
-+++ twisted/web2/static.py	(working copy)
-@@ -9,7 +9,6 @@
- # System Imports
- import os, time, stat
- import tempfile
--import md5
- 
- # Sibling Imports
- from twisted.web2 import http_headers, resource
-@@ -200,7 +199,10 @@
-         super(File, self).__init__()
- 
-         self.putChildren = {}
--        self.fp = filepath.FilePath(path)
-+        if isinstance(path, filepath.FilePath):
-+            self.fp = path
-+        else:
-+            self.fp = filepath.FilePath(path)
-         # Remove the dots from the path to split
-         self.defaultType = defaultType
-         self.ignoredExts = list(ignoredExts)
-@@ -383,7 +385,7 @@
-             return responsecode.NOT_FOUND
- 
-         if self.fp.isdir():
--            if req.uri[-1] != "/":
-+            if req.path[-1] != "/":
-                 # Redirect to include trailing '/' in URI
-                 return http.RedirectResponse(req.unparseURL(path=req.path+'/'))
-             else:

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.test.test_http.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.test.test_http.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.test.test_http.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,23 +0,0 @@
-Index: twisted/web2/test/test_http.py
-===================================================================
---- twisted/web2/test/test_http.py	(revision 19773)
-+++ twisted/web2/test/test_http.py	(working copy)
-@@ -324,6 +324,9 @@
-     def connectionLost(self, reason):
-         self.cmds.append(('connectionLost', reason))
- 
-+    def _finished(self, x):
-+        self._reallyFinished(x)
-+
- class TestResponse(object):
-     implements(iweb.IResponse)
- 
-@@ -1017,6 +1020,8 @@
-         response = TestResponse()
-         if self.uri == "/error":
-             response.code=402
-+        elif self.uri == "/forbidden":
-+            response.code=403
-         else:
-             response.code=404
-             response.write("URI %s unrecognized." % self.uri)

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.test.test_httpauth.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.test.test_httpauth.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.web2.test.test_httpauth.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,243 +0,0 @@
-Index: twisted/web2/test/test_httpauth.py
-===================================================================
---- twisted/web2/test/test_httpauth.py	(revision 19773)
-+++ twisted/web2/test/test_httpauth.py	(working copy)
-@@ -1,3 +1,4 @@
-+from twisted.internet.defer import inlineCallbacks
- import md5
- from twisted.internet import address
- from twisted.trial import unittest
-@@ -41,22 +42,25 @@
-         self.username = 'dreid'
-         self.password = 'S3CuR1Ty'
- 
-+    @inlineCallbacks
-     def testUsernamePassword(self):
-         response = base64.encodestring('%s:%s' % (
-                 self.username,
-                 self.password))
- 
--        creds = self.credentialFactory.decode(response, _trivial_GET)
-+        creds = (yield self.credentialFactory.decode(response, _trivial_GET))
-         self.failUnless(creds.checkPassword(self.password))
- 
-+    @inlineCallbacks
-     def testIncorrectPassword(self):
-         response = base64.encodestring('%s:%s' % (
-                 self.username,
-                 'incorrectPassword'))
- 
--        creds = self.credentialFactory.decode(response, _trivial_GET)
-+        creds = (yield self.credentialFactory.decode(response, _trivial_GET))
-         self.failIf(creds.checkPassword(self.password))
- 
-+    @inlineCallbacks
-     def testIncorrectPadding(self):
-         response = base64.encodestring('%s:%s' % (
-                 self.username,
-@@ -64,7 +68,7 @@
- 
-         response = response.strip('=')
- 
--        creds = self.credentialFactory.decode(response, _trivial_GET)
-+        creds = (yield self.credentialFactory.decode(response, _trivial_GET))
-         self.failUnless(creds.checkPassword(self.password))
- 
-     def testInvalidCredentials(self):
-@@ -135,6 +139,7 @@
-             )
-         return expected
- 
-+    @inlineCallbacks
-     def test_getChallenge(self):
-         """
-         Test that all the required fields exist in the challenge,
-@@ -142,42 +147,44 @@
-         DigestCredentialFactory
-         """
- 
--        challenge = self.credentialFactory.getChallenge(clientAddress)
-+        challenge = (yield self.credentialFactory.getChallenge(clientAddress))
-         self.assertEquals(challenge['qop'], 'auth')
-         self.assertEquals(challenge['realm'], 'test realm')
-         self.assertEquals(challenge['algorithm'], 'md5')
-         self.assertTrue(challenge.has_key("nonce"))
-         self.assertTrue(challenge.has_key("opaque"))
- 
-+    @inlineCallbacks
-     def test_response(self):
-         """
-         Test that we can decode a valid response to our challenge
-         """
- 
--        challenge = self.credentialFactory.getChallenge(clientAddress)
-+        challenge = (yield self.credentialFactory.getChallenge(clientAddress))
- 
-         clientResponse = authRequest1 % (
-             challenge['nonce'],
-             self.getDigestResponse(challenge, "00000001"),
-             challenge['opaque'])
- 
--        creds = self.credentialFactory.decode(clientResponse, _trivial_GET)
-+        creds = (yield self.credentialFactory.decode(clientResponse, _trivial_GET))
-         self.failUnless(creds.checkPassword('password'))
- 
-+    @inlineCallbacks
-     def test_multiResponse(self):
-         """
-         Test that multiple responses to to a single challenge are handled
-         successfully.
-         """
- 
--        challenge = self.credentialFactory.getChallenge(clientAddress)
-+        challenge = (yield self.credentialFactory.getChallenge(clientAddress))
- 
-         clientResponse = authRequest1 % (
-             challenge['nonce'],
-             self.getDigestResponse(challenge, "00000001"),
-             challenge['opaque'])
- 
--        creds = self.credentialFactory.decode(clientResponse, _trivial_GET)
-+        creds = (yield self.credentialFactory.decode(clientResponse, _trivial_GET))
-         self.failUnless(creds.checkPassword('password'))
- 
-         clientResponse = authRequest2 % (
-@@ -185,24 +192,25 @@
-             self.getDigestResponse(challenge, "00000002"),
-             challenge['opaque'])
- 
--        creds = self.credentialFactory.decode(clientResponse, _trivial_GET)
-+        creds = (yield self.credentialFactory.decode(clientResponse, _trivial_GET))
-         self.failUnless(creds.checkPassword('password'))
- 
-+    @inlineCallbacks
-     def test_failsWithDifferentMethod(self):
-         """
-         Test that the response fails if made for a different request method
-         than it is being issued for.
-         """
- 
--        challenge = self.credentialFactory.getChallenge(clientAddress)
-+        challenge = (yield self.credentialFactory.getChallenge(clientAddress))
- 
-         clientResponse = authRequest1 % (
-             challenge['nonce'],
-             self.getDigestResponse(challenge, "00000001"),
-             challenge['opaque'])
- 
--        creds = self.credentialFactory.decode(clientResponse,
--                                              SimpleRequest(None, 'POST', '/'))
-+        creds = (yield self.credentialFactory.decode(clientResponse,
-+                                              SimpleRequest(None, 'POST', '/')))
-         self.failIf(creds.checkPassword('password'))
- 
-     def test_noUsername(self):
-@@ -247,20 +255,21 @@
-                               _trivial_GET)
-         self.assertEquals(str(e), "Invalid response, no opaque given.")
- 
-+    @inlineCallbacks
-     def test_checkHash(self):
-         """
-         Check that given a hash of the form 'username:realm:password'
-         we can verify the digest challenge
-         """
- 
--        challenge = self.credentialFactory.getChallenge(clientAddress)
-+        challenge = (yield self.credentialFactory.getChallenge(clientAddress))
- 
-         clientResponse = authRequest1 % (
-             challenge['nonce'],
-             self.getDigestResponse(challenge, "00000001"),
-             challenge['opaque'])
- 
--        creds = self.credentialFactory.decode(clientResponse, _trivial_GET)
-+        creds = (yield self.credentialFactory.decode(clientResponse, _trivial_GET))
- 
-         self.failUnless(creds.checkHash(
-                 md5.md5('username:test realm:password').hexdigest()))
-@@ -268,6 +277,7 @@
-         self.failIf(creds.checkHash(
-                 md5.md5('username:test realm:bogus').hexdigest()))
- 
-+    @inlineCallbacks
-     def test_invalidOpaque(self):
-         """
-         Test that login fails when the opaque does not contain all the required
-@@ -276,7 +286,7 @@
- 
-         credentialFactory = FakeDigestCredentialFactory('md5', 'test realm')
- 
--        challenge = credentialFactory.getChallenge(clientAddress)
-+        challenge = (yield credentialFactory.getChallenge(clientAddress))
- 
-         self.assertRaises(
-             error.LoginFailed,
-@@ -302,6 +312,7 @@
-             challenge['nonce'],
-             clientAddress.host)
- 
-+    @inlineCallbacks
-     def test_incompatibleNonce(self):
-         """
-         Test that login fails when the given nonce from the response, does not
-@@ -310,7 +321,7 @@
- 
-         credentialFactory = FakeDigestCredentialFactory('md5', 'test realm')
- 
--        challenge = credentialFactory.getChallenge(clientAddress)
-+        challenge = (yield credentialFactory.getChallenge(clientAddress))
- 
-         badNonceOpaque = credentialFactory.generateOpaque(
-             '1234567890',
-@@ -330,6 +341,7 @@
-             '',
-             clientAddress.host)
- 
-+    @inlineCallbacks
-     def test_incompatibleClientIp(self):
-         """
-         Test that the login fails when the request comes from a client ip
-@@ -338,7 +350,7 @@
- 
-         credentialFactory = FakeDigestCredentialFactory('md5', 'test realm')
- 
--        challenge = credentialFactory.getChallenge(clientAddress)
-+        challenge = (yield credentialFactory.getChallenge(clientAddress))
- 
-         badNonceOpaque = credentialFactory.generateOpaque(
-             challenge['nonce'],
-@@ -351,6 +363,7 @@
-             challenge['nonce'],
-             clientAddress.host)
- 
-+    @inlineCallbacks
-     def test_oldNonce(self):
-         """
-         Test that the login fails when the given opaque is older than
-@@ -359,7 +372,7 @@
- 
-         credentialFactory = FakeDigestCredentialFactory('md5', 'test realm')
- 
--        challenge = credentialFactory.getChallenge(clientAddress)
-+        challenge = (yield credentialFactory.getChallenge(clientAddress))
- 
-         key = '%s,%s,%s' % (challenge['nonce'],
-                             clientAddress.host,
-@@ -376,6 +389,7 @@
-             challenge['nonce'],
-             clientAddress.host)
- 
-+    @inlineCallbacks
-     def test_mismatchedOpaqueChecksum(self):
-         """
-         Test that login fails when the opaque checksum fails verification
-@@ -383,7 +397,7 @@
- 
-         credentialFactory = FakeDigestCredentialFactory('md5', 'test realm')
- 
--        challenge = credentialFactory.getChallenge(clientAddress)
-+        challenge = (yield credentialFactory.getChallenge(clientAddress))
- 
- 
-         key = '%s,%s,%s' % (challenge['nonce'],

Deleted: CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.words.protocols.jabber.sasl_mechanisms.patch
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.words.protocols.jabber.sasl_mechanisms.patch	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/lib-patches/Twisted/twisted.words.protocols.jabber.sasl_mechanisms.patch	2009-05-01 17:19:57 UTC (rev 4130)
@@ -1,86 +0,0 @@
-Index: twisted/words/protocols/jabber/sasl_mechanisms.py
-===================================================================
---- twisted/words/protocols/jabber/sasl_mechanisms.py	(revision 19773)
-+++ twisted/words/protocols/jabber/sasl_mechanisms.py	(working copy)
-@@ -7,8 +7,13 @@
- Protocol agnostic implementations of SASL authentication mechanisms.
- """
- 
--import md5, binascii, random, time, os
-+import binascii, random, time, os
- 
-+try:
-+    from hashlib import md5
-+except ImportError:
-+    from md5 import new as md5
-+
- from zope.interface import Interface, Attribute, implements
- 
- class ISASLMechanism(Interface):
-@@ -108,16 +113,43 @@
-         @return: challenge directives and their values.
-         @rtype: L{dict} of L{str} to L{str}.
-         """
--        directive_list = challenge.split(',')
--        directives = {}
--        for directive in directive_list:
--            name, value = directive.split('=')
--            value = value.replace("'","")
--            value = value.replace('"','')
--            directives[name] = value
--        return directives
-+        s = challenge
-+        paramDict = {}
-+        cur = 0
-+        remainingParams = True
-+        while remainingParams:
-+            # Parse a param. We can't just split on commas, because there can
-+            # be some commas inside (quoted) param values, e.g.:
-+            # qop="auth,auth-int"
- 
-+            middle = s.index("=", cur)
-+            name = s[cur:middle].lstrip()
-+            middle += 1
-+            if s[middle] == '"':
-+                middle += 1
-+                end = s.index('"', middle)
-+                value = s[middle:end]
-+                cur = s.find(',', end) + 1
-+                if cur == 0:
-+                    remainingParams = False
-+            else:
-+                end = s.find(',', middle)
-+                if end == -1:
-+                    value = s[middle:].rstrip()
-+                    remainingParams = False
-+                else:
-+                    value = s[middle:end].rstrip()
-+                cur = end + 1
-+            paramDict[name] = value
- 
-+        for param in ('qop', 'cipher'):
-+            if param in paramDict:
-+                paramDict[param] = paramDict[param].split(',')
-+
-+        return paramDict
-+
-+
-+
-     def _unparse(self, directives):
-         """
-         Create message string from directives.
-@@ -153,7 +185,7 @@
-         """
- 
-         def H(s):
--            return md5.new(s).digest()
-+            return md5(s).digest()
- 
-         def HEX(n):
-             return binascii.b2a_hex(n)
-@@ -196,4 +228,4 @@
- 
- 
-     def _gen_nonce(self):
--        return md5.new("%s:%s:%s" % (str(random.random()) , str(time.gmtime()),str(os.getpid()))).hexdigest()
-+        return md5("%s:%s:%s" % (str(random.random()) , str(time.gmtime()),str(os.getpid()))).hexdigest()

Modified: CalendarServer/branches/exarkun/update-twisted-3816-3/run
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/run	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/run	2009-05-01 17:19:57 UTC (rev 4130)
@@ -104,7 +104,7 @@
     'R')       reactor="-R ${OPTARG}"; ;;
     't')  service_type="${OPTARG}"; ;;
     'K')      read_key="${OPTARG}"; ;;
-    'S')       profile="-p ${OPTARG}"; ;;
+    'S')       profile="--profiler cprofile -p ${OPTARG}"; ;;
     'g') do_get="true" ; do_setup="false"; do_run="false"; ;;
     's') do_get="true" ; do_setup="true" ; do_run="false"; ;;
     'p') do_get="false"; do_setup="false"; do_run="false"; print_path="true"; ;;
@@ -655,8 +655,8 @@
     proto="svn";
     ;;
 esac;
-svn_uri="${proto}://svn.twistedmatrix.com/svn/Twisted/branches/dav-acl-1608-4";
-svn_get "Twisted" "${twisted}" "${svn_uri}" 19773;
+svn_uri="${proto}://svn.twistedmatrix.com/svn/Twisted/branches/dav-take-two-3081-4";
+svn_get "Twisted" "${twisted}" "${svn_uri}" 26741;
 
 # No py_build step, since we tend to do edit Twisted, we want the sources in
 # PYTHONPATH, not a build directory.

Modified: CalendarServer/branches/exarkun/update-twisted-3816-3/twistedcaldav/notify.py
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/twistedcaldav/notify.py	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/twistedcaldav/notify.py	2009-05-01 17:19:57 UTC (rev 4130)
@@ -720,7 +720,8 @@
             return
 
         try:
-            iq = IQ(self.xmlStream, type='get')
+            # XXX This codepath is not unit tested
+            iq = IQ(self.xmlStream, 'get')
             child = iq.addElement('pubsub',
                 defaultUri=self.pubsubNS+"#owner")
             child = child.addElement('configure')
@@ -755,7 +756,7 @@
                     formElement = configureElement.firstChildElement()
                     if formElement['type'] == 'form':
                         # We've found the form; start building a response
-                        filledIq = IQ(self.xmlStream, type='set')
+                        filledIq = IQ(self.xmlStream, 'set')
                         filledPubSub = filledIq.addElement('pubsub',
                             defaultUri=self.pubsubNS+"#owner")
                         filledConfigure = filledPubSub.addElement('configure')
@@ -784,7 +785,8 @@
                                         valueElement.addContent(value)
                                         # filledForm.addChild(field)
                         if configMatches:
-                            cancelIq = IQ(self.xmlStream, type='set')
+                            # XXX This codepath is not unit tested
+                            cancelIq = IQ(self.xmlStream, 'set')
                             cancelPubSub = cancelIq.addElement('pubsub',
                                 defaultUri=self.pubsubNS+"#owner")
                             cancelConfig = cancelPubSub.addElement('configure')
@@ -892,7 +894,7 @@
     def requestRoster(self):
         if self.doRoster:
             self.roster = {}
-            rosterIq = IQ(self.xmlStream, type='get')
+            rosterIq = IQ(self.xmlStream, 'get')
             rosterIq.addElement("query", "jabber:iq:roster")
             d = rosterIq.send()
             d.addCallback(self.handleRoster)
@@ -950,7 +952,8 @@
             self.xmlStream.send(response)
 
             # remove from roster as well
-            removal = IQ(self.xmlStream, type='set')
+            # XXX This codepath is not unit tested
+            removal = IQ(self.xmlStream, 'set')
             query = removal.addElement("query", "jabber:iq:roster")
             query.addElement("item")
             query.item["jid"] = iq["from"]

Modified: CalendarServer/branches/exarkun/update-twisted-3816-3/twistedcaldav/test/test_index.py
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/twistedcaldav/test/test_index.py	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/twistedcaldav/test/test_index.py	2009-05-01 17:19:57 UTC (rev 4130)
@@ -19,7 +19,8 @@
 import twistedcaldav.test.util
 from twistedcaldav.test.util import InMemoryMemcacheProtocol
 from twistedcaldav.index import ReservationError, MemcachedUIDReserver
-from twisted.web2.test.test_http import deferLater
+from twisted.internet import reactor
+from twisted.internet.task import deferLater
 
 class SQLIndexTests (twistedcaldav.test.util.TestCase):
     """
@@ -80,7 +81,7 @@
         d.addCallback(lambda _: self.db.reserveUID(uid))
         d.addCallback(lambda _: self.db.isReservedUID(uid))
         d.addCallback(self.assertTrue)
-        d.addCallback(lambda _: deferLater(2))
+        d.addCallback(lambda _: deferLater(reactor, 2, lambda: None))
         d.addCallback(lambda _: self.db.isReservedUID(uid))
         d.addCallback(self.assertFalse)
         d.addBoth(_finally)

Modified: CalendarServer/branches/exarkun/update-twisted-3816-3/twistedcaldav/test/test_upgrade.py
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/twistedcaldav/test/test_upgrade.py	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/twistedcaldav/test/test_upgrade.py	2009-05-01 17:19:57 UTC (rev 4130)
@@ -188,7 +188,7 @@
         # Verify these values do require updating:
         #
 
-        expected = "<?xml version='1.0' encoding='UTF-8'?>\r\n<calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/calendar/</href>\r\n</calendar-free-busy-set>\r\n"
+        expected = "<?xml version='1.0' encoding='UTF-8'?><calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/calendar/</href>\r\n</calendar-free-busy-set>"
 
         # Uncompressed XML
         value = "<?xml version='1.0' encoding='UTF-8'?>\r\n<calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/users/wsanchez/calendar</href>\r\n</calendar-free-busy-set>\r\n"
@@ -314,7 +314,7 @@
                                 {
                                     "@xattrs" :
                                     {
-                                        freeBusyAttr : zlib.compress("<?xml version='1.0' encoding='UTF-8'?>\r\n<calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/calendar/</href>\r\n</calendar-free-busy-set>\r\n"),
+                                        freeBusyAttr : zlib.compress("<?xml version='1.0' encoding='UTF-8'?><calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/calendar/</href>\r\n</calendar-free-busy-set>"),
                                     },
                                 },
                             },
@@ -430,7 +430,7 @@
                                 {
                                     "@xattrs" :
                                     {
-                                        freeBusyAttr : zlib.compress("<?xml version='1.0' encoding='UTF-8'?>\r\n<calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/calendar/</href>\r\n</calendar-free-busy-set>\r\n"),
+                                        freeBusyAttr : zlib.compress("<?xml version='1.0' encoding='UTF-8'?><calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/calendar/</href>\r\n</calendar-free-busy-set>"),
                                     },
                                 },
                             },
@@ -550,7 +550,7 @@
                                 {
                                     "@xattrs" :
                                     {
-                                        freeBusyAttr : zlib.compress("<?xml version='1.0' encoding='UTF-8'?>\r\n<calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/calendar/</href>\r\n</calendar-free-busy-set>\r\n"),
+                                        freeBusyAttr : zlib.compress("<?xml version='1.0' encoding='UTF-8'?><calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/calendar/</href>\r\n</calendar-free-busy-set>"),
                                     },
                                 },
                             },

Modified: CalendarServer/branches/exarkun/update-twisted-3816-3/twistedcaldav/test/util.py
===================================================================
--- CalendarServer/branches/exarkun/update-twisted-3816-3/twistedcaldav/test/util.py	2009-05-01 17:16:27 UTC (rev 4129)
+++ CalendarServer/branches/exarkun/update-twisted-3816-3/twistedcaldav/test/util.py	2009-05-01 17:19:57 UTC (rev 4130)
@@ -117,6 +117,7 @@
                             try:
                                 if xattr.getxattr(childPath, attr) != value:
                                     print "Xattr mismatch:", childPath, attr
+                                    print (xattr.getxattr(childPath, attr), " != ", value)
                                     return False
                             except:
                                 return False
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20090501/8204b811/attachment-0001.html>


More information about the calendarserver-changes mailing list