[CalendarServer-changes] [15309] CalendarServer/branches/release/CalendarServer-5.4-dev
source_changes at macosforge.org
source_changes at macosforge.org
Fri Nov 13 09:49:04 PST 2015
Revision: 15309
http://trac.calendarserver.org//changeset/15309
Author: cdaboo at apple.com
Date: 2015-11-13 09:49:04 -0800 (Fri, 13 Nov 2015)
Log Message:
-----------
Enhanced X- property/parameter whitelisting.
Modified Paths:
--------------
CalendarServer/branches/release/CalendarServer-5.4-dev/conf/caldavd-test.plist
CalendarServer/branches/release/CalendarServer-5.4-dev/twistedcaldav/ical.py
CalendarServer/branches/release/CalendarServer-5.4-dev/twistedcaldav/stdconfig.py
CalendarServer/branches/release/CalendarServer-5.4-dev/txdav/caldav/datastore/scheduling/icaldiff.py
CalendarServer/branches/release/CalendarServer-5.4-dev/txdav/caldav/datastore/scheduling/itip.py
CalendarServer/branches/release/CalendarServer-5.4-dev/txdav/caldav/datastore/scheduling/processing.py
CalendarServer/branches/release/CalendarServer-5.4-dev/txdav/caldav/datastore/scheduling/test/test_itip.py
CalendarServer/branches/release/CalendarServer-5.4-dev/txdav/caldav/datastore/sql.py
Modified: CalendarServer/branches/release/CalendarServer-5.4-dev/conf/caldavd-test.plist
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.4-dev/conf/caldavd-test.plist 2015-11-13 17:46:32 UTC (rev 15308)
+++ CalendarServer/branches/release/CalendarServer-5.4-dev/conf/caldavd-test.plist 2015-11-13 17:49:04 UTC (rev 15309)
@@ -709,6 +709,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/branches/release/CalendarServer-5.4-dev/twistedcaldav/ical.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.4-dev/twistedcaldav/ical.py 2015-11-13 17:46:32 UTC (rev 15308)
+++ CalendarServer/branches/release/CalendarServer-5.4-dev/twistedcaldav/ical.py 2015-11-13 17:49:04 UTC (rev 15309)
@@ -78,6 +78,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>)
@@ -964,6 +969,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.
@@ -2866,24 +2881,25 @@
self.removeComponent(component)
- def removeXProperties(self, keep_properties=(), 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, remove_x_parameters, do_subcomponents=False)
+ component.removeXProperties(keep_properties, keep_parameters, do_subcomponents=False)
else:
if self.name() in ignoredComponents:
return
for p in tuple(self.properties()):
- xpname = p.name().startswith("X-")
- if xpname and p.name() not in keep_properties:
+ pname = p.name()
+ xpname = pname.startswith("X-")
+ if xpname and pname not in keep_properties:
self.removeProperty(p)
- elif not xpname and remove_x_parameters:
+ elif not xpname:
for paramname in p.parameterNames():
- if paramname.startswith("X-"):
+ if paramname.startswith("X-") and paramname not in keep_parameters:
p.removeParameter(paramname)
@@ -3425,8 +3441,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/branches/release/CalendarServer-5.4-dev/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.4-dev/twistedcaldav/stdconfig.py 2015-11-13 17:46:32 UTC (rev 15308)
+++ CalendarServer/branches/release/CalendarServer-5.4-dev/twistedcaldav/stdconfig.py 2015-11-13 17:49:04 UTC (rev 15309)
@@ -685,6 +685,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/branches/release/CalendarServer-5.4-dev/txdav/caldav/datastore/scheduling/icaldiff.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.4-dev/txdav/caldav/datastore/scheduling/icaldiff.py 2015-11-13 17:46:32 UTC (rev 15308)
+++ CalendarServer/branches/release/CalendarServer-5.4-dev/txdav/caldav/datastore/scheduling/icaldiff.py 2015-11-13 17:49:04 UTC (rev 15309)
@@ -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.cuaddress import normalizeCUAddr
from txdav.caldav.datastore.scheduling.itip import iTipGenerator
@@ -41,7 +41,6 @@
change is being triggered by an Organizer or an Attendee.
"""
-
def __init__(self, oldcalendar, newcalendar, smart_merge, forceTRANSP=False):
"""
Note that this object will always duplicate the calendar objects when doing
@@ -83,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
@@ -356,7 +358,7 @@
# If smart_merge is happening, then derive an instance in the new data as the change in the old
# data is valid and likely due to some other attendee changing their status.
- if self.smart_merge:
+ if self.smart_merge:
newOverride = self.newcalendar.deriveInstance(rid, allowCancelled=True)
if newOverride is None:
self._logDiffError("attendeeMerge: Could not derive instance for uncancelled component: %s" % (key,))
@@ -506,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", PyCalendarDateTime.getNowUTC().getText())
+ serverAttendee.setParameter(DTSTAMP_PARAM, PyCalendarDateTime.getNowUTC().getText())
serverAttendee.removeParameter("X-CALENDARSERVER-AUTO")
replyNeeded = True
@@ -527,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:
@@ -621,8 +635,8 @@
duration = component.getProperty("DURATION")
timeRange = PyCalendarPeriod(
- start=dtstart.value() if dtstart is not None else None,
- end=dtend.value() if dtend is not None else None,
+ start=dtstart.value() if dtstart is not None else None,
+ end=dtend.value() if dtend is not None else None,
duration=duration.value() if duration is not None else None,
)
newdue = None
@@ -633,7 +647,7 @@
if dtstart or duration:
timeRange = PyCalendarPeriod(
- start=dtstart.value() if dtstart is not None else None,
+ start=dtstart.value() if dtstart is not None else None,
duration=duration.value() if duration is not None else None,
)
else:
@@ -838,7 +852,7 @@
"DTSTAMP",
"CREATED",
"LAST-MODIFIED",
- "X-CALENDARSERVER-PRIVATE-COMMENT",
+ PRIVATE_COMMENT,
):
continue
propsChanged.setdefault(prop.name(), set())
Modified: CalendarServer/branches/release/CalendarServer-5.4-dev/txdav/caldav/datastore/scheduling/itip.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.4-dev/txdav/caldav/datastore/scheduling/itip.py 2015-11-13 17:46:32 UTC (rev 15308)
+++ CalendarServer/branches/release/CalendarServer-5.4-dev/txdav/caldav/datastore/scheduling/itip.py 2015-11-13 17:49:04 UTC (rev 15309)
@@ -31,10 +31,13 @@
from twistedcaldav.config import config
from twistedcaldav.ical import Property, iCalendarProductID, Component, \
- ignoredComponents
+ ignoredComponents, PRIVATE_COMMENT, ATTENDEE_COMMENT, ATTENDEE_COMMENT_REF, \
+ DTSTAMP_PARAM
from pycalendar.datetime import PyCalendarDateTime
+from collections import namedtuple
+
log = Logger()
__all__ = [
@@ -124,13 +127,13 @@
current_master = calendar.masterComponent()
if current_master:
valarms = [comp for comp in current_master.subcomponents() if comp.name() == "VALARM"]
- private_comments = tuple(current_master.properties("X-CALENDARSERVER-PRIVATE-COMMENT"))
+ private_comments = tuple(current_master.properties(PRIVATE_COMMENT))
transps = tuple(current_master.properties("TRANSP"))
completeds = tuple(current_master.properties("COMPLETED"))
organizer = current_master.getProperty("ORGANIZER")
organizer_schedule_status = organizer.parameterValue("SCHEDULE-STATUS", None) if organizer else None
attendee = current_master.getAttendeeProperty((recipient,))
- attendee_dtstamp = attendee.parameterValue("X-CALENDARSERVER-DTSTAMP") if attendee else None
+ attendee_dtstamp = attendee.parameterValue(DTSTAMP_PARAM) if attendee else None
other_props = {}
for pname in config.Scheduling.CalDAV.PerAttendeeProperties:
props = tuple(current_master.properties(pname))
@@ -310,6 +313,11 @@
return True, False, rids
+ # 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):
"""
@@ -340,13 +348,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.updateAttendeeData(new_master, old_master)
+ attendee, reply_changes = iTipProcessing.updateAttendeeData(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
@@ -392,11 +400,11 @@
log.error("Ignoring instance: %s in iTIP REPLY for: %s" % (rid, itip_message.resourceUID()))
continue
- attendee, partstat, private_comment = iTipProcessing.updateAttendeeData(itip_component, match_component)
+ attendee, reply_changes = iTipProcessing.updateAttendeeData(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)
@@ -419,11 +427,13 @@
@type from_component: L{Component}
@param to_component: component to copy to
@type to_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(from_component.properties("REQUEST-STATUS"))
@@ -436,7 +446,7 @@
attendees = tuple(from_component.properties("ATTENDEE"))
if len(attendees) != 1:
log.error("There must be one and only one ATTENDEE property in a REPLY\n%s" % (str(from_component),))
- return None, False, False
+ return None, None
attendee = attendees[0]
partstat = attendee.parameterValue("PARTSTAT", "NEEDS-ACTION")
@@ -444,28 +454,40 @@
# Now find matching ATTENDEE in to_component
existing_attendee = to_component.getAttendeeProperty((attendee.value(),))
if existing_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(from_component.properties("X-CALENDARSERVER-PRIVATE-COMMENT"))
+ attendee_comment = tuple(from_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(to_component.properties("X-CALENDARSERVER-ATTENDEE-COMMENT"))
+ private_comments = tuple(to_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
@@ -484,22 +506,22 @@
# We now remove the private comment on the organizer's side if the attendee removed it
to_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": PyCalendarDateTime.getNowUTC().getText(),
+ ATTENDEE_COMMENT_REF: attendee.value(),
+ DTSTAMP_PARAM: PyCalendarDateTime.getNowUTC().getText(),
}
)
to_component.addProperty(private_comment)
- private_comment_changed = True
+ reply_changes.props.append(PRIVATE_COMMENT)
else:
# Only change if different
@@ -508,17 +530,31 @@
private_comment.removeAllParameters()
# Add default parameters
- private_comment.setParameter("X-CALENDARSERVER-ATTENDEE-REF", attendee.value())
- private_comment.setParameter("X-CALENDARSERVER-DTSTAMP", PyCalendarDateTime.getNowUTC().getText())
+ private_comment.setParameter(ATTENDEE_COMMENT_REF, attendee.value())
+ private_comment.setParameter(DTSTAMP_PARAM, PyCalendarDateTime.getNowUTC().getText())
# Set new value
private_comment.setValue(attendee_comment.value())
- private_comment_changed = True
+ reply_changes.props.append(PRIVATE_COMMENT)
- 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(from_component.properties(propname))
+ if copy_props:
+ to_component.removeProperties(propname)
+ for prop in copy_props:
+ to_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 transferItems(from_calendar, to_component, valarms, private_comments, transps, completeds, organizer_schedule_status, attendee_dtstamp, other_props, recipient, remove_matched=False):
"""
@@ -558,13 +594,13 @@
matched = from_calendar.overriddenComponent(rid)
if matched:
valarms = [comp for comp in matched.subcomponents() if comp.name() == "VALARM"]
- private_comments = tuple(matched.properties("X-CALENDARSERVER-PRIVATE-COMMENT"))
+ private_comments = tuple(matched.properties(PRIVATE_COMMENT))
transps = tuple(matched.properties("TRANSP"))
completeds = tuple(matched.properties("COMPLETED"))
organizer = matched.getProperty("ORGANIZER")
organizer_schedule_status = organizer.parameterValue("SCHEDULE-STATUS", None) if organizer else None
attendee = matched.getAttendeeProperty((recipient,))
- attendee_dtstamp = attendee.parameterValue("X-CALENDARSERVER-DTSTAMP") if attendee else None
+ attendee_dtstamp = attendee.parameterValue(DTSTAMP_PARAM) if attendee else None
other_props = {}
for pname in config.Scheduling.CalDAV.PerAttendeeProperties:
props = tuple(matched.properties(pname))
@@ -631,7 +667,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 other_props.keys():
@@ -653,7 +689,7 @@
attendee = to_component.getAttendeeProperty((recipient,))
if attendee_dtstamp and attendee:
- attendee.setParameter("X-CALENDARSERVER-DTSTAMP", attendee_dtstamp)
+ attendee.setParameter(DTSTAMP_PARAM, attendee_dtstamp)
return False
@@ -928,7 +964,7 @@
itip.removeAlarms()
# Remove all but essential properties
- itip.filterProperties(keep=(
+ keep_properties = (
"UID",
"RECURRENCE-ID",
"SEQUENCE",
@@ -942,11 +978,13 @@
"EXDATE",
"ORGANIZER",
"ATTENDEE",
- "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:
@@ -982,14 +1020,16 @@
# 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",)
- itip.removeXProperties(keep_properties=keep_properties)
+ keep_properties = (PRIVATE_COMMENT,) + tuple(config.Scheduling.CalDAV.AttendeePublicProperties)
+ keep_parameters = config.Scheduling.CalDAV.AttendeePublicParameters
+ 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("ATTENDEE", ("SCHEDULE-AGENT", "SCHEDULE-STATUS", "SCHEDULE-FORCE-SEND", DTSTAMP_PARAM,))
itip.removePropertyParameters("ORGANIZER", ("SCHEDULE-AGENT", "SCHEDULE-STATUS", "SCHEDULE-FORCE-SEND",))
Modified: CalendarServer/branches/release/CalendarServer-5.4-dev/txdav/caldav/datastore/scheduling/processing.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.4-dev/txdav/caldav/datastore/scheduling/processing.py 2015-11-13 17:46:32 UTC (rev 15308)
+++ CalendarServer/branches/release/CalendarServer-5.4-dev/txdav/caldav/datastore/scheduling/processing.py 2015-11-13 17:49:04 UTC (rev 15309)
@@ -26,7 +26,7 @@
from twistedcaldav import customxml, caldavxml
from twistedcaldav.config import config
-from twistedcaldav.ical import Property
+from twistedcaldav.ical import Property, DTSTAMP_PARAM
from twistedcaldav.instance import InvalidOverriddenInstanceError
from twistedcaldav.memcachelock import MemcacheLock, MemcacheLockTimeoutError
from twistedcaldav.memcacher import Memcacher
@@ -207,21 +207,24 @@
# Build the schedule-changes XML element
attendeeReplying, rids = processed
- 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),)
@@ -235,7 +238,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,))
@@ -1130,7 +1133,7 @@
if madeChanges:
attendee.setParameter("X-CALENDARSERVER-AUTO", PyCalendarDateTime.getNowUTC().getText())
- attendee.removeParameter("X-CALENDARSERVER-DTSTAMP")
+ attendee.removeParameter(DTSTAMP_PARAM)
return madeChanges
Modified: CalendarServer/branches/release/CalendarServer-5.4-dev/txdav/caldav/datastore/scheduling/test/test_itip.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.4-dev/txdav/caldav/datastore/scheduling/test/test_itip.py 2015-11-13 17:46:32 UTC (rev 15308)
+++ CalendarServer/branches/release/CalendarServer-5.4-dev/txdav/caldav/datastore/scheduling/test/test_itip.py 2015-11-13 17:49:04 UTC (rev 15309)
@@ -21,7 +21,7 @@
from twisted.trial import unittest
from twistedcaldav.stdconfig import config
-from twistedcaldav.ical import Component
+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.
@@ -1178,7 +1214,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",
@@ -1219,7 +1255,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",
@@ -1327,7 +1363,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",
@@ -1388,7 +1427,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",
@@ -1450,7 +1489,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",
@@ -1532,7 +1571,11 @@
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",
@@ -1608,7 +1651,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",
@@ -1685,7 +1731,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",
@@ -1882,7 +1931,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",
@@ -1925,7 +1974,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",
@@ -1967,7 +2016,7 @@
END:VEVENT
END:VCALENDAR
""",
- True, "mailto:user1 at example.com", (("", True, False),),
+ True, "mailto:user1 at example.com", (("", iTipProcessing.ReplyChanges(params=['PARTSTAT'], props=[])),),
),
)
@@ -1975,11 +2024,6 @@
calendar = Component.fromString(calendar_txt)
itipmsg = Component.fromString(itipmsg_txt)
reply_success, reply_processed = iTipProcessing.processReply(itipmsg, calendar)
-# if not description.startswith("#3.1"):
-# continue
-# print(description)
-# print(str(calendar))
-# print(str(result))
self.assertEqual(
str(calendar).replace("\r", "").replace("\n ", ""),
str(result).replace("\n ", ""),
@@ -2010,6 +2054,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
@@ -2512,6 +3213,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 = (
@@ -3140,3 +3877,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/branches/release/CalendarServer-5.4-dev/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-5.4-dev/txdav/caldav/datastore/sql.py 2015-11-13 17:46:32 UTC (rev 15308)
+++ CalendarServer/branches/release/CalendarServer-5.4-dev/txdav/caldav/datastore/sql.py 2015-11-13 17:49:04 UTC (rev 15309)
@@ -53,7 +53,7 @@
from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
from twistedcaldav.dateops import normalizeForIndex, datetimeMktime, \
pyCalendarTodatetime, parseSQLDateToPyCalendar
-from twistedcaldav.ical import Component, InvalidICalendarDataError, Property
+from twistedcaldav.ical import Component, InvalidICalendarDataError, Property, ATTENDEE_COMMENT
from twistedcaldav.instance import InvalidOverriddenInstanceError
from twistedcaldav.memcacher import Memcacher
@@ -1785,7 +1785,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:
@@ -1793,7 +1793,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,
))
self.hasPrivateComment = new_has_private_comments
@@ -1803,7 +1803,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))
@inlineCallbacks
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20151113/c6c78ed6/attachment-0001.html>
More information about the calendarserver-changes
mailing list