[CalendarServer-changes] [4823] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Fri Dec 4 12:18:30 PST 2009
Revision: 4823
http://trac.macosforge.org/projects/calendarserver/changeset/4823
Author: cdaboo at apple.com
Date: 2009-12-04 12:18:30 -0800 (Fri, 04 Dec 2009)
Log Message:
-----------
Changes to implicit error handling to reduce the number of client 403s. Also tweaked account logging to
be able to better log implicit errors.
Modified Paths:
--------------
CalendarServer/trunk/twext/web2/channel/http.py
CalendarServer/trunk/twistedcaldav/accounting.py
CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py
CalendarServer/trunk/twistedcaldav/scheduling/itip.py
CalendarServer/trunk/twistedcaldav/scheduling/test/test_icaldiff.py
Modified: CalendarServer/trunk/twext/web2/channel/http.py
===================================================================
--- CalendarServer/trunk/twext/web2/channel/http.py 2009-12-04 20:13:23 UTC (rev 4822)
+++ CalendarServer/trunk/twext/web2/channel/http.py 2009-12-04 20:18:30 UTC (rev 4823)
@@ -165,7 +165,7 @@
if self.logData is not None:
doneTime = time.time()
self.logData.response.append("\r\n\r\n<<<< Response complete at: %.3f (elapsed: %.1f ms)\r\n" % (doneTime, 1000 * (doneTime - self.startTime),))
- accounting.emitAccounting("HTTP", "all", "".join(self.logData.request) + "".join(self.logData.response))
+ accounting.emitAccounting("HTTP", "", "".join(self.logData.request) + "".join(self.logData.response))
HTTPChannel.chanRequestFactory = HTTPLoggingChannelRequest
Modified: CalendarServer/trunk/twistedcaldav/accounting.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/accounting.py 2009-12-04 20:13:23 UTC (rev 4822)
+++ CalendarServer/trunk/twistedcaldav/accounting.py 2009-12-04 20:18:30 UTC (rev 4823)
@@ -1,5 +1,5 @@
##
-# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+# 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.
@@ -86,42 +86,51 @@
principal.record.guid
)
else:
- return
+ return None
try:
#
# Obtain the accounting log file name
#
logRoot = config.AccountingLogRoot
- logDirectory = os.path.join(
- logRoot,
- principalLogPath,
- category
+ logDirectory = category
+ if principalLogPath:
+ logDirectory = os.path.join(
+ logDirectory,
+ principalLogPath,
+ )
+ logFilename = os.path.join(
+ logDirectory,
+ datetime.datetime.now().isoformat()
)
- logFilename = os.path.join(logDirectory, datetime.datetime.now().isoformat())
- if not os.path.isdir(logDirectory):
- os.makedirs(logDirectory)
+ if not os.path.isdir(os.path.join(logRoot, logDirectory)):
+ os.makedirs(os.path.join(logRoot, logDirectory))
logFilename = "%s-01" % (logFilename,)
else:
index = 1
while True:
path = "%s-%02d" % (logFilename, index)
- if not os.path.isfile(path):
+ if not os.path.isfile(os.path.join(logRoot, path)):
logFilename = path
break
if index == 1000:
log.error("Too many %s accounting files for %s" % (category, principal))
- return
+ return None
+ index += 1
#
# Now write out the data to the log file
#
- logFile = open(logFilename, "a")
+ logFile = open(os.path.join(logRoot, logFilename), "a")
try:
logFile.write(data)
finally:
logFile.close()
+
+ return logFilename
+
except OSError, e:
# No failures in accounting should propagate out
log.error("Failed to write accounting data due to: %s" % (str(e),))
+ return None
Modified: CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py 2009-12-04 20:13:23 UTC (rev 4822)
+++ CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py 2009-12-04 20:18:30 UTC (rev 4823)
@@ -21,6 +21,7 @@
from twistedcaldav.log import Logger
from twistedcaldav.scheduling.cuaddress import normalizeCUAddr
from twistedcaldav.scheduling.itip import iTipGenerator
+from twistedcaldav import accounting
from difflib import unified_diff
@@ -36,17 +37,17 @@
class iCalDiff(object):
- def __init__(self, calendar1, calendar2, smart_merge):
+ def __init__(self, oldcalendar, newcalendar, smart_merge):
"""
- @param calendar1:
- @type calendar1:
- @param calendar2:
- @type calendar2:
+ @param oldcalendar:
+ @type oldcalendar:
+ @param newcalendar:
+ @type newcalendar:
"""
- self.calendar1 = calendar1
- self.calendar2 = calendar2
+ self.oldcalendar = oldcalendar
+ self.newcalendar = newcalendar
self.smart_merge = smart_merge
def organizerDiff(self):
@@ -75,34 +76,32 @@
return calendar
# Normalize components for comparison
- self.calendar1 = duplicateAndNormalize(self.calendar1)
- self.calendar2 = duplicateAndNormalize(self.calendar2)
+ oldcalendar_norm = duplicateAndNormalize(self.oldcalendar)
+ newcalendar_norm = duplicateAndNormalize(self.newcalendar)
- result = self.calendar1 == self.calendar2
- if not result:
- self._logDiffError("organizerDiff: Mismatched calendar objects")
+ result = oldcalendar_norm == newcalendar_norm
return result
def _organizerMerge(self):
"""
- Merge changes to ATTENDEE properties in calendar1 into calendar2.
+ Merge changes to ATTENDEE properties in oldcalendar into newcalendar.
"""
- organizer = normalizeCUAddr(self.calendar2.masterComponent().propertyValue("ORGANIZER"))
+ organizer = normalizeCUAddr(self.newcalendar.masterComponent().propertyValue("ORGANIZER"))
self._doSmartMerge(organizer, True)
def _doSmartMerge(self, ignore_attendee, is_organizer):
"""
- Merge changes to ATTENDEE properties in calendar1 into calendar2.
+ Merge changes to ATTENDEE properties in oldcalendar into newcalendar.
"""
- old_master = self.calendar1.masterComponent()
- new_master = self.calendar2.masterComponent()
+ old_master = self.oldcalendar.masterComponent()
+ new_master = self.newcalendar.masterComponent()
# Do master merge first
self._tryComponentMerge(old_master, new_master, ignore_attendee, is_organizer)
# New check the matching components
- for old_component in self.calendar1.subcomponents():
+ for old_component in self.oldcalendar.subcomponents():
# Make sure we have an appropriate component
if old_component.name() == "VTIMEZONE":
@@ -112,17 +111,17 @@
continue
# Find matching component in new calendar
- new_component = self.calendar2.overriddenComponent(rid)
+ new_component = self.newcalendar.overriddenComponent(rid)
if new_component is None:
# If the old component was cancelled ignore when an attendee
if not is_organizer and old_component.propertyValue("STATUS") == "CANCELLED":
continue
# Determine whether the instance is still valid in the new calendar
- new_component = self.calendar2.deriveInstance(rid)
+ new_component = self.newcalendar.deriveInstance(rid)
if new_component:
# Derive a new instance from the new calendar and transfer attendee status
- self.calendar2.addComponent(new_component)
+ self.newcalendar.addComponent(new_component)
self._tryComponentMerge(old_component, new_component, ignore_attendee, is_organizer)
else:
# Ignore the old instance as it no longer exists
@@ -131,7 +130,7 @@
self._tryComponentMerge(old_component, new_component, ignore_attendee, is_organizer)
# Check the new instances not in the old calendar
- for new_component in self.calendar2.subcomponents():
+ for new_component in self.newcalendar.subcomponents():
# Make sure we have an appropriate component
if new_component.name() == "VTIMEZONE":
@@ -141,16 +140,16 @@
continue
# Find matching component in old calendar
- old_component = self.calendar1.overriddenComponent(rid)
+ old_component = self.oldcalendar.overriddenComponent(rid)
if old_component is None:
# If the new component is cancelled ignore when an attendee
if not is_organizer and new_component.propertyValue("STATUS") == "CANCELLED":
continue
# Try to derive a new instance in the client and transfer attendee status
- old_component = self.calendar1.deriveInstance(rid)
+ old_component = self.oldcalendar.deriveInstance(rid)
if old_component:
- self.calendar1.addComponent(old_component)
+ self.oldcalendar.addComponent(old_component)
self._tryComponentMerge(old_component, new_component, ignore_attendee, is_organizer)
else:
# Ignore as we have no state for the new instance
@@ -248,11 +247,11 @@
if config.MaxInstancesForRRULE != 0:
try:
- self.calendar1.truncateRecurrence(config.MaxInstancesForRRULE)
+ self.oldcalendar.truncateRecurrence(config.MaxInstancesForRRULE)
except (ValueError, TypeError), ex:
log.err("Cannot truncate calendar resource: %s" % (ex,))
- self.newCalendar = self.calendar1.duplicate()
+ self.newCalendar = self.oldcalendar.duplicate()
self.newMaster = self.newCalendar.masterComponent()
changeCausesReply = False
@@ -285,60 +284,61 @@
return exdates, map, master
- exdates1, map1, master1 = mapComponents(self.calendar1)
- set1 = set(map1.keys())
- exdates2, map2, master2 = mapComponents(self.calendar2)
- set2 = set(map2.keys())
+ exdatesold, mapold, masterold = mapComponents(self.oldcalendar)
+ setold = set(mapold.keys())
+ exdatesnew, mapnew, masternew = mapComponents(self.newcalendar)
+ setnew = set(mapnew.keys())
# Handle case where iCal breaks events without a master component
- if master2 is not None and master1 is None:
- master2Start = master2.getStartDateUTC()
- key2 = (master2.name(), master2.propertyValue("UID"), master2Start)
- if key2 not in set1:
+ if masternew is not None and masterold is None:
+ masternewStart = masternew.getStartDateUTC()
+ keynew = (masternew.name(), masternew.propertyValue("UID"), masternewStart)
+ if keynew not in setold:
# The DTSTART in the fake master does not match a RECURRENCE-ID in the real data.
# We have to do a brute force search for the component that matches based on DTSTART
- for component1 in self.calendar1.subcomponents():
- if component1.name() == "VTIMEZONE":
+ for componentold in self.oldcalendar.subcomponents():
+ if componentold.name() == "VTIMEZONE":
continue
- if master2Start == component1.getStartDateUTC():
+ if masternewStart == componentold.getStartDateUTC():
break
else:
# Nothing matches - this has to be treated as an error
- log.debug("attendeeMerge: Unable to match fake master component: %s" % (key2,))
+ self._logDiffError("attendeeMerge: Unable to match fake master component: %s" % (keynew,))
return False, False, (), None
else:
- component1 = self.calendar1.overriddenComponent(master2Start)
+ componentold = self.oldcalendar.overriddenComponent(masternewStart)
# Take the recurrence ID from component1 and fix map2/set2
- key2 = (master2.name(), master2.propertyValue("UID"), None)
- component2 = map2[key2]
- del map2[key2]
+ keynew = (masternew.name(), masternew.propertyValue("UID"), None)
+ componentnew = mapnew[keynew]
+ del mapnew[keynew]
- rid1 = component1.getRecurrenceIDUTC()
- newkey2 = (master2.name(), master2.propertyValue("UID"), rid1)
- map2[newkey2] = component2
- set2.remove(key2)
- set2.add(newkey2)
+ ridold = componentold.getRecurrenceIDUTC()
+ newkeynew = (masternew.name(), masternew.propertyValue("UID"), ridold)
+ mapnew[newkeynew] = componentnew
+ setnew.remove(keynew)
+ setnew.add(newkeynew)
- # All the components in calendar1 must be in calendar2 unless they are CANCELLED
- result = set1 - set2
- for key in result:
+ # All the components in oldcalendar must be in newcalendar unless they are CANCELLED
+ for key in setold - setnew:
_ignore_name, _ignore_uid, rid = key
- component = map1[key]
+ component = mapold[key]
if component.propertyValue("STATUS") != "CANCELLED":
# Attendee may decline by EXDATE'ing an instance - we need to handle that
- if exdates2 is None or rid in exdates2:
+ if exdatesnew is None or rid in exdatesnew:
# Mark Attendee as DECLINED in the server instance
if self._attendeeDecline(self.newCalendar.overriddenComponent(rid)):
changeCausesReply = True
changedRids.append(toString(rid) if rid else "")
else:
- log.debug("attendeeMerge: Missing uncancelled component from first calendar: %s" % (key,))
- return False, False, (), None
+ # We used to generate a 403 here - but instead we now ignore this error and let the server data
+ # override the client
+ self._logDiffError("attendeeMerge: Missing uncancelled component from first calendar: %s" % (key,))
else:
- if exdates2 is not None and rid not in exdates2:
- log.debug("attendeeMerge: Missing EXDATE for cancelled components from first calendar: %s" % (key,))
- return False, False, (), None
+ if exdatesnew is not None and rid not in exdatesnew:
+ # We used to generate a 403 here - but instead we now ignore this error and let the server data
+ # override the client
+ self._logDiffError("attendeeMerge: Missing EXDATE for cancelled components from first calendar: %s" % (key,))
else:
# Remove the CANCELLED component from the new calendar and add an EXDATE
overridden = self.newCalendar.overriddenComponent(rid)
@@ -346,52 +346,63 @@
if self.newMaster:
self.newMaster.addProperty(Property("EXDATE", [rid,]))
- # Derive a new component in the new calendar for each new one in set2
- for key in set2 - set1:
+ # Derive a new component in the new calendar for each new one in setnew
+ for key in setnew - setold:
# First check if the attendee's copy is cancelled and properly EXDATE'd
# and skip it if so.
_ignore_name, _ignore_uid, rid = key
- component2 = map2[key]
- if component2.propertyValue("STATUS") == "CANCELLED":
- if exdates1 is None or rid not in exdates1:
- log.debug("attendeeMerge: Cancelled component not found in first calendar (or no EXDATE): %s" % (key,))
- return False, False, (), None
+ componentnew = mapnew[key]
+ if componentnew.propertyValue("STATUS") == "CANCELLED":
+ if exdatesold is None or rid not in exdatesold:
+ # We used to generate a 403 here - but instead we now ignore this error and let the server data
+ # override the client
+ self._logDiffError("attendeeMerge: Cancelled component not found in first calendar (or no EXDATE): %s" % (key,))
+ setnew.remove(key)
else:
# Derive new component with STATUS:CANCELLED and remove EXDATE
newOverride = self.newCalendar.deriveInstance(rid, allowCancelled=True)
if newOverride is None:
- log.debug("attendeeMerge: Could not derive instance for cancelled component: %s" % (key,))
- return False, False, (), None
- self.newCalendar.addComponent(newOverride)
+ # We used to generate a 403 here - but instead we now ignore this error and let the server data
+ # override the client
+ self._logDiffError("attendeeMerge: Could not derive instance for cancelled component: %s" % (key,))
+ setnew.remove(key)
+ else:
+ self.newCalendar.addComponent(newOverride)
else:
# Derive new component
newOverride = self.newCalendar.deriveInstance(rid)
if newOverride is None:
- log.debug("attendeeMerge: Could not derive instance for uncancelled component: %s" % (key,))
- return False, False, (), None
- self.newCalendar.addComponent(newOverride)
+ # We used to generate a 403 here - but instead we now ignore this error and let the server data
+ # override the client
+ self._logDiffError("attendeeMerge: Could not derive instance for uncancelled component: %s" % (key,))
+ setnew.remove(key)
+ else:
+ self.newCalendar.addComponent(newOverride)
# So now newCalendar has all the same components as set2. Check changes and do transfers.
# Make sure the same VCALENDAR properties match
- if not self._checkVCALENDARProperties(self.newCalendar, self.calendar2):
+ if not self._checkVCALENDARProperties(self.newCalendar, self.newcalendar):
+ # We used to generate a 403 here - but instead we now ignore this error and let the server data
+ # override the client
self._logDiffError("attendeeMerge: VCALENDAR properties do not match")
- return False, False, (), None
# Now we transfer per-Attendee
- # data from calendar2 into newCalendar to sync up changes, whilst verifying that other
+ # data from newcalendar into newCalendar to sync up changes, whilst verifying that other
# key properties are unchanged
declines = []
- for key in set2:
+ for key in setnew:
_ignore_name, _ignore_uid, rid = key
serverData = self.newCalendar.overriddenComponent(rid)
- clientData = map2[key]
+ clientData = mapnew[key]
allowed, reply = self._transferAttendeeData(serverData, clientData, declines)
if not allowed:
+ # We used to generate a 403 here - but instead we now ignore this error and let the server data
+ # override the client
self._logDiffError("attendeeMerge: Mismatched calendar objects")
- return False, False, (), None
+ #return False, False, (), None
changeCausesReply |= reply
if reply:
changedRids.append(toString(rid) if rid else "")
@@ -407,7 +418,7 @@
changeCausesReply = True
changedRids.append(toString(decline) if decline else "")
else:
- log.debug("Unable to override and instance to mark as DECLINED: %s" % (decline,))
+ self._logDiffError("attendeeMerge: Unable to override an instance to mark as DECLINED: %s" % (decline,))
return False, False, (), None
return True, changeCausesReply, changedRids, self.newCalendar
@@ -430,9 +441,9 @@
def _transferAttendeeData(self, serverComponent, clientComponent, declines):
- # First check validity of date-time related properties
- if not self._checkInvalidChanges(serverComponent, clientComponent, declines):
- return False, False
+ # We are skipping this check now - instead we let the server data override the broken client data
+ # First check validity of date-time related properties and get removed components which are declines
+ self._checkInvalidChanges(serverComponent, clientComponent, declines)
# Now look for items to transfer from one to the other.
# We care about the ATTENDEE's PARTSTAT, TRANSP, VALARMS, X-APPLE-NEEDS-REPLY,
@@ -462,9 +473,8 @@
self._transferProperty("LAST-MODIFIED", serverComponent, clientComponent)
self._transferProperty("X-APPLE-NEEDS-REPLY", serverComponent, clientComponent)
- # Dropbox
- if not self._transferDropBoxData(serverComponent, clientComponent):
- return False, False
+ # Dropbox - this now never returns false
+ self._transferDropBoxData(serverComponent, clientComponent)
# Handle VALARMs
serverComponent.removeAlarms()
@@ -483,14 +493,14 @@
if not clientDropbox:
return True
elif not serverDropbox:
- # Attendee not allowed to add a dropbox
- log.debug("Attendee not allowed to add dropbox: %s" % (clientDropbox,))
- return False
+ # Attendee not allowed to add a dropbox - ignore this
+ self._logDiffError("Attendee not allowed to add dropbox: %s" % (clientDropbox,))
+ return True
else:
- # Values must be the same
+ # Values must be the same - ignore this
if serverDropbox != clientDropbox:
- log.debug("Attendee not allowed to change dropbox from: %s to: %s" % (serverDropbox, clientDropbox,))
- return False
+ self._logDiffError("Attendee not allowed to change dropbox from: %s to: %s" % (serverDropbox, clientDropbox,))
+ return True
# Remove existing ATTACH's from server
for attachment in tuple(serverComponent.properties("ATTACH")):
@@ -622,39 +632,41 @@
rids = {}
- map1 = mapComponents(self.calendar1)
- set1 = set(map1.keys())
- map2 = mapComponents(self.calendar2)
- set2 = set(map2.keys())
+ oldmap = mapComponents(self.oldcalendar)
+ oldset = set(oldmap.keys())
+ newmap = mapComponents(self.newcalendar)
+ newset = set(newmap.keys())
- # Now verify that each component in set1 matches what is in set2
- for key in (set1 & set2):
- component1 = map1[key]
- component2 = map2[key]
+ # Now verify that each component in oldset matches what is in newset
+ for key in (oldset & newset):
+ component1 = oldmap[key]
+ component2 = newmap[key]
self._diffComponents(component1, component2, rids)
- # Now verify that each additional component in set1 matches a derived component in set2
- for key in set1 - set2:
- component1 = map1[key]
- component2 = self.calendar2.deriveInstance(key[2])
- if component2 is None:
+ # Now verify that each additional component in oldset matches a derived component in newset
+ for key in oldset - newset:
+ oldcomponent = oldmap[key]
+ newcomponent = self.newcalendar.deriveInstance(key[2])
+ if newcomponent is None:
continue
- self._diffComponents(component1, component2, rids)
+ self._diffComponents(oldcomponent, newcomponent, rids)
- # Now verify that each additional component in set1 matches a derived component in set2
- for key in set2 - set1:
- component1 = self.calendar1.deriveInstance(key[2])
- if component1 is None:
+ # Now verify that each additional component in oldset matches a derived component in newset
+ for key in newset - oldset:
+ oldcomponent = self.oldcalendar.deriveInstance(key[2])
+ if oldcomponent is None:
continue
- component2 = map2[key]
- self._diffComponents(component1, component2, rids)
+ newcomponent = newmap[key]
+ self._diffComponents(oldcomponent, newcomponent, rids)
return rids
- def _attendeeDuplicateAndNormalize(self, comp):
+ def _componentDuplicateAndNormalize(self, comp):
comp = comp.duplicate()
comp.normalizePropertyValueLists("EXDATE")
comp.removePropertyParameters("ORGANIZER", ("SCHEDULE-STATUS",))
+ comp.removePropertyParameters("ATTENDEE", ("SCHEDULE-STATUS", "SCHEDULE-FORCE-SEND",))
+ comp.removeAlarms()
comp.normalizeAll()
comp.normalizeAttachments()
iTipGenerator.prepareSchedulingMessage(comp, reply=True)
@@ -669,8 +681,8 @@
return
# Duplicate then normalize for comparison
- comp1 = self._attendeeDuplicateAndNormalize(comp1)
- comp2 = self._attendeeDuplicateAndNormalize(comp2)
+ comp1 = self._componentDuplicateAndNormalize(comp1)
+ comp2 = self._componentDuplicateAndNormalize(comp2)
# Diff all the properties
comp1.transformAllFromNative()
@@ -710,10 +722,30 @@
def _logDiffError(self, title):
- diff = "\n".join(unified_diff(
- str(self.calendar1).split("\n"),
- str(self.calendar2).split("\n"),
+ strcal1 = str(self.oldcalendar)
+ strcal2 = str(self.newcalendar)
+ strdiff = "\n".join(unified_diff(
+ strcal1.split("\n"),
+ strcal2.split("\n"),
fromfile='Existing Calendar Object',
tofile='New Calendar Object',
))
- log.debug("%s:\n%s" % (title, diff,))
+
+ logstr = """%s
+
+------ Existing Calendar Data ------
+%s
+------ New Calendar Data ------
+%s
+------ Diff ------
+%s
+""" % (title, strcal1, strcal2, strdiff,)
+
+ loggedUID = self.oldcalendar.resourceUID()
+ if loggedUID:
+ loggedUID = loggedUID.encode("base64")[:-1]
+ else:
+ loggedUID = "Unknown"
+ loggedName = accounting.emitAccounting("Implicit Errors", loggedUID, logstr)
+ if loggedName:
+ log.err("Generating Implicit Error accounting at path: %s" % (loggedName,))
Modified: CalendarServer/trunk/twistedcaldav/scheduling/itip.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/itip.py 2009-12-04 20:13:23 UTC (rev 4822)
+++ CalendarServer/trunk/twistedcaldav/scheduling/itip.py 2009-12-04 20:18:30 UTC (rev 4823)
@@ -99,10 +99,13 @@
master_valarms = [comp for comp in current_master.subcomponents() if comp.name() == "VALARM"]
private_comments = current_master.properties("X-CALENDARSERVER-PRIVATE-COMMENT")
transps = current_master.properties("TRANSP")
+ organizer = current_master.getProperty("ORGANIZER")
+ organizer_schedule_status = organizer.params().get("SCHEDULE-STATUS", None) if organizer else None
else:
master_valarms = ()
private_comments = ()
transps = ()
+ organizer_schedule_status = None
if itip_message.masterComponent() is not None:
@@ -117,11 +120,15 @@
master_component.addProperty(comment)
for transp in transps:
master_component.replaceProperty(transp)
+ if organizer_schedule_status:
+ organizer = master_component.getProperty("ORGANIZER")
+ if organizer:
+ organizer.params()["SCHEDULE-STATUS"] = organizer_schedule_status
# Now try to match recurrences
for component in new_calendar.subcomponents():
if component.name() != "VTIMEZONE" and component.getRecurrenceIDUTC() is not None:
- iTipProcessing.transferItems(calendar, master_valarms, private_comments, transps, component)
+ iTipProcessing.transferItems(calendar, master_valarms, private_comments, transps, organizer_schedule_status, component)
# Now try to match recurrences
for component in calendar.subcomponents():
@@ -132,7 +139,7 @@
new_component = new_calendar.deriveInstance(rid, allowCancelled=allowCancelled)
if new_component:
new_calendar.addComponent(new_component)
- iTipProcessing.transferItems(calendar, master_valarms, private_comments, transps, new_component)
+ iTipProcessing.transferItems(calendar, master_valarms, private_comments, transps, organizer_schedule_status, new_component)
# Replace the entire object
return new_calendar, rids
@@ -149,7 +156,7 @@
calendar.addComponent(component)
else:
component = component.duplicate()
- iTipProcessing.transferItems(calendar, master_valarms, private_comments, transps, component, remove_matched=True)
+ iTipProcessing.transferItems(calendar, master_valarms, private_comments, transps, organizer_schedule_status, component, remove_matched=True)
calendar.addComponent(component)
if recipient and not autoprocessing:
iTipProcessing.fixForiCal3((component,), recipient, config.Scheduling.CalDAV.OldDraftCompatibility)
@@ -446,7 +453,7 @@
return attendee.value(), partstat_changed, private_comment_changed
@staticmethod
- def transferItems(from_calendar, master_valarms, private_comments, transps, to_component, remove_matched=False):
+ def transferItems(from_calendar, master_valarms, private_comments, transps, organizer_schedule_status, to_component, remove_matched=False):
rid = to_component.getRecurrenceIDUTC()
@@ -458,6 +465,13 @@
[to_component.addProperty(prop) for prop in matched.properties("X-CALENDARSERVER-ATTENDEE-COMMENT")]
[to_component.replaceProperty(prop) for prop in matched.properties("TRANSP")]
+ organizer = matched.getProperty("ORGANIZER")
+ organizer_schedule_status = organizer.params().get("SCHEDULE-STATUS", None) if organizer else None
+ if organizer_schedule_status:
+ organizer = to_component.getProperty("ORGANIZER")
+ if organizer:
+ organizer.params()["SCHEDULE-STATUS"] = organizer_schedule_status
+
# Remove the old one
if remove_matched:
from_calendar.removeComponent(matched)
@@ -468,6 +482,10 @@
[to_component.addComponent(alarm) for alarm in master_valarms]
[to_component.addProperty(comment) for comment in private_comments]
[to_component.replaceProperty(transp) for transp in transps]
+ if organizer_schedule_status:
+ organizer = to_component.getProperty("ORGANIZER")
+ if organizer:
+ organizer.params()["SCHEDULE-STATUS"] = organizer_schedule_status
@staticmethod
def fixForiCal3(components, recipient, compatibilityMode):
Modified: CalendarServer/trunk/twistedcaldav/scheduling/test/test_icaldiff.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/test/test_icaldiff.py 2009-12-04 20:13:23 UTC (rev 4822)
+++ CalendarServer/trunk/twistedcaldav/scheduling/test/test_icaldiff.py 2009-12-04 20:18:30 UTC (rev 4823)
@@ -594,7 +594,19 @@
END:VCALENDAR
""",
"mailto:user2 at example.com",
- (False, False, (), None)
+ (True, False, (), """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;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""")
),
(
"#1.4 Simple component, valarm change",
@@ -753,7 +765,24 @@
END:VCALENDAR
""",
"mailto:user2 at example.com",
- (False, False, (), None)
+ (True, False, (), """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;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test for Attendee
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""")
),
(
"#1.7 Simple component, vtimezone no change",
@@ -917,7 +946,37 @@
END:VCALENDAR
""",
"mailto:user2 at example.com",
- (False, False, (), None)
+ (True, False, (), """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VTIMEZONE
+TZID:US-Eastern
+LAST-MODIFIED:20040110T032845Z
+BEGIN:STANDARD
+DTSTART:19901026T060000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19900404T010000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART;TZID=US-Eastern:20080601T120000
+DTEND;TZID=US-Eastern:20080601T130000
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""")
),
(
"#1.9 Simple component, vtimezone substitute",
@@ -1323,7 +1382,28 @@
END:VCALENDAR
""",
"mailto:user2 at example.com",
- (False, False, (), None)
+ (True, False, (), """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;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+RRULE:COUNT=400;FREQ=DAILY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T123000Z
+DTEND:20080602T130000Z
+ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""")
),
(
"#1.4 Complex component, additional override no change ok",
@@ -1557,7 +1637,37 @@
END:VCALENDAR
""",
"mailto:user2 at example.com",
- (False, False, (), None)
+ (True, False, (), """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;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+RRULE:COUNT=400;FREQ=DAILY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T123000Z
+DTEND:20080602T130000Z
+ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080604T120000Z
+DTSTART:20080604T120000Z
+DTEND:20080604T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""")
),
)
@@ -2195,7 +2305,20 @@
END:VCALENDAR
""",
"mailto:user2 at example.com",
- (False, False, (), None,)
+ (True, False, (), """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080604T120000Z
+DTSTART:20080604T130000Z
+DTEND:20080604T140000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",)
),
(
"#1.6 Add uncancelled component, no master",
@@ -2237,7 +2360,20 @@
END:VCALENDAR
""",
"mailto:user2 at example.com",
- (False, False, (), None,)
+ (True, False, (), """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080604T120000Z
+DTSTART:20080604T130000Z
+DTEND:20080604T140000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",)
),
)
@@ -2339,7 +2475,19 @@
END:VCALENDAR
""",
"mailto:user2 at example.com",
- (False, False, (), None,)
+ (True, False, (), """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;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",)
),
(
"#1.3 Different dropbox",
@@ -2378,7 +2526,23 @@
END:VCALENDAR
""",
"mailto:user2 at example.com",
- (False, False, (), None,)
+ (True, False, (), """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTACH;VALUE=URI:http://localhost/calendars/users/dropbox/6073432E-644B-49
+ 65-B6F7-C3F08E70BBF9.dropbox/caldavd.plist
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+X-APPLE-DROPBOX:/calendars/users/dropbox/6073432E-644B-4965-B6F7-C3F08E70B
+ BF9.dropbox
+END:VEVENT
+END:VCALENDAR
+""",)
),
(
"#1.4 Change dropbox - remove ATTACH",
@@ -2627,7 +2791,20 @@
END:VCALENDAR
""",
"mailto:user2 at example.com",
- (False, False, (), None,)
+ (True, True, ('20080601T120000Z',), """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080601T120000Z
+DTSTART:20080601T130000Z
+DTEND:20080601T140000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""",)
),
(
"#2.1 Two overridden components, partstat change - ok",
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20091204/efef56b3/attachment-0001.html>
More information about the calendarserver-changes
mailing list