[CalendarServer-changes] [11054] CalendarServer/branches/users/glyph/sharedgroups-2

source_changes at macosforge.org source_changes at macosforge.org
Tue Apr 16 15:22:28 PDT 2013


Revision: 11054
          http://trac.calendarserver.org//changeset/11054
Author:   glyph at apple.com
Date:     2013-04-16 15:22:28 -0700 (Tue, 16 Apr 2013)
Log Message:
-----------
Up to 11020.

Modified Paths:
--------------
    CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tap/caldav.py
    CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tap/test/test_caldav.py
    CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/calverify.py
    CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/config.py
    CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/test/test_calverify.py
    CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/test/test_config.py
    CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/test/test_gateway.py
    CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/util.py
    CalendarServer/branches/users/glyph/sharedgroups-2/contrib/performance/loadtest/ical.py
    CalendarServer/branches/users/glyph/sharedgroups-2/support/build.sh
    CalendarServer/branches/users/glyph/sharedgroups-2/support/submit
    CalendarServer/branches/users/glyph/sharedgroups-2/twext/enterprise/queue.py
    CalendarServer/branches/users/glyph/sharedgroups-2/txdav/common/datastore/file.py

Added Paths:
-----------
    CalendarServer/branches/users/glyph/sharedgroups-2/doc/RFC/RFC6868-Parameter Value Encoding.txt
    CalendarServer/branches/users/glyph/sharedgroups-2/support/Apple.make
    CalendarServer/branches/users/glyph/sharedgroups-2/support/CalendarServer.xcodeproj/
    CalendarServer/branches/users/glyph/sharedgroups-2/support/XCode.make

Removed Paths:
-------------
    CalendarServer/branches/users/glyph/sharedgroups-2/support/CalendarServer.tmproj
    CalendarServer/branches/users/glyph/sharedgroups-2/support/CalendarServer.xcodeproj/
    CalendarServer/branches/users/glyph/sharedgroups-2/support/Makefile.Apple

Property Changed:
----------------
    CalendarServer/branches/users/glyph/sharedgroups-2/
    CalendarServer/branches/users/glyph/sharedgroups-2/support/CalendarServer.xcodeproj/project.xcworkspace/


Property changes on: CalendarServer/branches/users/glyph/sharedgroups-2
___________________________________________________________________
Modified: svn:mergeinfo
   - /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/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/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
/CalendarServer/branches/users/cdaboo/ischedule-dkim:9747-9979
/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/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/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/cdaboo/timezones:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
/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/imip-and-admin-html:7866-7984
/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/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/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/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/purge_old_events:6735-6746
/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:9885-11000
   + /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/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/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
/CalendarServer/branches/users/cdaboo/ischedule-dkim:9747-9979
/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/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/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/cdaboo/timezones:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
/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/imip-and-admin-html:7866-7984
/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/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/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/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/purge_old_events:6735-6746
/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:9885-11020

Modified: CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tap/caldav.py
===================================================================
--- CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tap/caldav.py	2013-04-16 22:20:28 UTC (rev 11053)
+++ CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tap/caldav.py	2013-04-16 22:22:28 UTC (rev 11054)
@@ -1427,6 +1427,7 @@
             from twisted.internet import reactor
             pool = PeerConnectionPool(reactor, store.newTransaction,
                                       7654, schema)
+            store.queuer = store.queuer.transferProposalCallbacks(pool)
             controlSocket.addFactory(_QUEUE_ROUTE,
                                      pool.workerListenerFactory())
             # TODO: now that we have the shared control socket, we should get

Modified: CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tap/test/test_caldav.py
===================================================================
--- CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tap/test/test_caldav.py	2013-04-16 22:20:28 UTC (rev 11053)
+++ CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tap/test/test_caldav.py	2013-04-16 22:22:28 UTC (rev 11054)
@@ -33,11 +33,12 @@
 
 from twisted.internet.interfaces import IProcessTransport, IReactorProcess
 from twisted.internet.protocol import ServerFactory
-from twisted.internet.defer import Deferred, inlineCallbacks
+from twisted.internet.defer import Deferred, inlineCallbacks, passthru
 from twisted.internet.task import Clock
 from twisted.internet import reactor
 
-from twisted.application.service import IService, IServiceCollection
+from twisted.application.service import (IService, IServiceCollection,
+                                         MultiService)
 from twisted.application import internet
 
 from twext.web2.dav import auth
@@ -62,6 +63,7 @@
     _CONTROL_SERVICE_NAME, getSystemIDs
 )
 from calendarserver.provision.root import RootResource
+from twext.enterprise.queue import PeerConnectionPool, LocalQueuer
 from StringIO import StringIO
 
 
@@ -358,14 +360,16 @@
         writePlist(self.config, self.configFile)
 
 
-    def makeService(self):
+    def makeService(self, patcher=passthru):
         """
         Create a service by calling into CalDAVServiceMaker with
         self.configFile
         """
         self.options.parseOptions(["-f", self.configFile])
 
-        return CalDAVServiceMaker().makeService(self.options)
+        maker = CalDAVServiceMaker()
+        maker = patcher(maker)
+        return maker.makeService(self.options)
 
 
     def getSite(self):
@@ -507,8 +511,41 @@
         )
 
 
+    def test_storeQueuerSetInMaster(self):
+        """
+        In the master, the store's queuer should be set to a
+        L{PeerConnectionPool}, so that work can be distributed to other
+        processes.
+        """
+        self.config["ProcessType"] = "Combined"
+        self.writeConfig()
+        class NotAStore(object):
+            queuer = LocalQueuer(None)
+            def newTransaction(self):
+                return None
+            def callWithNewTransactions(self, x):
+                pass
+        store = NotAStore()
+        def something(proposal):
+            pass
+        store.queuer.callWithNewProposals(something)
+        def patch(maker):
+            def storageServiceStandIn(createMainService, logObserver,
+                                      uid=None, gid=None):
+                pool = None
+                logObserver = None
+                svc = createMainService(pool, store, logObserver)
+                multi = MultiService()
+                svc.setServiceParent(multi)
+                return multi
+            self.patch(maker, "storageService", storageServiceStandIn)
+            return maker
+        self.makeService(patch)
+        self.assertIsInstance(store.queuer, PeerConnectionPool)
+        self.assertIn(something, store.queuer.proposalCallbacks)
 
 
+
 class SlaveServiceTest(BaseServiceMakerTests):
     """
     Test various configurations of the Slave service

Modified: CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/calverify.py
===================================================================
--- CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/calverify.py	2013-04-16 22:20:28 UTC (rev 11053)
+++ CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/calverify.py	2013-04-16 22:22:28 UTC (rev 11054)
@@ -17,8 +17,6 @@
 ##
 from __future__ import print_function
 
-from twistedcaldav.directory.directory import DirectoryService
-from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
 
 """
 This tool scans the calendar store to analyze organizer/attendee event
