[CalendarServer-changes] [6800] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Tue Jan 25 08:18:02 PST 2011
Revision: 6800
http://trac.macosforge.org/projects/calendarserver/changeset/6800
Author: cdaboo at apple.com
Date: 2011-01-25 08:18:01 -0800 (Tue, 25 Jan 2011)
Log Message:
-----------
Changes to quota handling to add limits for calendar/address data and quota on attachments only. Also
changed attachments to be referenced by dropbox_id rather than resource_id. Fixed an issue with one of
the implicit UID queries added a short while ago.
Modified Paths:
--------------
CalendarServer/trunk/conf/caldavd-apple.plist
CalendarServer/trunk/conf/caldavd-test.plist
CalendarServer/trunk/conf/caldavd.plist
CalendarServer/trunk/twistedcaldav/customxml.py
CalendarServer/trunk/twistedcaldav/method/put_addressbook_common.py
CalendarServer/trunk/twistedcaldav/method/put_common.py
CalendarServer/trunk/twistedcaldav/resource.py
CalendarServer/trunk/twistedcaldav/stdconfig.py
CalendarServer/trunk/twistedcaldav/storebridge.py
CalendarServer/trunk/txdav/caldav/datastore/file.py
CalendarServer/trunk/txdav/caldav/datastore/sql.py
CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_1/1.ics
CalendarServer/trunk/txdav/carddav/datastore/file.py
CalendarServer/trunk/txdav/carddav/datastore/sql.py
CalendarServer/trunk/txdav/common/datastore/file.py
CalendarServer/trunk/txdav/common/datastore/sql.py
CalendarServer/trunk/txdav/common/datastore/sql_schema_v1.sql
Modified: CalendarServer/trunk/conf/caldavd-apple.plist
===================================================================
--- CalendarServer/trunk/conf/caldavd-apple.plist 2011-01-25 16:07:58 UTC (rev 6799)
+++ CalendarServer/trunk/conf/caldavd-apple.plist 2011-01-25 16:18:01 UTC (rev 6800)
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+ Copyright (c) 2006-2011 Apple Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -133,14 +133,24 @@
Quotas and limits
-->
- <!-- User quota (in bytes) [0 = no quota] -->
+ <!-- User quota (in bytes) [0 = no quota] applies to attachments only -->
<key>UserQuota</key>
<integer>104857600</integer><!-- 100Mb -->
- <!-- Attachment size limit (in bytes) -->
- <key>MaximumAttachmentSize</key>
- <integer>1048576</integer><!-- 1Mb -->
+ <!-- Maximum number of calendars/address books allowed in a home -->
+ <!-- 0 for no limit -->
+ <key>MaxCollectionsPerHome</key>
+ <integer>50</integer>
+ <!-- Maximum number of resources in a calendar/address book -->
+ <!-- 0 for no limit -->
+ <key>MaxResourcesPerCollection</key>
+ <integer>10000</integer>
+
+ <!-- Maximum resource size (in bytes) -->
+ <key>MaxResourceSize</key>
+ <integer>1048576</integer> <!-- 1Mb -->
+
<!-- Maximum number of unique attendees per entire event -->
<!-- 0 for no limit -->
<key>MaxAttendeesPerInstance</key>
Modified: CalendarServer/trunk/conf/caldavd-test.plist
===================================================================
--- CalendarServer/trunk/conf/caldavd-test.plist 2011-01-25 16:07:58 UTC (rev 6799)
+++ CalendarServer/trunk/conf/caldavd-test.plist 2011-01-25 16:18:01 UTC (rev 6800)
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+ Copyright (c) 2006-2011 Apple Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -131,14 +131,24 @@
Quotas and limits
-->
- <!-- User quota (in bytes) [0 = no quota] -->
+ <!-- User quota (in bytes) [0 = no quota] applies to attachments only -->
<key>UserQuota</key>
<integer>104857600</integer><!-- 100Mb -->
- <!-- Attachment size limit (in bytes) -->
- <key>MaximumAttachmentSize</key>
- <integer>1048576</integer><!-- 1Mb -->
+ <!-- Maximum number of calendars/address books allowed in a home -->
+ <!-- 0 for no limit -->
+ <key>MaxCollectionsPerHome</key>
+ <integer>50</integer>
+ <!-- Maximum number of resources in a calendar/address book -->
+ <!-- 0 for no limit -->
+ <key>MaxResourcesPerCollection</key>
+ <integer>10000</integer>
+
+ <!-- Maximum resource size (in bytes) -->
+ <key>MaxResourceSize</key>
+ <integer>1048576</integer> <!-- 1Mb -->
+
<!-- Maximum number of unique attendees per entire event -->
<!-- 0 for no limit -->
<key>MaxAttendeesPerInstance</key>
Modified: CalendarServer/trunk/conf/caldavd.plist
===================================================================
--- CalendarServer/trunk/conf/caldavd.plist 2011-01-25 16:07:58 UTC (rev 6799)
+++ CalendarServer/trunk/conf/caldavd.plist 2011-01-25 16:18:01 UTC (rev 6800)
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+ Copyright (c) 2006-2011 Apple Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -113,12 +113,22 @@
Quotas and limits
-->
- <!-- User quota (in bytes) [0 = no quota] -->
+ <!-- User quota (in bytes) [0 = no quota] applies to attachments only -->
<key>UserQuota</key>
<integer>104857600</integer> <!-- 100Mb -->
- <!-- Attachment size limit (in bytes) -->
- <key>MaximumAttachmentSize</key>
+ <!-- Maximum number of calendars/address books allowed in a home -->
+ <!-- 0 for no limit -->
+ <key>MaxCollectionsPerHome</key>
+ <integer>50</integer>
+
+ <!-- Maximum number of resources in a calendar/address book -->
+ <!-- 0 for no limit -->
+ <key>MaxResourcesPerCollection</key>
+ <integer>10000</integer>
+
+ <!-- Maximum resource size (in bytes) -->
+ <key>MaxResourceSize</key>
<integer>1048576</integer> <!-- 1Mb -->
<!-- Maximum number of unique attendees per entire event -->
Modified: CalendarServer/trunk/twistedcaldav/customxml.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/customxml.py 2011-01-25 16:07:58 UTC (rev 6799)
+++ CalendarServer/trunk/twistedcaldav/customxml.py 2011-01-25 16:18:01 UTC (rev 6800)
@@ -1,5 +1,5 @@
##
-# Copyright (c) 2006-2010 Apple Inc. All rights reserved.
+# Copyright (c) 2006-2011 Apple Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -224,6 +224,24 @@
return found
+class MaxCollections (davxml.WebDAVTextElement):
+ """
+ Maximum number of child collections in a home collection
+ """
+ namespace = calendarserver_namespace
+ name = "max-collections"
+ hidden = True
+ protected = True
+
+class MaxResources (davxml.WebDAVTextElement):
+ """
+ Maximum number of child resources in a collection
+ """
+ namespace = calendarserver_namespace
+ name = "max-resources"
+ hidden = True
+ protected = True
+
class Timezones (davxml.WebDAVEmptyElement):
"""
Denotes a timezone service resource.
Modified: CalendarServer/trunk/twistedcaldav/method/put_addressbook_common.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/put_addressbook_common.py 2011-01-25 16:07:58 UTC (rev 6799)
+++ CalendarServer/trunk/twistedcaldav/method/put_addressbook_common.py 2011-01-25 16:18:01 UTC (rev 6800)
@@ -1,5 +1,5 @@
##
-# Copyright (c) 2005-2009 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2011 Apple Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -31,7 +31,6 @@
from twisted.internet.defer import returnValue
from twext.web2 import responsecode
from twext.web2.dav import davxml
-from twext.web2.dav.element.base import dav_namespace
from twext.web2.dav.http import ErrorResponse
from twext.web2.dav.util import joinURL, parentForURL
from twext.web2.http import HTTPError
@@ -41,6 +40,7 @@
from twistedcaldav.config import config
from twistedcaldav.carddavxml import NoUIDConflict, carddav_namespace
+from twistedcaldav import customxml
from twistedcaldav.vcard import Component
from twext.python.log import Logger
@@ -162,6 +162,12 @@
log.err(message)
raise HTTPError(StatusResponse(responsecode.FORBIDDEN, "Resource name not allowed"))
+ # Valid collection size check on the destination parent resource
+ result, message = (yield self.validCollectionSize())
+ if not result:
+ log.err(message)
+ raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, customxml.MaxResources.qname()))
+
if not self.sourceadbk:
# Valid content type check on the source resource if its not in a vcard collection
if self.source is not None:
@@ -237,6 +243,21 @@
return result, message
+ @inlineCallbacks
+ def validCollectionSize(self):
+ """
+ Make sure that any limits on the number of resources in a collection are enforced.
+ """
+ result = True
+ message = ""
+ if not self.destination.exists() and \
+ config.MaxResourcesPerCollection and \
+ len((yield self.destinationparent.listChildren())) >= config.MaxResourcesPerCollection:
+ result = False
+ message = "Too many resources in collection %s" % (self.destinationparent,)
+
+ returnValue((result, message,))
+
def validAddressDataCheck(self):
"""
Check that the vcard data is valid vCard.
@@ -264,11 +285,11 @@
"""
result = True
message = ""
- if config.MaximumAttachmentSize:
+ if config.MaxResourceSize:
vcardsize = len(str(self.vcard))
- if vcardsize > config.MaximumAttachmentSize:
+ if vcardsize > config.MaxResourceSize:
result = False
- message = "Data size %d bytes is larger than allowed limit %d bytes" % (vcardsize, config.MaximumAttachmentSize)
+ message = "Data size %d bytes is larger than allowed limit %d bytes" % (vcardsize, config.MaxResourceSize)
return result, message
@@ -358,16 +379,6 @@
returnValue(None)
@inlineCallbacks
- def doDestinationQuotaCheck(self):
- """
- Look at current quota after changes and see if we have gone over the top.
- """
- quota = (yield self.destination.quota(self.request))
- if quota[0] < 0:
- log.err("Over quota by %d" % (-quota[0],))
- raise HTTPError(ErrorResponse(responsecode.INSUFFICIENT_STORAGE_SPACE, (dav_namespace, "quota-not-exceeded")))
-
- @inlineCallbacks
def run(self):
"""
Function that does common PUT/COPY/MOVE behavior.
@@ -414,8 +425,6 @@
)
# Do quota check on destination
- yield self.doDestinationQuotaCheck()
-
if reservation:
yield reservation.unreserve()
Modified: CalendarServer/trunk/twistedcaldav/method/put_common.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/put_common.py 2011-01-25 16:07:58 UTC (rev 6799)
+++ CalendarServer/trunk/twistedcaldav/method/put_common.py 2011-01-25 16:18:01 UTC (rev 6800)
@@ -1,6 +1,6 @@
# -*- test-case-name: twistedcaldav.test.test_validation -*-
##
-# Copyright (c) 2005-2010 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2011 Apple Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -33,12 +33,10 @@
from twext.web2.dav.util import joinURL, parentForURL
from twext.web2 import responsecode
from twext.web2.dav import davxml
-from twext.web2.dav.element.base import dav_namespace
from twext.web2.dav.element.base import PCDATAElement
from twext.web2.http import HTTPError
from twext.web2.http import StatusResponse
-from twext.web2.http_headers import generateContentType, MimeType
from twext.web2.iweb import IResponse
from twext.web2.stream import MemoryStream
@@ -51,6 +49,7 @@
from twistedcaldav.caldavxml import NoUIDConflict
from twistedcaldav.caldavxml import NumberOfRecurrencesWithinLimits
from twistedcaldav.caldavxml import caldav_namespace, MaxAttendeesPerInstance
+from twistedcaldav import customxml
from twistedcaldav.customxml import calendarserver_namespace
from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
@@ -209,6 +208,12 @@
log.err(message)
raise HTTPError(StatusResponse(responsecode.FORBIDDEN, "Resource name not allowed"))
+ # Valid collection size check on the destination parent resource
+ result, message = (yield self.validCollectionSize())
+ if not result:
+ log.err(message)
+ raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, customxml.MaxResources.qname()))
+
# Valid data sizes - do before parsing the data
if self.source is not None:
# Valid content length check on the source resource
@@ -384,13 +389,28 @@
"""
result = True
message = ""
- if config.MaximumAttachmentSize:
+ if config.MaxResourceSize:
calsize = self.source.contentLength()
- if calsize is not None and calsize > config.MaximumAttachmentSize:
+ if calsize is not None and calsize > config.MaxResourceSize:
result = False
- message = "File size %d bytes is larger than allowed limit %d bytes" % (calsize, config.MaximumAttachmentSize)
+ message = "File size %d bytes is larger than allowed limit %d bytes" % (calsize, config.MaxResourceSize)
return result, message
+
+ @inlineCallbacks
+ def validCollectionSize(self):
+ """
+ Make sure that any limits on the number of resources in a collection are enforced.
+ """
+ result = True
+ message = ""
+ if not self.destination.exists() and \
+ config.MaxResourcesPerCollection and \
+ len((yield self.destinationparent.listChildren())) >= config.MaxResourcesPerCollection:
+ result = False
+ message = "Too many resources in collection %s" % (self.destinationparent,)
+
+ returnValue((result, message,))
def validCalendarDataCheck(self):
"""
@@ -438,11 +458,11 @@
"""
result = True
message = ""
- if config.MaximumAttachmentSize:
+ if config.MaxResourceSize:
calsize = len(str(self.calendar))
- if calsize > config.MaximumAttachmentSize:
+ if calsize > config.MaxResourceSize:
result = False
- message = "Data size %d bytes is larger than allowed limit %d bytes" % (calsize, config.MaximumAttachmentSize)
+ message = "Data size %d bytes is larger than allowed limit %d bytes" % (calsize, config.MaxResourceSize)
return result, message
@@ -797,17 +817,6 @@
returnValue(None)
@inlineCallbacks
- def doDestinationQuotaCheck(self):
- """
- Look at current quota after changes and see if we have gone over the top.
- """
- quota = (yield self.destination.quota(self.request))
- if quota[0] < 0:
- log.err("Over quota by %d" % (-quota[0],))
- raise HTTPError(ErrorResponse(responsecode.INSUFFICIENT_STORAGE_SPACE, (dav_namespace, "quota-not-exceeded")))
-
-
- @inlineCallbacks
def run(self):
"""
Function that does common PUT/COPY/MOVE behavior.
@@ -890,9 +899,6 @@
self.request.addResponseFilter(_removeEtag, atEnd=True)
- # Do quota check on destination
- yield self.doDestinationQuotaCheck()
-
if reservation:
yield reservation.unreserve()
Modified: CalendarServer/trunk/twistedcaldav/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/resource.py 2011-01-25 16:07:58 UTC (rev 6799)
+++ CalendarServer/trunk/twistedcaldav/resource.py 2011-01-25 16:18:01 UTC (rev 6800)
@@ -1,6 +1,6 @@
# -*- test-case-name: twistedcaldav.test.test_resource,twistedcaldav.test.test_wrapping -*-
##
-# Copyright (c) 2005-2010 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2011 Apple Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -388,7 +388,7 @@
caldavxml.SupportedCalendarData.qname(),
customxml.GETCTag.qname(),
)
- if config.MaximumAttachmentSize:
+ if config.MaxResourceSize:
baseProperties += (
caldavxml.MaxResourceSize.qname(),
)
@@ -410,7 +410,7 @@
customxml.GETCTag.qname(),
customxml.PubSubXMPPPushKeyProperty.qname(),
)
- if config.MaximumAttachmentSize:
+ if config.MaxResourceSize:
baseProperties += (
carddavxml.MaxResourceSize.qname(),
)
@@ -609,9 +609,9 @@
elif qname == caldavxml.MaxResourceSize.qname():
# CalDAV-access-15, section 5.2.5
- if config.MaximumAttachmentSize:
+ if config.MaxResourceSize:
returnValue(caldavxml.MaxResourceSize.fromString(
- str(config.MaximumAttachmentSize)
+ str(config.MaxResourceSize)
))
elif qname == caldavxml.MaxAttendeesPerInstance.qname():
@@ -650,9 +650,9 @@
elif qname == carddavxml.MaxResourceSize.qname():
# CardDAV, section 6.2.3
- if config.MaximumAttachmentSize:
+ if config.MaxResourceSize:
returnValue(carddavxml.MaxResourceSize.fromString(
- str(config.MaximumAttachmentSize)
+ str(config.MaxResourceSize)
))
elif qname == customxml.Invite.qname():
@@ -1415,6 +1415,7 @@
return super(CalDAVResource, self).checkPreconditions(request)
+ @inlineCallbacks
def createCalendar(self, request):
"""
External API for creating a calendar. Verify that the parent is a
@@ -1442,21 +1443,28 @@
# Verify that no parent collection is a calendar also
#
- def _defer(parent):
- if parent is not None:
- self.log_error("Cannot create a calendar collection within a calendar collection %s" % (parent,))
+ parent = (yield self._checkParents(request, isPseudoCalendarCollectionResource))
+
+ if parent is not None:
+ self.log_error("Cannot create a calendar collection within a calendar collection %s" % (parent,))
+ raise HTTPError(ErrorResponse(
+ responsecode.FORBIDDEN,
+ (caldavxml.caldav_namespace, "calendar-collection-location-ok")
+ ))
+
+ # Check for any quota limits
+ if config.MaxCollectionsPerHome:
+ parent = (yield self.locateParent(request, request.urlForResource(self)))
+ if (yield parent.countOwnedChildren()) >= config.MaxCollectionsPerHome: # NB this ignores shares
+ self.log_error("Cannot create a calendar collection because there are too many already present in %s" % (parent,))
raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
- (caldavxml.caldav_namespace, "calendar-collection-location-ok")
+ customxml.MaxCollections.qname()
))
+
+ returnValue((yield self.createCalendarCollection()))
- return self.createCalendarCollection()
- parent = self._checkParents(request, isPseudoCalendarCollectionResource)
- parent.addCallback(_defer)
- return parent
-
-
def createCalendarCollection(self):
"""
Internal API for creating a calendar collection.
@@ -1496,6 +1504,7 @@
raise NotImplementedError()
+ @inlineCallbacks
def createAddressBook(self, request):
"""
External API for creating an addressbook. Verify that the parent is a
@@ -1528,20 +1537,26 @@
# Verify that no parent collection is a calendar also
#
- def _defer(parent):
- if parent is not None:
- self.log_error("Cannot create an address book collection within an address book collection %s" % (parent,))
+ parent = (yield self._checkParents(request, isAddressBookCollectionResource))
+ if parent is not None:
+ self.log_error("Cannot create an address book collection within an address book collection %s" % (parent,))
+ raise HTTPError(ErrorResponse(
+ responsecode.FORBIDDEN,
+ (carddavxml.carddav_namespace, "addressbook-collection-location-ok")
+ ))
+
+ # Check for any quota limits
+ if config.MaxCollectionsPerHome:
+ parent = (yield self.locateParent(request, request.urlForResource(self)))
+ if (yield parent.countOwnedChildren()) >= config.MaxCollectionsPerHome: # NB this ignores shares
+ self.log_error("Cannot create a calendar collection because there are too many already present in %s" % (parent,))
raise HTTPError(ErrorResponse(
responsecode.FORBIDDEN,
- (carddavxml.carddav_namespace, "addressbook-collection-location-ok")
+ customxml.MaxCollections.qname()
))
+
+ returnValue((yield self.createAddressBookCollection()))
- return self.createAddressBookCollection()
-
- parent = self._checkParents(request, isAddressBookCollectionResource)
- parent.addCallback(_defer)
- return parent
-
def createAddressBookCollection(self):
"""
Internal API for creating an addressbook collection.
@@ -2021,11 +2036,16 @@
def liveProperties(self):
- return super(CommonHomeResource, self).liveProperties() + (
+ props = super(CommonHomeResource, self).liveProperties() + (
(customxml.calendarserver_namespace, "push-transports"),
(customxml.calendarserver_namespace, "pushkey"),
)
+
+ if config.MaxCollectionsPerHome:
+ props += (customxml.MaxCollections.qname(),)
+ return props
+
def sharesDB(self):
"""
Retrieve the new-style shares DB wrapper.
@@ -2176,13 +2196,24 @@
@inlineCallbacks
+ def countOwnedChildren(self):
+ """
+ @return: the number of children (not shared ones).
+ """
+ returnValue(len(list((yield self._newStoreHome.listChildren()))))
+
+
+ @inlineCallbacks
def readProperty(self, property, request):
if type(property) is tuple:
qname = property
else:
qname = property.qname()
- if qname == (customxml.calendarserver_namespace, "push-transports"):
+ if qname == customxml.MaxCollections.qname() and config.MaxCollectionsPerHome:
+ returnValue(customxml.MaxCollections.fromString(config.MaxCollectionsPerHome))
+
+ elif qname == (customxml.calendarserver_namespace, "push-transports"):
notifierID = self._newStoreHome.notifierID()
if notifierID is not None and config.Notifications.Services.XMPPNotifier.Enabled:
children = []
@@ -2223,7 +2254,7 @@
else:
returnValue(customxml.PubSubPushTransportsProperty())
- if qname == (customxml.calendarserver_namespace, "pushkey"):
+ elif qname == (customxml.calendarserver_namespace, "pushkey"):
notifierID = self._newStoreHome.notifierID()
if notifierID is not None and config.Notifications.Services.XMPPNotifier.Enabled:
pubSubConfiguration = getPubSubConfiguration(config)
@@ -2242,7 +2273,7 @@
returnValue(customxml.PubSubXMPPPushKeyProperty())
- if qname == (customxml.calendarserver_namespace, "xmpp-uri"):
+ elif qname == (customxml.calendarserver_namespace, "xmpp-uri"):
notifierID = self._newStoreHome.notifierID()
if notifierID is not None and config.Notifications.Services.XMPPNotifier.Enabled:
pubSubConfiguration = getPubSubConfiguration(config)
Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/stdconfig.py 2011-01-25 16:07:58 UTC (rev 6799)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py 2011-01-25 16:18:01 UTC (rev 6800)
@@ -1,6 +1,6 @@
# -*- test-case-name: twistedcaldav.test.test_stdconfig -*-
##
-# Copyright (c) 2005-2010 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2011 Apple Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -193,11 +193,21 @@
"ConfigRoot" : "/etc/caldavd",
"LogRoot" : "/var/log/caldavd",
"RunRoot" : "/var/run/caldavd",
- "UserQuota" : 104857600, # User quota (in bytes)
- "MaximumAttachmentSize" : 1048576, # Attachment size limit (in bytes)
- "MaxAttendeesPerInstance" : 100, # Maximum number of unique attendees
- "MaxInstancesForRRULE" : 400, # Maximum number of instances for an RRULE
"WebCalendarRoot" : "/usr/share/collabd",
+
+ #
+ # Quotas
+ #
+
+ # Attachments
+ "UserQuota" : 104857600, # User attachment quota (in bytes)
+
+ # Resource data
+ "MaxCollectionsPerHome" : 50, # Maximum number of calendars/address books allowed in a home
+ "MaxResourcesPerCollection" : 10000, # Maximum number of resources in a calendar/address book
+ "MaxResourceSize" : 1048576, # Maximum resource size (in bytes)
+ "MaxAttendeesPerInstance" : 100, # Maximum number of unique attendees
+ "MaxInstancesForRRULE" : 400, # Maximum number of instances for an RRULE
# Set to URL path of wiki authentication service, e.g. "/auth", in order
# to use javascript authentication dialog. Empty string indicates standard
Modified: CalendarServer/trunk/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/storebridge.py 2011-01-25 16:07:58 UTC (rev 6799)
+++ CalendarServer/trunk/twistedcaldav/storebridge.py 2011-01-25 16:18:01 UTC (rev 6800)
@@ -1,6 +1,6 @@
# -*- test-case-name: twistedcaldav.test.test_wrapping -*-
##
-# Copyright (c) 2005-2010 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2011 Apple Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -48,6 +48,7 @@
from twistedcaldav.caldavxml import caldav_namespace
from twistedcaldav.config import config
+from twistedcaldav import customxml
from twistedcaldav.memcachelock import MemcacheLock, MemcacheLockTimeoutError
from twistedcaldav.notifications import NotificationCollectionResource, \
NotificationResource
@@ -216,6 +217,27 @@
) if self._newStoreObject else NonePropertyStore(self)
+ def liveProperties(self):
+
+ props = super(_CommonHomeChildCollectionMixin, self).liveProperties()
+
+ if config.MaxResourcesPerCollection:
+ props += (customxml.MaxResources.qname(),)
+
+ return props
+
+ @inlineCallbacks
+ def readProperty(self, property, request):
+ if type(property) is tuple:
+ qname = property
+ else:
+ qname = property.qname()
+
+ if qname == customxml.MaxResources.qname() and config.MaxResourcesPerCollection:
+ returnValue(customxml.MaxResources.fromString(config.MaxResourcesPerCollection))
+
+ returnValue((yield super(_CommonHomeChildCollectionMixin, self).readProperty(property, request)))
+
def index(self):
"""
Retrieve the new-style index wrapper.
Modified: CalendarServer/trunk/txdav/caldav/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/file.py 2011-01-25 16:07:58 UTC (rev 6799)
+++ CalendarServer/trunk/txdav/caldav/datastore/file.py 2011-01-25 16:18:01 UTC (rev 6800)
@@ -1,6 +1,6 @@
# -*- test-case-name: txdav.caldav.datastore.test.test_file -*-
##
-# Copyright (c) 2010 Apple Inc. All rights reserved.
+# Copyright (c) 2010-2011 Apple Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -292,8 +292,6 @@
@writeOperation
def setComponent(self, component, inserting=False):
- old_size = 0 if inserting else self.size()
-
validateCalendarComponent(self, self._calendar, component, inserting)
self._calendar.retrieveOldIndex().addResource(
@@ -328,16 +326,11 @@
# Now re-write the original properties on the updated file
self.properties().flush()
- # Adjust quota
- quota_adjustment = self.size() - old_size
- self._calendar._home.adjustQuotaUsedBytes(quota_adjustment)
-
def undo():
if backup:
backup.moveTo(self._path)
else:
self._path.remove()
- self._calendar._home.adjustQuotaUsedBytes(-quota_adjustment)
return undo
self._transaction.addOperation(do, "set calendar component %r" % (self.name(),))
Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/sql.py 2011-01-25 16:07:58 UTC (rev 6799)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py 2011-01-25 16:18:01 UTC (rev 6800)
@@ -1,6 +1,6 @@
# -*- test-case-name: txdav.caldav.datastore.test.test_sql -*-
##
-# Copyright (c) 2010 Apple Inc. All rights reserved.
+# Copyright (c) 2010-2011 Apple Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -374,8 +374,6 @@
@inlineCallbacks
def setComponent(self, component, inserting=False):
- old_size = 0 if inserting else self.size()
-
validateCalendarComponent(self, self._calendar, component, inserting)
yield self.updateDatabase(component, inserting=inserting)
@@ -384,9 +382,6 @@
else:
yield self._calendar._updateRevision(self._name)
- # Adjust quota
- yield self._calendar._home.adjustQuotaUsedBytes(self.size() - old_size)
-
self._calendar.notifyChanged()
@@ -674,11 +669,11 @@
attachment = Attachment(self, name)
yield self._txn.execSQL(
"""
- insert into ATTACHMENT (CALENDAR_OBJECT_RESOURCE_ID, CONTENT_TYPE,
+ insert into ATTACHMENT (DROPBOX_ID, CONTENT_TYPE,
SIZE, MD5, PATH) values (%s, %s, %s, %s, %s)
""",
[
- self._resourceID, generateContentType(contentType), 0, "",
+ self._dropboxID, generateContentType(contentType), 0, "",
name,
]
)
@@ -692,15 +687,17 @@
self._txn.postCommit(attachment._path.remove)
yield self._txn.execSQL(
"""
- delete from ATTACHMENT where CALENDAR_OBJECT_RESOURCE_ID = %s AND
+ delete from ATTACHMENT where DROPBOX_ID = %s AND
PATH = %s
- """, [self._resourceID, name]
+ """, [self._dropboxID, name]
)
# Adjust quota
yield self._calendar._home.adjustQuotaUsedBytes(-old_size)
+
+ # Send change notification to home
+ yield self._calendar._home.notifyChanged()
-
@inlineCallbacks
def attachmentWithName(self, name):
attachment = Attachment(self, name)
@@ -726,8 +723,8 @@
def attachments(self):
rows = yield self._txn.execSQL(
"""
- select PATH from ATTACHMENT where CALENDAR_OBJECT_RESOURCE_ID = %s
- """, [self._resourceID])
+ select PATH from ATTACHMENT where DROPBOX_ID = %s
+ """, [self._dropboxID])
result = []
for row in rows:
result.append((yield self.attachmentWithName(row[0])))
@@ -803,6 +800,9 @@
# Adjust quota
yield self.attachment._calendarObject._calendar._home.adjustQuotaUsedBytes(self.attachment.size() - old_size)
+
+ # Send change notification to home
+ yield self.attachment._calendarObject._calendar._home.notifyChanged()
def sqltime(value):
Modified: CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_1/1.ics
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_1/1.ics 2011-01-25 16:07:58 UTC (rev 6799)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/calendar_store/ho/me/home1/calendar_1/1.ics 2011-01-25 16:18:01 UTC (rev 6800)
@@ -32,6 +32,8 @@
LOCATION:Wilfredo's Office
SEQUENCE:2
X-APPLE-EWS-BUSYSTATUS:BUSY
+X-APPLE-DROPBOX:/calendars/__uids__/user01/dropbox/FE5CDC6F-7776-4607-83
+ A9-B90FF7ACC8D0.dropbox
SUMMARY:CalDAV protocol updates
DTSTART;TZID=US/Pacific:20090324T121500
CREATED:20090326T145440Z
Modified: CalendarServer/trunk/txdav/carddav/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/file.py 2011-01-25 16:07:58 UTC (rev 6799)
+++ CalendarServer/trunk/txdav/carddav/datastore/file.py 2011-01-25 16:18:01 UTC (rev 6800)
@@ -1,6 +1,6 @@
# -*- test-case-name: txdav.carddav.datastore.test.test_file -*-
##
-# Copyright (c) 2010 Apple Inc. All rights reserved.
+# Copyright (c) 2010-2011 Apple Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -175,8 +175,6 @@
@writeOperation
def setComponent(self, component, inserting=False):
- old_size = 0 if inserting else self.size()
-
validateAddressBookComponent(self, self._addressbook, component, inserting)
self._addressbook.retrieveOldIndex().addResource(
@@ -211,16 +209,11 @@
# Now re-write the original properties on the updated file
self.properties().flush()
- # Adjust quota
- quota_adjustment = self.size() - old_size
- self._addressbook._home.adjustQuotaUsedBytes(quota_adjustment)
-
def undo():
if backup:
backup.moveTo(self._path)
else:
self._path.remove()
- self._addressbook._home.adjustQuotaUsedBytes(-quota_adjustment)
return undo
self._transaction.addOperation(do, "set addressbook component %r" % (self.name(),))
Modified: CalendarServer/trunk/txdav/carddav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/sql.py 2011-01-25 16:07:58 UTC (rev 6799)
+++ CalendarServer/trunk/txdav/carddav/datastore/sql.py 2011-01-25 16:18:01 UTC (rev 6800)
@@ -1,6 +1,6 @@
# -*- test-case-name: txdav.carddav.datastore.test.test_sql -*-
##
-# Copyright (c) 2010 Apple Inc. All rights reserved.
+# Copyright (c) 2010-2011 Apple Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -192,8 +192,6 @@
@inlineCallbacks
def setComponent(self, component, inserting=False):
- old_size = 0 if inserting else self.size()
-
validateAddressBookComponent(self, self._addressbook, component, inserting)
yield self.updateDatabase(component, inserting=inserting)
@@ -202,9 +200,6 @@
else:
yield self._addressbook._updateRevision(self._name)
- # Adjust quota
- yield self._addressbook._home.adjustQuotaUsedBytes(self.size() - old_size)
-
self._addressbook.notifyChanged()
Modified: CalendarServer/trunk/txdav/common/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/file.py 2011-01-25 16:07:58 UTC (rev 6799)
+++ CalendarServer/trunk/txdav/common/datastore/file.py 2011-01-25 16:18:01 UTC (rev 6800)
@@ -1,6 +1,6 @@
# -*- test-case-name: txdav.caldav.datastore.test.test_file -*-
##
-# Copyright (c) 2010 Apple Inc. All rights reserved.
+# Copyright (c) 2010-2011 Apple Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -765,10 +765,6 @@
objectResourcePath = self._path.child(name)
if objectResourcePath.isfile():
- # Handle quota adjustment
- child = self.objectResourceWithName(name)
- old_size = child.size()
-
self._removedObjectResources.add(name)
# FIXME: test for undo
def do():
@@ -777,9 +773,6 @@
self._transaction.addOperation(do, "remove object resource object %r" %
(name,))
- # Adjust quota
- self._home.adjustQuotaUsedBytes(-old_size)
-
self.notifyChanged()
else:
raise NoSuchObjectResourceError(name)
Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py 2011-01-25 16:07:58 UTC (rev 6799)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py 2011-01-25 16:18:01 UTC (rev 6800)
@@ -1,6 +1,6 @@
# -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
##
-# Copyright (c) 2010 Apple Inc. All rights reserved.
+# Copyright (c) 2010-2011 Apple Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -701,11 +701,11 @@
))
if rows:
- childID, objectID = rows[0]
- child = (yield self.childWithID(childID))
- if child and child.name() not in ignore_children:
- objectResource = (yield child.objectResourceWithID(objectID))
- results.append(objectResource)
+ for childID, objectID in rows:
+ child = (yield self.childWithID(childID))
+ if child and child.name() not in ignore_children:
+ objectResource = (yield child.objectResourceWithID(objectID))
+ results.append(objectResource)
returnValue(results)
@@ -1284,40 +1284,34 @@
@inlineCallbacks
def removeObjectResourceWithName(self, name):
- uid, old_size = (yield self._txn.execSQL(
+ uid = (yield self._txn.execSQL(
"delete from %(name)s "
"where %(column_RESOURCE_NAME)s = %%s and %(column_PARENT_RESOURCE_ID)s = %%s "
- "returning %(column_UID)s, character_length(%(column_TEXT)s)" % self._objectTable,
+ "returning %(column_UID)s" % self._objectTable,
[name, self._resourceID],
raiseOnZeroRowCount=lambda:NoSuchObjectResourceError()
- ))[0]
+ ))[0][0]
self._objects.pop(name, None)
self._objects.pop(uid, None)
yield self._deleteRevision(name)
- # Adjust quota
- yield self._home.adjustQuotaUsedBytes(-old_size)
-
self.notifyChanged()
@inlineCallbacks
def removeObjectResourceWithUID(self, uid):
- name, old_size = (yield self._txn.execSQL(
+ name = (yield self._txn.execSQL(
"delete from %(name)s "
"where %(column_UID)s = %%s and %(column_PARENT_RESOURCE_ID)s = %%s "
- "returning %(column_RESOURCE_NAME)s, character_length(%(column_TEXT)s)" % self._objectTable,
+ "returning %(column_RESOURCE_NAME)s" % self._objectTable,
[uid, self._resourceID],
raiseOnZeroRowCount=lambda:NoSuchObjectResourceError()
- ))[0]
+ ))[0][0]
self._objects.pop(name, None)
self._objects.pop(uid, None)
yield self._deleteRevision(name)
- # Adjust quota
- yield self._home.adjustQuotaUsedBytes(-old_size)
-
self.notifyChanged()
Modified: CalendarServer/trunk/txdav/common/datastore/sql_schema_v1.sql
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql_schema_v1.sql 2011-01-25 16:07:58 UTC (rev 6799)
+++ CalendarServer/trunk/txdav/common/datastore/sql_schema_v1.sql 2011-01-25 16:18:01 UTC (rev 6800)
@@ -1,7 +1,7 @@
-- -*- test-case-name: txdav.caldav.datastore.test.test_sql,txdav.carddav.datastore.test.test_sql -*-
----
--- Copyright (c) 2010 Apple Inc. All rights reserved.
+-- Copyright (c) 2010-2011 Apple Inc. All rights reserved.
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
@@ -288,7 +288,7 @@
----------------
create table ATTACHMENT (
- CALENDAR_OBJECT_RESOURCE_ID integer not null references CALENDAR_OBJECT on delete cascade,
+ DROPBOX_ID varchar(255) not null,
CONTENT_TYPE varchar(255) not null,
SIZE integer not null,
MD5 char(32) not null,
@@ -296,11 +296,10 @@
MODIFIED timestamp default timezone('UTC', CURRENT_TIMESTAMP),
PATH varchar(1024) not null,
- unique(CALENDAR_OBJECT_RESOURCE_ID, PATH)
+ unique(DROPBOX_ID, PATH)
);
-create index ATTACHMENT_CALENDAR_OBJECT_RESOURCE_ID on
- ATTACHMENT(CALENDAR_OBJECT_RESOURCE_ID);
+create index ATTACHMENT_DROPBOX_ID on ATTACHMENT(DROPBOX_ID);
------------------
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20110125/8af4b915/attachment-0001.html>
More information about the calendarserver-changes
mailing list