[CalendarServer-changes] [3711] CalendarServer/branches/users/sagen/migration-3709
source_changes at macosforge.org
source_changes at macosforge.org
Mon Feb 23 14:57:06 PST 2009
Revision: 3711
http://trac.macosforge.org/projects/calendarserver/changeset/3711
Author: sagen at apple.com
Date: 2009-02-23 14:57:05 -0800 (Mon, 23 Feb 2009)
Log Message:
-----------
Bringing the migration branch up to trunk rev 3709
Revision Links:
--------------
http://trac.macosforge.org/projects/calendarserver/changeset/3709
Modified Paths:
--------------
CalendarServer/branches/users/sagen/migration-3709/calendarserver/tap/caldav.py
CalendarServer/branches/users/sagen/migration-3709/calendarserver/tools/util.py
CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/ical.py
CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/resource.py
CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/upgrade.py
Added Paths:
-----------
CalendarServer/branches/users/sagen/migration-3709/calendarserver/tools/migrate.py
Modified: CalendarServer/branches/users/sagen/migration-3709/calendarserver/tap/caldav.py
===================================================================
--- CalendarServer/branches/users/sagen/migration-3709/calendarserver/tap/caldav.py 2009-02-23 22:55:40 UTC (rev 3710)
+++ CalendarServer/branches/users/sagen/migration-3709/calendarserver/tap/caldav.py 2009-02-23 22:57:05 UTC (rev 3711)
@@ -73,7 +73,7 @@
from twistedcaldav.static import TimezoneServiceFile
from twistedcaldav.mail import IMIPInboxResource
from twistedcaldav.timezones import TimezoneCache
-from twistedcaldav.upgrade import UpgradeTheServer
+from twistedcaldav.upgrade import upgradeData
from twistedcaldav.pdmonster import PDClientAddressWrapper
from twistedcaldav import memcachepool
from twistedcaldav.notify import installNotificationClient
@@ -327,8 +327,6 @@
def makeService(self, options):
- # Now do any on disk upgrades we might need.
- UpgradeTheServer.doUpgrade()
serviceMethod = getattr(self, "makeService_%s" % (config.ProcessType,), None)
@@ -339,6 +337,16 @@
% (config.ProcessType,)
)
else:
+
+ if config.ProcessType in ('Combined', 'Single'):
+
+ # Process localization string files
+ processLocalizationFiles(config.Localization)
+
+ # Now do any on disk upgrades we might need.
+ upgradeData(config)
+
+
service = serviceMethod(options)
#
Copied: CalendarServer/branches/users/sagen/migration-3709/calendarserver/tools/migrate.py (from rev 3710, CalendarServer/branches/users/sagen/migration-3686/calendarserver/tools/migrate.py)
===================================================================
--- CalendarServer/branches/users/sagen/migration-3709/calendarserver/tools/migrate.py (rev 0)
+++ CalendarServer/branches/users/sagen/migration-3709/calendarserver/tools/migrate.py 2009-02-23 22:57:05 UTC (rev 3711)
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+
+##
+# Copyright (c) 2006-2009 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.
+##
+
+"""
+This tool migrates existing calendar data from any previous calendar server
+version to the current version.
+
+This tool requires access to the calendar server's configuration and
+data storage; it does not operate by talking to the server via the
+network.
+"""
+
+import os
+import sys
+from getopt import getopt, GetoptError
+from os.path import dirname, abspath
+
+from twistedcaldav.upgrade import upgradeData
+from calendarserver.tools.util import loadConfig
+
+def usage(e=None):
+ if e:
+ print e
+ print ""
+
+ name = os.path.basename(sys.argv[0])
+ print "usage: %s [options]" % (name,)
+ print ""
+ print "Migrate calendar data to current version"
+ print __doc__
+ print "options:"
+ print " -h --help: print this help and exit"
+ print " -f --config: Specify caldavd.plist configuration path"
+
+ if e:
+ sys.exit(64)
+ else:
+ sys.exit(0)
+
+def main():
+ try:
+ (optargs, args) = getopt(
+ sys.argv[1:], "hf:", [
+ "config=",
+ "help",
+ ],
+ )
+ except GetoptError, e:
+ usage(e)
+
+ configFileName = None
+
+
+ for opt, arg in optargs:
+ if opt in ("-h", "--help"):
+ usage()
+
+ elif opt in ("-f", "--config"):
+ configFileName = arg
+
+ if args:
+ usage("Too many arguments: %s" % (" ".join(args),))
+
+ config = loadConfig(configFileName)
+ upgradeData(config)
+
+if __name__ == "__main__":
+ main()
Modified: CalendarServer/branches/users/sagen/migration-3709/calendarserver/tools/util.py
===================================================================
--- CalendarServer/branches/users/sagen/migration-3709/calendarserver/tools/util.py 2009-02-23 22:55:40 UTC (rev 3710)
+++ CalendarServer/branches/users/sagen/migration-3709/calendarserver/tools/util.py 2009-02-23 22:57:05 UTC (rev 3711)
@@ -44,7 +44,7 @@
BaseDirectoryService = namedClass(config.DirectoryService.type)
class MyDirectoryService (BaseDirectoryService):
- def principalCollection(self):
+ def getPrincipalCollection(self):
if not hasattr(self, "_principalCollection"):
#
# Instantiating a CalendarHomeProvisioningResource with a directory
@@ -60,12 +60,26 @@
return self._principalCollection
+ def setPrincipalCollection(self, coll):
+ # See principal.py line 237: self.directory.principalCollection = self
+ pass
+
+ principalCollection = property(getPrincipalCollection, setPrincipalCollection)
+
def calendarHomeForShortName(self, recordType, shortName):
- principal = self.principalCollection().principalForShortName(recordType, shortName)
+ principal = self.principalCollection.principalForShortName(recordType, shortName)
if principal:
return principal.calendarHome()
return None
+ def principalForCalendarUserAddress(self, cua):
+ record = self.recordWithCalendarUserAddress(cua)
+ if record is not None:
+ return self.principalCollection.principalForUID(record.uid)
+ else:
+ return None
+
+
return MyDirectoryService(**config.DirectoryService.params)
class DummyDirectoryService (DirectoryService):
Modified: CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/ical.py
===================================================================
--- CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/ical.py 2009-02-23 22:55:40 UTC (rev 3710)
+++ CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/ical.py 2009-02-23 22:57:05 UTC (rev 3711)
@@ -45,6 +45,7 @@
import cStringIO as StringIO
import datetime
import heapq
+import itertools
log = Logger()
@@ -1808,6 +1809,66 @@
if dataValue.find(dropboxPrefix) != -1:
component.removeProperty(attachment)
+ def normalizeCalendarUserAddresses(self, lookupFunction):
+ """
+ Do the ORGANIZER/ATTENDEE property normalization.
+
+ @param lookupFunction: function returning full name, guid, CUAs for a given CUA
+ @type lookupFunction: L{Function}
+ """
+ for component in self.subcomponents():
+ if component.name() == "VTIMEZONE":
+ continue
+ for prop in itertools.chain(
+ component.properties("ORGANIZER"),
+ component.properties("ATTENDEE")
+ ):
+
+ # Check that we can lookup this calendar user address - if not
+ # we cannot do anything with it
+ cuaddr = normalizeCUAddr(prop.value())
+ name, guid, cuaddrs = lookupFunction(cuaddr)
+ if guid is None:
+ continue
+
+ # Always re-write value to urn:uuid
+ prop.setValue("urn:uuid:%s" % (guid,))
+
+ # Always re-write the CN parameter
+ if name:
+ prop.params()["CN"] = [name,]
+ else:
+ try:
+ del prop.params()["CN"]
+ except KeyError:
+ pass
+
+ # Re-write the X-CALENDARSERVER-EMAIL if its value no longer
+ # matches
+ oldemail = prop.params().get("X-CALENDARSERVER-EMAIL",
+ (None,))[0]
+ if oldemail:
+ oldemail = "mailto:%s" % (oldemail,)
+ if oldemail is None or oldemail not in cuaddrs:
+ if cuaddr.startswith("mailto:") and cuaddr in cuaddrs:
+ email = cuaddr[7:]
+ else:
+ for addr in cuaddrs:
+ if addr.startswith("mailto:"):
+ email = addr[7:]
+ break
+ else:
+ email = None
+
+ if email:
+ prop.params()["X-CALENDARSERVER-EMAIL"] = [email,]
+ else:
+ try:
+ del prop.params()["X-CALENDARSERVER-EMAIL"]
+ except KeyError:
+ pass
+
+
##
# Dates and date-times
##
Modified: CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/resource.py
===================================================================
--- CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/resource.py 2009-02-23 22:55:40 UTC (rev 3710)
+++ CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/resource.py 2009-02-23 22:57:05 UTC (rev 3711)
@@ -652,64 +652,17 @@
@param ical: calendar object to normalize.
@type ical: L{Component}
"""
-
- def normalizeCalendarUserAddress(prop):
- """
- Do the ORGANIZER/ATTENDEE property normalization.
- @param prop: organizer/attendee property
- @type prop: L{Property}
- """
-
- # Check that we have a principal for this calendar user address - if not we
- # cannot do anything with it
- cuaddr = normalizeCUAddr(prop.value())
+ def lookupFunction(cuaddr):
principal = self.principalForCalendarUserAddress(cuaddr)
if principal is None:
- return
-
- # Always re-write value to urn:uuid
- prop.setValue("urn:uuid:%s" % (principal.record.guid,))
-
- # Always re-write the CN parameter
- if principal.record.fullName:
- prop.params()["CN"] = [principal.record.fullName,]
+ return (None, None, None)
else:
- try:
- del prop.params()["CN"]
- except KeyError:
- pass
+ return (principal.record.fullName, principal.record.guid,
+ principal.record.calendarUserAddresses)
- # Re-write the X-CALENDARSERVER-EMAIL if its value no longer matches
- oldemail = prop.params().get("X-CALENDARSERVER-EMAIL", (None,))[0]
- if oldemail:
- oldemail = "mailto:%s" % (oldemail,)
- if oldemail is None or oldemail not in principal.record.calendarUserAddresses:
- if cuaddr.startswith("mailto:") and cuaddr in principal.record.calendarUserAddresses:
- email = cuaddr[7:]
- else:
- for addr in principal.record.calendarUserAddresses:
- if addr.startswith("mailto:"):
- email = addr[7:]
- break
- else:
- email = None
-
- if email:
- prop.params()["X-CALENDARSERVER-EMAIL"] = [email,]
- else:
- try:
- del prop.params()["X-CALENDARSERVER-EMAIL"]
- except KeyError:
- pass
+ ical.normalizeCalendarUserAddresses(lookupFunction)
- for component in ical.subcomponents():
- if component.name() != "VTIMEZONE":
- for prop in itertools.chain(
- component.properties("ORGANIZER"),
- component.properties("ATTENDEE")
- ):
- normalizeCalendarUserAddress(prop)
def principalForCalendarUserAddress(self, address):
for principalCollection in self.principalCollections():
Modified: CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/upgrade.py
===================================================================
--- CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/upgrade.py 2009-02-23 22:55:40 UTC (rev 3710)
+++ CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/upgrade.py 2009-02-23 22:57:05 UTC (rev 3711)
@@ -14,72 +14,366 @@
# limitations under the License.
##
+from __future__ import with_statement
+
from twisted.web2.dav.fileop import rmdir
-from twistedcaldav.config import config
+from twisted.web2.dav import davxml
+from twistedcaldav.directory.directory import DirectoryService
from twistedcaldav.directory.calendaruserproxy import CalendarUserProxyDatabase
from twistedcaldav.log import Logger
-import os
+from twistedcaldav.ical import Component
+from twistedcaldav.scheduling.cuaddress import normalizeCUAddr
+from twistedcaldav import caldavxml
+from calendarserver.tools.util import getDirectory, dummyDirectoryRecord
+import xattr, itertools, os, zlib
+from zlib import compress, decompress
+from cPickle import loads as unpickle, PicklingError, UnpicklingError
+
log = Logger()
-class UpgradeTheServer(object):
-
- @staticmethod
- def doUpgrade():
-
- UpgradeTheServer._doPrincipalCollectionInMemoryUpgrade()
-
- @staticmethod
- def _doPrincipalCollectionInMemoryUpgrade():
-
- # Look for the /principals/ directory on disk
- old_principals = os.path.join(config.DocumentRoot, "principals")
- if os.path.exists(old_principals):
- # First move the proxy database and rename it
- UpgradeTheServer._doProxyDatabaseMoveUpgrade()
-
- # Now delete the on disk representation of principals
- rmdir(old_principals)
- log.info(
- "Removed the old principal directory at '%s'."
- % (old_principals,)
- )
- @staticmethod
- def _doProxyDatabaseMoveUpgrade():
-
+
+#
+# upgrade_to_1
+#
+# Upconverts data from any calendar server version prior to data format 1
+#
+
+def upgrade_to_1(config):
+
+
+ def fixBadQuotes(data):
+ if (
+ data.find('\\"') != -1 or
+ data.find('\\\r\n "') != -1 or
+ data.find('\r\n \r\n "') != -1
+ ):
+ # Fix by continuously replacing \" with " until no more
+ # replacements occur
+ while True:
+ newData = data.replace('\\"', '"').replace('\\\r\n "', '\r\n "').replace('\r\n \r\n "', '\r\n "')
+ if newData == data:
+ break
+ else:
+ data = newData
+
+ return data, True
+ else:
+ return data, False
+
+
+
+ def normalizeCUAddrs(data, directory):
+ cal = Component.fromString(data)
+
+ def lookupFunction(cuaddr):
+ try:
+ principal = directory.principalForCalendarUserAddress(cuaddr)
+ except Exception, e:
+ log.debug("Lookup of %s failed: %s" % (cuaddr, e))
+ principal = None
+
+ if principal is None:
+ return (None, None, None)
+ else:
+ return (principal.record.fullName, principal.record.guid,
+ principal.record.calendarUserAddresses)
+
+ cal.normalizeCalendarUserAddresses(lookupFunction)
+
+ newData = str(cal)
+ return newData, not newData == data
+
+
+ def upgradeCalendarCollection(calPath, directory):
+
+ for resource in os.listdir(calPath):
+
+ if resource.startswith("."):
+ continue
+
+ resPath = os.path.join(calPath, resource)
+ resPathTmp = "%s.tmp" % resPath
+
+ log.info("Processing: %s" % (resPath,))
+ with open(resPath) as res:
+ data = res.read()
+
+ try:
+ data, fixed = fixBadQuotes(data)
+ if fixed:
+ log.info("Fixing bad quotes in %s" % (oldRes,))
+ except Exception, e:
+ log.error("Error while fixing bad quotes in %s: %s" %
+ (oldRes, e))
+ raise
+
+ try:
+ data, fixed = normalizeCUAddrs(data, directory)
+ if fixed:
+ log.info("Normalized CUAddrs in %s" % (oldRes,))
+ except Exception, e:
+ log.error("Error while normalizing %s: %s" %
+ (oldRes, e))
+ raise
+
+ # Write to a new file, then rename it over the old one
+ with open(resPathTmp, "w") as res:
+ res.write(data)
+ os.rename(resPathTmp, resPath)
+
+
+ # Remove the ctag xattr from the calendar collection
+ for attr, value in xattr.xattr(calPath).iteritems():
+ if attr == "WebDAV:{http:%2F%2Fcalendarserver.org%2Fns%2F}getctag":
+ xattr.removexattr(calPath, attr, value)
+
+
+ def upgradeCalendarHome(homePath, directory):
+
+ log.info("Upgrading calendar home: %s" % (homePath,))
+
+ for cal in os.listdir(homePath):
+ calPath = os.path.join(homePath, cal)
+ log.info("Upgrading calendar: %s" % (calPath,))
+ upgradeCalendarCollection(calPath, directory)
+
+ # Change the calendar-free-busy-set xattrs of the inbox to the
+ # __uids__/<guid> form
+ if cal == "inbox":
+ for attr, value in xattr.xattr(calPath).iteritems():
+ if attr == "WebDAV:{urn:ietf:params:xml:ns:caldav}calendar-free-busy-set":
+ value = updateFreeBusySet(value, directory)
+ if value is not None:
+ # Need to write the xattr back to disk
+ xattr.setxattr(calPath, attr, value)
+
+
+
+
+ def doProxyDatabaseMoveUpgrade(config):
+
# See if the old DB is present
- old_db_path = os.path.join(config.DocumentRoot, "principals", CalendarUserProxyDatabase.dbOldFilename)
- if not os.path.exists(old_db_path):
+ oldDbPath = os.path.join(config.DocumentRoot, "principals",
+ CalendarUserProxyDatabase.dbOldFilename)
+ if not os.path.exists(oldDbPath):
# Nothing to be done
return
-
+
# See if the new one is already present
- new_db_path = os.path.join(config.DataRoot, CalendarUserProxyDatabase.dbFilename)
- if os.path.exists(new_db_path):
- # We have a problem - both the old and new ones exist. Stop the server from starting
- # up and alert the admin to this condition
+ newDbPath = os.path.join(config.DataRoot,
+ CalendarUserProxyDatabase.dbFilename)
+ if os.path.exists(newDbPath):
+ # We have a problem - both the old and new ones exist. Stop the server
+ # from starting up and alert the admin to this condition
raise UpgradeError(
"Upgrade Error: unable to move the old calendar user proxy database at '%s' to '%s' because the new database already exists."
- % (old_db_path, new_db_path,)
+ % (oldDbPath, newDbPath,)
)
-
+
# Now move the old one to the new location
try:
- os.rename(old_db_path, new_db_path)
+ if not os.path.exists(config.DataRoot):
+ os.makedirs(config.DataRoot)
+ os.rename(oldDbPath, newDbPath)
except Exception, e:
raise UpgradeError(
"Upgrade Error: unable to move the old calendar user proxy database at '%s' to '%s' due to %s."
- % (old_db_path, new_db_path, str(e))
+ % (oldDbPath, newDbPath, str(e))
)
-
+
log.info(
"Moved the calendar user proxy database from '%s' to '%s'."
- % (old_db_path, new_db_path,)
+ % (oldDbPath, newDbPath,)
)
+
+ def moveCalendarHome(oldHome, newHome):
+ if os.path.exists(newHome):
+ # Both old and new homes exist; stop immediately to let the
+ # administrator fix it
+ raise UpgradeError(
+ "Upgrade Error: calendar home is in two places: %s and %s. Please remove one of them and restart calendar server."
+ % (oldHome, newHome)
+ )
+
+
+
+ directory = getDirectory()
+ docRoot = config.DocumentRoot
+
+ if os.path.exists(docRoot):
+
+ # Look for the /principals/ directory on disk
+ oldPrincipals = os.path.join(docRoot, "principals")
+ if os.path.exists(oldPrincipals):
+ # First move the proxy database and rename it
+ doProxyDatabaseMoveUpgrade(config)
+
+ # Now delete the on disk representation of principals
+ rmdir(oldPrincipals)
+ log.info(
+ "Removed the old principal directory at '%s'."
+ % (oldPrincipals,)
+ )
+
+ calRoot = os.path.join(docRoot, "calendars")
+ if os.path.exists(calRoot):
+
+ uidHomes = os.path.join(calRoot, "__uids__")
+
+ # Move calendar homes to new location:
+
+ if os.path.exists(uidHomes):
+ for home in os.listdir(uidHomes):
+
+ # MOR: This assumes no UID is going to be 2 chars or less
+ if len(home) <= 2:
+ continue
+
+ oldHome = os.path.join(uidHomes, home)
+ newHome = os.path.join(uidHomes, home[0:2], home[2:4], home)
+ moveCalendarHome(oldHome, newHome)
+
+ else:
+ os.mkdir(uidHomes)
+
+ for recordType, dirName in (
+ (DirectoryService.recordType_users, "users"),
+ (DirectoryService.recordType_groups, "groups"),
+ (DirectoryService.recordType_locations, "locations"),
+ (DirectoryService.recordType_resources, "resources"),
+ ):
+ dirPath = os.path.join(calRoot, dirName)
+ if os.path.exists(dirPath):
+ for shortName in os.listdir(dirPath):
+ record = directory.recordWithShortName(recordType,
+ shortName)
+ if record is not None:
+ uid = record.uid
+ oldHome = os.path.join(dirPath, shortName)
+ newHome = os.path.join(uidHomes, uid[0:2], uid[2:4],
+ uid)
+ moveCalendarHome(oldHome, newHome)
+
+ # Upgrade calendar homes in the new location:
+ for first in os.listdir(uidHomes):
+ if len(first) == 2:
+ firstPath = os.path.join(uidHomes, first)
+ for second in os.listdir(firstPath):
+ if len(second) == 2:
+ secondPath = os.path.join(firstPath, second)
+ for home in os.listdir(secondPath):
+ homePath = os.path.join(secondPath, home)
+ upgradeCalendarHome(homePath, directory)
+
+
+
+# Each method in this array will upgrade from one version to the next;
+# the index of each method within the array corresponds to the on-disk
+# version number that it upgrades from. For example, if the on-disk
+# .version file contains a "3", but there are 6 methods in this array,
+# methods 3 through 5 (using 0-based array indexing) will be executed in
+# order.
+upgradeMethods = [
+ upgrade_to_1,
+]
+# MOR: Change the above to an array of tuples
+
+def upgradeData(config):
+
+ # MOR: Temporary:
+ # config.DocumentRoot = "/Users/morgen/Migration/CalendarServer/Documents"
+ # config.DataRoot = "/Users/morgen/Migration/CalendarServer/Data"
+ docRoot = config.DocumentRoot
+
+ versionFilePath = os.path.join(docRoot, ".calendarserver_version")
+
+ newestVersion = len(upgradeMethods)
+
+ onDiskVersion = 0
+ if os.path.exists(versionFilePath):
+ try:
+ with open(versionFilePath) as versionFile:
+ onDiskVersion = int(versionFile.read().strip())
+ except IOError, e:
+ log.error("Cannot open %s; skipping migration" %
+ (versionFilePath,))
+ except ValueError, e:
+ log.error("Invalid version number in %s; skipping migration" %
+ (versionFilePath,))
+
+ for upgradeVersion in range(onDiskVersion, newestVersion):
+ log.info("Upgrading to version %d" % (upgradeVersion+1,))
+ upgradeMethods[upgradeVersion](config)
+ with open(versionFilePath, "w") as verFile:
+ verFile.write(str(upgradeVersion+1))
+
+
class UpgradeError(RuntimeError):
"""
Generic upgrade error.
"""
pass
+
+
+#
+# Utility functions
+#
+def updateFreeBusyHref(href, directory):
+ pieces = href.split("/")
+ if pieces[2] == "__uids__":
+ # Already updated
+ return None
+
+ recordType = pieces[2]
+ shortName = pieces[3]
+ record = directory.recordWithShortName(recordType, shortName)
+ if record is None:
+ msg = "Can't update free-busy href; %s is not in the directory" % shortName
+ log.error(msg)
+ raise UpgradeError(msg)
+
+ uid = record.uid
+ newHref = "/calendars/__uids__/%s/%s/" % (uid, pieces[4])
+ return newHref
+
+
+def updateFreeBusySet(value, directory):
+
+ try:
+ value = zlib.decompress(value)
+ except zlib.error:
+ # Legacy data - not zlib compressed
+ pass
+
+ try:
+ doc = davxml.WebDAVDocument.fromString(value)
+ freeBusySet = doc.root_element
+ except ValueError:
+ try:
+ freeBusySet = unpickle(value)
+ except UnpicklingError:
+ log.err("Invalid xattr property value for: %s" % attr)
+ # MOR: continue on?
+ return
+
+ fbset = set()
+ didUpdate = False
+ for href in freeBusySet.children:
+ href = str(href)
+ newHref = updateFreeBusyHref(href, directory)
+ if newHref is None:
+ fbset.add(href)
+ else:
+ didUpdate = True
+ fbset.add(newHref)
+
+ if didUpdate:
+ property = caldavxml.CalendarFreeBusySet(*[davxml.HRef(href)
+ for href in fbset])
+ value = compress(property.toxml())
+ return value
+
+ return None # no update required
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20090223/7e59c81c/attachment-0001.html>
More information about the calendarserver-changes
mailing list