@@ -42,30 +40,39 @@
 
 """
 
+from calendarserver.tools.cmdline import utilityMain
+
 from calendarserver.tools import tables
-from calendarserver.tools.cmdline import utilityMain
 from calendarserver.tools.util import getDirectory
+
 from pycalendar import definitions
 from pycalendar.calendar import PyCalendar
 from pycalendar.datetime import PyCalendarDateTime
 from pycalendar.exceptions import PyCalendarError
 from pycalendar.period import PyCalendarPeriod
 from pycalendar.timezone import PyCalendarTimezone
+
 from twext.enterprise.dal.syntax import Select, Parameter, Count
+
 from twisted.application.service import Service
 from twisted.internet.defer import inlineCallbacks, returnValue
 from twisted.python import log, usage
 from twisted.python.usage import Options
+
 from twistedcaldav import caldavxml
+from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
 from twistedcaldav.dateops import pyCalendarTodatetime
+from twistedcaldav.directory.directory import DirectoryService
 from twistedcaldav.ical import Component, ignoredComponents, \
     InvalidICalendarDataError, Property
 from twistedcaldav.scheduling.itip import iTipGenerator
 from twistedcaldav.stdconfig import DEFAULT_CONFIG_FILE
 from twistedcaldav.util import normalizationLookup
+
 from txdav.base.propertystore.base import PropertyName
 from txdav.common.datastore.sql_tables import schema, _BIND_MODE_OWN
 from txdav.common.icommondatastore import InternalDataStoreError
+
 import base64
 import collections
 import sys
@@ -195,7 +202,7 @@
 if not hasattr(Component, "maxAlarmCounts"):
     Component.hasDuplicateAlarms = new_hasDuplicateAlarms
 
-VERSION = "9"
+VERSION = "10"
 
 def printusage(e=None):
     if e:
@@ -228,6 +235,7 @@
 --missing           : display orphaned calendar homes - can be used.
                       with either --ical or --mismatch.
 --double            : detect double-bookings.
+--dark-purge        : purge room/resource events with invalid organizer
 
 --nuke PATH|RID     : remove specific calendar resources - can
                       only be used by itself. PATH is the full
@@ -263,6 +271,19 @@
 --summary  : report only which GUIDs have double-bookings - no details.
 --days     : number of days ahead to scan [DEFAULT: 365]
 
+Options for --dark-purge:
+
+--uuid     : only scan specified calendar homes. Can be a partial GUID
+             to scan all GUIDs with that as a prefix or "*" for all GUIDS
+             (that are marked as resources or locations in the directory).
+--summary  : report only which GUIDs have double-bookings - no details.
+--no-organizer       : only detect events without an organizer
+--invalid-organizer  : only detect events with an organizer not in the directory
+--disabled-organizer : only detect events with an organizer disabled for calendaring
+
+If none of (--no-organizer, --invalid-organizer, --disabled-organizer) is present, it
+will default to (--invalid-organizer, --disabled-organizer).
+
 CHANGES
 v8: Detects ORGANIZER or ATTENDEE properties with mailto: calendar user
     addresses for users that have valid directory records. Fix is to
@@ -293,13 +314,18 @@
         ['mismatch', 's', "Detect organizer/attendee mismatches."],
         ['missing', 'm', "Show 'orphaned' homes."],
         ['double', 'd', "Detect double-bookings."],
+        ['dark-purge', 'p', "Purge room/resource events with invalid organizer."],
         ['fix', 'x', "Fix problems."],
         ['verbose', 'v', "Verbose logging."],
         ['details', 'V', "Detailed logging."],
         ['summary', 'S', "Summary of double-bookings."],
         ['tzid', 't', "Timezone to adjust displayed times to."],
-    ]
 
+        ['no-organizer', '', "Detect dark events without an organizer"],
+        ['invalid-organizer', '', "Detect dark events with an organizer not in the directory"],
+        ['disabled-organizer', '', "Detect dark events with a disabled organizer"],
+]
+
     optParameters = [
         ['config', 'f', DEFAULT_CONFIG_FILE, "Specify caldavd.plist configuration path."],
         ['uuid', 'u', "", "Only check this user."],
@@ -564,7 +590,32 @@
         cb = schema.CALENDAR_BIND
         ch = schema.CALENDAR_HOME
         tr = schema.TIME_RANGE
+        kwds = {
+            "Start" : pyCalendarTodatetime(start),
+            "Max"   : pyCalendarTodatetime(PyCalendarDateTime(1900, 1, 1, 0, 0, 0)),
+            "UUID" : uuid,
+        }
+        rows = (yield Select(
+            [ch.OWNER_UID, co.RESOURCE_ID, co.ICALENDAR_UID, cb.CALENDAR_RESOURCE_NAME, co.MD5, co.ORGANIZER, co.CREATED, co.MODIFIED],
+            From=ch.join(
+                cb, type="inner", on=(ch.RESOURCE_ID == cb.CALENDAR_HOME_RESOURCE_ID)).join(
+                co, type="inner", on=(cb.CALENDAR_RESOURCE_ID == co.CALENDAR_RESOURCE_ID).And(
+                    cb.BIND_MODE == _BIND_MODE_OWN).And(
+                    cb.CALENDAR_RESOURCE_NAME != "inbox")).join(
+                tr, type="left", on=(co.RESOURCE_ID == tr.CALENDAR_OBJECT_RESOURCE_ID)),
+            Where=(ch.OWNER_UID == Parameter("UUID")).And((tr.START_DATE >= Parameter("Start")).Or(co.RECURRANCE_MAX <= Parameter("Start"))),
+            GroupBy=(ch.OWNER_UID, co.RESOURCE_ID, co.ICALENDAR_UID, cb.CALENDAR_RESOURCE_NAME, co.MD5, co.ORGANIZER, co.CREATED, co.MODIFIED,),
+        ).on(self.txn, **kwds))
+        returnValue(tuple(rows))
 
+
+    @inlineCallbacks
+    def getAllResourceInfoTimeRangeWithUUIDForAllUID(self, start, uuid):
+        co = schema.CALENDAR_OBJECT
+        cb = schema.CALENDAR_BIND
+        ch = schema.CALENDAR_HOME
+        tr = schema.TIME_RANGE
+
         cojoin = (cb.CALENDAR_RESOURCE_ID == co.CALENDAR_RESOURCE_ID).And(
                 cb.BIND_MODE == _BIND_MODE_OWN).And(
                 cb.CALENDAR_RESOURCE_NAME != "inbox")
@@ -1370,8 +1421,8 @@
             rows = yield self.getAllResourceInfoWithUID(self.options["uid"])
             descriptor = "getAllResourceInfoWithUID"
         elif self.options["uuid"]:
-            rows = yield self.getAllResourceInfoTimeRangeWithUUID(self.start, self.options["uuid"])
-            descriptor = "getAllResourceInfoTimeRangeWithUUID"
+            rows = yield self.getAllResourceInfoTimeRangeWithUUIDForAllUID(self.start, self.options["uuid"])
+            descriptor = "getAllResourceInfoTimeRangeWithUUIDForAllUID"
             self.options["uuid"] = None
         else:
             rows = yield self.getAllResourceInfoTimeRange(self.start)
@@ -2357,6 +2408,244 @@
 
 
 
+class DarkPurgeService(CalVerifyService):
+    """
+    Service which detects room/resource events that have an invalid organizer.
+    """
+
+    def title(self):
+        return "Dark Purge Service"
+
+
+    @inlineCallbacks
+    def doAction(self):
+
+        if not self.options["no-organizer"] and not self.options["invalid-organizer"] and not self.options["disabled-organizer"]:
+            self.options["invalid-organizer"] = self.options["disabled-organizer"] = True
+
+        self.output.write("\n---- Scanning calendar data ----\n")
+
+        self.tzid = PyCalendarTimezone(tzid=self.options["tzid"] if self.options["tzid"] else "America/Los_Angeles")
+        self.now = PyCalendarDateTime.getNowUTC()
+        self.start = self.options["start"] if "start" in self.options else PyCalendarDateTime.getToday()
+        self.start.setDateOnly(False)
+        self.start.setTimezone(self.tzid)
+        self.fix = self.options["fix"]
+
+        if self.options["verbose"] and self.options["summary"]:
+            ot = time.time()
+
+        # Check loop over uuid
+        UUIDDetails = collections.namedtuple("UUIDDetails", ("uuid", "rname", "purged",))
+        self.uuid_details = []
+        if len(self.options["uuid"]) != 36:
+            self.txn = self.store.newTransaction()
+            if self.options["uuid"]:
+                homes = yield self.getMatchingHomeUIDs(self.options["uuid"])
+            else:
+                homes = yield self.getAllHomeUIDs()
+            yield self.txn.commit()
+            self.txn = None
+            uuids = []
+            if self.options["verbose"]:
+                self.output.write("%d uuids to check\n" % (len(homes,)))
+            for uuid in sorted(homes):
+                record = self.directoryService().recordWithGUID(uuid)
+                if record is not None and record.recordType in (DirectoryService.recordType_locations, DirectoryService.recordType_resources):
+                    uuids.append(uuid)
+        else:
+            uuids = [self.options["uuid"], ]
+        if self.options["verbose"]:
+            self.output.write("%d uuids to scan\n" % (len(uuids,)))
+
+        count = 0
+        for uuid in uuids:
+            self.results = {}
+            self.summary = []
+            self.total = 0
+            count += 1
+
+            record = self.directoryService().recordWithGUID(uuid)
+            if record is None:
+                continue
+            if not record.thisServer() or not record.enabledForCalendaring:
+                continue
+
+            rname = record.fullName
+
+            if len(uuids) > 1 and not self.options["summary"]:
+                self.output.write("\n\n-----------------------------\n")
+
+            self.txn = self.store.newTransaction()
+
+            if self.options["verbose"]:
+                t = time.time()
+            rows = yield self.getAllResourceInfoTimeRangeWithUUID(self.start, uuid)
+            descriptor = "getAllResourceInfoTimeRangeWithUUID"
+
+            yield self.txn.commit()
+            self.txn = None
+
+            if self.options["verbose"]:
+                if not self.options["summary"]:
+                    self.output.write("%s time: %.1fs\n" % (descriptor, time.time() - t,))
+                else:
+                    self.output.write("%s (%d/%d)" % (uuid, count, len(uuids),))
+                    self.output.flush()
+
+            self.total = len(rows)
+            if not self.options["summary"]:
+                self.logResult("UUID to process", uuid)
+                self.logResult("Record name", rname)
+                self.addSummaryBreak()
+                self.logResult("Number of events to process", self.total)
+
+            if rows:
+                if not self.options["summary"]:
+                    self.addSummaryBreak()
+                purged = yield self.darkPurge(rows, uuid)
+            else:
+                purged = False
+
+            self.uuid_details.append(UUIDDetails(uuid, rname, purged))
+
+            if not self.options["summary"]:
+                self.printSummary()
+            else:
+                self.output.write(" - %s\n" % ("Dark Events" if purged else "OK",))
+                self.output.flush()
+
+        if count == 0:
+            self.output.write("Nothing to scan\n")
+
+        if self.options["summary"]:
+            table = tables.Table()
+            table.addHeader(("GUID", "Name", "RID", "UID", "Organizer",))
+            purged = 0
+            for item in sorted(self.uuid_details):
+                if not item.purged:
+                    continue
+                uuid = item.uuid
+                rname = item.rname
+                for detail in item.purged:
+                    table.addRow((
+                        uuid,
+                        rname,
+                        detail.resid,
+                        detail.uid,
+                        detail.organizer,
+                    ))
+                    uuid = ""
+                    rname = ""
+                    purged += 1
+            table.addFooter(("Total", "%d" % (purged,), "", "", "",))
+            self.output.write("\n")
+            table.printTable(os=self.output)
+
+            if self.options["verbose"]:
+                self.output.write("%s time: %.1fs\n" % ("Summary", time.time() - ot,))
+
+
+    @inlineCallbacks
+    def darkPurge(self, rows, uuid):
+        """
+        Check each calendar resource by looking at any ORGANIER property value and verifying it is valid.
+        """
+
+        if not self.options["summary"]:
+            self.output.write("\n---- Checking for dark events ----\n")
+        self.txn = self.store.newTransaction()
+
+        if self.options["verbose"]:
+            t = time.time()
+
+        Details = collections.namedtuple("Details", ("resid", "uid", "organizer",))
+
+        count = 0
+        total = len(rows)
+        details = []
+        fixed = 0
+        rjust = 10
+        for resid in rows:
+            resid = resid[1]
+            caldata = yield self.getCalendar(resid, self.fix)
+            if caldata is None:
+                if self.parseError:
+                    returnValue((False, self.parseError))
+                else:
+                    returnValue((True, "Nothing to scan"))
+
+            cal = Component(None, pycalendar=caldata)
+            uid = cal.resourceUID()
+
+            fail = False
+            organizer = cal.getOrganizer()
+            if organizer is None:
+                if self.options["no-organizer"]:
+                    fail = True
+            else:
+                principal = self.directoryService().principalForCalendarUserAddress(organizer)
+                if principal is None and organizer.startswith("urn:uuid:"):
+                    principal = self.directoryService().principalCollection.principalForUID(organizer[9:])
+                if principal is None:
+                    if self.options["invalid-organizer"]:
+                        fail = True
+                elif not principal.calendarsEnabled():
+                    if self.options["disabled-organizer"]:
+                        fail = True
+
+            if fail:
+                details.append(Details(resid, uid, organizer,))
+                if self.fix:
+                    yield self.removeEvent(resid)
+                    fixed += 1
+
+            if self.options["verbose"] and not self.options["summary"]:
+                if count == 1:
+                    self.output.write("Current".rjust(rjust) + "Total".rjust(rjust) + "Complete".rjust(rjust) + "\n")
+                if divmod(count, 100)[1] == 0:
+                    self.output.write((
+                        "\r" +
+                        ("%s" % count).rjust(rjust) +
+                        ("%s" % total).rjust(rjust) +
+                        ("%d%%" % safePercent(count, total)).rjust(rjust)
+                    ).ljust(80))
+                    self.output.flush()
+
+            # To avoid holding locks on all the rows scanned, commit every 100 resources
+            if divmod(count, 100)[1] == 0:
+                yield self.txn.commit()
+                self.txn = self.store.newTransaction()
+
+        yield self.txn.commit()
+        self.txn = None
+        if self.options["verbose"] and not self.options["summary"]:
+            self.output.write((
+                "\r" +
+                ("%s" % count).rjust(rjust) +
+                ("%s" % total).rjust(rjust) +
+                ("%d%%" % safePercent(count, total)).rjust(rjust)
+            ).ljust(80) + "\n")
+
+        # Print table of results
+        if not self.options["summary"]:
+            self.logResult("Number of dark events", len(details))
+
+        self.results["Dark Events"] = details
+        if self.fix:
+            self.results["Fix dark events"] = fixed
+
+        if self.options["verbose"] and not self.options["summary"]:
+            diff_time = time.time() - t
+            self.output.write("Time: %.2f s  Average: %.1f ms/resource\n" % (
+                diff_time,
+                safePercent(diff_time, total, 1000.0),
+            ))
+
+        returnValue(details)
+
+
+
 def main(argv=sys.argv, stderr=sys.stderr, reactor=None):
 
     if reactor is None:
@@ -2387,6 +2676,11 @@
             return SchedulingMismatchService(store, options, output, reactor, config)
         elif options["double"]:
             return DoubleBookingService(store, options, output, reactor, config)
+        elif options["dark-purge"]:
+            return DarkPurgeService(store, options, output, reactor, config)
+        else:
+            printusage("Invalid operation")
+            sys.exit(1)
 
     utilityMain(options['config'], makeService, reactor)
 

Modified: CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/config.py
===================================================================
--- CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/config.py	2013-04-16 22:20:28 UTC (rev 11053)
+++ CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/config.py	2013-04-16 22:22:28 UTC (rev 11054)
@@ -158,6 +158,7 @@
         rawInput = sys.stdin.read()
         try:
             plist = readPlistFromString(rawInput)
+            # Note: values in plist will already be unicode
         except xml.parsers.expat.ExpatError, e:
             respondWithError(str(e))
             return
@@ -234,6 +235,10 @@
         for keyPath in WRITABLE_CONFIG_KEYS:
             value = getKeyPath(config, keyPath)
             if value is not None:
+                # Note: config contains utf-8 encoded strings, but plistlib
+                # wants unicode, so decode here:
+                if isinstance(value, str):
+                    value = value.decode("utf-8")
                 setKeyPath(result, keyPath, value)
         respond(command, result)
 
@@ -247,6 +252,7 @@
         writable = WritableConfig(config, config.WritableConfigFile)
         writable.read()
         valuesToWrite = command.get("Values", {})
+        # Note: values are unicode if they contain non-ascii
         for keyPath, value in flattenDictionary(valuesToWrite):
             if keyPath in WRITABLE_CONFIG_KEYS:
                 writable.set(setKeyPath(ConfigDict(), keyPath, value))

Modified: CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/test/test_calverify.py
===================================================================
--- CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/test/test_calverify.py	2013-04-16 22:20:28 UTC (rev 11053)
+++ CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/test/test_calverify.py	2013-04-16 22:22:28 UTC (rev 11054)
@@ -19,10 +19,11 @@
 Tests for calendarserver.tools.calverify
 """
 
