[CalendarServer-changes] [14174] CalendarServer/branches/release/CalendarServer-6.0-dev
source_changes at macosforge.org
source_changes at macosforge.org
Fri Nov 14 13:08:05 PST 2014
Revision: 14174
http://trac.calendarserver.org//changeset/14174
Author: wsanchez at apple.com
Date: 2014-11-14 13:08:05 -0800 (Fri, 14 Nov 2014)
Log Message:
-----------
Pulled up r14164 r14172 from trunk.
Revision Links:
--------------
http://trac.calendarserver.org//changeset/14164
http://trac.calendarserver.org//changeset/14172
Modified Paths:
--------------
CalendarServer/branches/release/CalendarServer-6.0-dev/twistedcaldav/stdconfig.py
CalendarServer/branches/release/CalendarServer-6.0-dev/twistedcaldav/storebridge.py
CalendarServer/branches/release/CalendarServer-6.0-dev/twistedcaldav/test/test_collectioncontents.py
CalendarServer/branches/release/CalendarServer-6.0-dev/twistedcaldav/test/test_util.py
CalendarServer/branches/release/CalendarServer-6.0-dev/twistedcaldav/util.py
CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/datastore/scheduling/icaldiff.py
CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/datastore/scheduling/implicit.py
CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/datastore/scheduling/test/test_icaldiff.py
CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/datastore/sql.py
CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/datastore/sql_external.py
CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/icalendarstore.py
CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/carddav/datastore/sql.py
CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/common/datastore/sql.py
Property Changed:
----------------
CalendarServer/branches/release/CalendarServer-6.0-dev/
Property changes on: CalendarServer/branches/release/CalendarServer-6.0-dev
___________________________________________________________________
Modified: svn:mergeinfo
- /CalDAVTester/trunk:11193-11198
/CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/generic-sqlstore:6167-6191
/CalendarServer/branches/new-store:5594-5934
/CalendarServer/branches/new-store-no-caldavfile:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
/CalendarServer/branches/release/CalendarServer-4.3-dev:10180-10190,10192
/CalendarServer/branches/release/CalendarServer-5.1-dev:11846
/CalendarServer/branches/release/CalendarServer-5.2-dev:11972,12357-12358,12794,12814
/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
/CalendarServer/branches/users/cdaboo/cross-pod-sharing:12038-12191
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/fix-no-ischedule:11607-11871
/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
/CalendarServer/branches/users/cdaboo/ischedule-dkim:9747-9979
/CalendarServer/branches/users/cdaboo/json:11622-11912
/CalendarServer/branches/users/cdaboo/managed-attachments:9985-10145
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/performance-tweaks:11824-11836
/CalendarServer/branches/users/cdaboo/pods:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
/CalendarServer/branches/users/cdaboo/pycard:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/reverse-proxy-pods:11875-11900
/CalendarServer/branches/users/cdaboo/scheduling-queue-refresh:11783-12557
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/cdaboo/sharing-in-the-store:11935-12016
/CalendarServer/branches/users/cdaboo/store-scheduling:10876-11129
/CalendarServer/branches/users/cdaboo/timezones:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
/CalendarServer/branches/users/gaya/cleanrevisions:12152-12334
/CalendarServer/branches/users/gaya/groupsharee2:13669-13773
/CalendarServer/branches/users/gaya/sharedgroupfixes:12120-12142
/CalendarServer/branches/users/gaya/sharedgroups-3:11088-11204
/CalendarServer/branches/users/glyph/always-abort-txn-on-error:9958-9969
/CalendarServer/branches/users/glyph/case-insensitive-uid:8772-8805
/CalendarServer/branches/users/glyph/conn-limit:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/dalify:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
/CalendarServer/branches/users/glyph/deploybuild:7563-7572
/CalendarServer/branches/users/glyph/digest-auth-redux:10624-10635
/CalendarServer/branches/users/glyph/disable-quota:7718-7727
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/enforce-max-requests:11640-11643
/CalendarServer/branches/users/glyph/hang-fix:11465-11491
/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
/CalendarServer/branches/users/glyph/launchd-wrapper-bis:11413-11436
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/log-cleanups:11691-11731
/CalendarServer/branches/users/glyph/migrate-merge:8690-8713
/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
/CalendarServer/branches/users/glyph/new-export:7444-7485
/CalendarServer/branches/users/glyph/one-home-list-api:10048-10073
/CalendarServer/branches/users/glyph/oracle:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
/CalendarServer/branches/users/glyph/other-html:8062-8091
/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
/CalendarServer/branches/users/glyph/parallel-upgrade_to_1:8571-8583
/CalendarServer/branches/users/glyph/q:9560-9688
/CalendarServer/branches/users/glyph/queue-locking-and-timing:10204-10289
/CalendarServer/branches/users/glyph/quota:7604-7637
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-fixes:8436-8443
/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
/CalendarServer/branches/users/glyph/sharedpool:6490-6550
/CalendarServer/branches/users/glyph/sharing-api:9192-9205
/CalendarServer/branches/users/glyph/skip-lonely-vtimezones:8524-8535
/CalendarServer/branches/users/glyph/sql-store:5929-6073
/CalendarServer/branches/users/glyph/start-service-start-loop:11060-11065
/CalendarServer/branches/users/glyph/subtransactions:7248-7258
/CalendarServer/branches/users/glyph/table-alias:8651-8664
/CalendarServer/branches/users/glyph/uidexport:7673-7676
/CalendarServer/branches/users/glyph/unshare-when-access-revoked:10562-10595
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
/CalendarServer/branches/users/glyph/warning-cleanups:11347-11357
/CalendarServer/branches/users/glyph/whenNotProposed:11881-11897
/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
/CalendarServer/branches/users/sagen/applepush:8126-8184
/CalendarServer/branches/users/sagen/inboxitems:7380-7381
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/move2who:12819-12860
/CalendarServer/branches/users/sagen/move2who-2:12861-12898
/CalendarServer/branches/users/sagen/move2who-3:12899-12913
/CalendarServer/branches/users/sagen/move2who-4:12914-13157
/CalendarServer/branches/users/sagen/move2who-5:13158-13163
/CalendarServer/branches/users/sagen/newcua:13309-13327
/CalendarServer/branches/users/sagen/newcua-1:13328-13330
/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
/CalendarServer/branches/users/sagen/recordtypes:13648-13656
/CalendarServer/branches/users/sagen/recordtypes-2:13657
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/sagen/testing:10827-10851,10853-10855
/CalendarServer/branches/users/wsanchez/transations:5515-5593
/CalendarServer/trunk:14095,14117-14119
+ /CalDAVTester/trunk:11193-11198
/CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/generic-sqlstore:6167-6191
/CalendarServer/branches/new-store:5594-5934
/CalendarServer/branches/new-store-no-caldavfile:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
/CalendarServer/branches/release/CalendarServer-4.3-dev:10180-10190,10192
/CalendarServer/branches/release/CalendarServer-5.1-dev:11846
/CalendarServer/branches/release/CalendarServer-5.2-dev:11972,12357-12358,12794,12814
/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
/CalendarServer/branches/users/cdaboo/cross-pod-sharing:12038-12191
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/fix-no-ischedule:11607-11871
/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
/CalendarServer/branches/users/cdaboo/ischedule-dkim:9747-9979
/CalendarServer/branches/users/cdaboo/json:11622-11912
/CalendarServer/branches/users/cdaboo/managed-attachments:9985-10145
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/performance-tweaks:11824-11836
/CalendarServer/branches/users/cdaboo/pods:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
/CalendarServer/branches/users/cdaboo/pycard:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/reverse-proxy-pods:11875-11900
/CalendarServer/branches/users/cdaboo/scheduling-queue-refresh:11783-12557
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/cdaboo/sharing-in-the-store:11935-12016
/CalendarServer/branches/users/cdaboo/store-scheduling:10876-11129
/CalendarServer/branches/users/cdaboo/timezones:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
/CalendarServer/branches/users/gaya/cleanrevisions:12152-12334
/CalendarServer/branches/users/gaya/groupsharee2:13669-13773
/CalendarServer/branches/users/gaya/sharedgroupfixes:12120-12142
/CalendarServer/branches/users/gaya/sharedgroups-3:11088-11204
/CalendarServer/branches/users/glyph/always-abort-txn-on-error:9958-9969
/CalendarServer/branches/users/glyph/case-insensitive-uid:8772-8805
/CalendarServer/branches/users/glyph/conn-limit:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/dalify:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
/CalendarServer/branches/users/glyph/deploybuild:7563-7572
/CalendarServer/branches/users/glyph/digest-auth-redux:10624-10635
/CalendarServer/branches/users/glyph/disable-quota:7718-7727
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/enforce-max-requests:11640-11643
/CalendarServer/branches/users/glyph/hang-fix:11465-11491
/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
/CalendarServer/branches/users/glyph/launchd-wrapper-bis:11413-11436
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/log-cleanups:11691-11731
/CalendarServer/branches/users/glyph/migrate-merge:8690-8713
/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
/CalendarServer/branches/users/glyph/new-export:7444-7485
/CalendarServer/branches/users/glyph/one-home-list-api:10048-10073
/CalendarServer/branches/users/glyph/oracle:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
/CalendarServer/branches/users/glyph/other-html:8062-8091
/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
/CalendarServer/branches/users/glyph/parallel-upgrade_to_1:8571-8583
/CalendarServer/branches/users/glyph/q:9560-9688
/CalendarServer/branches/users/glyph/queue-locking-and-timing:10204-10289
/CalendarServer/branches/users/glyph/quota:7604-7637
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-fixes:8436-8443
/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
/CalendarServer/branches/users/glyph/sharedpool:6490-6550
/CalendarServer/branches/users/glyph/sharing-api:9192-9205
/CalendarServer/branches/users/glyph/skip-lonely-vtimezones:8524-8535
/CalendarServer/branches/users/glyph/sql-store:5929-6073
/CalendarServer/branches/users/glyph/start-service-start-loop:11060-11065
/CalendarServer/branches/users/glyph/subtransactions:7248-7258
/CalendarServer/branches/users/glyph/table-alias:8651-8664
/CalendarServer/branches/users/glyph/uidexport:7673-7676
/CalendarServer/branches/users/glyph/unshare-when-access-revoked:10562-10595
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
/CalendarServer/branches/users/glyph/warning-cleanups:11347-11357
/CalendarServer/branches/users/glyph/whenNotProposed:11881-11897
/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
/CalendarServer/branches/users/sagen/applepush:8126-8184
/CalendarServer/branches/users/sagen/inboxitems:7380-7381
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/move2who:12819-12860
/CalendarServer/branches/users/sagen/move2who-2:12861-12898
/CalendarServer/branches/users/sagen/move2who-3:12899-12913
/CalendarServer/branches/users/sagen/move2who-4:12914-13157
/CalendarServer/branches/users/sagen/move2who-5:13158-13163
/CalendarServer/branches/users/sagen/newcua:13309-13327
/CalendarServer/branches/users/sagen/newcua-1:13328-13330
/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
/CalendarServer/branches/users/sagen/recordtypes:13648-13656
/CalendarServer/branches/users/sagen/recordtypes-2:13657
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/sagen/testing:10827-10851,10853-10855
/CalendarServer/branches/users/wsanchez/transations:5515-5593
/CalendarServer/trunk:14095,14117-14119,14164,14172
Modified: CalendarServer/branches/release/CalendarServer-6.0-dev/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-6.0-dev/twistedcaldav/stdconfig.py 2014-11-14 21:01:03 UTC (rev 14173)
+++ CalendarServer/branches/release/CalendarServer-6.0-dev/twistedcaldav/stdconfig.py 2014-11-14 21:08:05 UTC (rev 14174)
@@ -839,6 +839,17 @@
"MaxPrincipalSearchReportResults": 500,
#
+ # Client fixes per user-agent match
+ #
+ "ClientFixes" : {
+ "ForceAttendeeTRANSP" : [
+ "iOS/8\\.0(\\..*)?",
+ "iOS/8\\.1(\\..*)?",
+ "iOS/8\\.2(\\..*)?",
+ ],
+ },
+
+ #
# Localization
#
"Localization" : {
@@ -1458,6 +1469,19 @@
+def _updateClientFixes(configDict, reloading=False):
+ #
+ # Compile ClientFixes expressions for speed
+ #
+ try:
+ configDict.ClientFixesCompiled = {}
+ for key, expressions in configDict.ClientFixes.items():
+ configDict.ClientFixesCompiled[key] = [re.compile("^{}$".format(x)) for x in expressions]
+ except re.error, e:
+ raise ConfigurationError("Invalid regular expression in ClientFixes: %s" % (e,))
+
+
+
def _updateLogLevels(configDict, reloading=False):
log.publisher.levels.clearLogLevels()
@@ -1648,6 +1672,7 @@
_postUpdateAugmentService,
_updateACLs,
_updateRejectClients,
+ _updateClientFixes,
_updateLogLevels,
_updateNotifications,
_updateICalendar,
Modified: CalendarServer/branches/release/CalendarServer-6.0-dev/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-6.0-dev/twistedcaldav/storebridge.py 2014-11-14 21:01:03 UTC (rev 14173)
+++ CalendarServer/branches/release/CalendarServer-6.0-dev/twistedcaldav/storebridge.py 2014-11-14 21:08:05 UTC (rev 14174)
@@ -49,7 +49,7 @@
from twistedcaldav.sharing import (
invitationBindStatusToXMLMap, invitationBindModeToXMLMap
)
-from twistedcaldav.util import bestAcceptType
+from twistedcaldav.util import bestAcceptType, matchClientFixes
from twistedcaldav.vcard import Component as VCard, InvalidVCardDataError
from txdav.base.propertystore.base import PropertyName
from txdav.caldav.icalendarstore import (
@@ -60,7 +60,7 @@
InvalidPerUserDataMerge,
AttendeeAllowedError, ResourceDeletedError, InvalidAttachmentOperation,
ShareeAllowedError, DuplicatePrivateCommentsError, InvalidSplit,
- AttachmentSizeTooLarge, UnknownTimezone)
+ AttachmentSizeTooLarge, UnknownTimezone, SetComponentOptions)
from txdav.carddav.iaddressbookstore import (
KindChangeNotAllowedError, GroupWithUnsharedAddressNotAllowedError
)
@@ -2892,8 +2892,18 @@
"Can't parse calendar data: %s" % (str(e),)
))
+ # Look for client fixes
+ ua = request.headers.getHeader("User-Agent")
+ client_fix_transp = matchClientFixes(config, ua)
+
+ # Setup options
+ options = {
+ SetComponentOptions.smartMerge: schedule_tag_match,
+ SetComponentOptions.clientFixTRANSP: client_fix_transp,
+ }
+
try:
- response = (yield self.storeComponent(component, smart_merge=schedule_tag_match))
+ response = (yield self.storeComponent(component, options=options))
except ResourceDeletedError:
# This is OK - it just means the server deleted the resource during the PUT. We make it look
# like the PUT succeeded.
Modified: CalendarServer/branches/release/CalendarServer-6.0-dev/twistedcaldav/test/test_collectioncontents.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-6.0-dev/twistedcaldav/test/test_collectioncontents.py 2014-11-14 21:01:03 UTC (rev 14173)
+++ CalendarServer/branches/release/CalendarServer-6.0-dev/twistedcaldav/test/test_collectioncontents.py 2014-11-14 21:08:05 UTC (rev 14174)
@@ -48,7 +48,7 @@
_getFakeMemcacheProtocol)
# Need to not do implicit behavior during these tests
- def _fakeDoImplicitScheduling(self, component, inserting, internal_state):
+ def _fakeDoImplicitScheduling(self, component, inserting, internal_state, options):
return False, None, False, None
self.patch(CalendarObject, "doImplicitScheduling",
Modified: CalendarServer/branches/release/CalendarServer-6.0-dev/twistedcaldav/test/test_util.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-6.0-dev/twistedcaldav/test/test_util.py 2014-11-14 21:01:03 UTC (rev 14173)
+++ CalendarServer/branches/release/CalendarServer-6.0-dev/twistedcaldav/test/test_util.py 2014-11-14 21:08:05 UTC (rev 14174)
@@ -16,8 +16,10 @@
from txweb2.http_headers import Headers
+from twistedcaldav.config import ConfigDict
+from twistedcaldav.stdconfig import _updateClientFixes
+from twistedcaldav.util import bestAcceptType, userAgentProductTokens, matchClientFixes
import twistedcaldav.test.util
-from twistedcaldav.util import bestAcceptType
class AcceptType(twistedcaldav.test.util.TestCase):
"""
@@ -142,3 +144,78 @@
hdrs.addRawHeader(*hdr)
check = bestAcceptType(hdrs.getHeader("accept"), allowedTypes)
self.assertEqual(check, result, msg="Failed %s" % (title,))
+
+
+ def test_userAgentProductTokens(self):
+ """
+ Test that L{userAgentProductTokens} correctly parses a User-Agent header.
+ """
+ for hdr, result in (
+ # Valid syntax
+ ("Client/1.0", ["Client/1.0", ]),
+ ("Client/1.0 FooBar/2", ["Client/1.0", "FooBar/2", ]),
+ ("Client/1.0 (commentary here)", ["Client/1.0", ]),
+ ("Client/1.0 (FooBar/2)", ["Client/1.0", ]),
+ ("Client/1.0 (commentary here) FooBar/2", ["Client/1.0", "FooBar/2", ]),
+ ("Client/1.0 (commentary here) FooBar/2 (more commentary here) ", ["Client/1.0", "FooBar/2", ]),
+
+ # Invalid syntax
+ ("Client/1.0 (commentary here FooBar/2", ["Client/1.0", ]),
+ ("Client/1.0 commentary here) FooBar/2", ["Client/1.0", "commentary", "here)", "FooBar/2", ]),
+ ):
+ self.assertEqual(userAgentProductTokens(hdr), result, msg="Mismatch: {}".format(hdr))
+
+
+ def test_matchClientFixes(self):
+ """
+ Test that L{matchClientFixes} correctly identifies clients with matching fix tokens.
+ """
+ c = ConfigDict()
+ c.ClientFixes = {
+ "fix1": [
+ "Client/1\\.0.*",
+ "Client/1\\.1(\\..*)?",
+ "Client/2",
+ ],
+ "fix2": [
+ "Other/1\\.0.*",
+ ],
+ }
+ _updateClientFixes(c)
+ _updateClientFixes(c)
+
+ # Valid matches
+ for ua in (
+ "Client/1.0 FooBar/2",
+ "Client/1.0.1 FooBar/2",
+ "Client/1.0.1.1 FooBar/2",
+ "Client/1.1 FooBar/2",
+ "Client/1.1.1 FooBar/2",
+ "Client/2 FooBar/2",
+ ):
+ self.assertEqual(
+ matchClientFixes(c, ua),
+ set(("fix1",)),
+ msg="Did not match {}".format(ua),
+ )
+
+ # Valid non-matches
+ for ua in (
+ "Client/1 FooBar/2",
+ "Client/1.10 FooBar/2",
+ "Client/2.0 FooBar/2",
+ "Client/2.0.1 FooBar/2",
+ "Client FooBar/2",
+ "Client/3 FooBar/2",
+ "Client/3.0 FooBar/2",
+ "Client/10 FooBar/2",
+ "Client/10.0 FooBar/2",
+ "Client/10.0.1 FooBar/2",
+ "Client/10.0.1 (Client/1.0) FooBar/2",
+ "Client/10.0.1 (foo Client/1.0 bar) FooBar/2",
+ ):
+ self.assertEqual(
+ matchClientFixes(c, ua),
+ set(),
+ msg="Incorrectly matched {}".format(ua),
+ )
Modified: CalendarServer/branches/release/CalendarServer-6.0-dev/twistedcaldav/util.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-6.0-dev/twistedcaldav/util.py 2014-11-14 21:01:03 UTC (rev 14173)
+++ CalendarServer/branches/release/CalendarServer-6.0-dev/twistedcaldav/util.py 2014-11-14 21:08:05 UTC (rev 14174)
@@ -18,6 +18,7 @@
import re
import sys
import base64
+import itertools
from subprocess import Popen, PIPE, STDOUT
from hashlib import md5, sha1
@@ -541,3 +542,57 @@
result_qval = qval
return result
+
+
+
+def userAgentProductTokens(user_agent):
+ """
+ Parse an HTTP User-Agent header to extract the product tokens and ignore
+ any parenthesized comment strings in the header.
+
+ @param user_agent: text of User-Agent header value
+ @type user_agent: L{str}
+
+ @return: list of product tokens extracted from the header
+ @rtype: L{list}
+ """
+
+ ua_hdr = user_agent.split()
+ ua_tokens = []
+ comment = False
+ for token in ua_hdr:
+ if comment:
+ if token.endswith(")"):
+ comment = False
+ elif token.startswith("("):
+ if not token.endswith(")"):
+ comment = True
+ else:
+ ua_tokens.append(token)
+
+ return ua_tokens
+
+
+
+def matchClientFixes(config, user_agent):
+ """
+ Given a user-agent string, see if it matches any of the configured client fixes.
+
+ @param config: the L{config} to match against.
+ @type config: L{ConfigDict}
+ @param user_agent: the HTTP User-Agent header value to test.
+ @type user_agent: L{str}
+ """
+
+ if len(config.ClientFixesCompiled) == 0 or not user_agent:
+ return set()
+
+ ua_tokens = userAgentProductTokens(user_agent)
+
+ client_fixes = set()
+ for fix, patterns in config.ClientFixesCompiled.items():
+ for pattern, token in itertools.product(patterns, ua_tokens):
+ if pattern.match(token) is not None:
+ client_fixes.add(fix)
+ break
+ return client_fixes
Modified: CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/datastore/scheduling/icaldiff.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/datastore/scheduling/icaldiff.py 2014-11-14 21:01:03 UTC (rev 14173)
+++ CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/datastore/scheduling/icaldiff.py 2014-11-14 21:08:05 UTC (rev 14174)
@@ -27,10 +27,6 @@
from txdav.caldav.datastore.scheduling.utils import normalizeCUAddr
from txdav.caldav.datastore.scheduling.itip import iTipGenerator
-"""
-Class that handles diff'ing two calendar objects.
-"""
-
__all__ = [
"iCalDiff",
]
@@ -38,19 +34,32 @@
log = Logger()
class iCalDiff(object):
+ """
+ This object is used for doing comparisons between two calendar objects to
+ work out what the changes are, in order to determine whether a scheduling
+ operation might be needed. The behavior will varying based on whether the
+ change is being triggered by an Organizer or an Attendee.
+ """
- def __init__(self, oldcalendar, newcalendar, smart_merge):
+ def __init__(self, oldcalendar, newcalendar, smart_merge, forceTRANSP=False):
"""
+ Note that this object will always duplicate the calendar objects when doing
+ comparisons so as not to change the calendar objects passed in.
- @param oldcalendar:
- @type oldcalendar:
- @param newcalendar:
- @type newcalendar:
+ @param oldcalendar: the existing calendar object to compare to
+ @type oldcalendar: L{Component}
+ @param newcalendar: the new calendar object
+ @type newcalendar: L{Component}
+ @param smart_merge: whether or not a "smart" CalDAV merge is done (If-Schedule-Tag-Match)
+ @type smart_merge: L{bool}
+ @param forceTRANSP: whether or not to apply a fix for clients failing to set TRANSP properly
+ @type forceTRANSP: L{bool}
"""
self.oldcalendar = oldcalendar
self.newcalendar = newcalendar
self.smart_merge = smart_merge
+ self.forceTRANSP = forceTRANSP
def organizerDiff(self):
@@ -501,6 +510,13 @@
replyNeeded = True
+ # Apply client fix only if the PARTSTAT was changed
+ if self.forceTRANSP:
+ if clientAttendee.parameterValue("PARTSTAT", "NEEDS-ACTION") in ("ACCEPTED", "TENTATIVE",):
+ clientComponent.replaceProperty(Property("TRANSP", "OPAQUE"))
+ else:
+ clientComponent.replaceProperty(Property("TRANSP", "TRANSPARENT"))
+
if serverAttendee.parameterValue("RSVP", "FALSE") != clientAttendee.parameterValue("RSVP", "FALSE"):
if clientAttendee.parameterValue("RSVP", "FALSE") == "FALSE":
try:
Modified: CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/datastore/scheduling/implicit.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/datastore/scheduling/implicit.py 2014-11-14 21:01:03 UTC (rev 14173)
+++ CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/datastore/scheduling/implicit.py 2014-11-14 21:08:05 UTC (rev 14174)
@@ -37,6 +37,7 @@
from txdav.caldav.datastore.scheduling.utils import getCalendarObjectForRecord
from txdav.caldav.datastore.scheduling.work import ScheduleReplyWork, \
ScheduleReplyCancelWork, ScheduleOrganizerWork, ScheduleOrganizerSendWork
+from txdav.caldav.icalendarstore import SetComponentOptions
import collections
@@ -65,10 +66,11 @@
STATUS_ORPHANED_CANCELLED_EVENT = 1
STATUS_ORPHANED_EVENT = 2
- def __init__(self, logItems=None):
+ def __init__(self, logItems=None, options=None):
self.return_status = ImplicitScheduler.STATUS_OK
self.logItems = logItems
+ self.options = options
self.allowed_to_schedule = True
self.suppress_refresh = False
@@ -1683,7 +1685,11 @@
(caldav_namespace, "valid-attendee-change"),
"Cannot use an event when not listed as an attendee in the organizer's copy",
))
- differ = iCalDiff(oldcalendar, self.calendar, self.do_smart_merge)
+
+ # Check for a required client fix
+ forceTRANSP = SetComponentOptions.value(self.options, SetComponentOptions.clientFixTRANSP)
+
+ differ = iCalDiff(oldcalendar, self.calendar, self.do_smart_merge, forceTRANSP=forceTRANSP)
return differ.attendeeMerge(self.attendee)
Modified: CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/datastore/scheduling/test/test_icaldiff.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/datastore/scheduling/test/test_icaldiff.py 2014-11-14 21:01:03 UTC (rev 14173)
+++ CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/datastore/scheduling/test/test_icaldiff.py 2014-11-14 21:08:05 UTC (rev 14174)
@@ -5248,6 +5248,391 @@
self.assertEqual(got_rescheduled, rescheduled, msg="%s expected rescheduled: '%s', got: '%s'" % (description, rescheduled, got_rescheduled,))
+ def test_attendee_merge_forceTRANSP(self):
+
+ data = (
+ (
+ "#1.1 Simple component, no change",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user2 at example.com",
+ (True, False, (), """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+END:VEVENT
+END:VCALENDAR
+""")
+ ),
+ (
+ "#1.2 Simple component, change to ACCEPTED",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user2 at example.com",
+ (True, True, (None,), """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED;X-CALENDARSERVER-DTSTAMP=XXXXXXXXTXXXXXXZ:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+""")
+ ),
+ (
+ "#1.3 Simple component, change to DECLINED",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=DECLINED:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user2 at example.com",
+ (True, True, (None,), """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=DECLINED;X-CALENDARSERVER-DTSTAMP=XXXXXXXXTXXXXXXZ:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:TRANSPARENT
+END:VEVENT
+END:VCALENDAR
+""")
+ ),
+ (
+ "#1.4 Simple component, change to TENTATIVE",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=TENTATIVE:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user2 at example.com",
+ (True, True, (None,), """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=TENTATIVE;X-CALENDARSERVER-DTSTAMP=XXXXXXXXTXXXXXXZ:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+""")
+ ),
+ (
+ "#1.5 Simple component, change to ACCEPTED with TRANSP",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:user2 at example.com
+TRANSP:TRANSPARENT
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=TENTATIVE:mailto:user2 at example.com
+TRANSP:TRANSPARENT
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user2 at example.com",
+ (True, True, (None,), """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=TENTATIVE;X-CALENDARSERVER-DTSTAMP=XXXXXXXXTXXXXXXZ:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+""")
+ ),
+ (
+ "#1.6 Simple component, remain as ACCEPTED with TRANSP",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+TRANSP:TRANSPARENT
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+TRANSP:TRANSPARENT
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user2 at example.com",
+ (True, False, (), """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:TRANSPARENT
+END:VEVENT
+END:VCALENDAR
+""")
+ ),
+ (
+ "#1.7 Simple component, remain as ACCEPTED change TRANSP",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+TRANSP:TRANSPARENT
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user2 at example.com",
+ (True, False, (), """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:TRANSPARENT
+END:VEVENT
+END:VCALENDAR
+""")
+ ),
+ (
+ "#1.8 Simple component, ACCEPTED->DECLINED with TRANSP",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=ACCEPTED:mailto:user2 at example.com
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=DECLINED:mailto:user2 at example.com
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+""",
+ "mailto:user2 at example.com",
+ (True, True, (None,), """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=DECLINED;X-CALENDARSERVER-DTSTAMP=XXXXXXXXTXXXXXXZ:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:TRANSPARENT
+END:VEVENT
+END:VCALENDAR
+""")
+ ),
+ )
+
+ for description, calendar1, calendar2, attendee, result in data:
+ differ = iCalDiff(
+ Component.fromString(calendar1),
+ Component.fromString(calendar2),
+ False,
+ forceTRANSP=True,
+ )
+ diffResult = differ.attendeeMerge(attendee)
+ diffResult = (
+ diffResult[0],
+ diffResult[1],
+ tuple(sorted(diffResult[2])),
+ re.sub(
+ "X-CALENDARSERVER-DTSTAMP=[^Z]+",
+ "X-CALENDARSERVER-DTSTAMP=XXXXXXXXTXXXXXX",
+ str(diffResult[3]).replace("\r", "").replace("\n ", "")
+ ) if diffResult[3] else None,
+ )
+ self.assertEqual(diffResult, result, msg="%s: actual result: (%s)" % (description, ", ".join([str(i).replace("\r", "") for i in diffResult]),))
+
+
def test_organizer_smart_merge(self):
data1 = (
Modified: CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/datastore/sql.py 2014-11-14 21:01:03 UTC (rev 14173)
+++ CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/datastore/sql.py 2014-11-14 21:08:05 UTC (rev 14174)
@@ -78,7 +78,7 @@
InvalidDefaultCalendar, \
InvalidAttachmentOperation, DuplicatePrivateCommentsError, \
TimeRangeUpperLimit, TimeRangeLowerLimit, InvalidSplit, \
- AttachmentSizeTooLarge, UnknownTimezone
+ AttachmentSizeTooLarge, UnknownTimezone, SetComponentOptions
from txdav.caldav.icalendarstore import QuotaExceeded
from txdav.common.datastore.sql import CommonHome, CommonHomeChild, \
CommonObjectResource, ECALENDARTYPE
@@ -2961,7 +2961,7 @@
@inlineCallbacks
- def doImplicitScheduling(self, component, inserting, internal_state, split_details=None):
+ def doImplicitScheduling(self, component, inserting, internal_state, options, split_details=None):
new_component = None
did_implicit_action = False
@@ -2983,7 +2983,7 @@
user_uuid = self._parentCollection.viewerHome().uid()
component = PerUserDataFilter(user_uuid).filter(component.duplicate())
- scheduler = ImplicitScheduler(logItems=self._txn.logItems)
+ scheduler = ImplicitScheduler(logItems=self._txn.logItems, options=options)
# PUT
do_implicit_action, is_scheduling_resource = (yield scheduler.testImplicitSchedulingPUT(
@@ -3120,7 +3120,7 @@
@inlineCallbacks
- def setComponent(self, component, inserting=False, smart_merge=False):
+ def setComponent(self, component, inserting=False, options=None):
"""
Public api for storing a component. This will do full data validation checks on the specified component.
Scheduling will be done automatically.
@@ -3133,7 +3133,7 @@
except InvalidICalendarDataError as e:
raise InvalidComponentForStoreError(str(e))
try:
- result = yield self._setComponentInternal(component, inserting, ComponentUpdateState.NORMAL, smart_merge)
+ result = yield self._setComponentInternal(component, inserting, ComponentUpdateState.NORMAL, options)
except Exception:
ex = Failure()
@@ -3148,7 +3148,7 @@
@inlineCallbacks
- def _setComponentInternal(self, component, inserting=False, internal_state=ComponentUpdateState.NORMAL, smart_merge=False, split_details=None):
+ def _setComponentInternal(self, component, inserting=False, internal_state=ComponentUpdateState.NORMAL, options=None, split_details=None):
"""
Setting the component internally to the store itself. This will bypass a whole bunch of data consistency checks
on the assumption that those have been done prior to the component data being provided, provided the flag is set.
@@ -3156,7 +3156,11 @@
"""
self._componentChanged = False
- self.schedule_tag_match = not self.calendar().isInbox() and internal_state == ComponentUpdateState.NORMAL and smart_merge
+ self.schedule_tag_match = (
+ not self.calendar().isInbox() and
+ internal_state == ComponentUpdateState.NORMAL and
+ SetComponentOptions.value(options, SetComponentOptions.smartMerge)
+ )
schedule_state = None
if internal_state in (ComponentUpdateState.SPLIT_OWNER, ComponentUpdateState.SPLIT_ATTENDEE,):
@@ -3175,7 +3179,7 @@
# Do scheduling only for owner split
if internal_state == ComponentUpdateState.SPLIT_OWNER:
- yield self.doImplicitScheduling(component, inserting, internal_state, split_details)
+ yield self.doImplicitScheduling(component, inserting, internal_state, options, split_details)
self.isScheduleObject = True
self.processScheduleTags(component, inserting, internal_state)
@@ -3211,7 +3215,7 @@
yield self.decorateHostedStatus(component)
# Do scheduling
- implicit_result = (yield self.doImplicitScheduling(component, inserting, internal_state))
+ implicit_result = (yield self.doImplicitScheduling(component, inserting, internal_state, options))
if isinstance(implicit_result, int):
if implicit_result == ImplicitScheduler.STATUS_ORPHANED_CANCELLED_EVENT:
raise ResourceDeletedError("Resource created but immediately deleted by the server.")
Modified: CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/datastore/sql_external.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/datastore/sql_external.py 2014-11-14 21:01:03 UTC (rev 14173)
+++ CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/datastore/sql_external.py 2014-11-14 21:08:05 UTC (rev 14174)
@@ -171,7 +171,7 @@
raise AssertionError("CalendarObjectExternal: not supported")
- def _setComponentInternal(self, component, inserting=False, internal_state=ComponentUpdateState.NORMAL, smart_merge=False, split_details=None):
+ def _setComponentInternal(self, component, inserting=False, internal_state=ComponentUpdateState.NORMAL, options=None, split_details=None):
raise AssertionError("CalendarObjectExternal: not supported")
Modified: CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/icalendarstore.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/icalendarstore.py 2014-11-14 21:01:03 UTC (rev 14173)
+++ CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/caldav/icalendarstore.py 2014-11-14 21:08:05 UTC (rev 14174)
@@ -950,3 +950,30 @@
NORMAL.description = "normal"
NORMAL_NO_IMPLICIT.description = "normal-no-implicit"
INTERNAL.description = "internal"
+
+
+
+class SetComponentOptions(Names):
+ """
+ Constants for keys used in the L{ICalendarObject.setComponent} method's
+ C{options} dict. The definitions below define the constant key name and
+ the type used for the dict entry's value.
+
+ @cvar smartMerge: Apply CalDAV smart merge to data (If-Schedule-Tag-Match)
+ Value: L{bool}
+
+ @cvar clientFixTRANSP: Apply fix for clients not setting TRANSP.
+ Value: L{bool}
+ """
+
+ smartMerge = NamedConstant()
+ smartMerge.description = u"Smart Merge: CalDAV If-Schedule-Tag-Match behavior"
+ smartMerge.defaultValue = False
+
+ clientFixTRANSP = NamedConstant()
+ clientFixTRANSP.description = u"Fix for clients not setting TRANSP"
+ clientFixTRANSP.defaultValue = False
+
+ @staticmethod
+ def value(options, key):
+ return options.get(key, key.defaultValue) if options is not None else key.defaultValue
Modified: CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/carddav/datastore/sql.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/carddav/datastore/sql.py 2014-11-14 21:01:03 UTC (rev 14173)
+++ CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/carddav/datastore/sql.py 2014-11-14 21:08:05 UTC (rev 14174)
@@ -2041,7 +2041,7 @@
@inlineCallbacks
- def remove(self):
+ def remove(self, options=None):
if self.owned():
yield self.unshare() # storebridge should already have done this
@@ -2412,7 +2412,7 @@
@inlineCallbacks
- def setComponent(self, component, inserting=False):
+ def setComponent(self, component, inserting=False, options=None):
if isinstance(component, str) or isinstance(component, unicode):
component = self._componentClass.fromString(component)
Modified: CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/common/datastore/sql.py 2014-11-14 21:01:03 UTC (rev 14173)
+++ CalendarServer/branches/release/CalendarServer-6.0-dev/txdav/common/datastore/sql.py 2014-11-14 21:08:05 UTC (rev 14174)
@@ -7217,7 +7217,7 @@
self._locked = True
- def setComponent(self, component, inserting=False):
+ def setComponent(self, component, inserting=False, options=None):
raise NotImplementedError
@@ -7264,7 +7264,7 @@
@inlineCallbacks
- def remove(self):
+ def remove(self, options=None):
yield self._deleteQuery.on(self._txn, NoSuchObjectResourceError,
resourceID=self._resourceID)
yield self.properties()._removeResource()
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20141114/d183f8a9/attachment-0001.html>
More information about the calendarserver-changes
mailing list