[CalendarServer-changes] [15307] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Fri Nov 13 09:46:03 PST 2015
Revision: 15307
http://trac.calendarserver.org//changeset/15307
Author: cdaboo at apple.com
Date: 2015-11-13 09:46:03 -0800 (Fri, 13 Nov 2015)
Log Message:
-----------
Enhanced X- property/parameter whitelisting.
Modified Paths:
--------------
CalendarServer/trunk/conf/caldavd-test.plist
CalendarServer/trunk/requirements-cs.txt
CalendarServer/trunk/twistedcaldav/ical.py
CalendarServer/trunk/twistedcaldav/stdconfig.py
CalendarServer/trunk/txdav/caldav/datastore/scheduling/icaldiff.py
CalendarServer/trunk/txdav/caldav/datastore/scheduling/itip.py
CalendarServer/trunk/txdav/caldav/datastore/scheduling/processing.py
CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_itip.py
CalendarServer/trunk/txdav/caldav/datastore/sql.py
Modified: CalendarServer/trunk/conf/caldavd-test.plist
===================================================================
--- CalendarServer/trunk/conf/caldavd-test.plist 2015-11-13 17:43:53 UTC (rev 15306)
+++ CalendarServer/trunk/conf/caldavd-test.plist 2015-11-13 17:46:03 UTC (rev 15307)
@@ -650,6 +650,40 @@
<true/>
<key>EnablePrivateComments</key>
<true/>
+ <key>OrganizerPublicProperties</key>
+ <array>
+ <string>X-APPLE-DROPBOX</string>
+ <string>X-APPLE-STRUCTURED-LOCATION</string>
+ <string>X-TEST-ORGANIZER-PROP1</string>
+ <string>X-TEST-ORGANIZER-PROP2</string>
+ <string>X-TEST-ORGANIZER-PROP3</string>
+ <string>X-TEST-ORGANIZER-PROP4</string>
+ <string>X-TEST-ORGANIZER-PROP5</string>
+ </array>
+ <key>OrganizerPublicParameters</key>
+ <array>
+ <string>X-TEST-ORGANIZER-PARAM1</string>
+ <string>X-TEST-ORGANIZER-PARAM2</string>
+ <string>X-TEST-ORGANIZER-PARAM3</string>
+ <string>X-TEST-ORGANIZER-PARAM4</string>
+ <string>X-TEST-ORGANIZER-PARAM5</string>
+ </array>
+ <key>AttendeePublicProperties</key>
+ <array>
+ <string>X-TEST-ALL-PROP1</string>
+ <string>X-TEST-ALL-PROP2</string>
+ <string>X-TEST-ALL-PROP3</string>
+ <string>X-TEST-ALL-PROP4</string>
+ <string>X-TEST-ALL-PROP5</string>
+ </array>
+ <key>AttendeePublicParameters</key>
+ <array>
+ <string>X-TEST-ALL-PARAM1</string>
+ <string>X-TEST-ALL-PARAM2</string>
+ <string>X-TEST-ALL-PARAM3</string>
+ <string>X-TEST-ALL-PARAM4</string>
+ <string>X-TEST-ALL-PARAM5</string>
+ </array>
</dict>
<!-- iSchedule protocol options -->
Modified: CalendarServer/trunk/requirements-cs.txt
===================================================================
--- CalendarServer/trunk/requirements-cs.txt 2015-11-13 17:43:53 UTC (rev 15306)
+++ CalendarServer/trunk/requirements-cs.txt 2015-11-13 17:46:03 UTC (rev 15307)
@@ -7,7 +7,7 @@
zope.interface==4.1.3
setuptools==18.5
- --editable svn+http://svn.calendarserver.org/repository/calendarserver/twext/trunk@15302#egg=twextpy
+ --editable svn+http://svn.calendarserver.org/repository/calendarserver/twext/trunk@15306#egg=twextpy
cffi==1.3.0
pycparser==2.14
#twisted
Modified: CalendarServer/trunk/twistedcaldav/ical.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/ical.py 2015-11-13 17:43:53 UTC (rev 15306)
+++ CalendarServer/trunk/twistedcaldav/ical.py 2015-11-13 17:46:03 UTC (rev 15307)
@@ -84,6 +84,11 @@
PERUSER_UID = "X-CALENDARSERVER-PERUSER-UID"
PERINSTANCE_COMPONENT = "X-CALENDARSERVER-PERINSTANCE"
+PRIVATE_COMMENT = "X-CALENDARSERVER-PRIVATE-COMMENT"
+ATTENDEE_COMMENT = "X-CALENDARSERVER-ATTENDEE-COMMENT"
+ATTENDEE_COMMENT_REF = "X-CALENDARSERVER-ATTENDEE-REF"
+DTSTAMP_PARAM = "X-CALENDARSERVER-DTSTAMP"
+
# 2445 default values and parameters
# Structure: propname: (<default value>, <parameter defaults dict>)
@@ -1112,6 +1117,16 @@
self._markAsDirty()
+ def removeProperties(self, name):
+ """
+ remove all properties with name
+ @param name: the name of the properties to remove.
+ """
+ self._pycalendar.removeProperties(name)
+ self._pycalendar.finalise()
+ self._markAsDirty()
+
+
def removeAllPropertiesWithName(self, pname):
"""
Remove all properties with the given name from all components.
@@ -3105,14 +3120,14 @@
self.removeComponent(component)
- def removeXProperties(self, keep_properties=(), keep_parameters={}, remove_x_parameters=True, do_subcomponents=True):
+ def removeXProperties(self, keep_properties=(), keep_parameters={}, do_subcomponents=True):
"""
Remove all X- properties except the specified ones
"""
if do_subcomponents and self.name() == "VCALENDAR":
for component in self.subcomponents():
- component.removeXProperties(keep_properties, keep_parameters, remove_x_parameters, do_subcomponents=False)
+ component.removeXProperties(keep_properties, keep_parameters, do_subcomponents=False)
else:
if self.ignored():
return
@@ -3120,8 +3135,9 @@
xpname = p.name().startswith("X-")
if xpname and p.name() not in keep_properties:
self.removeProperty(p)
- elif not xpname and remove_x_parameters:
+ elif not xpname:
preserve = keep_parameters.get(p.name(), set())
+ preserve.update(keep_parameters.get("", set()))
for paramname in p.parameterNames():
if paramname.startswith("X-") and paramname not in preserve:
p.removeParameter(paramname)
@@ -3769,8 +3785,8 @@
return True
else:
attendee_refs = set()
- for prop in tuple(self.properties("X-CALENDARSERVER-ATTENDEE-COMMENT")):
- ref = prop.parameterValue("X-CALENDARSERVER-ATTENDEE-REF")
+ for prop in tuple(self.properties(ATTENDEE_COMMENT)):
+ ref = prop.parameterValue(ATTENDEE_COMMENT_REF)
if ref in attendee_refs:
if doFix:
self.removeProperty(prop)
Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/stdconfig.py 2015-11-13 17:43:53 UTC (rev 15306)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py 2015-11-13 17:46:03 UTC (rev 15307)
@@ -709,6 +709,14 @@
"X-APPLE-DROPBOX",
"X-APPLE-STRUCTURED-LOCATION",
],
+ "OrganizerPublicParameters" : [ # Names of X- iCalendar parameters that are sent from ORGANIZER to ATTENDEE
+ ],
+ "AttendeePublicProperties" : [ # Names of X- iCalendar properties that are sent from ATTENDEE to ORGANIZER
+ # These are also implicitly added to OrganizerPublicProperties
+ ],
+ "AttendeePublicParameters" : [ # Names of X- iCalendar parameters that are sent from ATTENDEE to ORGANIZER
+ # These are also implicitly added to OrganizerPublicParameters
+ ],
},
"iSchedule": {
Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/icaldiff.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/icaldiff.py 2015-11-13 17:43:53 UTC (rev 15306)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/icaldiff.py 2015-11-13 17:46:03 UTC (rev 15307)
@@ -23,7 +23,7 @@
from twistedcaldav import accounting
from twistedcaldav.config import config
-from twistedcaldav.ical import Component, Property
+from twistedcaldav.ical import Component, Property, PRIVATE_COMMENT, DTSTAMP_PARAM
from txdav.caldav.datastore.scheduling.utils import normalizeCUAddr
from txdav.caldav.datastore.scheduling.itip import iTipGenerator
@@ -82,7 +82,10 @@
"DTSTAMP",
"LAST-MODIFIED",
))
- calendar.removeXProperties(keep_properties=config.Scheduling.CalDAV.OrganizerPublicProperties)
+ calendar.removeXProperties(keep_properties=(
+ config.Scheduling.CalDAV.OrganizerPublicProperties +
+ config.Scheduling.CalDAV.AttendeePublicProperties
+ ))
calendar.removePropertyParameters("ATTENDEE", ("RSVP", "SCHEDULE-STATUS", "SCHEDULE-FORCE-SEND",))
calendar.normalizeAll()
return calendar
@@ -505,7 +508,7 @@
# If PARTSTAT was changed by the attendee, add a timestamp if needed
if config.Scheduling.Options.TimestampAttendeePartStatChanges:
- serverAttendee.setParameter("X-CALENDARSERVER-DTSTAMP", DateTime.getNowUTC().getText())
+ serverAttendee.setParameter(DTSTAMP_PARAM, DateTime.getNowUTC().getText())
serverAttendee.removeParameter("X-CALENDARSERVER-AUTO")
replyNeeded = True
@@ -526,14 +529,26 @@
else:
serverAttendee.setParameter("RSVP", "TRUE")
+ for pname in config.Scheduling.CalDAV.AttendeePublicParameters:
+ serverValue = serverAttendee.parameterValue(pname)
+ clientValue = clientAttendee.parameterValue(pname)
+ if serverValue != clientValue:
+ if clientValue is None:
+ serverAttendee.removeParameter(pname)
+ else:
+ serverAttendee.setParameter(pname, clientValue)
+ replyNeeded = True
+
# Transfer these properties from the client data
- replyNeeded |= self._transferProperty("X-CALENDARSERVER-PRIVATE-COMMENT", serverComponent, clientComponent)
self._transferProperty("TRANSP", serverComponent, clientComponent)
self._transferProperty("DTSTAMP", serverComponent, clientComponent)
self._transferProperty("LAST-MODIFIED", serverComponent, clientComponent)
self._transferProperty("COMPLETED", serverComponent, clientComponent)
for pname in config.Scheduling.CalDAV.PerAttendeeProperties:
self._transferProperty(pname, serverComponent, clientComponent)
+ replyNeeded |= self._transferProperty(PRIVATE_COMMENT, serverComponent, clientComponent)
+ for pname in config.Scheduling.CalDAV.AttendeePublicProperties:
+ replyNeeded |= self._transferProperty(pname, serverComponent, clientComponent)
# Dropbox - this now never returns false
if config.EnableDropBox:
@@ -957,7 +972,7 @@
"DTSTAMP",
"CREATED",
"LAST-MODIFIED",
- "X-CALENDARSERVER-PRIVATE-COMMENT",
+ PRIVATE_COMMENT,
):
continue
if prop.name() == "ATTENDEE":
Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/itip.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/itip.py 2015-11-13 17:43:53 UTC (rev 15306)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/itip.py 2015-11-13 17:46:03 UTC (rev 15307)
@@ -26,15 +26,16 @@
types, etc.
"""
-from collections import namedtuple
-
from twext.python.log import Logger
from twistedcaldav.config import config
-from twistedcaldav.ical import Property, iCalendarProductID, Component
+from twistedcaldav.ical import Property, iCalendarProductID, Component, \
+ PRIVATE_COMMENT, ATTENDEE_COMMENT, ATTENDEE_COMMENT_REF, DTSTAMP_PARAM
from pycalendar.datetime import DateTime
+from collections import namedtuple
+
log = Logger()
__all__ = [
@@ -67,13 +68,13 @@
"""
if component:
valarms = [comp for comp in component.subcomponents() if comp.name() == "VALARM"]
- private_comments = tuple(component.properties("X-CALENDARSERVER-PRIVATE-COMMENT"))
+ private_comments = tuple(component.properties(PRIVATE_COMMENT))
transps = tuple(component.properties("TRANSP"))
completeds = tuple(component.properties("COMPLETED"))
organizer = component.getProperty("ORGANIZER")
organizer_schedule_status = organizer.parameterValue("SCHEDULE-STATUS", None) if organizer else None
attendee = component.getAttendeeProperty((recipient,))
- attendee_dtstamp = attendee.parameterValue("X-CALENDARSERVER-DTSTAMP") if attendee else None
+ attendee_dtstamp = attendee.parameterValue(DTSTAMP_PARAM) if attendee else None
sequence = component.propertyValue("SEQUENCE", 0)
other_props = {}
for pname in config.Scheduling.CalDAV.PerAttendeeProperties:
@@ -391,6 +392,11 @@
return calendar
+ # Tuple used to hold information about what an ATTENDEE changed in their REPLY
+ # "params" indicates which parameters in the ATTENDEE property changes
+ # "props" indicates which properties changed
+ ReplyChanges = namedtuple("ReplyChanges", ("params", "props"))
+
@staticmethod
def processReply(itip_message, calendar):
"""
@@ -421,13 +427,13 @@
old_master = calendar.masterComponent()
new_master = itip_message.masterComponent()
attendees = set()
- rids = set()
+ rids = []
if new_master is not None and old_master is not None:
- attendee, partstat, private_comment = iTipProcessing.updateAttendeeDataFromReply(new_master, old_master)
+ attendee, reply_changes = iTipProcessing.updateAttendeeDataFromReply(new_master, old_master)
if attendee:
attendees.add(attendee)
- if partstat or private_comment:
- rids.add(("", partstat, private_comment,))
+ if reply_changes is not None:
+ rids.append(("", reply_changes,))
# Make sure all overridden components in the organizer's copy have matching overridden components
# in the iTIP message
@@ -473,11 +479,11 @@
log.error("Ignoring instance: {rid} in iTIP REPLY for: {uid}", rid=rid, uid=itip_message.resourceUID())
continue
- attendee, partstat, private_comment = iTipProcessing.updateAttendeeDataFromReply(itip_component, match_component)
+ attendee, reply_changes = iTipProcessing.updateAttendeeDataFromReply(itip_component, match_component)
if attendee:
attendees.add(attendee)
- if rids is not None and (partstat or private_comment):
- rids.add((rid.getText(), partstat, private_comment,))
+ if rids is not None and reply_changes is not None:
+ rids.append((rid.getText(), reply_changes,))
# Check for an invalid instance by itself
len_attendees = len(attendees)
@@ -505,11 +511,13 @@
@type reply_component: L{Component}
@param organizer_component: component to copy to
@type organizer_component: L{Component}
+
+ @return: tuple of attendee property value and reply changes
+ @rtype: L{tuple} of L{str}, L{ReplyChanges}
"""
# Track what changed
- partstat_changed = False
- private_comment_changed = False
+ reply_changes = iTipProcessing.ReplyChanges([], [])
# Get REQUEST-STATUS as we need to write that into the saved ATTENDEE property
reqstatus = tuple(reply_component.properties("REQUEST-STATUS"))
@@ -522,7 +530,7 @@
attendees = tuple(reply_component.getRecipientProperties())
if len(attendees) != 1:
log.error("There must be one and only one ATTENDEE property in a REPLY\n{msg}", msg=str(reply_component))
- return None, False, False
+ return None, None
attendee = attendees[0]
partstat = attendee.parameterValue("PARTSTAT", "NEEDS-ACTION")
@@ -535,28 +543,40 @@
# Only process the change for this component if it was made after the last partstat reset
if existing_attendee and reply_sequence >= existing_reset_sequence:
if existing_attendee.name() == "ATTENDEE":
+ # Look for change to partstat
oldpartstat = existing_attendee.parameterValue("PARTSTAT", "NEEDS-ACTION")
existing_attendee.setParameter("PARTSTAT", partstat)
existing_attendee.setParameter("SCHEDULE-STATUS", reqstatus)
- partstat_changed = (oldpartstat != partstat)
+ if oldpartstat != partstat:
+ reply_changes.params.append("PARTSTAT")
- # Always delete RSVP on PARTSTAT change
- if partstat_changed:
+ # Always delete RSVP on PARTSTAT change
try:
existing_attendee.removeParameter("RSVP")
except KeyError:
pass
+ # Look for change to X- parameters
+ for paramname in config.Scheduling.CalDAV.AttendeePublicParameters:
+ oldparam = existing_attendee.parameterValue(paramname)
+ newparam = attendee.parameterValue(paramname)
+ if oldparam != newparam:
+ if newparam is None:
+ existing_attendee.removeParameter(paramname)
+ else:
+ existing_attendee.setParameter(paramname, newparam)
+ reply_changes.params.append(paramname)
+
# Handle attendee comments
if config.Scheduling.CalDAV.get("EnablePrivateComments", True):
# Look for X-CALENDARSERVER-PRIVATE-COMMENT property in iTIP component (State 1 in spec)
- attendee_comment = tuple(reply_component.properties("X-CALENDARSERVER-PRIVATE-COMMENT"))
+ attendee_comment = tuple(reply_component.properties(PRIVATE_COMMENT))
attendee_comment = attendee_comment[0] if len(attendee_comment) else None
# Look for matching X-CALENDARSERVER-ATTENDEE-COMMENT property in existing data (State 2 in spec)
- private_comments = tuple(organizer_component.properties("X-CALENDARSERVER-ATTENDEE-COMMENT"))
+ private_comments = tuple(organizer_component.properties(ATTENDEE_COMMENT))
for comment in private_comments:
- attendeeref = comment.parameterValue("X-CALENDARSERVER-ATTENDEE-REF")
+ attendeeref = comment.parameterValue(ATTENDEE_COMMENT_REF)
if attendeeref == attendee.value():
private_comment = comment
break
@@ -575,22 +595,22 @@
# We now remove the private comment on the organizer's side if the attendee removed it
organizer_component.removeProperty(private_comment)
- private_comment_changed = True
+ reply_changes.props.append(PRIVATE_COMMENT)
elif attendee_comment is not None and private_comment is None:
# Add new property
private_comment = Property(
- "X-CALENDARSERVER-ATTENDEE-COMMENT",
+ ATTENDEE_COMMENT,
attendee_comment.value(),
params={
- "X-CALENDARSERVER-ATTENDEE-REF": attendee.value(),
- "X-CALENDARSERVER-DTSTAMP": DateTime.getNowUTC().getText(),
+ ATTENDEE_COMMENT_REF: attendee.value(),
+ DTSTAMP_PARAM: DateTime.getNowUTC().getText(),
}
)
organizer_component.addProperty(private_comment)
- private_comment_changed = True
+ reply_changes.props.append(PRIVATE_COMMENT)
else:
# Only change if different
@@ -599,22 +619,38 @@
private_comment.removeAllParameters()
# Add default parameters
- private_comment.setParameter("X-CALENDARSERVER-ATTENDEE-REF", attendee.value())
- private_comment.setParameter("X-CALENDARSERVER-DTSTAMP", DateTime.getNowUTC().getText())
+ private_comment.setParameter(ATTENDEE_COMMENT_REF, attendee.value())
+ private_comment.setParameter(DTSTAMP_PARAM, DateTime.getNowUTC().getText())
# Set new value
private_comment.setValue(attendee_comment.value())
- private_comment_changed = True
+ reply_changes.props.append(PRIVATE_COMMENT)
# Do VPOLL transfer
if reply_component.name() == "VPOLL":
# TODO: figure out how to report changes back
- partstat_changed = iTipProcessing.updateVPOLLDataFromReply(reply_component, organizer_component, attendee)
+ if iTipProcessing.updateVPOLLDataFromReply(reply_component, organizer_component, attendee):
+ reply_changes.params.append("PARTSTAT")
- return attendee.value(), partstat_changed, private_comment_changed
+ for propname in config.Scheduling.CalDAV.AttendeePublicProperties:
+ # Copy any property in the incoming component to the existing one.
+ # We do not currently delete anything in the existing component.
+ # We also remove all properties that match the name of the incoming one
+ # (i.e. we do not allow multi-occurring properties.
+ copy_props = tuple(reply_component.properties(propname))
+ if copy_props:
+ organizer_component.removeProperties(propname)
+ for prop in copy_props:
+ organizer_component.addProperty(prop.duplicate())
+ reply_changes.props.append(propname)
+ if len(reply_changes.props) == 0 and len(reply_changes.params) == 0:
+ reply_changes = None
+ return attendee.value(), reply_changes
+
+
@staticmethod
def updateVPOLLDataFromReply(reply_component, organizer_component, attendee):
"""
@@ -756,7 +792,7 @@
# into the new one. But first remove any of the stuff we want to copy from
# the component being copied to.
to_component.removeAlarms()
- to_component.removeProperty("X-CALENDARSERVER-PRIVATE-COMMENT")
+ to_component.removeProperty(PRIVATE_COMMENT)
to_component.removeProperty("TRANSP")
to_component.removeProperty("COMPLETED")
for propname in details.other_props.keys():
@@ -788,7 +824,7 @@
iTipProcessing.mergePartStat(details.attendee, attendee)
if details.attendee_dtstamp and attendee:
- attendee.setParameter("X-CALENDARSERVER-DTSTAMP", details.attendee_dtstamp)
+ attendee.setParameter(DTSTAMP_PARAM, details.attendee_dtstamp)
return False
@@ -1085,7 +1121,7 @@
itip.removeAlarms()
# Remove all but essential properties
- itip.filterProperties(keep=(
+ keep_properties = (
"UID",
"RECURRENCE-ID",
"SEQUENCE",
@@ -1100,11 +1136,13 @@
"ORGANIZER",
"ATTENDEE",
"VOTER",
- "X-CALENDARSERVER-PRIVATE-COMMENT",
"SUMMARY",
"LOCATION",
"DESCRIPTION",
- ))
+ PRIVATE_COMMENT,
+ )
+ keep_properties += tuple(config.Scheduling.CalDAV.AttendeePublicProperties)
+ itip.filterProperties(keep=keep_properties)
# Now set each ATTENDEE's PARTSTAT to DECLINED
if force_decline:
@@ -1165,18 +1203,21 @@
# Component properties - remove all X- except for those specified
if not reply:
# Organizer properties that need to go to the Attendees
- keep_properties = config.Scheduling.CalDAV.OrganizerPublicProperties
+ keep_properties = config.Scheduling.CalDAV.OrganizerPublicProperties + config.Scheduling.CalDAV.AttendeePublicProperties
+ keep_parameters = config.Scheduling.CalDAV.OrganizerPublicParameters + config.Scheduling.CalDAV.AttendeePublicParameters
else:
# Attendee properties that need to go to the Organizer
- keep_properties = ("X-CALENDARSERVER-PRIVATE-COMMENT",)
+ keep_properties = (PRIVATE_COMMENT,) + tuple(config.Scheduling.CalDAV.AttendeePublicProperties)
+ keep_parameters = config.Scheduling.CalDAV.AttendeePublicParameters
keep_parameters = {
"ATTENDEE": set(("X-CALENDARSERVER-RESET-PARTSTAT",)),
+ "": keep_parameters,
}
itip.removeXProperties(keep_properties=keep_properties, keep_parameters=keep_parameters)
# Property Parameters
- itip.removePropertyParameters("ATTENDEE", ("SCHEDULE-AGENT", "SCHEDULE-STATUS", "SCHEDULE-FORCE-SEND", "X-CALENDARSERVER-DTSTAMP",))
- itip.removePropertyParameters("VOTER", ("SCHEDULE-AGENT", "SCHEDULE-STATUS", "SCHEDULE-FORCE-SEND", "X-CALENDARSERVER-DTSTAMP",))
+ itip.removePropertyParameters("ATTENDEE", ("SCHEDULE-AGENT", "SCHEDULE-STATUS", "SCHEDULE-FORCE-SEND", DTSTAMP_PARAM,))
+ itip.removePropertyParameters("VOTER", ("SCHEDULE-AGENT", "SCHEDULE-STATUS", "SCHEDULE-FORCE-SEND", DTSTAMP_PARAM,))
itip.removePropertyParameters("ORGANIZER", ("SCHEDULE-AGENT", "SCHEDULE-STATUS", "SCHEDULE-FORCE-SEND",))
Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/processing.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/processing.py 2015-11-13 17:43:53 UTC (rev 15306)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/processing.py 2015-11-13 17:46:03 UTC (rev 15307)
@@ -27,7 +27,7 @@
from twistedcaldav import customxml
from twistedcaldav.accounting import emitAccounting, accountingEnabled
from twistedcaldav.config import config
-from twistedcaldav.ical import Property
+from twistedcaldav.ical import Property, DTSTAMP_PARAM
from twistedcaldav.instance import InvalidOverriddenInstanceError
from txdav.caldav.datastore.scheduling.freebusy import FreebusyQuery
@@ -220,26 +220,29 @@
if self.method == "X-RESTORE":
changes = None
- partstatChanged = True
+ refreshNeeded = True
yield self._doRefresh(self.organizer_calendar_resource, only_attendees=(attendeeReplying,))
else:
# Build the schedule-changes XML element
- partstatChanged = False
+ refreshNeeded = False
reply_details = (customxml.Attendee.fromString(attendeeReplying),)
- for rid, partstatChanged, privateCommentChanged in sorted(rids):
+ for rid, reply_changes in sorted(rids):
recurrence = []
if rid == "":
recurrence.append(customxml.Master())
else:
recurrence.append(customxml.RecurrenceID.fromString(rid))
changes = []
- if partstatChanged:
- changes.append(customxml.ChangedProperty(customxml.ChangedParameter(name="PARTSTAT"), name="ATTENDEE"))
- partstatChanged = True
- if privateCommentChanged:
- changes.append(customxml.ChangedProperty(name="X-CALENDARSERVER-PRIVATE-COMMENT"))
+
+ for param in reply_changes.params:
+ changes.append(customxml.ChangedProperty(customxml.ChangedParameter(name=param), name="ATTENDEE"))
+ refreshNeeded = True
+
+ for prop in reply_changes.props:
+ changes.append(customxml.ChangedProperty(name=prop))
+
recurrence.append(customxml.Changes(*changes))
reply_details += (customxml.Recurrence(*recurrence),)
@@ -253,7 +256,7 @@
# Only update other attendees when the partstat was changed by the reply,
# and only if the request does not indicate we should skip attendee refresh
# (e.g. inbox item processing during migration from non-implicit server)
- if partstatChanged and not self.noAttendeeRefresh:
+ if refreshNeeded and not self.noAttendeeRefresh:
# Check limit of attendees
if config.Scheduling.Options.AttendeeRefreshCountLimit == 0 or len(self.recipient_calendar.getAllUniqueAttendees()) <= config.Scheduling.Options.AttendeeRefreshCountLimit:
yield self.queueAttendeeUpdate((attendeeReplying, organizer,))
@@ -1062,7 +1065,7 @@
if madeChanges:
attendee.setParameter("X-CALENDARSERVER-AUTO", DateTime.getNowUTC().getText())
- attendee.removeParameter("X-CALENDARSERVER-DTSTAMP")
+ attendee.removeParameter(DTSTAMP_PARAM)
return madeChanges
Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_itip.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_itip.py 2015-11-13 17:43:53 UTC (rev 15306)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_itip.py 2015-11-13 17:46:03 UTC (rev 15307)
@@ -21,7 +21,7 @@
from twisted.trial import unittest
from twistedcaldav.stdconfig import config
-from twistedcaldav.ical import Component, normalize_iCalStr
+from twistedcaldav.ical import Component, diff_iCalStrs, normalize_iCalStr
from txdav.caldav.datastore.scheduling.itip import iTipProcessing, iTipGenerator
@@ -34,6 +34,42 @@
iCalendar support tests
"""
+ def setUp(self):
+ self.patch(config.Scheduling.CalDAV, "OrganizerPublicProperties", [
+ "X-APPLE-DROPBOX",
+ "X-APPLE-STRUCTURED-LOCATION",
+ "X-TEST-ORGANIZER-PROP1",
+ "X-TEST-ORGANIZER-PROP2",
+ "X-TEST-ORGANIZER-PROP3",
+ "X-TEST-ORGANIZER-PROP4",
+ "X-TEST-ORGANIZER-PROP5",
+ ])
+
+ self.patch(config.Scheduling.CalDAV, "OrganizerPublicParameters", [
+ "X-TEST-ORGANIZER-PARAM1",
+ "X-TEST-ORGANIZER-PARAM2",
+ "X-TEST-ORGANIZER-PARAM3",
+ "X-TEST-ORGANIZER-PARAM4",
+ "X-TEST-ORGANIZER-PARAM5",
+ ])
+
+ self.patch(config.Scheduling.CalDAV, "AttendeePublicProperties", [
+ "X-TEST-ALL-PROP1",
+ "X-TEST-ALL-PROP2",
+ "X-TEST-ALL-PROP3",
+ "X-TEST-ALL-PROP4",
+ "X-TEST-ALL-PROP5",
+ ])
+
+ self.patch(config.Scheduling.CalDAV, "AttendeePublicParameters", [
+ "X-TEST-ALL-PARAM1",
+ "X-TEST-ALL-PARAM2",
+ "X-TEST-ALL-PARAM3",
+ "X-TEST-ALL-PARAM4",
+ "X-TEST-ALL-PARAM5",
+ ])
+
+
def test_processRequest(self):
"""
Test iTIPProcessing.processRequest works properly for various scenarios.
@@ -1884,7 +1920,7 @@
END:VCALENDAR
""",
True,
- ('mailto:user2 at example.com', set([("", True, False,)])),
+ ('mailto:user2 at example.com', [("", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=[]))]),
),
(
"3.2 Simple VPOLL Reply - response changed",
@@ -1989,7 +2025,7 @@
END:VCALENDAR
""",
True,
- ('mailto:user2 at example.com', set([("", True, False,)])),
+ ('mailto:user2 at example.com', [("", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=[]))]),
),
(
"3.3 Simple VPOLL Reply - response added and changed",
@@ -2102,7 +2138,7 @@
END:VCALENDAR
""",
True,
- ('mailto:user2 at example.com', set([("", True, False,)])),
+ ('mailto:user2 at example.com', [("", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=[]))]),
),
(
"3.4 Simple VPOLL Reply - response one changed",
@@ -2215,7 +2251,7 @@
END:VCALENDAR
""",
True,
- ('mailto:user2 at example.com', set([("", True, False,)])),
+ ('mailto:user2 at example.com', [("", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=[]))])
),
(
"3.5 Simple VPOLL Reply - no changes",
@@ -2332,7 +2368,7 @@
END:VCALENDAR
""",
True,
- ('mailto:user2 at example.com', set([])),
+ ('mailto:user2 at example.com', []),
),
)
@@ -2726,7 +2762,7 @@
END:VEVENT
END:VCALENDAR
""",
- True, "mailto:user1 at example.com", (("", True, False),),
+ True, "mailto:user1 at example.com", (("", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=[])),),
),
(
"#1.2 Simple component, accepted",
@@ -2767,7 +2803,7 @@
END:VEVENT
END:VCALENDAR
""",
- True, "mailto:user1 at example.com", (("", True, False),),
+ True, "mailto:user1 at example.com", (("", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=[])),),
),
(
"#1.3 Simple component, no change",
@@ -2875,7 +2911,10 @@
END:VEVENT
END:VCALENDAR
""",
- True, "mailto:user1 at example.com", (("", True, False), ("20080801T120000Z", True, False),),
+ True, "mailto:user1 at example.com", (
+ ("", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=[])),
+ ("20080801T120000Z", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=[])),
+ ),
),
(
"#2.2 Recurring component, change master only",
@@ -2936,7 +2975,7 @@
END:VEVENT
END:VCALENDAR
""",
- True, "mailto:user1 at example.com", (("", True, False),),
+ True, "mailto:user1 at example.com", (("", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=[])),),
),
(
"#2.3 Recurring component, change override only",
@@ -2998,7 +3037,7 @@
END:VEVENT
END:VCALENDAR
""",
- True, "mailto:user1 at example.com", (("20080801T120000Z", True, False),),
+ True, "mailto:user1 at example.com", (("20080801T120000Z", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=[])),),
),
(
"#3.1 Recurring component, change master/override, new override",
@@ -3080,7 +3119,10 @@
END:VEVENT
END:VCALENDAR
""",
- True, "mailto:user1 at example.com", (("", True, False), ("20080801T120000Z", True, False), ("20080901T120000Z", True, False),),
+ True, "mailto:user1 at example.com", (
+ ("", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=[])),
+ ("20080801T120000Z", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=[])),
+ ("20080901T120000Z", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=[])),),
),
(
"#3.2 Recurring component, change master, new override",
@@ -3156,7 +3198,10 @@
END:VEVENT
END:VCALENDAR
""",
- True, "mailto:user1 at example.com", (("", True, False), ("20080901T120000Z", True, False),),
+ True, "mailto:user1 at example.com", (
+ ("", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=[])),
+ ("20080901T120000Z", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=[])),
+ ),
),
(
"#3.3 Recurring component, change override, new override",
@@ -3233,7 +3278,10 @@
END:VEVENT
END:VCALENDAR
""",
- True, "mailto:user1 at example.com", (("20080801T120000Z", True, False), ("20080901T120000Z", True, False),),
+ True, "mailto:user1 at example.com", (
+ ("20080801T120000Z", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=[])),
+ ("20080901T120000Z", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=[])),
+ ),
),
(
"#4.1 Recurring component, invalid override",
@@ -3430,7 +3478,7 @@
END:VEVENT
END:VCALENDAR
""",
- True, "mailto:user1 at example.com", (("", True, False),),
+ True, "mailto:user1 at example.com", (("", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=[])),),
),
(
"#6.2 Multiple REQUEST-STATUS",
@@ -3473,7 +3521,7 @@
END:VEVENT
END:VCALENDAR
""",
- True, "mailto:user1 at example.com", (("", True, False),),
+ True, "mailto:user1 at example.com", (("", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=[])),),
),
(
"#6.3 Bad REQUEST-STATUS",
@@ -3515,7 +3563,7 @@
END:VEVENT
END:VCALENDAR
""",
- True, "mailto:user1 at example.com", (("", True, False),),
+ True, "mailto:user1 at example.com", (("", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=[])),),
),
)
@@ -3553,6 +3601,663 @@
)
+ def test_processReply_XDash(self):
+ """
+ Test iTIPProcessing.processReply with X- property and parameter changes
+ """
+
+ data = (
+ (
+ "1.1 Simple Reply - with X- param",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:user02 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071115T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE;X-TEST-ALL-PARAM1=p1;PARTSTAT=ACCEPTED:mailto:user02 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;X-TEST-ALL-PARAM1=p1;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user02 at example.com", (
+ ("", iTipProcessing.ReplyChanges(params=['PARTSTAT', 'X-TEST-ALL-PARAM1'], props=[])),
+ ),
+ ),
+ (
+ "1.2 Simple Reply - with X- param update",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;X-TEST-ALL-PARAM1=p1;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071115T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE;X-TEST-ALL-PARAM1=p1-1;PARTSTAT=ACCEPTED:mailto:user02 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;X-TEST-ALL-PARAM1=p1-1;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user02 at example.com", (
+ ("", iTipProcessing.ReplyChanges(params=['X-TEST-ALL-PARAM1'], props=[])),
+ ),
+ ),
+ (
+ "1.3 Simple Reply - with X- param remove",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;X-TEST-ALL-PARAM1=p1;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071115T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user02 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user02 at example.com", (
+ ("", iTipProcessing.ReplyChanges(params=['X-TEST-ALL-PARAM1'], props=[])),
+ ),
+ ),
+ (
+ "2.1 Simple Reply - with X- prop",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:user02 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071115T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user02 at example.com
+X-TEST-ALL-PROP1:p1
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+X-TEST-ALL-PROP1:p1
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user02 at example.com", (
+ ("", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=["X-TEST-ALL-PROP1"])),
+ ),
+ ),
+ (
+ "2.2 Simple Reply - with X- prop update",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+X-TEST-ALL-PROP1:p1
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071115T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user02 at example.com
+X-TEST-ALL-PROP1:p1-1
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+X-TEST-ALL-PROP1:p1-1
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user02 at example.com", (
+ ("", iTipProcessing.ReplyChanges(params=[], props=["X-TEST-ALL-PROP1"])),
+ ),
+ ),
+ (
+ "2.3 Simple Reply - with X- prop preserve",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+X-TEST-ALL-PROP1:p1-1
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071115T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=DECLINED:mailto:user02 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=DECLINED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+X-TEST-ALL-PROP1:p1-1
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user02 at example.com", (
+ ("", iTipProcessing.ReplyChanges(params=["PARTSTAT"], props=[])),
+ ),
+ ),
+ (
+ "3.1 Recurrence Reply - with X- param",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:user02 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20071116T000000Z
+DTSTART:20071116T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE;X-TEST-ALL-PARAM1=p1;PARTSTAT=ACCEPTED:mailto:user02 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:user02 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20071116T000000Z
+DTSTART:20071116T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;X-TEST-ALL-PARAM1=p1;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user02 at example.com", (
+ ("20071116T000000Z", iTipProcessing.ReplyChanges(params=['PARTSTAT', 'X-TEST-ALL-PARAM1'], props=[])),
+ ),
+ ),
+ (
+ "3.2 Recurrence Reply - with X- param update",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;X-TEST-ALL-PARAM1=p1;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20071116T000000Z
+DTSTART:20071116T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE;X-TEST-ALL-PARAM1=p1-1;PARTSTAT=ACCEPTED:mailto:user02 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;X-TEST-ALL-PARAM1=p1;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20071116T000000Z
+DTSTART:20071116T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;X-TEST-ALL-PARAM1=p1-1;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user02 at example.com", (
+ ("20071116T000000Z", iTipProcessing.ReplyChanges(params=['X-TEST-ALL-PARAM1'], props=[])),
+ ),
+ ),
+ (
+ "3.3 Recurrence Reply - with X- param remove",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;X-TEST-ALL-PARAM1=p1;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20071116T000000Z
+DTSTART:20071116T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user02 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;X-TEST-ALL-PARAM1=p1;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20071116T000000Z
+DTSTART:20071116T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user02 at example.com", (
+ ("20071116T000000Z", iTipProcessing.ReplyChanges(params=['X-TEST-ALL-PARAM1'], props=[])),
+ ),
+ ),
+ (
+ "4.1 Recurrence Reply - with X- prop",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:user02 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20071116T000000Z
+DTSTART:20071116T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user02 at example.com
+X-TEST-ALL-PROP1:p1
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:user02 at example.com
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20071116T000000Z
+DTSTART:20071116T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+X-TEST-ALL-PROP1:p1
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user02 at example.com", (
+ ("20071116T000000Z", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=["X-TEST-ALL-PROP1"])),
+ ),
+ ),
+ (
+ "4.2 Recurrence Reply - with X- prop update",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+RRULE:FREQ=DAILY
+X-TEST-ALL-PROP1:p1
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20071116T000000Z
+DTSTART:20071116T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user02 at example.com
+X-TEST-ALL-PROP1:p1-1
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+RRULE:FREQ=DAILY
+X-TEST-ALL-PROP1:p1
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20071116T000000Z
+DTSTART:20071116T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+X-TEST-ALL-PROP1:p1-1
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user02 at example.com", (
+ ("20071116T000000Z", iTipProcessing.ReplyChanges(params=[], props=["X-TEST-ALL-PROP1"])),
+ ),
+ ),
+ (
+ "4.3 Recurrence Reply - with X- prop preserve",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+RRULE:FREQ=DAILY
+X-TEST-ALL-PROP1:p1
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+METHOD:REPLY
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20071116T000000Z
+DTSTART:20071116T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=DECLINED:mailto:user02 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+RRULE:FREQ=DAILY
+X-TEST-ALL-PROP1:p1
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890-1
+RECURRENCE-ID:20071116T000000Z
+DTSTART:20071116T000000Z
+DTSTAMP:20071114T000000Z
+ORGANIZER:mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE;PARTSTAT=DECLINED;SCHEDULE-STATUS=2.0:mailto:user02 at example.com
+X-TEST-ALL-PROP1:p1
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user02 at example.com", (
+ ("20071116T000000Z", iTipProcessing.ReplyChanges(params=["PARTSTAT"], props=[])),
+ ),
+ ),
+ )
+
+ for title, calendar_txt, itip_txt, changed_txt, attendee, rids in data:
+ calendar = Component.fromString(calendar_txt)
+ itip = Component.fromString(itip_txt)
+ changed = Component.fromString(changed_txt)
+
+ result, reply_processed = iTipProcessing.processReply(itip, calendar)
+ self.assertTrue(result)
+ self.assertEqual(normalize_iCalStr(changed), normalize_iCalStr(calendar), "Calendar mismatch: {}:\n{}".format(title, diff_iCalStrs(changed, calendar),))
+ reply_attendee, reply_rids, = reply_processed
+ self.assertEqual(
+ reply_attendee,
+ attendee,
+ msg=title
+ )
+ self.assertEqual(
+ tuple(sorted(list(reply_rids), key=lambda x: x[0])),
+ rids,
+ msg=title
+ )
+
+
def test_sequenceComparison(self):
"""
Test iTIPProcessing.sequenceComparison
@@ -4055,6 +4760,42 @@
"""
data_dir = os.path.join(os.path.dirname(__file__), "data")
+ def setUp(self):
+ self.patch(config.Scheduling.CalDAV, "OrganizerPublicProperties", [
+ "X-APPLE-DROPBOX",
+ "X-APPLE-STRUCTURED-LOCATION",
+ "X-TEST-ORGANIZER-PROP1",
+ "X-TEST-ORGANIZER-PROP2",
+ "X-TEST-ORGANIZER-PROP3",
+ "X-TEST-ORGANIZER-PROP4",
+ "X-TEST-ORGANIZER-PROP5",
+ ])
+
+ self.patch(config.Scheduling.CalDAV, "OrganizerPublicParameters", [
+ "X-TEST-ORGANIZER-PARAM1",
+ "X-TEST-ORGANIZER-PARAM2",
+ "X-TEST-ORGANIZER-PARAM3",
+ "X-TEST-ORGANIZER-PARAM4",
+ "X-TEST-ORGANIZER-PARAM5",
+ ])
+
+ self.patch(config.Scheduling.CalDAV, "AttendeePublicProperties", [
+ "X-TEST-ALL-PROP1",
+ "X-TEST-ALL-PROP2",
+ "X-TEST-ALL-PROP3",
+ "X-TEST-ALL-PROP4",
+ "X-TEST-ALL-PROP5",
+ ])
+
+ self.patch(config.Scheduling.CalDAV, "AttendeePublicParameters", [
+ "X-TEST-ALL-PARAM1",
+ "X-TEST-ALL-PARAM2",
+ "X-TEST-ALL-PARAM3",
+ "X-TEST-ALL-PARAM4",
+ "X-TEST-ALL-PARAM5",
+ ])
+
+
def test_request(self):
data = (
@@ -4684,3 +5425,283 @@
itipped = str(itipped).replace("\r", "")
itipped = "".join([line for line in itipped.splitlines(True) if not line.startswith("DTSTAMP:")])
self.assertEqual(filtered, itipped)
+
+
+ def test_prepareSchedulingMessage(self):
+ """
+ Make sure L{iTIPGenerator.prepareSchedulingMessage} correctly filters X-
+ properties and parameters.
+ """
+
+ data = (
+ (
+ "Nothing to filter",
+ """BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=UTC:20071114T000000
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=UTC:20071114T000000
+END:VEVENT
+END:VCALENDAR
+""",
+ False,
+ ),
+ (
+ "Filter X- property",
+ """BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=UTC:20071114T000000
+X-FOO:BAR
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=UTC:20071114T000000
+END:VEVENT
+END:VCALENDAR
+""",
+ False,
+ ),
+ (
+ "Filter X- param",
+ """BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=UTC:20071114T000000
+LOCATION;X-FOO=BAR:Home
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=UTC:20071114T000000
+LOCATION:Home
+END:VEVENT
+END:VCALENDAR
+""",
+ False,
+ ),
+ (
+ "Keep X- property",
+ """BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=UTC:20071114T000000
+X-TEST-ORGANIZER-PROP1:organizer
+X-TEST-ALL-PROP1:all
+X-FOO:BAR
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=UTC:20071114T000000
+X-TEST-ORGANIZER-PROP1:organizer
+X-TEST-ALL-PROP1:all
+END:VEVENT
+END:VCALENDAR
+""",
+ False,
+ ),
+ (
+ "Keep X- param",
+ """BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=UTC:20071114T000000
+LOCATION;X-TEST-ORGANIZER-PARAM1=o;X-TEST-ALL-PARAM1=a:Home
+X-TEST-ORGANIZER-PROP1;X-TEST-ORGANIZER-PARAM2=o;X-FOO=BAR:dropped-it
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=UTC:20071114T000000
+LOCATION;X-TEST-ORGANIZER-PARAM1=o;X-TEST-ALL-PARAM1=a:Home
+X-TEST-ORGANIZER-PROP1;X-TEST-ORGANIZER-PARAM2=o;X-FOO=BAR:dropped-it
+END:VEVENT
+END:VCALENDAR
+""",
+ False,
+ ),
+ (
+ "Nothing to filter - reply",
+ """BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=UTC:20071114T000000
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=UTC:20071114T000000
+END:VEVENT
+END:VCALENDAR
+""",
+ True,
+ ),
+ (
+ "Filter X- property - reply",
+ """BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=UTC:20071114T000000
+X-TEST-ORGANIZER-PROP1:organizer
+X-FOO:BAR
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=UTC:20071114T000000
+END:VEVENT
+END:VCALENDAR
+""",
+ True,
+ ),
+ (
+ "Filter X- param - reply",
+ """BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=UTC:20071114T000000
+LOCATION;X-TEST-ORGANIZER-PARAM1=o;X-FOO=BAR:Home
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=UTC:20071114T000000
+LOCATION:Home
+END:VEVENT
+END:VCALENDAR
+""",
+ True,
+ ),
+ (
+ "Keep X- property - reply",
+ """BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=UTC:20071114T000000
+X-TEST-ORGANIZER-PROP1:organizer
+X-TEST-ALL-PROP1:all
+X-FOO:BAR
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=UTC:20071114T000000
+X-TEST-ALL-PROP1:all
+END:VEVENT
+END:VCALENDAR
+""",
+ True,
+ ),
+ (
+ "Keep X- param - reply",
+ """BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=UTC:20071114T000000
+LOCATION;X-TEST-ORGANIZER-PARAM1=o;X-TEST-ALL-PARAM1=a:Home
+X-TEST-ALL-PROP1;X-TEST-ALL-PARAM2=a;X-FOO=BAR:oragnizer
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+METHOD:REQUEST
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART;TZID=UTC:20071114T000000
+LOCATION;X-TEST-ALL-PARAM1=a:Home
+X-TEST-ALL-PROP1;X-TEST-ALL-PARAM2=a;X-FOO=BAR:oragnizer
+END:VEVENT
+END:VCALENDAR
+""",
+ True,
+ ),
+ )
+
+ for title, original, processed, reply in data:
+ component = Component.fromString(original)
+ new_component = Component.fromString(processed)
+ iTipGenerator.prepareSchedulingMessage(component, reply)
+ self.assertEqual(normalize_iCalStr(new_component), normalize_iCalStr(component), "Failed {}:\n{}".format(title, diff_iCalStrs(new_component, component),))
Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/sql.py 2015-11-13 17:43:53 UTC (rev 15306)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py 2015-11-13 17:46:03 UTC (rev 15307)
@@ -48,7 +48,7 @@
from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
from twistedcaldav.dateops import normalizeForIndex, \
pyCalendarToSQLTimestamp, parseSQLDateToPyCalendar
-from twistedcaldav.ical import Component, InvalidICalendarDataError, Property
+from twistedcaldav.ical import Component, InvalidICalendarDataError, Property, ATTENDEE_COMMENT
from twistedcaldav.instance import InvalidOverriddenInstanceError
from twistedcaldav.timezones import TimezoneException, readVTZ, hasTZ
@@ -3157,7 +3157,7 @@
if config.Scheduling.CalDAV.get("EnablePrivateComments", True):
old_has_private_comments = not inserting and self.hasPrivateComment
new_has_private_comments = component.hasPropertyInAnyComponent((
- "X-CALENDARSERVER-ATTENDEE-COMMENT",
+ ATTENDEE_COMMENT,
))
if old_has_private_comments and not new_has_private_comments and internal_state == ComponentUpdateState.NORMAL:
@@ -3165,7 +3165,7 @@
log.debug("Organizer private comment properties were entirely removed by the client. Restoring existing properties.")
old_calendar = (yield self.componentForUser())
component.transferProperties(old_calendar, (
- "X-CALENDARSERVER-ATTENDEE-COMMENT",
+ ATTENDEE_COMMENT,
))
changed = True
@@ -3176,7 +3176,7 @@
# Look for properties with duplicate "X-CALENDARSERVER-ATTENDEE-REF" values in the same component
if component.hasDuplicatePrivateComments(doFix=config.RemoveDuplicatePrivateComments) and internal_state == ComponentUpdateState.NORMAL:
- raise DuplicatePrivateCommentsError("Duplicate X-CALENDARSERVER-ATTENDEE-COMMENT properties present.")
+ raise DuplicatePrivateCommentsError("Duplicate {} properties present.".format(ATTENDEE_COMMENT))
returnValue(changed)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20151113/e11244bd/attachment-0001.html>
More information about the calendarserver-changes
mailing list