+from calendarserver.tools.calverify import BadDataService, \
+    SchedulingMismatchService, DoubleBookingService, DarkPurgeService
+
 from StringIO import StringIO
 from calendarserver.tap.util import getRootResource
-from calendarserver.tools.calverify import BadDataService, \
-    SchedulingMismatchService, DoubleBookingService
 from pycalendar.datetime import PyCalendarDateTime
 from twisted.internet import reactor
 from twisted.internet.defer import inlineCallbacks, returnValue
@@ -2525,67 +2526,338 @@
         self.assertEqual(sync_token_oldl1, sync_token_newl1)
 
 
-    def test_instance(self):
-        """
-        CalVerifyService.doScan without fix for mismatches. Make sure it detects
-        as much as it can. Make sure sync-token is not changed.
-        """
 
-        s = """BEGIN:VCALENDAR
+class CalVerifyDarkPurge(CalVerifyMismatchTestsBase):
+    """
+    Tests calverify for events.
+    """
+
+    # No organizer
+    INVITE_NO_ORGANIZER_ICS = """BEGIN:VCALENDAR
 VERSION:2.0
+PRODID:-//Apple Inc.//iCal 4.0.1//EN
 CALSCALE:GREGORIAN
-PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
-BEGIN:VTIMEZONE
-TZID:America/Los_Angeles
-BEGIN:DAYLIGHT
-DTSTART:20070311T020000
-RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
-TZNAME:PDT
-TZOFFSETFROM:-0800
-TZOFFSETTO:-0700
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:20071104T020000
-RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
-TZNAME:PST
-TZOFFSETFROM:-0700
-TZOFFSETTO:-0800
-END:STANDARD
-END:VTIMEZONE
 BEGIN:VEVENT
-UID:4760FF93-C7F8-4EB0-B3E8-0B22A96DB1BC
-DTSTART;TZID=America/Los_Angeles:20130221T170000
-DTEND;TZID=America/Los_Angeles:20130221T180000
-ATTENDEE;CN=Casa Blanca APPLE EMP ONLY (12) DA03 4th;CUTYPE=ROOM;PARTSTAT=
- ACCEPTED;ROLE=REQ-PARTICIPANT:urn:uuid:366CC7BE-FEF7-4FFF-B713-6B883538A24
- 9
-ATTENDEE;CN=Mark Chu;CUTYPE=INDIVIDUAL;EMAIL=markchu at apple.com;PARTSTAT=AC
- CEPTED;ROLE=REQ-PARTICIPANT:urn:uuid:46F9D5D9-08E8-4987-9636-CC796F4093C6
-ATTENDEE;CN=Kristie Phan;CUTYPE=INDIVIDUAL;EMAIL=kristie_phan at apple.com;PA
- RTSTAT=ACCEPTED:urn:uuid:97E8720F-4364-DBEC-6721-123E9A92B980
-CREATED:20130220T200530Z
-DTSTAMP:20130222T002246Z
-EXDATE:20130228T010000Z
-EXDATE:20130314T000000Z
-EXDATE:20130321T000000Z
-EXDATE:20130327T000000Z
-EXDATE:20130328T000000Z
-EXDATE:20130403T000000Z
-LOCATION:Casa Blanca APPLE EMP ONLY (12) DA03 4th
-ORGANIZER;CN=Kristie Phan;EMAIL=kristie_phan at apple.com;SCHEDULE-STATUS=1.2
- :urn:uuid:97E8720F-4364-DBEC-6721-123E9A92B980
-RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;WKST=SU
-SEQUENCE:13
-SUMMARY:ESD Daily Meeting
+CREATED:20100303T181216Z
+UID:INVITE_NO_ORGANIZER_ICS
+TRANSP:OPAQUE
+SUMMARY:INVITE_NO_ORGANIZER_ICS
+DTSTART:%(year)s%(month)02d07T100000Z
+DURATION:PT1H
+DTSTAMP:20100303T181220Z
+SEQUENCE:2
 END:VEVENT
 END:VCALENDAR
-"""
-        from twistedcaldav.ical import Component
-        c = Component.fromString(s)
-        start = PyCalendarDateTime.getToday()
-        start.setDateOnly(False)
-        end = start.duplicate()
-        end.offsetDay(30)
-        config.MaxAllowedInstances = 3000
-        i = c.expandTimeRanges(end, start, ignoreInvalidInstances=True)
-        print(i)
+""".replace("\n", "\r\n") % {"year": nowYear, "month": nowMonth}
+
+    # Valid organizer
+    INVITE_VALID_ORGANIZER_ICS = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//iCal 4.0.1//EN
+CALSCALE:GREGORIAN
+BEGIN:VEVENT
+CREATED:20100303T181216Z
+UID:INVITE_VALID_ORGANIZER_ICS
+TRANSP:OPAQUE
+SUMMARY:INVITE_VALID_ORGANIZER_ICS
+DTSTART:%(year)s%(month)02d08T100000Z
+DURATION:PT1H
+DTSTAMP:20100303T181220Z
+SEQUENCE:2
+ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
+ATTENDEE:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
+ATTENDEE:urn:uuid:75EA36BE-F71B-40F9-81F9-CF59BF40CA8F
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n") % {"year": nowYear, "month": nowMonth}
+
+    # Invalid organizer #1
+    INVITE_INVALID_ORGANIZER_1_ICS = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//iCal 4.0.1//EN
+CALSCALE:GREGORIAN
+BEGIN:VEVENT
+CREATED:20100303T181216Z
+UID:INVITE_INVALID_ORGANIZER_1_ICS
+TRANSP:OPAQUE
+SUMMARY:INVITE_INVALID_ORGANIZER_1_ICS
+DTSTART:%(year)s%(month)02d09T100000Z
+DURATION:PT1H
+DTSTAMP:20100303T181220Z
+SEQUENCE:2
+ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0-1
+ATTENDEE:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0-1
+ATTENDEE:urn:uuid:75EA36BE-F71B-40F9-81F9-CF59BF40CA8F
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n") % {"year": nowYear, "month": nowMonth}
+
+    # Invalid organizer #2
+    INVITE_INVALID_ORGANIZER_2_ICS = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//iCal 4.0.1//EN
+CALSCALE:GREGORIAN
+BEGIN:VEVENT
+CREATED:20100303T181216Z
+UID:INVITE_INVALID_ORGANIZER_2_ICS
+TRANSP:OPAQUE
+SUMMARY:INVITE_INVALID_ORGANIZER_2_ICS
+DTSTART:%(year)s%(month)02d10T100000Z
+DURATION:PT1H
+DTSTAMP:20100303T181220Z
+SEQUENCE:2
+ORGANIZER:mailto:foobar at example.com
+ATTENDEE:mailto:foobar at example.com
+ATTENDEE:urn:uuid:75EA36BE-F71B-40F9-81F9-CF59BF40CA8F
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n") % {"year": nowYear, "month": nowMonth}
+
+    allEvents = {
+        "invite1.ics"      : (INVITE_NO_ORGANIZER_ICS, CalVerifyMismatchTestsBase.metadata,),
+        "invite2.ics"      : (INVITE_VALID_ORGANIZER_ICS, CalVerifyMismatchTestsBase.metadata,),
+        "invite3.ics"      : (INVITE_INVALID_ORGANIZER_1_ICS, CalVerifyMismatchTestsBase.metadata,),
+        "invite4.ics"      : (INVITE_INVALID_ORGANIZER_2_ICS, CalVerifyMismatchTestsBase.metadata,),
+    }
+
+    requirements = {
+        CalVerifyMismatchTestsBase.uuid1 : {
+            "calendar" : {},
+            "inbox" : {},
+        },
+        CalVerifyMismatchTestsBase.uuid2 : {
+            "calendar" : {},
+            "inbox" : {},
+        },
+        CalVerifyMismatchTestsBase.uuid3 : {
+            "calendar" : {},
+            "inbox" : {},
+        },
+        CalVerifyMismatchTestsBase.uuidl1 : {
+            "calendar" : allEvents,
+            "inbox" : {},
+        },
+    }
+
+    @inlineCallbacks
+    def test_scanDarkEvents(self):
+        """
+        CalVerifyService.doScan without fix for dark events. Make sure it detects
+        as much as it can. Make sure sync-token is not changed.
+        """
+
+        sync_token_oldl1 = (yield (yield self.calendarUnderTest(self.uuidl1)).syncToken())
+        self.commit()
+
+        options = {
+            "ical": False,
+            "badcua": False,
+            "mismatch": False,
+            "nobase64": False,
+            "double": True,
+            "dark-purge": False,
+            "fix": False,
+            "verbose": False,
+            "details": False,
+            "summary": False,
+            "days": 365,
+            "uid": "",
+            "uuid": self.uuidl1,
+            "tzid": "utc",
+            "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
+            "no-organizer": False,
+            "invalid-organizer": False,
+            "disabled-organizer": False,
+        }
+        output = StringIO()
+        calverify = DarkPurgeService(self._sqlCalendarStore, options, output, reactor, config)
+        yield calverify.doAction()
+
+        self.assertEqual(calverify.results["Number of events to process"], len(self.requirements[CalVerifyMismatchTestsBase.uuidl1]["calendar"]))
+        self.assertEqual(
+            sorted([i.uid for i in calverify.results["Dark Events"]]),
+            ["INVITE_INVALID_ORGANIZER_1_ICS", "INVITE_INVALID_ORGANIZER_2_ICS", ]
+        )
+        self.assertEqual(calverify.results["Number of dark events"], 2)
+        self.assertTrue("Fix dark events" not in calverify.results)
+        self.assertTrue("Fix remove" not in calverify.results)
+
+        sync_token_newl1 = (yield (yield self.calendarUnderTest(self.uuidl1)).syncToken())
+        self.assertEqual(sync_token_oldl1, sync_token_newl1)
+
+
+    @inlineCallbacks
+    def test_fixDarkEvents(self):
+        """
+        CalVerifyService.doScan with fix for dark events. Make sure it detects
+        as much as it can. Make sure sync-token is changed.
+        """
+
+        sync_token_oldl1 = (yield (yield self.calendarUnderTest(self.uuidl1)).syncToken())
+        self.commit()
+
+        options = {
+            "ical": False,
+            "badcua": False,
+            "mismatch": False,
+            "nobase64": False,
+            "double": True,
+            "dark-purge": False,
+            "fix": True,
+            "verbose": False,
+            "details": False,
+            "summary": False,
+            "days": 365,
+            "uid": "",
+            "uuid": self.uuidl1,
+            "tzid": "utc",
+            "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
+            "no-organizer": False,
+            "invalid-organizer": False,
+            "disabled-organizer": False,
+        }
+        output = StringIO()
+        calverify = DarkPurgeService(self._sqlCalendarStore, options, output, reactor, config)
+        yield calverify.doAction()
+
+        self.assertEqual(calverify.results["Number of events to process"], len(self.requirements[CalVerifyMismatchTestsBase.uuidl1]["calendar"]))
+        self.assertEqual(
+            sorted([i.uid for i in calverify.results["Dark Events"]]),
+            ["INVITE_INVALID_ORGANIZER_1_ICS", "INVITE_INVALID_ORGANIZER_2_ICS", ]
+        )
+        self.assertEqual(calverify.results["Number of dark events"], 2)
+        self.assertEqual(calverify.results["Fix dark events"], 2)
+        self.assertTrue("Fix remove" in calverify.results)
+
+        sync_token_newl1 = (yield (yield self.calendarUnderTest(self.uuidl1)).syncToken())
+        self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
+
+        # Re-scan after changes to make sure there are no errors
+        self.commit()
+        options["fix"] = False
+        options["uuid"] = self.uuidl1
+        calverify = DarkPurgeService(self._sqlCalendarStore, options, output, reactor, config)
+        yield calverify.doAction()
+
+        self.assertEqual(calverify.results["Number of events to process"], 2)
+        self.assertEqual(len(calverify.results["Dark Events"]), 0)
+        self.assertTrue("Fix dark events" not in calverify.results)
+        self.assertTrue("Fix remove" not in calverify.results)
+
+
+    @inlineCallbacks
+    def test_fixDarkEventsNoOrganizerOnly(self):
+        """
+        CalVerifyService.doScan with fix for dark events. Make sure it detects
+        as much as it can. Make sure sync-token is changed.
+        """
+
+        sync_token_oldl1 = (yield (yield self.calendarUnderTest(self.uuidl1)).syncToken())
+        self.commit()
+
+        options = {
+            "ical": False,
+            "badcua": False,
+            "mismatch": False,
+            "nobase64": False,
+            "double": True,
+            "dark-purge": False,
+            "fix": True,
+            "verbose": False,
+            "details": False,
+            "summary": False,
+            "days": 365,
+            "uid": "",
+            "uuid": self.uuidl1,
+            "tzid": "utc",
+            "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
+            "no-organizer": True,
+            "invalid-organizer": False,
+            "disabled-organizer": False,
+        }
+        output = StringIO()
+        calverify = DarkPurgeService(self._sqlCalendarStore, options, output, reactor, config)
+        yield calverify.doAction()
+
+        self.assertEqual(calverify.results["Number of events to process"], len(self.requirements[CalVerifyMismatchTestsBase.uuidl1]["calendar"]))
+        self.assertEqual(
+            sorted([i.uid for i in calverify.results["Dark Events"]]),
+            ["INVITE_NO_ORGANIZER_ICS", ]
+        )
+        self.assertEqual(calverify.results["Number of dark events"], 1)
+        self.assertEqual(calverify.results["Fix dark events"], 1)
+        self.assertTrue("Fix remove" in calverify.results)
+
+        sync_token_newl1 = (yield (yield self.calendarUnderTest(self.uuidl1)).syncToken())
+        self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
+
+        # Re-scan after changes to make sure there are no errors
+        self.commit()
+        options["fix"] = False
+        options["uuid"] = self.uuidl1
+        calverify = DarkPurgeService(self._sqlCalendarStore, options, output, reactor, config)
+        yield calverify.doAction()
+
+        self.assertEqual(calverify.results["Number of events to process"], 3)
+        self.assertEqual(len(calverify.results["Dark Events"]), 0)
+        self.assertTrue("Fix dark events" not in calverify.results)
+        self.assertTrue("Fix remove" not in calverify.results)
+
+
+    @inlineCallbacks
+    def test_fixDarkEventsAllTypes(self):
+        """
+        CalVerifyService.doScan with fix for dark events. Make sure it detects
+        as much as it can. Make sure sync-token is changed.
+        """
+
+        sync_token_oldl1 = (yield (yield self.calendarUnderTest(self.uuidl1)).syncToken())
+        self.commit()
+
+        options = {
+            "ical": False,
+            "badcua": False,
+            "mismatch": False,
+            "nobase64": False,
+            "double": True,
+            "dark-purge": False,
+            "fix": True,
+            "verbose": False,
+            "details": False,
+            "summary": False,
+            "days": 365,
+            "uid": "",
+            "uuid": self.uuidl1,
+            "tzid": "utc",
+            "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
+            "no-organizer": True,
+            "invalid-organizer": True,
+            "disabled-organizer": True,
+        }
+        output = StringIO()
+        calverify = DarkPurgeService(self._sqlCalendarStore, options, output, reactor, config)
+        yield calverify.doAction()
+
+        self.assertEqual(calverify.results["Number of events to process"], len(self.requirements[CalVerifyMismatchTestsBase.uuidl1]["calendar"]))
+        self.assertEqual(
+            sorted([i.uid for i in calverify.results["Dark Events"]]),
+            ["INVITE_INVALID_ORGANIZER_1_ICS", "INVITE_INVALID_ORGANIZER_2_ICS", "INVITE_NO_ORGANIZER_ICS", ]
+        )
+        self.assertEqual(calverify.results["Number of dark events"], 3)
+        self.assertEqual(calverify.results["Fix dark events"], 3)
+        self.assertTrue("Fix remove" in calverify.results)
+
+        sync_token_newl1 = (yield (yield self.calendarUnderTest(self.uuidl1)).syncToken())
+        self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
+
+        # Re-scan after changes to make sure there are no errors
+        self.commit()
+        options["fix"] = False
+        options["uuid"] = self.uuidl1
+        calverify = DarkPurgeService(self._sqlCalendarStore, options, output, reactor, config)
+        yield calverify.doAction()
+
+        self.assertEqual(calverify.results["Number of events to process"], 1)
+        self.assertEqual(len(calverify.results["Dark Events"]), 0)
+        self.assertTrue("Fix dark events" not in calverify.results)
+        self.assertTrue("Fix remove" not in calverify.results)

