[CalendarServer-changes] [5806] CalendarServer/branches/new-store
source_changes at macosforge.org
source_changes at macosforge.org
Tue Jun 22 16:38:47 PDT 2010
Revision: 5806
http://trac.macosforge.org/projects/calendarserver/changeset/5806
Author: glyph at apple.com
Date: 2010-06-22 16:38:46 -0700 (Tue, 22 Jun 2010)
Log Message:
-----------
Merge from trunk again, adjusting slightly for errors introduced by doing so.
Modified Paths:
--------------
CalendarServer/branches/new-store/calendarserver/tap/caldav.py
CalendarServer/branches/new-store/conf/caldavd-test.plist
CalendarServer/branches/new-store/conf/carddavd-test.plist
CalendarServer/branches/new-store/testserver
CalendarServer/branches/new-store/twext/web2/dav/method/delete.py
CalendarServer/branches/new-store/twext/web2/dav/resource.py
CalendarServer/branches/new-store/twistedcaldav/customxml.py
CalendarServer/branches/new-store/twistedcaldav/datafilters/peruserdata.py
CalendarServer/branches/new-store/twistedcaldav/datafilters/test/test_peruserdata.py
CalendarServer/branches/new-store/twistedcaldav/directory/principal.py
CalendarServer/branches/new-store/twistedcaldav/mail.py
CalendarServer/branches/new-store/twistedcaldav/method/put_common.py
CalendarServer/branches/new-store/twistedcaldav/resource.py
CalendarServer/branches/new-store/twistedcaldav/scheduling/utils.py
CalendarServer/branches/new-store/twistedcaldav/sharing.py
CalendarServer/branches/new-store/twistedcaldav/static.py
CalendarServer/branches/new-store/twistedcaldav/stdconfig.py
CalendarServer/branches/new-store/twistedcaldav/storebridge.py
CalendarServer/branches/new-store/twistedcaldav/test/data/calendar.10.tgz
CalendarServer/branches/new-store/twistedcaldav/test/data/calendar.100.tgz
CalendarServer/branches/new-store/twistedcaldav/test/data/calendar.1000.tgz
CalendarServer/branches/new-store/twistedcaldav/test/data/makelargecalendars.py
CalendarServer/branches/new-store/twistedcaldav/test/test_sharing.py
Added Paths:
-----------
CalendarServer/branches/new-store/twext/web2/dav/method/delete_common.py
Property Changed:
----------------
CalendarServer/branches/new-store/
Property changes on: CalendarServer/branches/new-store
___________________________________________________________________
Modified: svn:mergeinfo
- /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/wsanchez/transations:5515-5593
/CalendarServer/trunk:5594-5718
+ /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/wsanchez/transations:5515-5593
/CalendarServer/trunk:5594-5803
Modified: CalendarServer/branches/new-store/calendarserver/tap/caldav.py
===================================================================
--- CalendarServer/branches/new-store/calendarserver/tap/caldav.py 2010-06-22 23:35:33 UTC (rev 5805)
+++ CalendarServer/branches/new-store/calendarserver/tap/caldav.py 2010-06-22 23:38:46 UTC (rev 5806)
@@ -1354,6 +1354,7 @@
log.error("Could not get passphrase for %s: %s"
% (config.SSLPrivateKey, error))
else:
+ log.info("Obtained passphrase for %s" % (config.SSLPrivateKey))
return output.strip()
if (
Modified: CalendarServer/branches/new-store/conf/caldavd-test.plist
===================================================================
--- CalendarServer/branches/new-store/conf/caldavd-test.plist 2010-06-22 23:35:33 UTC (rev 5805)
+++ CalendarServer/branches/new-store/conf/caldavd-test.plist 2010-06-22 23:38:46 UTC (rev 5806)
@@ -686,8 +686,6 @@
<dict>
<key>Enabled</key>
<true/>
- <key>AllowScheduling</key>
- <false/>
</dict>
<key>AddressBooks</key>
<dict>
Modified: CalendarServer/branches/new-store/conf/carddavd-test.plist
===================================================================
--- CalendarServer/branches/new-store/conf/carddavd-test.plist 2010-06-22 23:35:33 UTC (rev 5805)
+++ CalendarServer/branches/new-store/conf/carddavd-test.plist 2010-06-22 23:38:46 UTC (rev 5806)
@@ -704,8 +704,6 @@
<dict>
<key>Enabled</key>
<false/>
- <key>AllowScheduling</key>
- <false/>
</dict>
<key>AddressBooks</key>
<dict>
Modified: CalendarServer/branches/new-store/testserver
===================================================================
--- CalendarServer/branches/new-store/testserver 2010-06-22 23:35:33 UTC (rev 5805)
+++ CalendarServer/branches/new-store/testserver 2010-06-22 23:38:46 UTC (rev 5806)
@@ -63,10 +63,10 @@
export PYTHONPATH=$("${wd}/run" -p);
-if [ ! -e "${documentroot}/calendars/__uids__/user01" ]; then
- curl "http://localhost:8008/calendars/__uids__/user01/";
+if [ ! -e "${documentroot}/calendars/__uids__/user09" ]; then
+ curl "http://localhost:8008/calendars/__uids__/user09/";
fi;
-python twistedcaldav/test/data/makelargecalendars.py -n 1 -d "${documentroot}";
+python twistedcaldav/test/data/makelargecalendars.py -o 9 -d "${documentroot}";
cd "${cdt}" && python testcaldav.py -s "${serverinfo}" "$@";
Modified: CalendarServer/branches/new-store/twext/web2/dav/method/delete.py
===================================================================
--- CalendarServer/branches/new-store/twext/web2/dav/method/delete.py 2010-06-22 23:35:33 UTC (rev 5805)
+++ CalendarServer/branches/new-store/twext/web2/dav/method/delete.py 2010-06-22 23:38:46 UTC (rev 5806)
@@ -35,7 +35,7 @@
from twext.web2 import responsecode
from twext.web2.http import HTTPError
from twext.web2.dav import davxml
-from twext.web2.dav.fileop import delete
+from twext.web2.dav.method.delete_common import deleteResource
from twext.web2.dav.util import parentForURL
log = Logger()
@@ -62,28 +62,8 @@
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))
+ x = waitForDeferred(deleteResource(request, self, request.uri, depth))
yield x
- result = x.getResult()
+ yield 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)
Copied: CalendarServer/branches/new-store/twext/web2/dav/method/delete_common.py (from rev 5803, CalendarServer/trunk/twext/web2/dav/method/delete_common.py)
===================================================================
--- CalendarServer/branches/new-store/twext/web2/dav/method/delete_common.py (rev 0)
+++ CalendarServer/branches/new-store/twext/web2/dav/method/delete_common.py 2010-06-22 23:38:46 UTC (rev 5806)
@@ -0,0 +1,72 @@
+# -*- test-case-name: twext.web2.dav.test.test_delete -*-
+##
+# Copyright (c) 2005-2010 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.
+##
+
+"""
+WebDAV DELETE method
+"""
+
+__all__ = ["deleteResource"]
+
+from twisted.internet.defer import waitForDeferred, deferredGenerator
+
+from twext.python.log import Logger
+from twext.web2 import responsecode
+from twext.web2.http import HTTPError
+from twext.web2.dav.fileop import delete
+
+log = Logger()
+
+
+def deleteResource(request, resource, resource_uri, depth="0"):
+ """
+ Handle a resource delete with proper quota etc updates
+ """
+ if not resource.fp.exists():
+ log.err("File not found: %s" % (resource.fp.path,))
+ raise HTTPError(responsecode.NOT_FOUND)
+
+ # Do quota checks before we start deleting things
+ myquota = waitForDeferred(resource.quota(request))
+ yield myquota
+ myquota = myquota.getResult()
+ if myquota is not None:
+ old_size = waitForDeferred(resource.quotaSize(request))
+ yield old_size
+ old_size = old_size.getResult()
+ else:
+ old_size = 0
+
+ # Do delete
+ x = waitForDeferred(delete(resource_uri, resource.fp, depth))
+ yield x
+ result = x.getResult()
+
+ # Adjust quota
+ if myquota is not None:
+ d = waitForDeferred(resource.quotaSizeAdjust(request, -old_size))
+ yield d
+ d.getResult()
+
+ yield result
+
+deleteResource = deferredGenerator(deleteResource)
Modified: CalendarServer/branches/new-store/twext/web2/dav/resource.py
===================================================================
--- CalendarServer/branches/new-store/twext/web2/dav/resource.py 2010-06-22 23:35:33 UTC (rev 5805)
+++ CalendarServer/branches/new-store/twext/web2/dav/resource.py 2010-06-22 23:38:46 UTC (rev 5806)
@@ -145,7 +145,7 @@
#(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, "current-user-principal" ), # RFC 5397, Section 3
(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
@@ -171,7 +171,7 @@
@return: a dict-like object from which one can read and to
which one can write dead properties. Keys are qname
- tuples (ie. C{(namespace, name)}) as returned by
+ tuples (i.e. C{(namespace, name)}) as returned by
L{davxml.WebDAVElement.qname()} and values are
L{davxml.WebDAVElement} instances.
"""
@@ -220,7 +220,7 @@
if namespace == dav_namespace:
if name == "resourcetype":
- # Allow live property to be overriden by dead property
+ # Allow live property to be overridden by dead property
if self.deadProperties().contains(qname):
return self.deadProperties().get(qname)
if self.isCollection():
@@ -1055,7 +1055,7 @@
access control list for this resource.
"""
#
- # The default behaviour is no ACL; we should inherrit from the parent
+ # The default behaviour is no ACL; we should inherit from the parent
# collection.
#
return davxml.ACL()
@@ -1274,7 +1274,7 @@
for. If C{None}, it is deduced from C{request} by calling
L{currentPrincipal}.
@param inherited_aces: a list of L{davxml.ACE}s corresponding
- to the precomputed inheritable aces from the parent
+ to the pre-computed inheritable aces from the parent
resource hierarchy.
@return: a L{Deferred} that callbacks with C{None} or errbacks
with an L{AccessDeniedError}
@@ -1533,8 +1533,8 @@
def principalsForAuthID(self, request, authid):
"""
- Return authentication and authorization prinicipal identifiers
- for the authentication identifer passed in. In this
+ Return authentication and authorization principal identifiers
+ for the authentication identifier passed in. In this
implementation authn and authz principals are the same.
@param request: the L{IRequest} for the request in progress.
@@ -1562,7 +1562,7 @@
def findPrincipalForAuthID(self, authid):
"""
Return authentication and authorization principal identifiers
- for the authentication identifer passed in. In this
+ for the authentication identifier passed in. In this
implementation authn and authz principals are the same.
@param authid: a string containing the
@@ -1583,7 +1583,7 @@
"""
Determine the authorization principal for the given request
and authentication principal. This implementation simply uses
- aht authentication principalk as the authoization principal.
+ that authentication principal as the authorization principal.
@param request: the L{IRequest} for the request in progress.
@param authid: a string containing the
@@ -1599,7 +1599,7 @@
def samePrincipal(self, principal1, principal2):
"""
- Check whether the two prinicpals are exactly the same in terms of
+ Check whether the two principals are exactly the same in terms of
elements and data.
@param principal1: a L{Principal} to test.
@@ -1965,7 +1965,7 @@
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
+ To determine that 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,
@@ -1980,7 +1980,7 @@
Get current available & used quota values for this resource's
quota root collection.
- @return: an L{Defered} with result C{tuple} containing two
+ @return: an L{Deferred} 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.
@@ -1993,60 +1993,45 @@
else:
request.quota = {}
- # Check this resource first
- if self.isCollection():
- qroot = self.quotaRoot(request)
- if qroot is not None:
- def gotUsage(used):
- available = qroot - used
- if available < 0:
- available = 0
- request.quota[self] = (available, used)
- return (available, used)
+ # Find the quota root for this resource and return its data
+ def gotQuotaRootResource(qroot_resource):
+ if qroot_resource:
+ qroot = qroot_resource.quotaRoot(request)
+ if qroot is not None:
+ def gotUsage(used):
+ available = qroot - used
+ if available < 0:
+ available = 0
+ request.quota[self] = (available, used)
+ return (available, used)
+
+ d = qroot_resource.currentQuotaUse(request)
+ d.addCallback(gotUsage)
+ return d
- d = self.currentQuotaUse(request)
- d.addCallback(gotUsage)
- return d
-
- # Check the next parent
- url = request.urlForResource(self)
- if url != "/":
- def gotQuota(quota):
- request.quota[self] = quota
- return quota
-
- d = request.locateResource(parentForURL(url))
- d.addCallback(lambda p: p.quota(request))
- d.addCallback(gotQuota)
- return d
- else:
request.quota[self] = None
+ return None
- return succeed(request.quota[self])
+
+ d = self.quotaRootResource(request)
+ d.addCallback(gotQuotaRootResource)
+ return d
def hasQuota(self, request):
"""
- Check whether this resource is undre quota control by checking
+ Check whether this resource is under 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 gotQuotaRootResource(qroot_resource):
+
+ return qroot_resource is not None
- # Check this one first
- if self.hasQuotaRoot(request):
- return succeed(True)
-
- # Look at each parent
- try:
- url = request.urlForResource(self)
- if url != "/":
- d = request.locateResource(parentForURL(url))
- d.addCallback(lambda p: p.hasQuota(request))
- return d
- else:
- return succeed(False)
- except NoURLForResourceError:
- return succeed(False)
+ d = self.quotaRootResource(request)
+ d.addCallback(gotQuotaRootResource)
+ return d
def hasQuotaRoot(self, request):
"""
@@ -2066,15 +2051,21 @@
return None
@inlineCallbacks
- def quotaRootParent(self, request):
+ def quotaRootResource(self, request):
"""
- Return the next quota root above this resource.
+ Return the quota root for this resource.
@return: L{DAVResource} or C{None}
"""
+ if self.hasQuotaRoot(request):
+ returnValue(self)
+
# Check the next parent
- url = request.urlForResource(self)
+ try:
+ url = request.urlForResource(self)
+ except NoURLForResourceError:
+ returnValue(None)
while (url != "/"):
url = parentForURL(url)
parent = (yield request.locateResource(url))
@@ -2082,11 +2073,11 @@
returnValue(parent)
returnValue(None)
-
+
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
+ for the contents of this collection, or C{None} to remove
quota restriction.
"""
assert self.isCollection(), "Only collections can have a quota root"
@@ -2111,7 +2102,6 @@
"""
unimplemented(self)
- @inlineCallbacks
def checkQuota(self, request, available):
"""
Check to see whether all quota roots have sufficient available
@@ -2125,19 +2115,20 @@
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]:
- returnValue(False)
+ def _defer(quotaroot):
+ if quotaroot:
+ # Check quota on this root (if it has one)
+ quota = quotaroot.quotaRoot(request)
+ if quota is not None:
+ if available > quota[0]:
+ return False
+
+ return True
- # Check the next parent with a quota root
- quotaroot = (yield quotaroot.quotaRootParent(request))
+ d = self.quotaRootResource(request)
+ d.addCallback(_defer)
+ return d
- returnValue(True)
-
def quotaSizeAdjust(self, request, adjust):
"""
Update the quota used value on all quota root parents of this
@@ -2148,19 +2139,15 @@
adjust the cached total.
"""
- # Check this resource first
- if self.isCollection():
- if self.hasQuotaRoot(request):
- return self.updateQuotaUse(request, adjust)
- # Check the next parent
- url = request.urlForResource(self)
- if url != "/":
- d = request.locateResource(parentForURL(url))
- d.addCallback(lambda p: p.quotaSizeAdjust(request, adjust))
- return d
+ def _defer(quotaroot):
+ if quotaroot:
+ # Check quota on this root (if it has one)
+ return quotaroot.updateQuotaUse(request, adjust)
- return succeed(None)
+ d = self.quotaRootResource(request)
+ d.addCallback(_defer)
+ return d
def currentQuotaUse(self, request):
"""
Modified: CalendarServer/branches/new-store/twistedcaldav/customxml.py
===================================================================
--- CalendarServer/branches/new-store/twistedcaldav/customxml.py 2010-06-22 23:35:33 UTC (rev 5805)
+++ CalendarServer/branches/new-store/twistedcaldav/customxml.py 2010-06-22 23:38:46 UTC (rev 5806)
@@ -37,6 +37,7 @@
import datetime
calendarserver_namespace = "http://calendarserver.org/ns/"
+mobileme_namespace = "http://cal.me.com/_namespace/"
calendarserver_proxy_compliance = (
"calendar-proxy",
@@ -298,7 +299,7 @@
}
allowed_children = {
(calendarserver_namespace, "subscription-url") : (1, 1),
- (calendarserver_namespace, "apsbundleid") : (1, 1),
+ (mobileme_namespace, "apsbundleid") : (1, 1),
(calendarserver_namespace, "xmpp-server") : (1, 1),
(calendarserver_namespace, "xmpp-uri") : (1, 1),
}
@@ -311,7 +312,7 @@
allowed_children = { (davxml.dav_namespace, "href"): (0, 1) }
class PubSubAPSBundleIDProperty (davxml.WebDAVTextElement):
- namespace = calendarserver_namespace
+ namespace = mobileme_namespace
name = "apsbundleid"
protected = True
hidden = True
Modified: CalendarServer/branches/new-store/twistedcaldav/datafilters/peruserdata.py
===================================================================
--- CalendarServer/branches/new-store/twistedcaldav/datafilters/peruserdata.py 2010-06-22 23:35:33 UTC (rev 5805)
+++ CalendarServer/branches/new-store/twistedcaldav/datafilters/peruserdata.py 2010-06-22 23:38:46 UTC (rev 5806)
@@ -124,6 +124,11 @@
# Make sure input is valid
icalnew = self.validCalendar(icalnew)
+
+ # There cannot be any X-CALENDARSERVER-PERUSER components in the new data
+ for component in tuple(icalnew.subcomponents()):
+ if component.name() == PerUserDataFilter.PERUSER_COMPONENT:
+ raise ValueError("Cannot merge calendar data with X-CALENDARSERVER-PERUSER components in it")
# First split the new data into common and per-user pieces
self._splitPerUserData(icalnew)
Modified: CalendarServer/branches/new-store/twistedcaldav/datafilters/test/test_peruserdata.py
===================================================================
--- CalendarServer/branches/new-store/twistedcaldav/datafilters/test/test_peruserdata.py 2010-06-22 23:35:33 UTC (rev 5805)
+++ CalendarServer/branches/new-store/twistedcaldav/datafilters/test/test_peruserdata.py 2010-06-22 23:38:46 UTC (rev 5806)
@@ -976,6 +976,35 @@
for item in (data, Component.fromString(data),):
self.assertEqual(str(PerUserDataFilter("").merge(item, None)), result02)
+ def test_prevent_injection(self):
+
+ data = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""".replace("\n", "\r\n")
+
+ for item in (data, Component.fromString(data),):
+ filter = PerUserDataFilter("user01")
+ self.assertRaises(ValueError, filter.merge, item, None)
+ for item in (data, Component.fromString(data),):
+ filter = PerUserDataFilter("")
+ self.assertRaises(ValueError, filter.merge, item, None)
+
class PerUserDataMergeTestNewRecurring (twistedcaldav.test.util.TestCase):
def test_public_noperuser(self):
Modified: CalendarServer/branches/new-store/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/branches/new-store/twistedcaldav/directory/principal.py 2010-06-22 23:35:33 UTC (rev 5805)
+++ CalendarServer/branches/new-store/twistedcaldav/directory/principal.py 2010-06-22 23:38:46 UTC (rev 5806)
@@ -518,6 +518,7 @@
(calendarserver_namespace, "first-name" ),
(calendarserver_namespace, "last-name" ),
(calendarserver_namespace, "email-address-set"),
+ davxml.ResourceID.qname(),
)
def __init__(self, parent, record):
@@ -557,7 +558,9 @@
namespace, name = qname
- if namespace == calendarserver_namespace:
+ if qname == davxml.ResourceID.qname():
+ returnValue(davxml.ResourceID(davxml.HRef.fromString("urn:uuid:%s" % (self.record.guid,))))
+ elif namespace == calendarserver_namespace:
if name == "first-name":
firstName = self.record.firstName
if firstName:
Modified: CalendarServer/branches/new-store/twistedcaldav/mail.py
===================================================================
--- CalendarServer/branches/new-store/twistedcaldav/mail.py 2010-06-22 23:35:33 UTC (rev 5805)
+++ CalendarServer/branches/new-store/twistedcaldav/mail.py 2010-06-22 23:38:46 UTC (rev 5806)
@@ -708,6 +708,17 @@
self.log_error("Mail gateway couldn't parse To: address (%s) in message %s" % (msg['To'], msg['Message-ID']))
return
+ result = self.db.lookupByToken(token)
+ if result is None:
+ # This isn't a token we recognize
+ self.log_error("Mail gateway found a token (%s) but didn't recognize it in message %s" % (token, msg['Message-ID']))
+ return
+
+ organizer, attendee, icaluid = result
+ organizer = str(organizer)
+ attendee = str(attendee)
+ icaluid = str(icaluid)
+
for part in msg.walk():
if part.get_content_type() == "text/calendar":
calBody = part.get_payload(decode=True)
@@ -715,23 +726,46 @@
else:
# No icalendar attachment
self.log_error("Mail gateway didn't find an icalendar attachment in message %s" % (msg['Message-ID'],))
- return
+ if not organizer.startswith("mailto:"):
+ self.log_error("Don't have an email address for the organizer; ignoring reply.")
+ return
+
+ # Forward this email to organizer
+ toAddr = organizer[7:]
+ fromAddr = attendee[7:]
+
+ settings = config.Scheduling["iMIP"]["Sending"]
+ if settings["UseSSL"]:
+ contextFactory = ssl.ClientContextFactory()
+ else:
+ contextFactory = None
+
+ deferred = defer.Deferred()
+ del msg["From"]
+ msg["From"] = fromAddr
+ del msg["Reply-To"]
+ msg["Reply-To"] = fromAddr
+ del msg["To"]
+ msg["To"] = toAddr
+ factory = ESMTPSenderFactory(
+ settings["Username"], settings["Password"],
+ fromAddr, toAddr, StringIO(str(msg)), deferred,
+ contextFactory=contextFactory,
+ requireAuthentication=False,
+ requireTransportSecurity=settings["UseSSL"],
+ )
+
+ reactor.connectTCP(settings["Server"], settings["Port"], factory)
+ return deferred
+
+
+ # Process the imip attachment; inject to calendar server
+
self.log_debug(calBody)
calendar = ical.Component.fromString(calBody)
event = calendar.mainComponent()
- # process mail messages from POP or IMAP, inject to calendar server
- result = self.db.lookupByToken(token)
- if result is None:
- # This isn't a token we recognize
- self.log_error("Mail gateway found a token (%s) but didn't recognize it in message %s" % (token, msg['Message-ID']))
- return
-
- organizer, attendee, icaluid = result
- organizer = str(organizer)
- attendee = str(attendee)
- icaluid = str(icaluid)
calendar.removeAllButOneAttendee(attendee)
organizerProperty = calendar.getOrganizerProperty()
if organizerProperty is None:
Modified: CalendarServer/branches/new-store/twistedcaldav/method/put_common.py
===================================================================
--- CalendarServer/branches/new-store/twistedcaldav/method/put_common.py 2010-06-22 23:35:33 UTC (rev 5805)
+++ CalendarServer/branches/new-store/twistedcaldav/method/put_common.py 2010-06-22 23:38:46 UTC (rev 5806)
@@ -672,6 +672,16 @@
))
if do_implicit_action and self.allowImplicitSchedule:
+
+ # Cannot do implicit in sharee's shared calendar
+ isvirt = (yield self.destinationparent.isVirtualShare(self.request))
+ if isvirt:
+ raise HTTPError(ErrorResponse(
+ responsecode.FORBIDDEN,
+ (calendarserver_namespace, "sharee-privilege-needed",),
+ description="Sharee's cannot schedule"
+ ))
+
new_calendar = (yield scheduler.doImplicitScheduling(self.schedule_tag_match))
if new_calendar:
if isinstance(new_calendar, int):
@@ -698,7 +708,12 @@
# and we should not change it. This is not ideal as we may duplicate it unnecessarily
# but we currently have no api to let the caller tell us whether it cares about the
# whether the calendar data is changed or not.
- self.calendar = PerUserDataFilter(accessUID).merge(self.calendar.duplicate(), oldCal)
+ try:
+ self.calendar = PerUserDataFilter(accessUID).merge(self.calendar.duplicate(), oldCal)
+ except ValueError:
+ msg = "Invalid per-user data merge"
+ log.err(msg)
+ raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-calendar-data"), description=msg))
self.calendardata = None
@inlineCallbacks
Modified: CalendarServer/branches/new-store/twistedcaldav/resource.py
===================================================================
--- CalendarServer/branches/new-store/twistedcaldav/resource.py 2010-06-22 23:35:33 UTC (rev 5805)
+++ CalendarServer/branches/new-store/twistedcaldav/resource.py 2010-06-22 23:38:46 UTC (rev 5806)
@@ -1215,6 +1215,34 @@
"""
return None
+ @inlineCallbacks
+ def quotaRootResource(self, request):
+ """
+ Return the quota root for this resource.
+
+ @return: L{DAVResource} or C{None}
+ """
+
+ sharedParent = None
+ isvirt = (yield self.isVirtualShare(request))
+ if isvirt:
+ # A virtual share's quota root is the resource owner's root
+ sharedParent = (yield request.locateResource(parentForURL(self._share.hosturl)))
+ else:
+ parent = (yield self.locateParent(request, request.urlForResource(self)))
+ if isCalendarCollectionResource(parent) or isAddressBookCollectionResource(parent):
+ isvirt = (yield parent.isVirtualShare(request))
+ if isvirt:
+ # A virtual share's quota root is the resource owner's root
+ sharedParent = (yield request.locateResource(parentForURL(parent._share.hosturl)))
+
+ if sharedParent:
+ result = (yield sharedParent.quotaRootResource(request))
+ else:
+ result = (yield super(CalDAVResource, self).quotaRootResource(request))
+
+ returnValue(result)
+
class CalendarPrincipalCollectionResource (DAVPrincipalCollectionResource, CalDAVResource):
"""
CalDAV principal collection.
Modified: CalendarServer/branches/new-store/twistedcaldav/scheduling/utils.py
===================================================================
--- CalendarServer/branches/new-store/twistedcaldav/scheduling/utils.py 2010-06-22 23:35:33 UTC (rev 5805)
+++ CalendarServer/branches/new-store/twistedcaldav/scheduling/utils.py 2010-06-22 23:38:46 UTC (rev 5806)
@@ -14,12 +14,12 @@
# limitations under the License.
##
-from twisted.internet.defer import inlineCallbacks, succeed, returnValue
+from twisted.internet.defer import inlineCallbacks, returnValue
from twistedcaldav.method import report_common
from twext.web2.dav.util import joinURL
@inlineCallbacks
-def getCalendarObjectForPrincipals(request, principal, uid):
+def getCalendarObjectForPrincipals(request, principal, uid, allow_shared=False):
"""
Get a copy of the event for a principal.
"""
@@ -40,8 +40,13 @@
request._rememberResource(calendar_home, calendar_home.url())
# Run a UID query against the UID
+ @inlineCallbacks
+ def queryCalendarCollection(collection, uri):
+ if not allow_shared:
+ isvirt = (yield collection.isVirtualShare(request))
+ if isvirt:
+ returnValue(True)
- def queryCalendarCollection(collection, uri):
rname = collection.index().resourceNameForUID(uid)
if rname:
resource = collection.getChild(rname)
@@ -51,9 +56,9 @@
result["resource_name"] = rname
result["calendar_collection"] = collection
result["calendar_collection_uri"] = uri
- return succeed(False)
+ returnValue(False)
else:
- return succeed(True)
+ returnValue(True)
# NB We are by-passing privilege checking here. That should be OK as the data found is not
# exposed to the user.
Modified: CalendarServer/branches/new-store/twistedcaldav/sharing.py
===================================================================
--- CalendarServer/branches/new-store/twistedcaldav/sharing.py 2010-06-22 23:35:33 UTC (rev 5805)
+++ CalendarServer/branches/new-store/twistedcaldav/sharing.py 2010-06-22 23:38:46 UTC (rev 5806)
@@ -70,11 +70,6 @@
def upgradeToShare(self, request):
""" Upgrade this collection to a shared state """
- # For calendars we only allow upgrades is shared-scheduling is on
- if request.method not in ("MKCALENDAR", "MKCOL") and self.isCalendarCollection() and \
- not config.Sharing.Calendars.AllowScheduling and len(self.listChildren()) != 0:
- raise HTTPError(StatusResponse(responsecode.FORBIDDEN, "Cannot upgrade to shared calendar"))
-
# Change resourcetype
rtype = (yield self.resourceType(request))
rtype = davxml.ResourceType(*(rtype.children + (customxml.SharedOwner(),)))
@@ -95,8 +90,8 @@
self.writeDeadProperty(rtype)
# Remove all invitees
- records = self.invitesDB().allRecords()
- yield self.uninviteUserToShare([record.userid for record in records], None, request)
+ for record in self.invitesDB().allRecords():
+ yield self.uninviteRecordFromShare(record, request)
# Remove invites database
self.invitesDB().remove()
@@ -111,7 +106,7 @@
return succeed(True)
@inlineCallbacks
- def changeUserInviteState(self, request, inviteUID, userid, state, summary=None):
+ def changeUserInviteState(self, request, inviteUID, principalURL, state, summary=None):
shared = (yield self.isShared(request))
if not shared:
@@ -122,7 +117,7 @@
))
record = self.invitesDB().recordForInviteUID(inviteUID)
- if record is None or record.userid != userid:
+ if record is None or record.principalURL != principalURL:
raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
(customxml.calendarserver_namespace, "valid-request"),
@@ -369,14 +364,14 @@
# First try to resolve as a principal
principal = self.principalForCalendarUserAddress(userid)
if principal:
- return principal.principalURL(), principal.displayName()
+ return userid, principal.principalURL(), principal.displayName()
# TODO: we do not support external users right now so this is being hard-coded
# off in spite of the config option.
#elif config.Sharing.AllowExternalUsers:
- # return userid, cn
+ # return userid, None, cn
else:
- return None, None
+ return None, None, None
def validateInvites(self):
"""
@@ -395,19 +390,23 @@
@param ace: Must be one of customxml.ReadWriteAccess or customxml.ReadAccess
"""
- # Check for valid userid first
- userid, cn = self.validUserIDWithCommonNameForShare(userid, cn)
- if userid is None:
- return succeed(False)
-
# TODO: Check if this collection is shared, and error out if it isn't
+ resultIsList = True
if type(userid) is not list:
userid = [userid]
+ resultIsList = False
if type(cn) is not list:
cn = [cn]
+ def _defer(resultset):
+ results = [result if success else False for success, result in resultset]
+ if resultIsList:
+ return results
+ else:
+ return results[0]
+
dl = [self.inviteSingleUserToShare(user, cn, ace, summary, request) for user, cn in zip(userid, cn)]
- return DeferredList(dl).addCallback(lambda _:True)
+ return DeferredList(dl).addCallback(_defer)
def uninviteUserToShare(self, userid, ace, request):
""" Send out in uninvite first, and then remove this user from the share list."""
@@ -423,29 +422,41 @@
def inviteUserUpdateToShare(self, userid, cn, aceOLD, aceNEW, summary, request):
- # Check for valid userid first
- userid, cn = self.validUserIDWithCommonNameForShare(userid, cn)
- if userid is None:
- return succeed(False)
-
+ resultIsList = True
if type(userid) is not list:
userid = [userid]
+ resultIsList = False
if type(cn) is not list:
cn = [cn]
+
+ def _defer(resultset):
+ results = [result if success else False for success, result in resultset]
+ if resultIsList:
+ return results
+ else:
+ return results[0]
+
dl = [self.inviteSingleUserUpdateToShare(user, cn, aceOLD, aceNEW, summary, request) for user, cn in zip(userid, cn)]
- return DeferredList(dl).addCallback(lambda _:True)
+ return DeferredList(dl).addCallback(_defer)
@inlineCallbacks
def inviteSingleUserToShare(self, userid, cn, ace, summary, request):
+ # Validate userid and cn
+ userid, principalURL, cn = self.validUserIDWithCommonNameForShare(userid, cn)
+
+ # We currently only handle local users
+ if principalURL is None:
+ returnValue(False)
+
# Look for existing invite and update its fields or create new one
- record = self.invitesDB().recordForUserID(userid)
+ record = self.invitesDB().recordForPrincipalURL(principalURL)
if record:
record.name = cn
record.access = inviteAccessMapFromXML[type(ace)]
record.summary = summary
else:
- record = Invite(str(uuid4()), userid, cn, inviteAccessMapFromXML[type(ace)], "NEEDS-ACTION", summary)
+ record = Invite(str(uuid4()), userid, principalURL, cn, inviteAccessMapFromXML[type(ace)], "NEEDS-ACTION", summary)
# Send invite
yield self.sendInvite(record, request)
@@ -455,16 +466,15 @@
returnValue(True)
- @inlineCallbacks
def uninviteSingleUserFromShare(self, userid, aces, request):
- newuserid = self.validUserIDForShare(userid)
- if newuserid:
- userid = newuserid
-
- # Cancel invites
+ # Cancel invites - we'll just use whatever userid we are given
record = self.invitesDB().recordForUserID(userid)
+ return self.uninviteRecordFromShare(record, request) if record else succeed(True)
+ @inlineCallbacks
+ def uninviteRecordFromShare(self, record, request):
+
# Remove any shared calendar or address book
sharee = self.principalForCalendarUserAddress(record.userid)
if sharee:
@@ -483,7 +493,7 @@
yield self.sendInvite(record, request)
# Remove from database
- self.invitesDB().removeRecordForUserID(userid)
+ self.invitesDB().removeRecordForUserID(record.userid)
returnValue(True)
@@ -614,8 +624,7 @@
def _autoShare(isShared, request):
if not isShared:
- if not self.isCalendarCollection() or config.Sharing.Calendars.AllowScheduling or len(self.listChildren()) == 0:
- return self.upgradeToShare(request)
+ return self.upgradeToShare(request)
else:
return succeed(True)
raise HTTPError(StatusResponse(responsecode.FORBIDDEN, "Cannot upgrade to shared calendar"))
@@ -751,9 +760,10 @@
class Invite(object):
- def __init__(self, inviteuid, userid, common_name, access, state, summary):
+ def __init__(self, inviteuid, userid, principalURL, common_name, access, state, summary):
self.inviteuid = inviteuid
self.userid = userid
+ self.principalURL = principalURL
self.name = common_name
self.access = access
self.state = state
@@ -800,6 +810,11 @@
row = self._db_execute("select * from INVITE where USERID = :1", userid)
return self._makeRecord(row[0]) if row else None
+ def recordForPrincipalURL(self, principalURL):
+
+ row = self._db_execute("select * from INVITE where PRINCIPALURL = :1", principalURL)
+ return self._makeRecord(row[0]) if row else None
+
def recordForInviteUID(self, inviteUID):
row = self._db_execute("select * from INVITE where INVITEUID = :1", inviteUID)
@@ -807,15 +822,19 @@
def addOrUpdateRecord(self, record):
- self._db_execute("""insert or replace into INVITE (INVITEUID, USERID, NAME, ACCESS, STATE, SUMMARY)
- values (:1, :2, :3, :4, :5, :6)
- """, record.inviteuid, record.userid, record.name, record.access, record.state, record.summary,
+ self._db_execute("""insert or replace into INVITE (INVITEUID, USERID, PRINCIPALURL, NAME, ACCESS, STATE, SUMMARY)
+ values (:1, :2, :3, :4, :5, :6, :7)
+ """, record.inviteuid, record.userid, record.principalURL, record.name, record.access, record.state, record.summary,
)
def removeRecordForUserID(self, userid):
self._db_execute("delete from INVITE where USERID = :1", userid)
+ def removeRecordForPrincipalURL(self, principalURL):
+
+ self._db_execute("delete from INVITE where PRINCIPALURL = :1", principalURL)
+
def removeRecordForInviteUID(self, inviteUID):
self._db_execute("delete from INVITE where INVITEUID = :1", inviteUID)
@@ -846,6 +865,7 @@
# INVITE table is the primary table
# INVITEUID: UID for this invite
# USERID: identifier of invitee
+ # PRINCIPALURL: principal-URL of invitee
# NAME: common name of invitee
# ACCESS: Access mode for share
# STATE: Invite response status
@@ -856,6 +876,7 @@
create table INVITE (
INVITEUID text unique,
USERID text unique,
+ PRINCIPALURL text unique,
NAME text,
ACCESS text,
STATE text,
@@ -871,6 +892,11 @@
)
q.execute(
"""
+ create index PRINCIPALURL on INVITE (PRINCIPALURL)
+ """
+ )
+ q.execute(
+ """
create index INVITEUID on INVITE (INVITEUID)
"""
)
@@ -984,7 +1010,7 @@
@inlineCallbacks
def declineShare(self, request, hostUrl, inviteUID):
- # Remove it if its in the DB
+ # Remove it if it is in the DB
yield self.removeShareByUID(request, inviteUID)
yield self._changeShare(request, "DECLINED", hostUrl, inviteUID)
Modified: CalendarServer/branches/new-store/twistedcaldav/static.py
===================================================================
--- CalendarServer/branches/new-store/twistedcaldav/static.py 2010-06-22 23:35:33 UTC (rev 5805)
+++ CalendarServer/branches/new-store/twistedcaldav/static.py 2010-06-22 23:38:46 UTC (rev 5806)
@@ -57,9 +57,10 @@
from twext.web2.http import HTTPError, StatusResponse
from twext.web2.dav import davxml
from twext.web2.dav.element.base import dav_namespace
-from twext.web2.dav.fileop import mkcollection, rmdir, delete
+from twext.web2.dav.fileop import mkcollection, rmdir
from twext.web2.dav.http import ErrorResponse
from twext.web2.dav.idav import IDAVResource
+from twext.web2.dav.method import put_common, delete_common
from twext.web2.dav.noneprops import NonePropertyStore
from twext.web2.dav.resource import AccessDeniedError
from twext.web2.dav.resource import davPrivilegeSet
@@ -1467,15 +1468,25 @@
# TODO: use the generic StoreObject api so that quota, sync-token etc all get changed properly
child = self.createSimilarFile(self.fp.child(rname).path)
- child.fp.setContent(xmldata)
- child.writeDeadProperty(davxml.GETContentType.fromString(generateContentType(MimeType("text", "xml", params={"charset":"utf-8"}))))
- child.writeDeadProperty(customxml.NotificationType(xmltype))
-
- return succeed(True)
+ def _defer(_):
+ child.writeDeadProperty(davxml.GETContentType.fromString(generateContentType(MimeType("text", "xml", params={"charset":"utf-8"}))))
+ child.writeDeadProperty(customxml.NotificationType(xmltype))
+ return True
+ url = request.urlForResource(self)
+ url = joinURL(url, rname)
+ request._rememberResource(child, url)
+ d = put_common.storeResource(request, data=xmldata, destination=child, destination_uri=url)
+ d.addCallback(_defer)
+ return d
+
+
def _deleteNotification(self, request, rname):
- childfp = self.fp.child(rname)
- return delete("", childfp)
+ child = self.createSimilarFile(self.fp.child(rname).path)
+ url = request.urlForResource(self)
+ url = joinURL(url, rname)
+ request._rememberResource(child, url)
+ return delete_common.deleteResource(request, child, url)
class NotificationFile(NotificationResource, CalDAVFile):
Modified: CalendarServer/branches/new-store/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/branches/new-store/twistedcaldav/stdconfig.py 2010-06-22 23:35:33 UTC (rev 5805)
+++ CalendarServer/branches/new-store/twistedcaldav/stdconfig.py 2010-06-22 23:38:46 UTC (rev 5806)
@@ -338,7 +338,6 @@
"Calendars" : {
"Enabled" : True, # Calendar on/off switch
- "AllowScheduling" : False, # Scheduling in shared calendars
},
"AddressBooks" : {
"Enabled" : True, # Address Books on/off switch
Modified: CalendarServer/branches/new-store/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/branches/new-store/twistedcaldav/storebridge.py 2010-06-22 23:35:33 UTC (rev 5805)
+++ CalendarServer/branches/new-store/twistedcaldav/storebridge.py 2010-06-22 23:38:46 UTC (rev 5806)
@@ -204,8 +204,11 @@
home = self.parent._newStoreCalendarHome
storage = home.calendarWithName("inbox")
if storage is None:
- # FIXME: spurious error, sanity check, should not be needed
- raise RuntimeError("backend should be handling this for us")
+ # raise RuntimeError("backend should be handling this for us")
+ # FIXME: spurious error, sanity check, should not be needed;
+ # unfortunately, user09's calendar home does not have an inbox, so
+ # this is a temporary workaround.
+ home.createCalendarWithName("inbox")
storage = home.calendarWithName("inbox")
self._initializeWithCalendar(
storage,
Modified: CalendarServer/branches/new-store/twistedcaldav/test/data/calendar.10.tgz
===================================================================
(Binary files differ)
Modified: CalendarServer/branches/new-store/twistedcaldav/test/data/calendar.100.tgz
===================================================================
(Binary files differ)
Modified: CalendarServer/branches/new-store/twistedcaldav/test/data/calendar.1000.tgz
===================================================================
(Binary files differ)
Modified: CalendarServer/branches/new-store/twistedcaldav/test/data/makelargecalendars.py
===================================================================
--- CalendarServer/branches/new-store/twistedcaldav/test/data/makelargecalendars.py 2010-06-22 23:35:33 UTC (rev 5805)
+++ CalendarServer/branches/new-store/twistedcaldav/test/data/makelargecalendars.py 2010-06-22 23:38:46 UTC (rev 5806)
@@ -24,13 +24,16 @@
wd = os.path.dirname(__file__)
document_root = "."
user_max = 99
+ user_one = None
calendars = ("calendar.10", "calendar.100", "calendar.1000",)
- options, args = getopt.getopt(sys.argv[1:], "n:d:")
+ options, args = getopt.getopt(sys.argv[1:], "n:d:o:")
for option, value in options:
if option == "-n":
user_max = int(value)
+ elif option == "-o":
+ user_one = int(value)
elif option == "-d":
document_root = os.path.abspath(value)
else:
@@ -38,7 +41,7 @@
raise ValueError
- for ctr in xrange(1, user_max + 1):
+ for ctr in (xrange(user_one, user_one + 1) if user_one else xrange(1, user_max + 1)):
path = os.path.join(document_root, "calendars/__uids__/us/er/user%02d" % (ctr,))
try: os.makedirs(path)
Modified: CalendarServer/branches/new-store/twistedcaldav/test/test_sharing.py
===================================================================
--- CalendarServer/branches/new-store/twistedcaldav/test/test_sharing.py 2010-06-22 23:35:33 UTC (rev 5805)
+++ CalendarServer/branches/new-store/twistedcaldav/test/test_sharing.py 2010-06-22 23:38:46 UTC (rev 5806)
@@ -74,12 +74,12 @@
return userid
if userid.endswith("@example.com"):
principal = SharingTests.FakePrincipal(userid)
- return principal.path if len(args) == 0 else (principal.path, principal.displayname,)
+ return principal.path if len(args) == 0 else (userid, principal.path, principal.displayname,)
else:
- return None if len(args) == 0 else (None, None,)
+ return None if len(args) == 0 else (None, None, None,)
def _fakeInvalidUserID(self, userid, *args):
- return None if len(args) == 0 else (None, None,)
+ return None if len(args) == 0 else (None, None, None,)
@inlineCallbacks
def _doPOST(self, body, resultcode = responsecode.OK):
@@ -183,7 +183,7 @@
self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
customxml.InviteUser(
customxml.UID.fromString(""),
- davxml.HRef.fromString("/principals/__uids__/user02"),
+ davxml.HRef.fromString("mailto:user02 at example.com"),
customxml.CommonName.fromString("USER02"),
customxml.InviteAccess(customxml.ReadWriteAccess()),
customxml.InviteStatusNoResponse(),
@@ -213,7 +213,7 @@
self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
customxml.InviteUser(
customxml.UID.fromString(""),
- davxml.HRef.fromString("/principals/__uids__/user02"),
+ davxml.HRef.fromString("mailto:user02 at example.com"),
customxml.CommonName.fromString("USER02"),
customxml.InviteAccess(customxml.ReadWriteAccess()),
customxml.InviteStatusNoResponse(),
@@ -254,7 +254,7 @@
self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
customxml.InviteUser(
customxml.UID.fromString(""),
- davxml.HRef.fromString("/principals/__uids__/user02"),
+ davxml.HRef.fromString("mailto:user02 at example.com"),
customxml.CommonName.fromString("USER02"),
customxml.InviteAccess(customxml.ReadAccess()),
customxml.InviteStatusNoResponse(),
@@ -320,21 +320,21 @@
self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
customxml.InviteUser(
customxml.UID.fromString(""),
- davxml.HRef.fromString("/principals/__uids__/user02"),
+ davxml.HRef.fromString("mailto:user02 at example.com"),
customxml.CommonName.fromString("USER02"),
customxml.InviteAccess(customxml.ReadWriteAccess()),
customxml.InviteStatusNoResponse(),
),
customxml.InviteUser(
customxml.UID.fromString(""),
- davxml.HRef.fromString("/principals/__uids__/user03"),
+ davxml.HRef.fromString("mailto:user03 at example.com"),
customxml.CommonName.fromString("USER03"),
customxml.InviteAccess(customxml.ReadWriteAccess()),
customxml.InviteStatusNoResponse(),
),
customxml.InviteUser(
customxml.UID.fromString(""),
- davxml.HRef.fromString("/principals/__uids__/user04"),
+ davxml.HRef.fromString("mailto:user04 at example.com"),
customxml.CommonName.fromString("USER04"),
customxml.InviteAccess(customxml.ReadWriteAccess()),
customxml.InviteStatusNoResponse(),
@@ -377,14 +377,14 @@
self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
customxml.InviteUser(
customxml.UID.fromString(""),
- davxml.HRef.fromString("/principals/__uids__/user02"),
+ davxml.HRef.fromString("mailto:user02 at example.com"),
customxml.CommonName.fromString("USER02"),
customxml.InviteAccess(customxml.ReadWriteAccess()),
customxml.InviteStatusNoResponse(),
),
customxml.InviteUser(
customxml.UID.fromString(""),
- davxml.HRef.fromString("/principals/__uids__/user04"),
+ davxml.HRef.fromString("mailto:user04 at example.com"),
customxml.CommonName.fromString("USER04"),
customxml.InviteAccess(customxml.ReadWriteAccess()),
customxml.InviteStatusNoResponse(),
@@ -427,14 +427,14 @@
self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
customxml.InviteUser(
customxml.UID.fromString(""),
- davxml.HRef.fromString("/principals/__uids__/user02"),
+ davxml.HRef.fromString("mailto:user02 at example.com"),
customxml.CommonName.fromString("USER02"),
customxml.InviteAccess(customxml.ReadWriteAccess()),
customxml.InviteStatusNoResponse(),
),
customxml.InviteUser(
customxml.UID.fromString(""),
- davxml.HRef.fromString("/principals/__uids__/user03"),
+ davxml.HRef.fromString("mailto:user03 at example.com"),
customxml.CommonName.fromString("USER03"),
customxml.InviteAccess(customxml.ReadAccess()),
customxml.InviteStatusNoResponse(),
@@ -491,7 +491,7 @@
self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
customxml.InviteUser(
customxml.UID.fromString(""),
- davxml.HRef.fromString("/principals/__uids__/user01"),
+ davxml.HRef.fromString("mailto:user01 at example.com"),
customxml.CommonName.fromString("USER01"),
customxml.InviteAccess(customxml.ReadWriteAccess()),
customxml.InviteStatusNoResponse(),
@@ -506,7 +506,7 @@
self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
customxml.InviteUser(
customxml.UID.fromString(""),
- davxml.HRef.fromString("/principals/__uids__/user01"),
+ davxml.HRef.fromString("mailto:user01 at example.com"),
customxml.CommonName.fromString("USER01"),
customxml.InviteAccess(customxml.ReadWriteAccess()),
customxml.InviteStatusInvalid(),
@@ -516,7 +516,7 @@
yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
<CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
<CS:remove>
- <D:href>/principals/__uids__/user01</D:href>
+ <D:href>mailto:user01 at example.com</D:href>
</CS:remove>
</CS:share>
""")
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100622/f29dfc49/attachment-0001.html>
More information about the calendarserver-changes
mailing list