Modified: CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/test/test_config.py
===================================================================
--- CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/test/test_config.py	2013-04-16 22:20:28 UTC (rev 11053)
+++ CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/test/test_config.py	2013-04-16 22:22:28 UTC (rev 11054)
@@ -119,6 +119,8 @@
         self.assertEquals(results["result"]["EnableSSL"], True)
         self.assertEquals(results["result"]["Notifications"]["Services"]["APNS"]["Enabled"], True)
         self.assertEquals(results["result"]["Notifications"]["Services"]["APNS"]["CalDAV"]["CertificatePath"], "/example/changed.cer")
+        dataRoot = "Data/%s/%s" % (unichr(208), u"\ud83d\udca3")
+        self.assertTrue(results["result"]["DataRoot"].endswith(dataRoot))
 
         # The static plist should still have EnableCalDAV = True
         staticPlist = plistlib.readPlist(self.configFileName)
@@ -204,10 +206,12 @@
             <true/>
             <key>Notifications.Services.APNS.CalDAV.CertificatePath</key>
             <string>/example/changed.cer</string>
+            <key>DataRoot</key>
+            <string>Data/%s/%s</string>
         </dict>
 </dict>
 </plist>
-"""
+""" % (unichr(208), u"\ud83d\udca3")
 
 command_bogusCommand = """<?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

Modified: CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/test/test_gateway.py
===================================================================
--- CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/test/test_gateway.py	2013-04-16 22:20:28 UTC (rev 11053)
+++ CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/test/test_gateway.py	2013-04-16 22:22:28 UTC (rev 11054)
@@ -124,7 +124,8 @@
         self.assertEquals(results["result"]["RecordName"], ["createdlocation01"])
         self.assertEquals(results["result"]["State"], "CA")
         self.assertEquals(results["result"]["Street"], "1 Infinite Loop")
-        self.assertEquals(results["result"]["RealName"], "Created Location 01 %s" % unichr(208))
+        self.assertEquals(results["result"]["RealName"],
+            "Created Location 01 %s %s" % (unichr(208), u"\ud83d\udca3" ))
         self.assertEquals(results["result"]["Comment"], "Test Comment")
         self.assertEquals(results["result"]["AutoSchedule"], True)
         self.assertEquals(results["result"]["AutoAcceptGroup"], "E5A6142C-4189-4E9E-90B0-9CD0268B314B")
@@ -151,7 +152,6 @@
 
         record = directory.recordWithUID("836B1B66-2E9A-4F46-8B1C-3DD6772C20B2")
         self.assertEquals(record, None)
-
         yield self.runCommand(command_createLocation)
 
         directory.flushCaches()
@@ -162,7 +162,8 @@
         augmentService.refresh()
 
         record = directory.recordWithUID("836B1B66-2E9A-4F46-8B1C-3DD6772C20B2")
-        self.assertEquals(record.fullName.decode("utf-8"), "Created Location 01 %s" % unichr(208))
+        self.assertEquals(record.fullName.decode("utf-8"),
+            "Created Location 01 %s %s" % (unichr(208), u"\ud83d\udca3"))
 
         self.assertNotEquals(record, None)
         self.assertEquals(record.autoSchedule, True)
@@ -334,7 +335,7 @@
         <key>GeneratedUID</key>
         <string>836B1B66-2E9A-4F46-8B1C-3DD6772C20B2</string>
         <key>RealName</key>
-        <string>Created Location 01 %s</string>
+        <string>Created Location 01 %s %s</string>
         <key>RecordName</key>
         <array>
                 <string>createdlocation01</string>
@@ -373,7 +374,7 @@
         </array>
 </dict>
 </plist>
-""" % unichr(208)
+""" % (unichr(208), u"\ud83d\udca3")
 
 
 command_createResource = """<?xml version="1.0" encoding="UTF-8"?>

Modified: CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/util.py
===================================================================
--- CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/util.py	2013-04-16 22:20:28 UTC (rev 11053)
+++ CalendarServer/branches/users/glyph/sharedgroups-2/calendarserver/tools/util.py	2013-04-16 22:22:28 UTC (rev 11054)
@@ -76,6 +76,8 @@
 
     return config
 
+
+
 def getDirectory(config=config):
 
     class MyDirectoryService (AggregateDirectoryService):
@@ -93,7 +95,7 @@
                     notifierFactory = None
 
                 # Need a data store
-                _newStore = CommonDataStore(FilePath(config.DocumentRoot), 
+                _newStore = CommonDataStore(FilePath(config.DocumentRoot),
                     notifierFactory, True, False)
                 if notifierFactory is not None:
                     notifierFactory.store = _newStore
@@ -136,6 +138,8 @@
         def principalForCalendarUserAddress(self, cua):
             return self.principalCollection.principalForCalendarUserAddress(cua)
 
+        def principalForUID(self, uid):
+            return self.principalCollection.principalForUID(uid)
 
     # Load augment/proxy db classes now
     if config.AugmentService.type:
@@ -154,7 +158,6 @@
     while not directory.isAvailable():
         sleep(5)
 
-
     directories = [directory]
 
     if config.ResourceService.Enabled:
@@ -189,34 +192,47 @@
 
     return aggregate
 
+
+
 class DummyDirectoryService (DirectoryService):
     realmName = ""
     baseGUID = "51856FD4-5023-4890-94FE-4356C4AAC3E4"
-    def recordTypes(self): return ()
-    def listRecords(self): return ()
-    def recordWithShortName(self): return None
+    def recordTypes(self):
+        return ()
 
+
+    def listRecords(self):
+        return ()
+
+
+    def recordWithShortName(self):
+        return None
+
 dummyDirectoryRecord = DirectoryRecord(
-    service = DummyDirectoryService(),
-    recordType = "dummy",
-    guid = "8EF0892F-7CB6-4B8E-B294-7C5A5321136A",
-    shortNames = ("dummy",),
-    fullName = "Dummy McDummerson",
-    firstName = "Dummy",
-    lastName = "McDummerson",
+    service=DummyDirectoryService(),
+    recordType="dummy",
+    guid="8EF0892F-7CB6-4B8E-B294-7C5A5321136A",
+    shortNames=("dummy",),
+    fullName="Dummy McDummerson",
+    firstName="Dummy",
+    lastName="McDummerson",
 )
 
 class UsageError (StandardError):
     pass
 
+
+
 def booleanArgument(arg):
-    if   arg in ("true",  "yes", "yup",  "uh-huh", "1", "t", "y"):
+    if   arg in ("true", "yes", "yup", "uh-huh", "1", "t", "y"):
         return True
-    elif arg in ("false", "no",  "nope", "nuh-uh", "0", "f", "n"):
+    elif arg in ("false", "no", "nope", "nuh-uh", "0", "f", "n"):
         return False
     else:
         raise ValueError("Not a boolean: %s" % (arg,))
 
+
+
 def autoDisableMemcached(config):
     """
     If memcached is not running, set config.Memcached.ClientEnabled to False
@@ -235,6 +251,7 @@
         config.Memcached.Pools.Default.ClientEnabled = False
 
 
+
 def setupMemcached(config):
     #
     # Connect to memcached
@@ -246,6 +263,7 @@
     autoDisableMemcached(config)
 
 
+
 def checkDirectory(dirpath, description, access=None, create=None, wait=False):
     """
     Make sure dirpath is an existing directory, and optionally ensure it has the
@@ -319,9 +337,9 @@
 
 
 def principalForPrincipalID(principalID, checkOnly=False, directory=None):
-    
+
     # Allow a directory parameter to be passed in, but default to config.directory
-    # But config.directory isn't set right away, so only use it when we're doing more 
+    # But config.directory isn't set right away, so only use it when we're doing more
     # than checking.
     if not checkOnly and not directory:
         directory = config.directory
@@ -339,7 +357,6 @@
 
         return directory.principalCollection.principalForUID(uid)
 
-
     if principalID.startswith("("):
         try:
             i = principalID.index(")")
@@ -348,7 +365,7 @@
                 return None
 
             recordType = principalID[1:i]
-            shortName = principalID[i+1:]
+            shortName = principalID[i + 1:]
 
             if not recordType or not shortName or "(" in recordType:
                 raise ValueError()
@@ -379,9 +396,13 @@
 
     raise ValueError("Invalid principal identifier: %s" % (principalID,))
 
+
+
 def proxySubprincipal(principal, proxyType):
     return principal.getChild("calendar-proxy-" + proxyType)
 
+
+
 @inlineCallbacks
 def action_addProxyPrincipal(rootResource, directory, store, principal, proxyType, proxyPrincipal):
     try:
@@ -394,6 +415,8 @@
     except ProxyWarning, e:
         print(e)
 
+
+
 @inlineCallbacks
 def action_removeProxyPrincipal(rootResource, directory, store, principal, proxyPrincipal, **kwargs):
     try:
@@ -408,6 +431,8 @@
     except ProxyWarning, e:
         print(e)
 
+
+
 @inlineCallbacks
 def addProxy(rootResource, directory, store, principal, proxyType, proxyPrincipal):
     proxyURL = proxyPrincipal.url()
@@ -439,6 +464,8 @@
 
     yield scheduleNextGroupCachingUpdate(store, 0)
 
+
+
 @inlineCallbacks
 def removeProxy(rootResource, directory, store, principal, proxyPrincipal, **kwargs):
     removed = False
@@ -478,16 +505,17 @@
     return "\"%s\" (%s:%s)" % (record.fullName, record.recordType,
         record.shortNames[0])
 
+
+
 class ProxyError(Exception):
     """
     Raised when proxy assignments cannot be performed
     """
 
+
+
 class ProxyWarning(Exception):
     """
     Raised for harmless proxy assignment failures such as trying to add a
     duplicate or remove a non-existent assignment.
     """
-
-
-

Modified: CalendarServer/branches/users/glyph/sharedgroups-2/contrib/performance/loadtest/ical.py
===================================================================
--- CalendarServer/branches/users/glyph/sharedgroups-2/contrib/performance/loadtest/ical.py	2013-04-16 22:20:28 UTC (rev 11053)
+++ CalendarServer/branches/users/glyph/sharedgroups-2/contrib/performance/loadtest/ical.py	2013-04-16 22:22:28 UTC (rev 11054)
@@ -1186,7 +1186,7 @@
         self._pushFactories.append(factory)
         connect(GAIEndpoint(self.reactor, host, port), factory)
 
-    def _receivedPush(self, inboundID):
+    def _receivedPush(self, inboundID, dataChangedTimestamp):
         for href, id in self.ampPushKeys.iteritems():
             if inboundID == id:
                 self._checkCalendarsForEvents(href, push=True)

Copied: CalendarServer/branches/users/glyph/sharedgroups-2/doc/RFC/RFC6868-Parameter Value Encoding.txt (from rev 11020, CalendarServer/trunk/doc/RFC/RFC6868-Parameter Value Encoding.txt)
===================================================================
--- CalendarServer/branches/users/glyph/sharedgroups-2/doc/RFC/RFC6868-Parameter Value Encoding.txt	                        (rev 0)
+++ CalendarServer/branches/users/glyph/sharedgroups-2/doc/RFC/RFC6868-Parameter Value Encoding.txt	2013-04-16 22:22:28 UTC (rev 11054)
@@ -0,0 +1,395 @@
+
+
+
+
+
+
+Internet Engineering Task Force (IETF)                          C. Daboo
+Request for Comments: 6868                                         Apple
+Updates: 5545, 6321, 6350, 6351                            February 2013
+Category: Standards Track
+ISSN: 2070-1721
+
+
+            Parameter Value Encoding in iCalendar and vCard
+
+Abstract
+
+   This specification updates the data formats for iCalendar (RFC 5545)
+   and vCard (RFC 6350) to allow parameter values to include certain
+   characters forbidden by the existing specifications.
+
+Status of This Memo
+
+   This is an Internet Standards Track document.
+
+   This document is a product of the Internet Engineering Task Force
+   (IETF).  It represents the consensus of the IETF community.  It has
+   received public review and has been approved for publication by the
+   Internet Engineering Steering Group (IESG).  Further information on
+   Internet Standards is available in Section 2 of RFC 5741.
+
+   Information about the current status of this document, any errata,
+   and how to provide feedback on it may be obtained at
+   http://www.rfc-editor.org/info/rfc6868.
+
+Copyright Notice
+
+   Copyright (c) 2013 IETF Trust and the persons identified as the
+   document authors.  All rights reserved.
+
+   This document is subject to BCP 78 and the IETF Trust's Legal
+   Provisions Relating to IETF Documents
+   (http://trustee.ietf.org/license-info) in effect on the date of
+   publication of this document.  Please review these documents
+   carefully, as they describe your rights and restrictions with respect
+   to this document.  Code Components extracted from this document must
+   include Simplified BSD License text as described in Section 4.e of
+   the Trust Legal Provisions and are provided without warranty as
+   described in the Simplified BSD License.
+
+
+
+
+
+
+
+
+Daboo                        Standards Track                    [Page 1]
+
+RFC 6868                   Parameter Encoding              February 2013
+
+
+Table of Contents
+
+   1. Introduction ....................................................2
+   2. Conventions Used in This Document ...............................2
+   3. Parameter Value Encoding Scheme .................................3
+      3.1. iCalendar Example ..........................................4
+      3.2. vCard Example ..............................................4
+   4. Security Considerations .........................................4
+   5. Acknowledgments .................................................4
+   6. Normative References ............................................5
+   Appendix A. Choice of Quoting Mechanism ............................6
+
+1.  Introduction
+
+   The iCalendar [RFC5545] specification defines a standard way to
+   describe calendar data.  The vCard [RFC6350] specification defines a
+   standard way to describe contact data.  Both of these use a similar
+   text-based data format.  Each iCalendar and vCard data object can
+   include "properties" that have "parameters" and a "value".  The value
+   of a "parameter" is typically a token or URI value, but a "generic"
+   text value is also allowed.  However, the syntax rules for both
+   iCalendar and vCard prevent the use of a double-quote character or
+   control characters in such values, though double-quote characters and
+   some subset of control characters are allowed in the actual property
+   values.
+
+   As more and more extensions are being developed for these data
+   formats, there is a need to allow at least double-quotes and line
+   feeds to be included in parameter values.  The \-escaping mechanism
+   used for property text values is not defined for use with parameter
+   values and cannot be easily used in a backwards-compatible manner.
+   This specification defines a new character escaping mechanism,
+   compatible with existing parsers and chosen to minimize any impact on
+   existing data.
+
+2.  Conventions Used in This Document
+
+   The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+   "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
+   "OPTIONAL" in this document are to be interpreted as described in
+   [RFC2119].
+
+
+
+
+
+
+
+
+
+
+Daboo                        Standards Track                    [Page 2]
+
+RFC 6868                   Parameter Encoding              February 2013
+
+
+3.  Parameter Value Encoding Scheme
+
+   This specification defines the ^ character (U+005E -- Circumflex
+   Accent) as an escape character in parameter values whose value type
+   is defined using the "param-value" syntax element (Section 3.1 of
+   iCalendar [RFC5545] and Section 3.3 of vCard [RFC6350]).  The
+   ^-escaping mechanism can be used when the value is either unquoted or
+   quoted (i.e., whether or not the value is surrounded by double-
+   quotes).
+
+   When generating iCalendar or vCard parameter values, the following
+   apply:
+
+   o  formatted text line breaks are encoded into ^n (U+005E, U+006E)
+
+   o  the ^ character (U+005E) is encoded into ^^ (U+005E, U+005E)
+
+   o  the " character (U+0022) is encoded into ^' (U+005E, U+0027)
+
+   When parsing iCalendar or vCard parameter values, the following
+   apply:
+
+   o  the character sequence ^n (U+005E, U+006E) is decoded into an
+      appropriate formatted line break according to the type of system
+      being used
+
+   o  the character sequence ^^ (U+005E, U+005E) is decoded into the ^
+      character (U+005E)
+
+   o  the character sequence ^' (U+005E, U+0027) is decoded into the "
+      character (U+0022)
+
+   o  if a ^ (U+005E) character is followed by any character other than
+      the ones above, parsers MUST leave both the ^ and the following
+      character in place
+
+   When converting between iCalendar and vCard text-based data formats
+   and alternative data-format representations such as XML (as described
+   in [RFC6321] and [RFC6351], respectively), implementations MUST
+   ensure that parameter value escape sequences are generated correctly
+   in the text-based format and are decoded when the parameter values
+   appear in the alternate data formats.
+
+
+
+
+
+
+
+
+
+Daboo                        Standards Track                    [Page 3]
+
+RFC 6868                   Parameter Encoding              February 2013
+
+
+3.1.  iCalendar Example
+
+   The following example is an "ATTENDEE" property with a "CN" parameter
+   whose value includes two double-quote characters.  The parameter
+   value is not quoted, as there are no characters in the value that
+   would trigger quoting as required by iCalendar.
+
+   ATTENDEE;CN=George Herman ^'Babe^' Ruth:mailto:babe at example.com
+
+   The unescaped parameter value is
+
+   George Herman "Babe" Ruth
+
+3.2.  vCard Example
+
+   The following example is a "GEO" property with an "X-ADDRESS"
+   parameter whose value includes several line feed characters.  The
+   parameter value is also quoted, since it contains a comma, which
+   triggers quoting as required by vCard.
+
+   GEO;X-ADDRESS="Pittsburgh Pirates^n115 Federal St^nPitt
+    sburgh, PA 15212":geo:40.446816,-80.00566
+
+   The unescaped parameter value (where each line is terminated by a
+   line break character sequence) is
+
+   Pittsburgh Pirates
+   115 Federal St
+   Pittsburgh, PA 15212
+
+4.  Security Considerations
+
+   There are no additional security issues beyond those of iCalendar
+   [RFC5545] and vCard [RFC6350].
+
+5.  Acknowledgments
+
+   Thanks to Michael Angstadt, Tim Bray, Mike Douglass, Barry Leiba,
+   Simon Perreault, and Pete Resnick for feedback on this specification.
+
+
+
+
+
+
+
+
+
+
+
+
+Daboo                        Standards Track                    [Page 4]
+
+RFC 6868                   Parameter Encoding              February 2013
+
+
+6.  Normative References
+
+   [RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
+              Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+   [RFC5545]  Desruisseaux, B., "Internet Calendaring and Scheduling
+              Core Object Specification (iCalendar)", RFC 5545,
+              September 2009.
+
+   [RFC6321]  Daboo, C., Douglass, M., and S. Lees, "xCal: The XML
+              Format for iCalendar", RFC 6321, August 2011.
+
+   [RFC6350]  Perreault, S., "vCard Format Specification", RFC 6350,
+              August 2011.
+
+   [RFC6351]  Perreault, S., "xCard: vCard XML Representation",
+              RFC 6351, August 2011.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Daboo                        Standards Track                    [Page 5]
+
+RFC 6868                   Parameter Encoding              February 2013
+
+
+Appendix A.  Choice of Quoting Mechanism
+
+   Having recognized the need for escaping parameter values, the
+   question is what mechanism to use?  One obvious choice would be to
+   adopt the \-escaping used for property values.  However, that could
+   not be used as-is, because it escapes a double-quote as the sequence
+   of \ followed by double-quote.  Consider what the example in
+   Section 3.1 might look like using \-escaping:
+
+   ATTENDEE;CN="George Herman \"Babe\" Ruth":mailto:babe at example.com
+
+   Existing iCalendar/vCard parsers know nothing about escape sequences
+   in parameters.  So they would parse the parameter value as:
+
+   George Herman \
+
+   i.e., the text between the first and second occurrence of a double-
+   quote.  However, the text after the second double-quote ought to be
+   either a : or a ; (to delimit the parameter value from the following
+   parameter or property) but is not, so the parser could legitimately
+   throw an error at that point because the data is syntactically
+   invalid.  Thus, for backwards-compatibility reasons, a double-quote
+   cannot be escaped using a sequence that itself includes a double-
+   quote, and hence the choice of using a single-quote in this
+   specification.
+
+   Another option would be to use a form of \-escaping modified for use
+   in parameter values only.  However, some incorrect, non-interoperable
+   use of \ in parameter values has been observed, and thus it is best
+   to steer clear of that to achieve guaranteed, reliable
+   interoperability.  Also, given that double-quote gets changed to
+   single-quote in the escape sequence for a parameter, but not for a
+   value, it is better to not give the impression that the same escape
+   mechanism (and thus code) can be used for both (which could lead to
+   other issues, such as an implementation incorrectly escaping a ; as
+   \; as opposed to quoting the parameter value).
+
+   The choice of ^ as the escape character was made based on the
+   requirement that an ASCII symbol (non-alphanumeric character) be
+   used, and it ought to be one least likely to be found in existing
+   data.
+
+
+
+
+
+
+
+
+
+
+Daboo                        Standards Track                    [Page 6]
+
+RFC 6868                   Parameter Encoding              February 2013
+
+
+Author's Address
+
+   Cyrus Daboo
+   Apple Inc.
+   1 Infinite Loop
+   Cupertino, CA  95014
+   USA
+
+   EMail: cyrus at daboo.name
+   URI:   http://www.apple.com/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Daboo                        Standards Track                    [Page 7]
+
\ No newline at end of file

Copied: CalendarServer/branches/users/glyph/sharedgroups-2/support/Apple.make (from rev 11020, CalendarServer/trunk/support/Apple.make)
===================================================================
--- CalendarServer/branches/users/glyph/sharedgroups-2/support/Apple.make	                        (rev 0)
+++ CalendarServer/branches/users/glyph/sharedgroups-2/support/Apple.make	2013-04-16 22:22:28 UTC (rev 11054)
@@ -0,0 +1,170 @@
+# -*- mode: Makefile; -*-
+##
+# B&I Makefile for CalendarServer
+#
+# This is only useful internally at Apple, probably.
+##
+# Copyright (c) 2005-2013 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+# Project info
+Project	    = CalendarServer
+ProjectName = CalendarServer
+UserType    = Server
+ToolType    = Applications
+
+# Include common makefile targets for B&I
+include $(MAKEFILEPATH)/CoreOS/ReleaseControl/Common.make
+include /AppleInternal/ServerTools/ServerBuildVariables.xcconfig
+
+SIPP = $(SERVER_INSTALL_PATH_PREFIX)
+SERVERSETUP = $(SIPP)$(NSSYSTEMDIR)$(NSLIBRARYSUBDIR)/ServerSetup
+
+Cruft += .dependencies
+Extra_Environment += PATH="$(SIPP)/usr/bin:$$PATH"
+
+CALDAVDSUBDIR = /caldavd
+
+PYTHON = $(USRBINDIR)/python
+PY_HOME = $(SIPP)$(SHAREDIR)$(CALDAVDSUBDIR)
+PY_INSTALL_FLAGS = --root="$(DSTROOT)" --prefix="$(SIPP)" --install-lib="$(PY_HOME)/lib/python" --install-scripts="$(SIPP)$(LIBEXECDIR)$(CALDAVDSUBDIR)"
+CS_INSTALL_FLAGS = --install-scripts="$(SIPP)$(USRSBINDIR)" --install-data="$(SIPP)$(ETCDIR)"
+CS_BUILD_EXT_FLAGS = --include-dirs="$(SIPP)/usr/include" --library-dirs="$(SIPP)/usr/lib"
+
+CS_USER  = _calendar
+CS_GROUP = _calendar
+
+#
+# Build
+#
+
+.phony: $(Project) pycalendar build setup prep install install-ossfiles buildit
+
+CalDAVTester::          $(BuildDirectory)/CalDAVTester
+PyKerberos::            $(BuildDirectory)/PyKerberos
+pycalendar::            $(BuildDirectory)/pycalendar
+PyGreSQL-4.0::          $(BuildDirectory)/PyGreSQL-4.0
+sqlparse-0.1.2::        $(BuildDirectory)/sqlparse-0.1.2
+setproctitle-1.1.6::	$(BuildDirectory)/setproctitle-1.1.6
+psutil-0.6.1::		$(BuildDirectory)/psutil-0.6.1
+pycrypto-2.5::		$(BuildDirectory)/pycrypto-2.5
+$(Project)::            $(BuildDirectory)/$(Project)
+
+build:: PyKerberos pycalendar PyGreSQL-4.0 sqlparse-0.1.2 setproctitle-1.1.6 psutil-0.6.1 pycrypto-2.5 $(Project)
+
+setup:
+	$(_v) ./run -g
+
+prep:: setup CalDAVTester.tgz PyKerberos.tgz pycalendar.tgz PyGreSQL-4.0.tgz sqlparse-0.1.2.tgz setproctitle-1.1.6.tgz psutil-0.6.1.tgz pycrypto-2.5.tgz
+
+PyKerberos pycalendar PyGreSQL-4.0 sqlparse-0.1.2 setproctitle-1.1.6 psutil-0.6.1 pycrypto-2.5 $(Project)::
+	@echo "Building $@..."
+	$(_v) cd $(BuildDirectory)/$@ && $(Environment) $(PYTHON) setup.py build
+
+install:: build
+	$(_v) cd $(BuildDirectory)/$(Project)         && $(Environment) $(PYTHON) setup.py build_ext $(CS_BUILD_EXT_FLAGS) install $(PY_INSTALL_FLAGS) $(CS_INSTALL_FLAGS)
+	$(_v) cd $(BuildDirectory)/PyKerberos         && $(Environment) $(PYTHON) setup.py install $(PY_INSTALL_FLAGS)
+	$(_v) cd $(BuildDirectory)/pycalendar         && $(Environment) $(PYTHON) setup.py install $(PY_INSTALL_FLAGS)
+	$(_v) cd $(BuildDirectory)/PyGreSQL-4.0       && $(Environment) $(PYTHON) setup.py install $(PY_INSTALL_FLAGS)
+	$(_v) cd $(BuildDirectory)/sqlparse-0.1.2     && $(Environment) $(PYTHON) setup.py install $(PY_INSTALL_FLAGS)
+	$(_v) cd $(BuildDirectory)/setproctitle-1.1.6 && $(Environment) $(PYTHON) setup.py install $(PY_INSTALL_FLAGS)
+	$(_v) cd $(BuildDirectory)/psutil-0.6.1       && $(Environment) $(PYTHON) setup.py install $(PY_INSTALL_FLAGS)
+	$(_v) cd $(BuildDirectory)/pycrypto-2.5       && $(Environment) $(PYTHON) setup.py install $(PY_INSTALL_FLAGS)
+	$(_v) for so in $$(find "$(DSTROOT)$(PY_HOME)/lib" -type f -name '*.so'); do $(STRIP) -Sx "$${so}"; done 
+	$(_v) $(INSTALL_DIRECTORY) "$(DSTROOT)$(SIPP)$(ETCDIR)$(CALDAVDSUBDIR)"
+	$(_v) $(INSTALL_FILE) "$(Sources)/conf/caldavd-apple.plist" "$(DSTROOT)$(SIPP)$(ETCDIR)$(CALDAVDSUBDIR)/caldavd-apple.plist"
+	$(_v) chmod -R ugo+r "$(DSTROOT)$(PY_HOME)"
+	$(_v) for f in $$(find "$(DSTROOT)$(SIPP)$(ETCDIR)" -type f ! -name '*.default'); do cp "$${f}" "$${f}.default"; done
+
+install::
+	@echo "Installing manual pages..."
+	$(_v) $(INSTALL_DIRECTORY) "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
+	$(_v) $(INSTALL_FILE) "$(Sources)/doc/caldavd.8"                              "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
+	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_bootstrap_database.8"    "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
+	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_command_gateway.8"       "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
+	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_export.8"                "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
+	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_manage_principals.8"     "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
+	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_migrate_resources.8"     "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
+	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_purge_attachments.8"     "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
+	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_purge_events.8"          "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
+	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_purge_principals.8"      "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
+	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_manage_push.8"           "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
+	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_shell.8"                 "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
+	$(_v) gzip -9 -f "$(DSTROOT)$(SIPP)$(MANDIR)/man8/"*.[0-9]
+	@echo "Installing launchd config..."
+	$(_v) $(INSTALL_DIRECTORY) "$(DSTROOT)$(NSLOCALDIR)/$(NSLIBRARYSUBDIR)/Server/Calendar and Contacts"
+	$(_v) $(INSTALL_DIRECTORY) -o "$(CS_USER)" -g "$(CS_GROUP)" -m 0755 "$(DSTROOT)$(VARDIR)/log$(CALDAVDSUBDIR)"
+	$(_v) $(INSTALL_DIRECTORY) "$(DSTROOT)$(SIPP)$(NSLIBRARYDIR)/LaunchDaemons"
+	$(_v) $(INSTALL_FILE) "$(Sources)/contrib/launchd/calendarserver.plist" "$(DSTROOT)$(SIPP)$(NSLIBRARYDIR)/LaunchDaemons/org.calendarserver.calendarserver.plist"
+	@echo "Installing changeip script..."
+	$(_v) $(INSTALL_DIRECTORY) "$(DSTROOT)$(SIPP)$(LIBEXECDIR)/changeip"
+	$(_v) $(INSTALL_FILE) "$(Sources)/calendarserver/tools/changeip_calendar.py" "$(DSTROOT)$(SIPP)$(LIBEXECDIR)/changeip/changeip_calendar.py"
+	$(_v) chmod ugo+x "$(DSTROOT)$(SIPP)$(LIBEXECDIR)/changeip/changeip_calendar.py"
+
+install::
+	@echo "Installing CalDAVTester package..."
+	$(_v) $(INSTALL_DIRECTORY) "$(DSTROOT)/AppleInternal/ServerTools"
+	$(_v) $(INSTALL_FILE) "$(Sources)/CalDAVTester.tgz" "$(DSTROOT)/AppleInternal/ServerTools/CalDAVTester.tgz"
+
+#
+# Automatic Extract
+#
+
+$(BuildDirectory)/$(Project):
+	@echo "Copying source for $(Project)..."
+	$(_v) $(MKDIR) -p "$@"
+	$(_v) pax -rw bin conf Makefile lib-patches setup.py calendarserver twistedcaldav twext txdav twisted support "$@/"
+
+$(BuildDirectory)/%: %.tgz
+	@echo "Extracting source for $(notdir $<)..."
+	$(_v) $(MKDIR) -p "$(BuildDirectory)"
+	$(_v) $(RMDIR) "$@"
+	$(_v) $(TAR) -C "$(BuildDirectory)" -xzf "$<"
+
+%.tgz: ../%
+	@echo "Archiving sources for $(notdir $<)..."
+	$(_v) if [ -f "$</setup.py" ] && grep setuptools "$</setup.py" > /dev/null; then \
+	        echo "Working around setuptools' stupid need to download a new version."; \
+	        cd "$<" && $(PYTHON) "setup.py" --help >/dev/null; \
+	      fi
+	$(_v) $(TAR) -C "$(dir $<)"        \
+	          --exclude=.svn           \
+	          --exclude=build          \
+	          --exclude=_trial_temp    \
+	          --exclude=dropin.cache   \
+	          -czf "$@" "$(notdir $<)"
+
+#
+# Open Source Hooey
+#
+
+OSV = $(USRDIR)/local/OpenSourceVersions
+OSL = $(USRDIR)/local/OpenSourceLicenses
+
+#install:: install-ossfiles
+
+install-ossfiles::
+	$(_v) $(INSTALL_DIRECTORY) $(DSTROOT)/$(OSV)
+	$(_v) $(INSTALL_FILE) $(Sources)/$(ProjectName).plist $(DSTROOT)/$(OSV)/$(ProjectName).plist
+	$(_v) $(INSTALL_DIRECTORY) $(DSTROOT)/$(OSL)
+	$(_v) $(INSTALL_FILE) $(BuildDirectory)/$(Project)/LICENSE $(DSTROOT)/$(OSL)/$(ProjectName).txt
+
+#
+# B&I Hooey
+#
+
+buildit: prep
+	@echo "Running buildit..."
+	$(_v) sudo ~rc/bin/buildit $(CC_Archs) $(Sources)

Deleted: CalendarServer/branches/users/glyph/sharedgroups-2/support/CalendarServer.tmproj
===================================================================
--- CalendarServer/branches/users/glyph/sharedgroups-2/support/CalendarServer.tmproj	2013-04-16 22:20:28 UTC (rev 11053)
+++ CalendarServer/branches/users/glyph/sharedgroups-2/support/CalendarServer.tmproj	2013-04-16 22:22:28 UTC (rev 11054)
@@ -1,122 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>documents</key>
-	<array>
-		<dict>
-			<key>expanded</key>
-			<true/>
-			<key>name</key>
-			<string>twistedcaldav</string>
-			<key>regexFolderFilter</key>
-			<string>!.*/(\.[^/]*)$</string>
-			<key>sourceDirectory</key>
-			<string>../twistedcaldav</string>
-		</dict>
-		<dict>
-			<key>name</key>
-			<string>web2</string>
-			<key>regexFolderFilter</key>
-			<string>!.*/(\.[^/]*)$</string>
-			<key>sourceDirectory</key>
-			<string>../../Twisted/twisted/web2</string>
-		</dict>
-		<dict>
-			<key>name</key>
-			<string>twisted</string>
-			<key>regexFolderFilter</key>
-			<string>!.*/(\.[^/]*)$</string>
-			<key>sourceDirectory</key>
-			<string>../../Twisted/twisted</string>
-		</dict>
-		<dict>
-			<key>name</key>
-			<string>PyOpenDirectory</string>
-			<key>regexFolderFilter</key>
-			<string>!.*/(\.[^/]*)$</string>
-			<key>sourceDirectory</key>
-			<string>../../PyOpenDirectory</string>
-		</dict>
-		<dict>
-			<key>name</key>
-			<string>PyKerberos</string>
-			<key>regexFolderFilter</key>
-			<string>!.*/(\.[^/]*)$</string>
-			<key>sourceDirectory</key>
-			<string>../../PyKerberos</string>
-		</dict>
-		<dict>
-			<key>name</key>
-			<string>conf</string>
-			<key>regexFolderFilter</key>
-			<string>!.*/(\.[^/]*)$</string>
-			<key>sourceDirectory</key>
-			<string>../conf</string>
-		</dict>
-		<dict>
-			<key>name</key>
-			<string>bin</string>
-			<key>regexFolderFilter</key>
-			<string>!.*/(\.[^/]*)$</string>
-			<key>sourceDirectory</key>
-			<string>../bin</string>
-		</dict>
-		<dict>
-			<key>name</key>
-			<string>doc</string>
-			<key>regexFolderFilter</key>
-			<string>!.*/(\.[^/]*)$</string>
-			<key>sourceDirectory</key>
-			<string>../doc</string>
-		</dict>
-		<dict>
-			<key>name</key>
-			<string>support</string>
-			<key>regexFolderFilter</key>
-			<string>!.*/(\.[^/]*|.*\.xcodeproj)$</string>
-			<key>sourceDirectory</key>
-			<string></string>
-		</dict>
-		<dict>
-			<key>children</key>
-			<array>
-				<dict>
-					<key>filename</key>
-					<string>../run</string>
-				</dict>
-				<dict>
-					<key>filename</key>
-					<string>../test</string>
-				</dict>
-				<dict>
-					<key>filename</key>
-					<string>../testcaldav</string>
-				</dict>
-				<dict>
-					<key>filename</key>
-					<string>../setup.py</string>
-				</dict>
-				<dict>
-					<key>filename</key>
-					<string>../LICENSE</string>
-				</dict>
-				<dict>
-					<key>filename</key>
-					<string>../README</string>
-				</dict>
-			</array>
-			<key>name</key>
-			<string>topfiles</string>
-		</dict>
-	</array>
-	<key>fileHierarchyDrawerWidth</key>
-	<integer>249</integer>
-	<key>metaData</key>
-	<dict/>
-	<key>showFileHierarchyDrawer</key>
-	<true/>
-	<key>windowFrame</key>
-	<string>{{414, 114}, {987, 1030}}</string>
-</dict>
-</plist>


Property changes on: CalendarServer/branches/users/glyph/sharedgroups-2/support/CalendarServer.xcodeproj
___________________________________________________________________
Added: svn:ignore
   + xcuserdata


Deleted: CalendarServer/branches/users/glyph/sharedgroups-2/support/Makefile.Apple
===================================================================
--- CalendarServer/branches/users/glyph/sharedgroups-2/support/Makefile.Apple	2013-04-16 22:20:28 UTC (rev 11053)
+++ CalendarServer/branches/users/glyph/sharedgroups-2/support/Makefile.Apple	2013-04-16 22:22:28 UTC (rev 11054)
@@ -1,170 +0,0 @@
-# -*- mode: Makefile; -*-
-##
-# B&I Makefile for CalendarServer
-#
-# This is only useful internally at Apple, probably.
-##
-# Copyright (c) 2005-2013 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-
-# Project info
-Project	    = CalendarServer
-ProjectName = CalendarServer
-UserType    = Server
-ToolType    = Applications
-
-# Include common makefile targets for B&I
-include $(MAKEFILEPATH)/CoreOS/ReleaseControl/Common.make
-include /AppleInternal/ServerTools/ServerBuildVariables.xcconfig
-
-SIPP = $(SERVER_INSTALL_PATH_PREFIX)
-SERVERSETUP = $(SIPP)$(NSSYSTEMDIR)$(NSLIBRARYSUBDIR)/ServerSetup
-
-Cruft += .dependencies
-Extra_Environment += PATH="$(SIPP)/usr/bin:$$PATH"
-
-CALDAVDSUBDIR = /caldavd
-
-PYTHON = $(USRBINDIR)/python
-PY_HOME = $(SIPP)$(SHAREDIR)$(CALDAVDSUBDIR)
-PY_INSTALL_FLAGS = --root="$(DSTROOT)" --prefix="$(SIPP)" --install-lib="$(PY_HOME)/lib/python" --install-scripts="$(SIPP)$(LIBEXECDIR)$(CALDAVDSUBDIR)"
-CS_INSTALL_FLAGS = --install-scripts="$(SIPP)$(USRSBINDIR)" --install-data="$(SIPP)$(ETCDIR)"
-CS_BUILD_EXT_FLAGS = --include-dirs="$(SIPP)/usr/include" --library-dirs="$(SIPP)/usr/lib"
-
-CS_USER  = _calendar
-CS_GROUP = _calendar
-
-#
-# Build
-#
-
-.phony: $(Project) pycalendar build setup prep install install-ossfiles buildit
-
-CalDAVTester::          $(BuildDirectory)/CalDAVTester
-PyKerberos::            $(BuildDirectory)/PyKerberos
-pycalendar::            $(BuildDirectory)/pycalendar
-PyGreSQL-4.0::          $(BuildDirectory)/PyGreSQL-4.0
-sqlparse-0.1.2::        $(BuildDirectory)/sqlparse-0.1.2
-setproctitle-1.1.6::	$(BuildDirectory)/setproctitle-1.1.6
-psutil-0.6.1::		$(BuildDirectory)/psutil-0.6.1
-pycrypto-2.5::		$(BuildDirectory)/pycrypto-2.5
-$(Project)::            $(BuildDirectory)/$(Project)
-
-build:: PyKerberos pycalendar PyGreSQL-4.0 sqlparse-0.1.2 setproctitle-1.1.6 psutil-0.6.1 pycrypto-2.5 $(Project)
-
-setup:
-	$(_v) ./run -g
-
-prep:: setup CalDAVTester.tgz PyKerberos.tgz pycalendar.tgz PyGreSQL-4.0.tgz sqlparse-0.1.2.tgz setproctitle-1.1.6.tgz psutil-0.6.1.tgz pycrypto-2.5.tgz
-
-PyKerberos pycalendar PyGreSQL-4.0 sqlparse-0.1.2 setproctitle-1.1.6 psutil-0.6.1 pycrypto-2.5 $(Project)::
-	@echo "Building $@..."
-	$(_v) cd $(BuildDirectory)/$@ && $(Environment) $(PYTHON) setup.py build
-
-install:: build
-	$(_v) cd $(BuildDirectory)/$(Project)         && $(Environment) $(PYTHON) setup.py build_ext $(CS_BUILD_EXT_FLAGS) install $(PY_INSTALL_FLAGS) $(CS_INSTALL_FLAGS)
-	$(_v) cd $(BuildDirectory)/PyKerberos         && $(Environment) $(PYTHON) setup.py install $(PY_INSTALL_FLAGS)
-	$(_v) cd $(BuildDirectory)/pycalendar         && $(Environment) $(PYTHON) setup.py install $(PY_INSTALL_FLAGS)
-	$(_v) cd $(BuildDirectory)/PyGreSQL-4.0       && $(Environment) $(PYTHON) setup.py install $(PY_INSTALL_FLAGS)
-	$(_v) cd $(BuildDirectory)/sqlparse-0.1.2     && $(Environment) $(PYTHON) setup.py install $(PY_INSTALL_FLAGS)
-	$(_v) cd $(BuildDirectory)/setproctitle-1.1.6 && $(Environment) $(PYTHON) setup.py install $(PY_INSTALL_FLAGS)
-	$(_v) cd $(BuildDirectory)/psutil-0.6.1       && $(Environment) $(PYTHON) setup.py install $(PY_INSTALL_FLAGS)
-	$(_v) cd $(BuildDirectory)/pycrypto-2.5       && $(Environment) $(PYTHON) setup.py install $(PY_INSTALL_FLAGS)
-	$(_v) for so in $$(find "$(DSTROOT)$(PY_HOME)/lib" -type f -name '*.so'); do $(STRIP) -Sx "$${so}"; done 
-	$(_v) $(INSTALL_DIRECTORY) "$(DSTROOT)$(SIPP)$(ETCDIR)$(CALDAVDSUBDIR)"
-	$(_v) $(INSTALL_FILE) "$(Sources)/conf/caldavd-apple.plist" "$(DSTROOT)$(SIPP)$(ETCDIR)$(CALDAVDSUBDIR)/caldavd-apple.plist"
-	$(_v) chmod -R ugo+r "$(DSTROOT)$(PY_HOME)"
-	$(_v) for f in $$(find "$(DSTROOT)$(SIPP)$(ETCDIR)" -type f ! -name '*.default'); do cp "$${f}" "$${f}.default"; done
-
-install::
-	@echo "Installing manual pages..."
-	$(_v) $(INSTALL_DIRECTORY) "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
-	$(_v) $(INSTALL_FILE) "$(Sources)/doc/caldavd.8"                              "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
-	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_bootstrap_database.8"    "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
-	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_command_gateway.8"       "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
-	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_export.8"                "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
-	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_manage_principals.8"     "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
-	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_migrate_resources.8"     "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
-	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_purge_attachments.8"     "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
-	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_purge_events.8"          "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
-	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_purge_principals.8"      "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
-	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_manage_push.8"           "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
-	$(_v) $(INSTALL_FILE) "$(Sources)/doc/calendarserver_shell.8"                 "$(DSTROOT)$(SIPP)$(MANDIR)/man8"
-	$(_v) gzip -9 -f "$(DSTROOT)$(SIPP)$(MANDIR)/man8/"*.[0-9]
-	@echo "Installing launchd config..."
-	$(_v) $(INSTALL_DIRECTORY) "$(DSTROOT)$(NSLOCALDIR)/$(NSLIBRARYSUBDIR)/Server/Calendar and Contacts"
-	$(_v) $(INSTALL_DIRECTORY) -o "$(CS_USER)" -g "$(CS_GROUP)" -m 0755 "$(DSTROOT)$(VARDIR)/log$(CALDAVDSUBDIR)"
-	$(_v) $(INSTALL_DIRECTORY) "$(DSTROOT)$(SIPP)$(NSLIBRARYDIR)/LaunchDaemons"
-	$(_v) $(INSTALL_FILE) "$(Sources)/contrib/launchd/calendarserver.plist" "$(DSTROOT)$(SIPP)$(NSLIBRARYDIR)/LaunchDaemons/org.calendarserver.calendarserver.plist"
-	@echo "Installing changeip script..."
-	$(_v) $(INSTALL_DIRECTORY) "$(DSTROOT)$(SIPP)$(LIBEXECDIR)/changeip"
-	$(_v) $(INSTALL_FILE) "$(Sources)/calendarserver/tools/changeip_calendar.py" "$(DSTROOT)$(SIPP)$(LIBEXECDIR)/changeip/changeip_calendar.py"
-	$(_v) chmod ugo+x "$(DSTROOT)$(SIPP)$(LIBEXECDIR)/changeip/changeip_calendar.py"
-
-install::
-	@echo "Installing CalDAVTester package..."
-	$(_v) $(INSTALL_DIRECTORY) "$(DSTROOT)/AppleInternal/ServerTools"
-	$(_v) $(INSTALL_FILE) "$(Sources)/CalDAVTester.tgz" "$(DSTROOT)/AppleInternal/ServerTools/CalDAVTester.tgz"
-
-#
-# Automatic Extract
-#
-
-$(BuildDirectory)/$(Project):
-	@echo "Copying source for $(Project)..."
-	$(_v) $(MKDIR) -p "$@"
-	$(_v) pax -rw bin conf Makefile lib-patches setup.py calendarserver twistedcaldav twext txdav twisted support "$@/"
-
-$(BuildDirectory)/%: %.tgz
-	@echo "Extracting source for $(notdir $<)..."
-	$(_v) $(MKDIR) -p "$(BuildDirectory)"
-	$(_v) $(RMDIR) "$@"
-	$(_v) $(TAR) -C "$(BuildDirectory)" -xzf "$<"
-
-%.tgz: ../%
-	@echo "Archiving sources for $(notdir $<)..."
-	$(_v) if [ -f "$</setup.py" ] && grep setuptools "$</setup.py" > /dev/null; then \
-	        echo "Working around setuptools' stupid need to download a new version."; \
-	        cd "$<" && $(PYTHON) "setup.py" --help >/dev/null; \
-	      fi
-	$(_v) $(TAR) -C "$(dir $<)"        \
-	          --exclude=.svn           \
-	          --exclude=build          \
-	          --exclude=_trial_temp    \
-	          --exclude=dropin.cache   \
-	          -czf "$@" "$(notdir $<)"
-
-#
-# Open Source Hooey
-#
-
-OSV = $(USRDIR)/local/OpenSourceVersions
-OSL = $(USRDIR)/local/OpenSourceLicenses
-
-#install:: install-ossfiles
-
-install-ossfiles::
-	$(_v) $(INSTALL_DIRECTORY) $(DSTROOT)/$(OSV)
-	$(_v) $(INSTALL_FILE) $(Sources)/$(ProjectName).plist $(DSTROOT)/$(OSV)/$(ProjectName).plist
-	$(_v) $(INSTALL_DIRECTORY) $(DSTROOT)/$(OSL)
-	$(_v) $(INSTALL_FILE) $(BuildDirectory)/$(Project)/LICENSE $(DSTROOT)/$(OSL)/$(ProjectName).txt
-
-#
-# B&I Hooey
-#
-
-buildit: prep
-	@echo "Running buildit..."
-	$(_v) sudo ~rc/bin/buildit $(CC_Archs) $(Sources)

Copied: CalendarServer/branches/users/glyph/sharedgroups-2/support/XCode.make (from rev 11020, CalendarServer/trunk/support/XCode.make)
===================================================================
--- CalendarServer/branches/users/glyph/sharedgroups-2/support/XCode.make	                        (rev 0)
+++ CalendarServer/branches/users/glyph/sharedgroups-2/support/XCode.make	2013-04-16 22:22:28 UTC (rev 11054)
@@ -0,0 +1,34 @@
+# -*- mode: Makefile; -*-
+##
+# XCode Makefile for CalendarServer
+##
+# Copyright (c) 2005-2013 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+default: build
+
+#
+# Run sed to suppress XCode's misguided attempts to interpret our output.
+#
+build::
+	../run -s 2>&1 \
+	  | sed \
+	    -e 's|error|oops|' \
+	    -e 's|warning|oopsie|' \
+	    -e 's|^\(..*\):\([0-9][0-9]*\):\([0-9][0-9]*\): |\1-\2-\3: |';
+
+clean::
+	rm -rf ../.dependencies/ ../build/ ../data/ ../calendarserver/version.py;
+	find .. -name '*.pyc' -o -name '*.so' -o -name dropin.cache -print0 | xargs -0 rm -f;

Modified: CalendarServer/branches/users/glyph/sharedgroups-2/support/build.sh
===================================================================
--- CalendarServer/branches/users/glyph/sharedgroups-2/support/build.sh	2013-04-16 22:20:28 UTC (rev 11053)
+++ CalendarServer/branches/users/glyph/sharedgroups-2/support/build.sh	2013-04-16 22:22:28 UTC (rev 11054)
@@ -775,7 +775,7 @@
     "${pypi}/p/python-ldap/${ld}.tar.gz";
 
   # XXX actually PyCalendar should be imported in-place.
-  py_dependency -fe -i "src" -r 10988 \
+  py_dependency -fe -i "src" -r 11005 \
     "pycalendar" "pycalendar" "pycalendar" \
     "${svn_uri_base}/PyCalendar/trunk";
 

Modified: CalendarServer/branches/users/glyph/sharedgroups-2/support/submit
===================================================================
--- CalendarServer/branches/users/glyph/sharedgroups-2/support/submit	2013-04-16 22:20:28 UTC (rev 11053)
+++ CalendarServer/branches/users/glyph/sharedgroups-2/support/submit	2013-04-16 22:22:28 UTC (rev 11054)
@@ -144,7 +144,7 @@
 
 echo ""
 echo "Tweaking for B&I...";
-ln -s support/Makefile.Apple "${wc}/Makefile";
+ln -s support/Apple.make "${wc}/Makefile";
 
 version_file="${wc}/SubmissionInfo.xml";
 cat - >> "${version_file}" <<EOF

Modified: CalendarServer/branches/users/glyph/sharedgroups-2/twext/enterprise/queue.py
===================================================================
--- CalendarServer/branches/users/glyph/sharedgroups-2/twext/enterprise/queue.py	2013-04-16 22:20:28 UTC (rev 11053)
+++ CalendarServer/branches/users/glyph/sharedgroups-2/twext/enterprise/queue.py	2013-04-16 22:22:28 UTC (rev 11054)
@@ -635,7 +635,7 @@
         """
         The total load of all currently connected workers.
         """
-        return sum(worker.currentLoad() for worker in self.workers)
+        return sum(worker.currentLoad for worker in self.workers)
 
 
     def _selectLowestLoadWorker(self):
@@ -646,7 +646,7 @@
         @return: a worker connection with the lowest current load.
         @rtype: L{ConnectionFromWorker}
         """
-        return sorted(self.workers[:], key=lambda w: w.currentLoad())[0]
+        return sorted(self.workers[:], key=lambda w: w.currentLoad)[0]
 
 
     def performWork(self, table, workID):

Modified: CalendarServer/branches/users/glyph/sharedgroups-2/txdav/common/datastore/file.py
===================================================================
--- CalendarServer/branches/users/glyph/sharedgroups-2/txdav/common/datastore/file.py	2013-04-16 22:20:28 UTC (rev 11053)
+++ CalendarServer/branches/users/glyph/sharedgroups-2/txdav/common/datastore/file.py	2013-04-16 22:22:28 UTC (rev 11054)
@@ -77,6 +77,14 @@
 )
 UIDPATH = "__uids__"
 
+
+
+class _StubQueuer(object):
+    def transferProposalCallbacks(self, otherQueuer):
+        return otherQueuer
+
+
+
 class CommonDataStore(DataStore):
     """
     Shared logic for SQL-based data stores, between calendar and addressbook
@@ -87,7 +95,6 @@
 
     @ivar quota: the amount of space granted to each calendar home (in bytes)
         for storing attachments, or C{None} if quota should not be enforced.
-
     @type quota: C{int} or C{NoneType}
 
     @ivar _propertyStoreClass: The class (or callable object / factory) that
@@ -95,6 +102,10 @@
         signature of the L{XattrPropertyStore} type: take 2 arguments
         C{(default-user-uid, path-factory)}, return an L{IPropertyStore}
         provider.
+
+    @ivar queuer: For compatibility with SQL-based store; currently a
+        non-functional implementation just for tests, but could be fixed to be
+        backed by SQLite or something.
     """
     implements(ICalendarStore)
 
@@ -118,6 +129,8 @@
         self._migrating = False
         self._enableNotifications = True
         self._newTransactionCallbacks = set()
+        # FIXME: see '@ivar queuer' above.
+        self.queuer = _StubQueuer()
 
     def callWithNewTransactions(self, callback):
         """
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130416/6d496180/attachment-0001.html>


More information about the calendarserver-changes mailing list