[CalendarServer-changes] [8868] CalendarServer/branches/users/gaya/ldapdirectorybacker

source_changes at macosforge.org source_changes at macosforge.org
Wed Mar 14 13:49:10 PDT 2012


Revision: 8868
          http://trac.macosforge.org/projects/calendarserver/changeset/8868
Author:   gaya at apple.com
Date:     2012-03-14 13:49:10 -0700 (Wed, 14 Mar 2012)
Log Message:
-----------
merge in TOT

Modified Paths:
--------------
    CalendarServer/branches/users/gaya/ldapdirectorybacker/calendarserver/tools/principals.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/calendarserver/tools/shell/cmd.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/calendarserver/tools/shell/terminal.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/calendarserver/tools/shell/vfs.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/calendarserver/tools/validcalendardata.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/contrib/migration/calendarmigrator.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/contrib/migration/test/test_migrator.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/contrib/tools/protocolanalysis.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/contrib/tools/request_monitor.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/idirectory.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/ldapdirectory.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/ldapdirectorybacker.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/test/test_ldapdirectory.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directorybackedaddressbook.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/caldav.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/imip.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/implicit.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/ischedule.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/itip.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/processing.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/scheduler.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/test/test_itip.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/stdconfig.py

Property Changed:
----------------
    CalendarServer/branches/users/gaya/ldapdirectorybacker/
    CalendarServer/branches/users/gaya/ldapdirectorybacker/support/build.sh
    CalendarServer/branches/users/gaya/ldapdirectorybacker/txdav/caldav/datastore/index_file.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/txdav/caldav/datastore/test/test_index_file.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/txdav/carddav/datastore/index_file.py
    CalendarServer/branches/users/gaya/ldapdirectorybacker/txdav/carddav/datastore/test/test_index_file.py


Property changes on: CalendarServer/branches/users/gaya/ldapdirectorybacker
___________________________________________________________________
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/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/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/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/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/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/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/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/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/use-system-twisted:5084-5149
/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/wsanchez/transations:5515-5593
   + /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/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/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/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/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/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/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/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/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/use-system-twisted:5084-5149
/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/wsanchez/transations:5515-5593
/CalendarServer/trunk:8831-8867

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/calendarserver/tools/principals.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/calendarserver/tools/principals.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/calendarserver/tools/principals.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -657,7 +657,11 @@
 @inlineCallbacks
 def action_removeProxyPrincipal(principal, proxyPrincipal, **kwargs):
     try:
-        (yield removeProxy(principal, proxyPrincipal, **kwargs))
+        removed = (yield removeProxy(principal, proxyPrincipal, **kwargs))
+        if removed:
+            print "Removed %s as a proxy for %s" % (
+                prettyPrincipal(proxyPrincipal),
+                prettyPrincipal(principal))
     except ProxyError, e:
         print "Error:", e
     except ProxyWarning, e:
@@ -666,6 +670,7 @@
 
 @inlineCallbacks
 def removeProxy(principal, proxyPrincipal, **kwargs):
+    removed = False
     proxyTypes = kwargs.get("proxyTypes", ("read", "write"))
     for proxyType in proxyTypes:
         proxyURL = proxyPrincipal.url()
@@ -685,11 +690,15 @@
         if len(memberURLs) == len(membersProperty.children):
             # No change
             continue
+        else:
+            removed = True
 
         membersProperty = davxml.GroupMemberSet(*memberURLs)
         (yield subPrincipal.writeProperty(membersProperty, None))
 
+        returnValue(removed)
 
+
 @inlineCallbacks
 def action_setAutoSchedule(principal, autoSchedule):
     if principal.record.recordType == "groups":

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/calendarserver/tools/shell/cmd.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/calendarserver/tools/shell/cmd.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/calendarserver/tools/shell/cmd.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 ##
 # Copyright (c) 2011-2012 Apple Inc. All rights reserved.
 #
@@ -347,6 +346,9 @@
 
         usage: exit
         """
+        if tokens:
+            raise UnknownArguments(tokens)
+
         self.exit()
 
 
@@ -356,6 +358,9 @@
 
         usage: python
         """
+        if tokens:
+            raise UnknownArguments(tokens)
+
         if not hasattr(self, "_interpreter"):
             # Bring in some helpful local variables.
             from txdav.common.datastore.sql_tables import schema
@@ -397,9 +402,26 @@
 
 
     def addOutput(self, bytes, async=False):
+        """
+        This is a delegate method, called by ManholeInterpreter.
+        """
         if async:
             self.terminal.write("... interrupted for Deferred ...\n")
         self.terminal.write(bytes)
         if async:
             self.terminal.write("\n")
             self.drawInputLine()
+
+
+    def cmd_sql(self, tokens):
+        """
+        Switch to an SQL prompt.
+
+        usage: sql
+        """
+        if tokens:
+            raise UnknownArguments(tokens)
+
+        raise NotImplementedError("")
+
+    cmd_sql.hidden = "Not implemented."

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/calendarserver/tools/shell/terminal.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/calendarserver/tools/shell/terminal.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/calendarserver/tools/shell/terminal.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 ##
 # Copyright (c) 2011-2012 Apple Inc. All rights reserved.
 #

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/calendarserver/tools/shell/vfs.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/calendarserver/tools/shell/vfs.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/calendarserver/tools/shell/vfs.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 ##
 # Copyright (c) 2011-2012 Apple Inc. All rights reserved.
 #
@@ -21,12 +20,14 @@
 
 from cStringIO import StringIO
 
-#from twisted.python import log
+from twisted.python import log
 from twisted.internet.defer import succeed
 from twisted.internet.defer import inlineCallbacks, returnValue
 
 from txdav.common.icommondatastore import NotFoundError
 
+from twistedcaldav.ical import InvalidICalendarDataError
+
 from calendarserver.tools.tables import Table
 
 
@@ -153,6 +154,7 @@
         self._childClasses["users"    ] = UsersFolder
         self._childClasses["locations"] = LocationsFolder
         self._childClasses["resources"] = ResourcesFolder
+        self._childClasses["groups"   ] = GroupsFolder
 
 
 class UIDsFolder(Folder):
@@ -191,7 +193,7 @@
 
         return PrincipalHomeFolder(
             self.service,
-            self.path + (record.uid,),
+            self.path + (name,),
             record.uid,
             record=record
         )
@@ -226,6 +228,13 @@
     recordType = "resources"
 
 
+class GroupsFolder(RecordFolder):
+    """
+    Folder containing all group principals by name.
+    """
+    recordType = "groups"
+
+
 class PrincipalHomeFolder(Folder):
     """
     Folder containing everything related to a given principal.
@@ -295,7 +304,63 @@
     def list(self):
         return Folder.list(self)
 
+    def describe(self):
+        result = []
+        result.append("Principal home for UID: %s\n" % (self.uid,))
 
+        if self.record is not None:
+            #
+            # Basic record info
+            #
+
+            rows = []
+
+            def add(name, value):
+                if value:
+                    rows.append((name, value))
+
+            add("Service"    , self.record.service   )
+            add("Record Type", self.record.recordType)
+
+            for shortName in self.record.shortNames:
+                add("Short Name", shortName)
+
+            add("GUID"      , self.record.guid     )
+            add("Full Name" , self.record.fullName )
+            add("First Name", self.record.firstName)
+            add("Last Name" , self.record.lastName )
+
+            for email in self.record.emailAddresses:
+                add("Email Address", email)
+
+            for cua in self.record.calendarUserAddresses:
+                add("Calendar User Address", cua)
+
+            add("Server ID"           , self.record.serverID              )
+            add("Partition ID"        , self.record.partitionID           )
+            add("Enabled"             , self.record.enabled               )
+            add("Enabled for Calendar", self.record.enabledForCalendaring )
+            add("Enabled for Contacts", self.record.enabledForAddressBooks)
+
+            if rows:
+                result.append("Directory Record:")
+                result.append(tableString(rows, header=("Name", "Value")))
+
+            #
+            # Group memberships
+            #
+            rows = []
+
+            for group in self.record.groups():
+                rows.append((group.uid, group.shortNames[0], group.fullName))
+
+            if rows:
+                result.append("Group Memberships:")
+                result.append(tableString(rows, header=("UID", "Short Name", "Full Name")))
+
+        return "\n".join(result)
+
+
 class CalendarHomeFolder(Folder):
     """
     Calendar home folder.
@@ -332,7 +397,7 @@
         properties   = (yield self.home.properties())
 
         result = []
-        result.append("Calendar home for UID: %s" % (uid,))
+        result.append("Calendar home for UID: %s\n" % (uid,))
 
         #
         # Attributes
@@ -352,14 +417,14 @@
             ))
 
         if len(rows):
-            result.append("\nAttributes:")
+            result.append("Attributes:")
             result.append(tableString(rows, header=("Name", "Value")))
 
         #
         # Properties
         #
         if properties:
-            result.append("\Properties:")
+            result.append("Properties:")
             result.append(tableString(
                 ((name, properties[name]) for name in sorted(properties)),
                 header=("Name", "Value")
@@ -379,8 +444,8 @@
 
     @inlineCallbacks
     def _childWithObject(self, object):
-        name = (yield object.uid())
-        returnValue(CalendarObject(self.service, self.path + (name,), object))
+        uid = (yield object.uid())
+        returnValue(CalendarObject(self.service, self.path + (uid,), object, uid))
 
     @inlineCallbacks
     def child(self, name):
@@ -408,27 +473,39 @@
     """
     Calendar object.
     """
-    def __init__(self, service, path, calendarObject):
+    def __init__(self, service, path, calendarObject, uid):
         File.__init__(self, service, path)
 
         self.object = calendarObject
+        self.uid    = uid
 
     @inlineCallbacks
     def lookup(self):
         if not hasattr(self, "component"):
             component = (yield self.object.component())
-            mainComponent = component.mainComponent()
 
-            self.componentType = mainComponent.name()
-            self.uid           = mainComponent.propertyValue("UID")
-            self.summary       = mainComponent.propertyValue("SUMMARY")
-            self.mainComponent = mainComponent
-            self.component     = component
+            try:
+                mainComponent = component.mainComponent(allow_multiple=True)
 
+                assert self.uid == mainComponent.propertyValue("UID")
+
+                self.componentType = mainComponent.name()
+                self.summary       = mainComponent.propertyValue("SUMMARY")
+                self.mainComponent = mainComponent
+
+            except InvalidICalendarDataError, e:
+                log.err("%s: %s" % (self.path, e))
+
+                self.componentType = "?"
+                self.summary       = "** Invalid data **"
+                self.mainComponent = None
+
+            self.component = component
+
     @inlineCallbacks
     def list(self):
         (yield self.lookup())
-        returnValue(((CalendarObject, self.uid, self.componentType, self.summary),))
+        returnValue(((CalendarObject, self.uid, self.componentType, self.summary.replace("\n", " ")),))
 
     @inlineCallbacks
     def text(self):
@@ -464,7 +541,6 @@
 #           log.msg("%r" % (attachment,))
 #           # FIXME: Not getting any results here
 
-
         returnValue("Calendar object:\n%s" % tableString(rows))
 
 class AddressBookHomeFolder(Folder):

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/calendarserver/tools/validcalendardata.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/calendarserver/tools/validcalendardata.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/calendarserver/tools/validcalendardata.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -24,6 +24,7 @@
 from twisted.application.service import Service
 from twisted.python.text import wordWrap
 from twisted.python.usage import Options
+from twistedcaldav.config import config
 from twistedcaldav.ical import Component
 from twistedcaldav.stdconfig import DEFAULT_CONFIG_FILE
 import os
@@ -151,8 +152,11 @@
     
         result = True
         message = ""
+        truncated = False
         try:
             component = Component.fromString(self.input.read())
+            if config.MaxInstancesForRRULE != 0:
+                truncated = component.truncateRecurrence(config.MaxInstancesForRRULE)
             component.validCalendarData(doFix=False, validateRecurrences=True)
             component.validCalendarForCalDAV(methodAllowed=True)
             component.validOrganizerForScheduling(doFix=False)
@@ -161,6 +165,8 @@
             message = str(e)
             if message.startswith(errorPrefix):
                 message = message[len(errorPrefix):]
+            if truncated:
+                message = "Calendar data RRULE truncated\n" + message
     
         return (result, message,)
 
@@ -184,7 +190,6 @@
         stderr.write("Unable to open input file for reading: %s\n" % (e))
         sys.exit(1)
     def makeService(store):
-        from twistedcaldav.config import config
         return ValidService(store, options, output, input, reactor, config)
     utilityMain(options['config'], makeService, reactor)
 

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/contrib/migration/calendarmigrator.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/contrib/migration/calendarmigrator.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/contrib/migration/calendarmigrator.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -147,7 +147,7 @@
 
             # Create log directory
             try:
-                logDir = os.path.join(options.targetDir, LOG_DIR)
+                logDir = os.path.join(options.targetRoot, LOG_DIR)
                 os.mkdir(logDir, 0755)
             except OSError:
                 # Already exists
@@ -436,10 +436,6 @@
     newDocumentRootValue = "Documents"
     newDocumentRoot = os.path.join(newDataRoot, newDocumentRootValue)
 
-    if not diskAccessor.exists(newServerRoot):
-        log("Creating calendar server root: %s" % (newServerRoot,))
-        diskAccessor.mkdir(newServerRoot)
-
     if sourceVersion < "10.7":
         # Before 10.7 there was no ServerRoot; DocumentRoot and DataRoot were separate.
         # Reconfigure so DocumentRoot is under DataRoot is under ServerRoot.  DataRoot
@@ -510,6 +506,7 @@
 
         if oldServerRootValue:
             if diskAccessor.exists(oldServerRootValue): # external volume
+                log("Using external calendar server root: %s" % (newServerRoot,))
                 # ServerRoot needs to be /Library/Server/Calendar and Contacts
                 # Since DocumentRoot is now relative to DataRoot, move DocumentRoot into DataRoot
                 newDataRoot = newDataRootValue = os.path.join(oldServerRootValue, "Data")
@@ -522,24 +519,52 @@
                 else:
                     diskAccessor.mkdir(newDocumentRoot)
             elif diskAccessor.exists(absolutePathWithRoot(sourceRoot, oldServerRootValue)):
+                log("Copying calendar server root: %s" % (newServerRoot,))
                 diskAccessor.ditto(
                     absolutePathWithRoot(sourceRoot, oldServerRootValue),
                     newServerRoot
                 )
+                newDataRoot = os.path.join(newServerRoot, "Data")
+                if not diskAccessor.exists(newDataRoot):
+                    diskAccessor.mkdir(newDataRoot)
+                newDocumentRoot = os.path.join(newDataRoot, "Documents")
+                if diskAccessor.exists(os.path.join(newServerRoot, "Documents")):
+                    log("Moving Documents into Data root: %s" % (newDataRoot,))
+                    diskAccessor.rename(os.path.join(newServerRoot, "Documents"),
+                        newDocumentRoot)
+                else:
+                    diskAccessor.mkdir(newDocumentRoot)
+            else:
+                log("Creating new calendar server root: %s" % (newServerRoot,))
+                diskAccessor.mkdir(newServerRoot)
+                newDataRoot = os.path.join(newServerRoot, "Data")
+                diskAccessor.mkdir(newDataRoot)
+                newDocumentRoot = os.path.join(newDataRoot, "Documents")
+                diskAccessor.mkdir(newDocumentRoot)
 
+
     else: # 10.8 -> 10.8
 
         if oldServerRootValue:
             if diskAccessor.exists(oldServerRootValue): # external volume
-                pass
+                log("Using external calendar server root: %s" % (oldServerRootValue,))
             elif diskAccessor.exists(absolutePathWithRoot(sourceRoot, oldServerRootValue)):
+                log("Copying calendar server root: %s" % (newServerRoot,))
                 diskAccessor.ditto(
                     absolutePathWithRoot(sourceRoot, oldServerRootValue),
                     newServerRoot
                 )
+            else:
+                log("Creating new calendar server root: %s" % (newServerRoot,))
+                diskAccessor.mkdir(newServerRoot)
+                newDataRoot = os.path.join(newServerRoot, "Data")
+                diskAccessor.mkdir(newDataRoot)
+                newDocumentRoot = os.path.join(newDataRoot, "Documents")
+                diskAccessor.mkdir(newDocumentRoot)
 
-    if diskAccessor.exists(newServerRoot):
-        diskAccessor.chown(newServerRoot, uid, gid, recursive=True)
+    if not diskAccessor.exists(newServerRoot):
+        diskAccessor.mkdir(newServerRoot)
+    diskAccessor.chown(newServerRoot, uid, gid, recursive=True)
 
     newServerRootValue, newDataRootValue = relativize(newServerRootValue,
         newDataRootValue)

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/contrib/migration/test/test_migrator.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/contrib/migration/test/test_migrator.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/contrib/migration/test/test_migrator.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -937,7 +937,6 @@
                 "Data"
             ),
             [   # expected DiskAccessor history
-                ('mkdir', '/Volumes/new/Library/Server/Calendar and Contacts'),
                 ('ditto', '/Library/Server/Previous/Library/CalendarServer/Data', '/Volumes/new/Library/Server/Calendar and Contacts/Data'),
                 ('ditto', '/Library/Server/Previous/Library/CalendarServer/Documents', '/Volumes/new/Library/Server/Calendar and Contacts/Data/Documents'),
                 ('ditto', '/Library/Server/Previous/Library/AddressBookServer/Documents/addressbooks', '/Volumes/new/Library/Server/Calendar and Contacts/Data/Documents/addressbooks'),
@@ -1011,7 +1010,6 @@
                 "/Volumes/External/CalendarServer/Calendar and Contacts Data",
             ),
             [   # expected DiskAccessor history
-                ('mkdir', '/Volumes/new/Library/Server/Calendar and Contacts'),
                 ('rename',
                  '/Volumes/External/CalendarServer/Calendar and Contacts Data',
                  '/Volumes/External/CalendarServer/Calendar and Contacts Data.bak'),
@@ -1019,6 +1017,7 @@
                 ('rename', '/Volumes/External/CalendarServer/Documents', '/Volumes/External/CalendarServer/Calendar and Contacts Data/Documents'),
                 ('chown-recursive', '/Volumes/External/CalendarServer/Calendar and Contacts Data', FakeUser.pw_uid, FakeGroup.gr_gid),
                 ('ditto', '/Volumes/External/AddressBookServer/Documents/addressbooks', '/Volumes/External/CalendarServer/Calendar and Contacts Data/Documents/addressbooks'),
+                ('mkdir', '/Volumes/new/Library/Server/Calendar and Contacts'),
                 ('chown-recursive', '/Volumes/new/Library/Server/Calendar and Contacts', FakeUser.pw_uid, FakeGroup.gr_gid),
             ]
         ),
@@ -1088,7 +1087,6 @@
                 "Data"
             ),
             [
-                ('mkdir', '/Volumes/new/Library/Server/Calendar and Contacts'),
                 ('ditto', '/Library/Server/Previous/NonStandard/CalendarServer/Data', '/Volumes/new/Library/Server/Calendar and Contacts/Data'),
                 ('ditto', '/Library/Server/Previous/NonStandard/CalendarServer/Documents', '/Volumes/new/Library/Server/Calendar and Contacts/Data/Documents'),
                 ('ditto', '/Library/Server/Previous/NonStandard/AddressBookServer/Documents/addressbooks', '/Volumes/new/Library/Server/Calendar and Contacts/Data/Documents/addressbooks'),
@@ -1161,13 +1159,13 @@
                 "/Volumes/External/CalendarServer/Calendar and Contacts Data",
             ),
             [
-                ('mkdir', '/Volumes/new/Library/Server/Calendar and Contacts'),
                 ('ditto',
                  '/Library/Server/Previous/Library/CalendarServer/Data',
                  '/Volumes/External/CalendarServer/Calendar and Contacts Data'),
                 ('rename', '/Volumes/External/CalendarServer/Documents', '/Volumes/External/CalendarServer/Calendar and Contacts Data/Documents'),
                 ('chown-recursive', '/Volumes/External/CalendarServer/Calendar and Contacts Data', FakeUser.pw_uid, FakeGroup.gr_gid),
                 ('ditto', '/Library/Server/Previous/Library/AddressBookServer/Documents/addressbooks', '/Volumes/External/CalendarServer/Calendar and Contacts Data/Documents/addressbooks'),
+                ('mkdir', '/Volumes/new/Library/Server/Calendar and Contacts'),
                 ('chown-recursive', '/Volumes/new/Library/Server/Calendar and Contacts', FakeUser.pw_uid, FakeGroup.gr_gid),
             ]
         ),
@@ -1211,6 +1209,8 @@
                 "/Library/Server/Previous/Library/Server/Calendar and Contacts/Documents/" : True,
                 "/Library/Server/Previous/Library/Server/Calendar and Contacts/Data/" : True,
                 "/Volumes/new/Library/Server/Calendar and Contacts/" : True,
+                "/Volumes/new/Library/Server/Calendar and Contacts/Data" : True,
+                "/Volumes/new/Library/Server/Calendar and Contacts/Documents" : True,
             },
             (   # args
                 "/Library/Server/Previous", # sourceRoot
@@ -1229,6 +1229,7 @@
             ),
             [
                 ('ditto', '/Library/Server/Previous/Library/Server/Calendar and Contacts', '/Volumes/new/Library/Server/Calendar and Contacts'),
+                ('rename', '/Volumes/new/Library/Server/Calendar and Contacts/Documents', '/Volumes/new/Library/Server/Calendar and Contacts/Data/Documents'),
                 ('chown-recursive', '/Volumes/new/Library/Server/Calendar and Contacts', FakeUser.pw_uid, FakeGroup.gr_gid),
             ]
         ),
@@ -1288,10 +1289,10 @@
                 "/Volumes/External/Library/Server/Calendar and Contacts/Data",
             ),
             [
-            ('mkdir', '/Volumes/new/Library/Server/Calendar and Contacts'),
             ('rename',
               '/Volumes/External/Library/Server/Calendar and Contacts/Documents',
               '/Volumes/External/Library/Server/Calendar and Contacts/Data/Documents'),
+            ('mkdir', '/Volumes/new/Library/Server/Calendar and Contacts'),
             ('chown-recursive', '/Volumes/new/Library/Server/Calendar and Contacts', FakeUser.pw_uid, FakeGroup.gr_gid),
             ]
         ),
@@ -1350,7 +1351,6 @@
                 "Data"
             ),
             [
-                ('mkdir', '/Volumes/new/Library/Server/Calendar and Contacts'),
                 ('ditto', '/Library/Server/Previous/Library/Server/Calendar and Contacts', '/Volumes/new/Library/Server/Calendar and Contacts'),
                 ('chown-recursive', '/Volumes/new/Library/Server/Calendar and Contacts', FakeUser.pw_uid, FakeGroup.gr_gid),
             ]

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/contrib/tools/protocolanalysis.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/contrib/tools/protocolanalysis.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/contrib/tools/protocolanalysis.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -287,7 +287,7 @@
         self.currentLine = None
         self.linesRead = 0
         
-    def analyzeLogFile(self, logFilePath, ctr):
+    def analyzeLogFile(self, logFilePath, lineCtr):
         fpath = os.path.expanduser(logFilePath)
         if fpath.endswith(".gz"):
             f = GzipFile(fpath)
@@ -297,14 +297,18 @@
         self.maxIndex = (self.endHour - self.startHour + 1) * 60 / self.resolutionMinutes
         try:
             for line in f:
-                ctr += 1
-                if ctr <= self.linesRead:
+                lineCtr += 1
+                if lineCtr <= self.linesRead:
                     continue
                 self.linesRead += 1
                 if line.startswith("Log"):
                     continue
         
-                self.parseLine(line)
+                try:
+                    self.parseLine(line)
+                except:
+                    print "Could not parse line:\n%s" % (line,)
+                    continue
         
                 # Filter method
                 if self.ignoreNonHTTPMethods and not self.currentLine.method.startswith("REPORT(") and self.currentLine.method not in httpMethods:
@@ -461,7 +465,7 @@
                 self.userInteractionAnalysis(adjustedMethod)
 
         except Exception:
-            print line
+            print "Failed to process line:\n%s" % (line,)
             raise
     
         # Average various items
@@ -505,7 +509,7 @@
         for client, data in self.clientByMethodCount.iteritems():
             self.clientIDByMethodCount[self.clientIDMap[client]] = data
 
-        return ctr
+        return lineCtr
 
     def parseLine(self, line):
     
@@ -1863,7 +1867,7 @@
         pwd = os.getcwd()
 
         analyzers = []
-        ctr = 0
+        ctr = []
         for arg in args:
             arg = os.path.expanduser(arg)
             if not arg.startswith("/"):
@@ -1876,8 +1880,9 @@
            
             if diffMode or not analyzers:
                 analyzers.append(CalendarServerLogAnalyzer(startHour, endHour, utcoffset, resolution, filterByUser, filterByClient))
+                ctr.append(0)
             print "Analyzing: %s" % (arg,)
-            ctr = analyzers[-1].analyzeLogFile(arg, ctr)
+            ctr[-1] = analyzers[-1].analyzeLogFile(arg, ctr[-1])
 
         if diffMode and len(analyzers) > 1:
             Differ(analyzers).printAll(doTabDelimited)
@@ -1890,9 +1895,9 @@
                     if again.lower()[0] == "n":
                         break
                     print "\n\n\n"
-                    analyzers[0].analyzeLogFile(arg)
+                    analyzers[0].analyzeLogFile(arg, ctr[0])
                     analyzers[0].printAll(doTabDelimited)
                 
     except Exception, e:
+        print traceback.print_exc()
         sys.exit(str(e))
-        print traceback.print_exc()

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/contrib/tools/request_monitor.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/contrib/tools/request_monitor.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/contrib/tools/request_monitor.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 ##
-# Copyright (c) 2009-2010 Apple Inc. All rights reserved.
+# Copyright (c) 2009-2012 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.
@@ -24,18 +24,48 @@
 import time
 import traceback
 
-NETSTAT = "/usr/sbin/netstat"
-enableListenQueue = os.path.exists(NETSTAT)
-IOSTAT = "/usr/sbin/iostat"
-enableCpuIdle = os.path.exists(IOSTAT)
-VMSTAT = "/usr/bin/vm_stat"
-enableFreeMem = os.path.exists(VMSTAT)
+# Detect which OS this is being run on
+child = Popen(
+    args=[
+        "uname",
+    ],
+    stdout=PIPE, stderr=STDOUT,
+)
+output, _ignore_error = child.communicate()
+output = output.strip()
+if output == "Darwin":
+    OS = "OS X"
+elif output == "Linux":
+    OS = "Linux"
+else:
+    print "Unknown OS: %s" % (output,)
+    sys.exit(1)
 
+# Some system commands we need to detect
+if OS == "OS X":
+    NETSTAT = "/usr/sbin/netstat"
+    enableListenQueue = os.path.exists(NETSTAT)
+elif OS == "Linux":
+    enableListenQueue = False
 
+if OS == "OS X":
+    IOSTAT = "/usr/sbin/iostat"
+    enableCpuIdle = os.path.exists(IOSTAT)
+elif OS == "Linux":
+    IOSTAT = "/usr/bin/iostat"
+    enableCpuIdle = os.path.exists(IOSTAT)
+
+if OS == "OS X":
+    VMSTAT = "/usr/bin/vm_stat"
+    enableFreeMem = os.path.exists(VMSTAT)
+elif OS == "Linux":
+    VMSTAT = "/usr/bin/vmstat"
+    enableFreeMem = os.path.exists(VMSTAT)
+
 sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) 
 
 
-filename = "/var/log/caldavd/access.log"
+filenames = ["/var/log/caldavd/access.log",]
 debug = False
 
 def listenq():
@@ -75,28 +105,32 @@
 
 
 
-def tail(filename, n):
-    child = Popen(
-        args=[
-            "/usr/bin/tail", "-%d" % (n,), filename,
-        ],
-        stdout=PIPE, stderr=STDOUT,
-    )
-    output, _ignore_error = child.communicate()
-    return output.split("\n")
+def tail(filenames, n):
+    results = []
+    for filename in filenames:
+        child = Popen(
+            args=[
+                "/usr/bin/tail", "-%d" % (n,), filename,
+            ],
+            stdout=PIPE, stderr=STDOUT,
+        )
+        output, _ignore_error = child.communicate()
+        results.extend(output.splitlines())
+    return results
 
-def range(filename, start, end):
+def range(filenames, start, end):
     results = []
-    with open(filename) as f:
-        for count, line in enumerate(f):
-            if count >= start:
-                results.append(line)
-            if count > end:
-                break
+    for filename in filenames:
+        with open(filename) as f:
+            for count, line in enumerate(f):
+                if count >= start:
+                    results.append(line)
+                if count > end:
+                    break
     return results
 
 def cpuPerDaemon():
-    a = []
+    a = {}
     child = Popen(
         args=[
             "ps", "auxw",
@@ -107,37 +141,68 @@
     for l in output.split("\n"):
         if "ProcessType=" in l:
             f = l.split()
-            a.append(f[2])
-    return ", ".join(a)
+            for l in f:
+                if l.startswith("LogID="):
+                    logID = int(l[6:])
+                    break
+            else:
+                logID = None
+            if logID is not None:
+                a[logID] = f[2]
+    return ", ".join([v for _ignore_k, v in sorted(a.items(), key=lambda i:i[0])])
 
 
 def cpuidle():
-    child = Popen(
-        args=[
-            IOSTAT, "-c", "2", "-n", "0",
-        ],
-        stdout=PIPE, stderr=STDOUT,
-    )
-    output, _ignore_ = child.communicate()
-    return output.split("\n")[-2].split()[2]
-
-def freemem():
-    try:
+    if OS == "OS X":
         child = Popen(
             args=[
-                VMSTAT,
+                IOSTAT, "-c", "2", "-n", "0",
             ],
             stdout=PIPE, stderr=STDOUT,
         )
         output, _ignore_ = child.communicate()
-        lines = output.split("\n")
-        
-        line = lines[0]
-        pageSize = int(line[line.find("page size of")+12:].split()[0])
-        line = lines[1]
-        freeSize = int(line[line.find("Pages free:")+11:].split()[0][:-1])
-        freed = freeSize * pageSize
-        return "%d bytes (%.1f GB)" % (freed, freed / (1024.0 * 1024 * 1024),)
+        return output.splitlines[-2].split()[2]
+    elif OS == "Linux":
+        child = Popen(
+            args=[
+                IOSTAT, "-c", "1", "2"
+            ],
+            stdout=PIPE, stderr=STDOUT,
+        )
+        output, _ignore_ = child.communicate()
+        return output.splitlines()[-2].split()[5]
+
+def freemem():
+    try:
+        if OS == "OS X":
+            child = Popen(
+                args=[
+                    VMSTAT,
+                ],
+                stdout=PIPE, stderr=STDOUT,
+            )
+            output, _ignore_ = child.communicate()
+            lines = output.split("\n")
+            
+            line = lines[0]
+            pageSize = int(line[line.find("page size of")+12:].split()[0])
+            line = lines[1]
+            freeSize = int(line[line.find("Pages free:")+11:].split()[0][:-1])
+            freed = freeSize * pageSize
+            return "%d bytes (%.1f GB)" % (freed, freed / (1024.0 * 1024 * 1024),)
+        elif OS == "Linux":
+            child = Popen(
+                args=[
+                    VMSTAT, "-s", "-S", "K"
+                ],
+                stdout=PIPE, stderr=STDOUT,
+            )
+            output, _ignore_ = child.communicate()
+            lines = output.splitlines()
+            
+            line = lines[4]
+            freed = int(line.split()[0]) * 1024
+            return "%d bytes (%.1f GB)" % (freed, freed / (1024.0 * 1024 * 1024),)
     except Exception, e:
         if debug:
             print "freemem failure", e
@@ -251,22 +316,24 @@
         numTop = int(value)
 
 if len(args):
-    filename = os.path.expanduser(args[0])
+    filenames = [os.path.expanduser(arg) for arg in args]
 
-if not os.path.isfile(filename):
-    print "Path %s does not exist" % (filename,)
-    print
-    usage()
-    sys.exit(1)
+for filename in filenames:
+    if not os.path.isfile(filename):
+        print "Path %s does not exist" % (filename,)
+        print
+        usage()
+        sys.exit(1)
 
-if not os.access(filename, os.R_OK):
-    print "Path %s does not exist" % (filename,)
-    print
-    usage()
-    sys.exit(1)
+for filename in filenames:
+    if not os.access(filename, os.R_OK):
+        print "Path %s does not exist" % (filename,)
+        print
+        usage()
+        sys.exit(1)
 
 if debug:
-    print "Starting: access log file: %s" % (filename,)
+    print "Starting: access log files: %s" % (", ".join(filenames),)
     print
 
 while True:
@@ -297,7 +364,7 @@
     parseErrors = 0
 
     try:
-        lines = tail(filename, numLines) if lineRange is None else range(filename, *lineRange)
+        lines = tail(filenames, numLines) if lineRange is None else range(filenames, *lineRange)
         for line in lines:
             if not line or line.startswith("Log"):
                 continue


Property changes on: CalendarServer/branches/users/gaya/ldapdirectorybacker/support/build.sh
___________________________________________________________________
Deleted: svn:mergeinfo
   - /CalendarServer/branches/config-separation/support/build.sh:4379-4443
/CalendarServer/branches/egg-info-351/support/build.sh:4589-4615
/CalendarServer/branches/generic-sqlstore/support/build.sh:6167-6191
/CalendarServer/branches/new-store/support/build.sh:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/support/build.sh:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/support/build.sh:5936-5981
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/support/build.sh:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes/support/build.sh:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/support/build.sh:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace/support/build.sh:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591/support/build.sh:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/support/build.sh:4465-4957
/CalendarServer/branches/users/cdaboo/pods/support/build.sh:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/support/build.sh:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/support/build.sh:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes/support/build.sh:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/support/build.sh:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/support/build.sh:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/support/build.sh:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging/support/build.sh:8730-8743
/CalendarServer/branches/users/glyph/case-insensitive-uid/support/build.sh:8772-8805
/CalendarServer/branches/users/glyph/conn-limit/support/build.sh:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/support/build.sh:4971-5080
/CalendarServer/branches/users/glyph/dalify/support/build.sh:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect/support/build.sh:6824-6876
/CalendarServer/branches/users/glyph/deploybuild/support/build.sh:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/support/build.sh:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/support/build.sh:6893-6900
/CalendarServer/branches/users/glyph/migrate-merge/support/build.sh:8690-8713
/CalendarServer/branches/users/glyph/misc-portability-fixes/support/build.sh:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/support/build.sh:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7/support/build.sh:6369-6445
/CalendarServer/branches/users/glyph/new-export/support/build.sh:7444-7485
/CalendarServer/branches/users/glyph/oracle-nulls/support/build.sh:7340-7351
/CalendarServer/branches/users/glyph/parallel-upgrade/support/build.sh:8376-8400
/CalendarServer/branches/users/glyph/parallel-upgrade_to_1/support/build.sh:8571-8583
/CalendarServer/branches/users/glyph/sendfdport/support/build.sh:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-fixes/support/build.sh:8436-8443
/CalendarServer/branches/users/glyph/sharedpool/support/build.sh:6490-6550
/CalendarServer/branches/users/glyph/skip-lonely-vtimezones/support/build.sh:8524-8535
/CalendarServer/branches/users/glyph/sql-store/support/build.sh:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/support/build.sh:7248-7258
/CalendarServer/branches/users/glyph/table-alias/support/build.sh:8651-8664
/CalendarServer/branches/users/glyph/use-system-twisted/support/build.sh:5084-5149
/CalendarServer/branches/users/sagen/applepush/support/build.sh:8126-8184
/CalendarServer/branches/users/sagen/inboxitems/support/build.sh:7380-7381
/CalendarServer/branches/users/sagen/locations-resources/support/build.sh:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/support/build.sh:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events/support/build.sh:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/support/build.sh:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/support/build.sh:4068-4075
/CalendarServer/branches/users/sagen/resources-2/support/build.sh:5084-5093
/CalendarServer/branches/users/wsanchez/transations/support/build.sh:5515-5593

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/idirectory.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/idirectory.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/idirectory.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -110,7 +110,7 @@
     fullName              = Attribute("The full name of this record.")
     firstName             = Attribute("The first name of this record.")
     lastName              = Attribute("The last name of this record.")
-    emailAddress          = Attribute("The email address of this record.")
+    emailAddresses        = Attribute("The email addresses of this record.")
     enabledForCalendaring = Attribute("Determines whether this record creates a principal with a calendar home.")
     enabledForAddressBooks = Attribute("Determines whether this record creates a principal with an address book home.")
     calendarUserAddresses = Attribute(

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/ldapdirectory.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/ldapdirectory.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/ldapdirectory.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -86,6 +86,9 @@
             "negativeCaching": False,
             "warningThresholdSeconds": 3,
             "batchSize": 500, # for splitting up large queries
+            "requestTimeoutSeconds" : 10,
+            "requestResultsLimit" : 200,
+            "optimizeMultiName" : False,
             "queryLocationsImplicitly": True,
             "restrictEnabledRecords": False,
             "restrictToGroup": "",
@@ -195,6 +198,11 @@
 
         self.warningThresholdSeconds = params["warningThresholdSeconds"]
         self.batchSize = params["batchSize"]
+        self.requestTimeoutSeconds = params["requestTimeoutSeconds"]
+        self.requestResultsLimit = params["requestResultsLimit"]
+        self.optimizeMultiName = params["optimizeMultiName"]
+        if self.batchSize > self.requestResultsLimit:
+            self.batchSize = self.requestResultsLimit
         self.queryLocationsImplicitly = params["queryLocationsImplicitly"]
         self.augmentService = params["augmentService"]
         self.groupMembershipCache = params["groupMembershipCache"]
@@ -261,25 +269,10 @@
                 self.rdnSchema[recordType]["rdn"].lower()
             ) + self.base
 
-        # Create LDAP connection
-        self.log_info("Connecting to LDAP %s" % (repr(self.uri),))
 
-        self.ldap = self.createLDAPConnection()
-        if self.credentials.get("dn", ""):
-            try:
-                self.log_info("Binding to LDAP %s" %
-                    (repr(self.credentials.get("dn")),))
-                self.ldap.simple_bind_s(self.credentials.get("dn"),
-                    self.credentials.get("password"))
-            except ldap.SERVER_DOWN:
-                msg = "Can't connect to LDAP %s: server down" % (self.uri,)
-                self.log_error(msg)
-                raise DirectoryConfigurationError(msg)
-            except ldap.INVALID_CREDENTIALS:
-                msg = "Can't bind to LDAP %s: check credentials" % (self.uri,)
-                self.log_error(msg)
-                raise DirectoryConfigurationError(msg)
+        self.ldap = None
 
+
         # Separate LDAP connection used solely for authenticating clients
         self.authLDAP = None
 
@@ -390,12 +383,30 @@
 
         return assignments
 
+    def getLDAPConnection(self):
+        if self.ldap is None:
+            self.log_info("Connecting to LDAP %s" % (repr(self.uri),))
+            self.ldap = self.createLDAPConnection()
+            self.log_info("Connection established to LDAP %s" % (repr(self.uri),))
+            if self.credentials.get("dn", ""):
+                try:
+                    self.log_info("Binding to LDAP %s" %
+                        (repr(self.credentials.get("dn")),))
+                    self.ldap.simple_bind_s(self.credentials.get("dn"),
+                        self.credentials.get("password"))
+                    self.log_info("Successfully authenticated with LDAP as %s" %
+                        (repr(self.credentials.get("dn")),))
+                except ldap.INVALID_CREDENTIALS:
+                    msg = "Can't bind to LDAP %s: check credentials" % (self.uri,)
+                    self.log_error(msg)
+                    raise DirectoryConfigurationError(msg)
+        return self.ldap
 
     def createLDAPConnection(self):
         """
         Create and configure LDAP connection
         """
-        cxn = ldap.ldapobject.ReconnectLDAPObject(self.uri)
+        cxn = ldap.initialize(self.uri)
 
         if self.tlsCACertFile:
             cxn.set_option(ldap.OPT_X_TLS_CACERTFILE, self.tlsCACertFile)
@@ -468,44 +479,53 @@
 
 
     def timedSearch(self, base, scope, filterstr="(objectClass=*)",
-        attrlist=None, timeout=-1, sizelimit=0):
+        attrlist=None, timeoutSeconds=-1, resultLimit=0):
         """
-        Execute an ldap.search_s( ); if it takes longer than the configured
+        Execute an LDAP query, retrying up to 3 times in case the LDAP server has
+        gone down and we need to reconnect. If it takes longer than the configured
         threshold, emit a log error.
+        The number of records requested is controlled by resultLimit (0=no limit).
+        If timeoutSeconds is not -1, the query will abort after the specified number
+        of seconds and the results retrieved so far are returned.
         """
-        s = ldap.async.List( self.ldap )
-        s.startSearch( base, scope, filterStr=filterstr, attrList=attrlist, timeout=timeout, sizelimit=sizelimit, )
-        
-        startTime = time.time()
-        try:
-            s.processResults()
+        TRIES = 3
 
-        except ldap.SERVER_DOWN:
-            self.log_error("LDAP server unavailable")
-            raise HTTPError(StatusResponse(responsecode.SERVICE_UNAVAILABLE, "LDAP server unavailable"))
-        except ldap.NO_SUCH_OBJECT:
-            pass
-        except ldap.FILTER_ERROR, e:
-            self.log_error("LDAP filter error: %s %s" % (e, filterstr))
-        except ldap.SIZELIMIT_EXCEEDED, e:
-            self.log_error("LDAP size limited exceeded: %s sizelimit %s (#results=%d)" % (e, sizelimit, len(s.allResults), ))
-        except ldap.TIMELIMIT_EXCEEDED, e:
-            self.log_error("LDAP timeout %s timeout %s (#results=%d)" % (e, timeout, len(s.allResults), ))
-        except ldap.TIMEOUT, e:
-            self.log_error("LDAP timeout %s (#results=%d)" % (e, len(s.allResults), ))
-        
-        # change format, ignoring resultsType
-        result = [resultItem for resultType, resultItem in s.allResults]
+        for i in xrange(TRIES):
+            try:
+                s = ldap.async.List(self.getLDAPConnection())
+                s.startSearch(base, scope, filterstr, attrList=attrlist,
+                    timeout=timeoutSeconds,
+                    sizelimit=resultLimit)
+                startTime = time.time()
+                s.processResults()
+            except ldap.NO_SUCH_OBJECT:
+                return []
+            except ldap.FILTER_ERROR, e:
+                self.log_error("LDAP filter error: %s %s" % (e, filterstr))
+                return []
+            except ldap.SIZELIMIT_EXCEEDED, e:
+                self.log_debug("LDAP result limit exceeded: %d" % (resultLimit,))
+            except ldap.TIMELIMIT_EXCEEDED, e:
+                self.log_warn("LDAP timeout exceeded: %d seconds" % (timeoutSeconds,))
+            except ldap.SERVER_DOWN:
+                self.ldap = None
+                self.log_error("LDAP server unavailable (tried %d times)" % (i+1,))
+                continue
 
-        totalTime = time.time() - startTime
-        if totalTime > self.warningThresholdSeconds:
-            if filterstr and len(filterstr) > 100:
-                filterstr = "%s..." % (filterstr[:100],)
-            self.log_error("LDAP query exceeded threshold: %.2f seconds for %s %s %s (#results=%d)" %
-                (totalTime, base, filterstr, attrlist, len(result)))
-        return result
+            # change format, ignoring resultsType
+            result = [resultItem for resultType, resultItem in s.allResults]
 
+            totalTime = time.time() - startTime
+            if totalTime > self.warningThresholdSeconds:
+                if filterstr and len(filterstr) > 100:
+                    filterstr = "%s..." % (filterstr[:100],)
+                self.log_error("LDAP query exceeded threshold: %.2f seconds for %s %s %s (#results=%d)" %
+                    (totalTime, base, filterstr, attrlist, len(result)))
+            return result
 
+        raise HTTPError(StatusResponse(responsecode.SERVICE_UNAVAILABLE, "LDAP server unavailable"))
+
+
     @property
     def restrictedGUIDs(self):
         """
@@ -951,15 +971,19 @@
 
             else:
                 scope = ldap.SCOPE_SUBTREE
-                filterstr = buildFilter(self.rdnSchema[recordType]["mapping"],
-                    fields, operand=operand)
+                filterstr = buildFilter(recordType,
+                    self.rdnSchema[recordType]["mapping"],
+                    fields, operand=operand,
+                    optimizeMultiName=self.optimizeMultiName)
 
             if filterstr is not None:
                 # Query the LDAP server
                 self.log_debug("LDAP search %s %s %s" %
                     (ldap.dn.dn2str(base), scope, filterstr))
-                results = self.timedSearch(ldap.dn.dn2str(base), scope, filterstr=filterstr,
-                    attrlist=self.attrlist)
+                results = self.timedSearch(ldap.dn.dn2str(base), scope,
+                    filterstr=filterstr, attrlist=self.attrlist,
+                    timeoutSeconds=self.requestTimeoutSeconds,
+                    resultLimit=self.requestResultsLimit)
                 self.log_debug("LDAP search returned %d results" % (len(results),))
                 numMissingGuids = 0
                 numMissingRecordNames = 0
@@ -1105,7 +1129,17 @@
     return ' '.join(ldap.dn.dn2str(ldap.dn.str2dn(dnStr.lower())).split())
 
 
-def buildFilter(mapping, fields, operand="or"):
+def _convertValue(value, matchType):
+    if matchType == "starts-with":
+        value = "%s*" % (ldapEsc(value),)
+    elif matchType == "contains":
+        value = "*%s*" % (ldapEsc(value),)
+    # otherwise it's an exact match
+    else:
+        value = ldapEsc(value)
+    return value
+
+def buildFilter(recordType, mapping, fields, operand="or", optimizeMultiName=False):
     """
     Create an LDAP filter string from a list of tuples representing directory
     attributes to search
@@ -1117,31 +1151,57 @@
     """
 
     converted = []
+    combined = {}
     for field, value, caseless, matchType in fields:
         ldapField = mapping.get(field, None)
         if ldapField:
-            if matchType == "starts-with":
-                value = "%s*" % (ldapEsc(value),)
-            elif matchType == "contains":
-                value = "*%s*" % (ldapEsc(value),)
-            # otherwise it's an exact match
-            else:
-                value = ldapEsc(value)
+            combined.setdefault(field, []).append((value, caseless, matchType))
+            value = _convertValue(value, matchType)
             converted.append("(%s=%s)" % (ldapField, value))
 
     if len(converted) == 0:
-        filterstr = None
-    elif len(converted) == 1:
+        return None
+
+    if optimizeMultiName and recordType in ("users", "groups"):
+        for field in [key for key in combined.keys() if key != "guid"]:
+            if len(combined.get(field, [])) > 1:
+                # Client is searching on more than one name -- interpret this as the user
+                # explicitly looking up a user by name (ignoring other record types), and
+                # try the various firstName/lastName permutations:
+                if recordType == "users":
+                    converted = []
+                    for firstName, firstCaseless, firstMatchType in combined["firstName"]:
+                        for lastName, lastCaseless, lastMatchType in combined["lastName"]:
+                            if firstName != lastName:
+                                firstValue = _convertValue(firstName, firstMatchType)
+                                lastValue = _convertValue(lastName, lastMatchType)
+                                converted.append("(&(%s=%s)(%s=%s))" %
+                                    (mapping["firstName"], firstValue,
+                                     mapping["lastName"], lastValue)
+                                )
+                else:
+                    return None
+
+    if len(converted) == 1:
         filterstr = converted[0]
     else:
         operand = ("|" if operand == "or" else "&")
         filterstr = "(%s%s)" % (operand, "".join(converted))
 
+    if filterstr:
+        # To reduce the amount of records returned, filter out the ones
+        # that don't have (possibly) required attribute values (record
+        # name, guid)
+        additional = []
+        for key in ("recordName", "guid"):
+            if mapping.has_key(key):
+                additional.append("(%s=*)" % (mapping.get(key),))
+        if additional:
+            filterstr = "(&%s%s)" % ("".join(additional), filterstr)
+
     return filterstr
 
 
-
-
 class LdapDirectoryRecord(CachingDirectoryRecord):
     """
     LDAP implementation of L{IDirectoryRecord}.

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/ldapdirectorybacker.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/ldapdirectorybacker.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/ldapdirectorybacker.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -256,9 +256,9 @@
                 # can't resist also using a timeout, 1 sec per requested record for now
                 timeout = maxRecords
 
-                self.log_debug("vCardRecordsForAddressBookQuery:LDAP query base=%s and filter=%s and attributes=%s timeout=%s sizelimit=%s" % (ldap.dn.dn2str(base), filterstr, attributes, timeout, maxRecords))
+                self.log_debug("vCardRecordsForAddressBookQuery:LDAP query base=%s and filter=%s and attributes=%s timeout=%s resultLimit=%s" % (ldap.dn.dn2str(base), filterstr, attributes, timeout, maxRecords))
                 
-                ldapSearchResult = (yield self.timedSearch(ldap.dn.dn2str(base), ldap.SCOPE_SUBTREE, filterstr=filterstr, attrlist=attributes, timeout=timeout, sizelimit=maxRecords))
+                ldapSearchResult = (yield self.timedSearch(ldap.dn.dn2str(base), ldap.SCOPE_SUBTREE, filterstr=filterstr, attrlist=attributes, timeoutSeconds=timeout, resultLimit=maxRecords))
     
                 self.log_debug("vCardRecordsForAddressBookQuery: ldapSearchResult=%s" % (ldapSearchResult,))
                 

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/test/test_ldapdirectory.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/test/test_ldapdirectory.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directory/test/test_ldapdirectory.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -37,10 +37,12 @@
 
         def test_buildFilter(self):
             mapping = {
+                "recordName" : "uid",
                 "fullName" : "cn",
                 "emailAddresses" : "mail",
                 "firstName" : "givenName",
                 "lastName" : "sn",
+                "guid" : "generateduid",
             }
 
             entries = [
@@ -52,8 +54,9 @@
                         ("lastName", "mor", True, u"starts-with")
                     ],
                     "operand" : "or",
-                    "recordType" : None,
-                    "expected" : "(|(cn=mor*)(mail=mor*)(givenName=mor*)(sn=mor*))"
+                    "recordType" : "users",
+                    "expected" : "(&(uid=*)(generateduid=*)(|(cn=mor*)(mail=mor*)(givenName=mor*)(sn=mor*)))",
+                    "optimize" : False,
                 },
                 {
                     "fields" : [
@@ -63,16 +66,18 @@
                         ("lastName", "mor\\", True, u"starts-with")
                     ],
                     "operand" : "or",
-                    "recordType" : None,
-                    "expected" : "(|(cn=mor\\28*)(mail=*mor\\29*)(givenName=mor\\2a)(sn=mor\\5c*))"
+                    "recordType" : "users",
+                    "expected" : "(&(uid=*)(generateduid=*)(|(cn=mor\\28*)(mail=*mor\\29*)(givenName=mor\\2a)(sn=mor\\5c*)))",
+                    "optimize" : False,
                 },
                 {
                     "fields" : [
                         ("fullName", "mor", True, u"starts-with"),
                     ],
                     "operand" : "or",
-                    "recordType" : None,
-                    "expected" : "(cn=mor*)"
+                    "recordType" : "users",
+                    "expected" : "(&(uid=*)(generateduid=*)(cn=mor*))",
+                    "optimize" : False,
                 },
                 {
                     "fields" : [
@@ -81,8 +86,9 @@
                         ("invalid", "mor", True, u"starts-with"),
                     ],
                     "operand" : "and",
-                    "recordType" : None,
-                    "expected" : "(&(cn=*mor*)(mail=mor))"
+                    "recordType" : "users",
+                    "expected" : "(&(uid=*)(generateduid=*)(&(cn=*mor*)(mail=mor)))",
+                    "optimize" : False,
                 },
                 {
                     "fields" : [
@@ -90,32 +96,135 @@
                         ("invalid", "mor", True, u"starts-with"),
                     ],
                     "operand" : "and",
-                    "recordType" : None,
-                    "expected" : None
+                    "recordType" : "users",
+                    "expected" : None,
+                    "optimize" : False,
                 },
                 {
                     "fields" : [ ],
                     "operand" : "and",
-                    "recordType" : None,
-                    "expected" : None
+                    "recordType" : "users",
+                    "expected" : None,
+                    "optimize" : False,
                 },
+                {
+                    "fields" : [
+                        ("fullName", "mor", True, u"starts-with"),
+                        ("fullName", "sag", True, u"starts-with"),
+                        ("emailAddresses", "mor", True, u"starts-with"),
+                        ("emailAddresses", "sag", True, u"starts-with"),
+                        ("firstName", "mor", True, u"starts-with"),
+                        ("firstName", "sag", True, u"starts-with"),
+                        ("lastName", "mor", True, u"starts-with"),
+                        ("lastName", "sag", True, u"starts-with"),
+                    ],
+                    "operand" : "or",
+                    "recordType" : "users",
+                    "expected" : "(&(uid=*)(generateduid=*)(|(&(givenName=mor*)(sn=sag*))(&(givenName=sag*)(sn=mor*))))",
+                    "optimize" : True,
+                },
+                {
+                    "fields" : [
+                        ("fullName", "mor", True, u"starts-with"),
+                        ("fullName", "sag", True, u"starts-with"),
+                        ("emailAddresses", "mor", True, u"starts-with"),
+                        ("emailAddresses", "sag", True, u"starts-with"),
+                        ("firstName", "mor", True, u"starts-with"),
+                        ("firstName", "sag", True, u"starts-with"),
+                        ("lastName", "mor", True, u"starts-with"),
+                        ("lastName", "sag", True, u"starts-with"),
+                    ],
+                    "operand" : "or",
+                    "recordType" : "groups",
+                    "expected" : None,
+                    "optimize" : True,
+                },
+                {
+                    "fields" : [
+                        ("fullName", "mor", True, u"starts-with"),
+                        ("fullName", "sag", True, u"starts-with"),
+                        ("emailAddresses", "mor", True, u"starts-with"),
+                        ("emailAddresses", "sag", True, u"starts-with"),
+                        ("firstName", "mor", True, u"starts-with"),
+                        ("firstName", "sag", True, u"starts-with"),
+                        ("lastName", "mor", True, u"starts-with"),
+                        ("lastName", "sag", True, u"starts-with"),
+                    ],
+                    "operand" : "or",
+                    "recordType" : "groups",
+                    "expected" : None,
+                    "optimize" : "(&(uid=*)(generateduid=*)(|(cn=mor*)(cn=sag*)(mail=mor*)(mail=sag*)(givenName=mor*)(givenName=sag*)(sn=mor*)(sn=sag*)))",
+                },
+                {
+                    "fields" : [
+                        ("guid", "xyzzy", True, u"equals"),
+                        ("guid", "plugh", True, u"equals"),
+                    ],
+                    "operand" : "or",
+                    "recordType" : "groups",
+                    "expected" : "(&(uid=*)(generateduid=*)(|(generateduid=xyzzy)(generateduid=plugh)))",
+                    "optimize" : True,
+                },
+                {
+                    "fields" : [
+                        ("fullName", "mor", True, u"contains"),
+                        ("fullName", "sag", True, u"contains"),
+                    ],
+                    "operand" : "or",
+                    "recordType" : "locations",
+                    "expected" : "(&(uid=*)(generateduid=*)(|(cn=*mor*)(cn=*sag*)))",
+                    "optimize" : True,
+                },
+                {
+                    "fields" : [
+                        ("fullName", "mor", True, u"contains"),
+                        ("fullName", "sag", True, u"contains"),
+                    ],
+                    "operand" : "or",
+                    "recordType" : "resources",
+                    "expected" : "(&(uid=*)(generateduid=*)(|(cn=*mor*)(cn=*sag*)))",
+                    "optimize" : True,
+                },
             ]
             for entry in entries:
                 self.assertEquals(
-                    buildFilter(mapping, entry["fields"],
-                        operand=entry["operand"]),
+                    buildFilter(entry["recordType"], mapping, entry["fields"],
+                        operand=entry["operand"], optimizeMultiName=entry["optimize"]),
                     entry["expected"]
                 )
 
+    class StubList(object):
+        def __init__(self, wrapper):
+            self.ldap = wrapper
 
+        def startSearch(self, base, scope, filterstr, attrList=None,
+            timeout=-1, sizelimit=0):
+            self.base = base
+            self.scope = scope
+            self.filterstr = filterstr
+            self.attrList = attrList
+            self.timeout = timeout
+            self.sizelimit = sizelimit
+
+        def processResults(self):
+            self.allResults = self.ldap.search_s(self.base, self.scope,
+                self.filterstr, attrlist=self.attrList)
+
+    class StubAsync(object):
+        def List(self, wrapper):
+            return StubList(wrapper)
+
+
     class LdapDirectoryTestWrapper(object):
         """
         A test stub which replaces search_s( ) with a version that will return
         whatever you have previously called addTestResults( ) with.
         """
 
+
         def __init__(self, actual):
             self.actual = actual
+            self.async = StubAsync()
 
             # Test data returned from search_s.
             # Note that some DNs have various extra whitespace added and mixed
@@ -242,10 +351,10 @@
             for dn, attrs in self.records:
                 dn = normalizeDNstr(dn)
                 if dn == base:
-                    results.append((dn, attrs))
+                    results.append(("ignored", (dn, attrs)))
                 elif dnContainedIn(ldap.dn.str2dn(dn), ldap.dn.str2dn(base)):
                     if filterstr in ("(objectClass=*)", "(!(objectClass=organizationalUnit))"):
-                        results.append((dn, attrs))
+                        results.append(("ignored", (dn, attrs)))
                     else:
                         trans = maketrans("&(|)", "   |")
                         fragments = filterstr.encode("utf-8").translate(trans).split("|")
@@ -255,7 +364,7 @@
                             fragment = fragment.strip()
                             key, value = fragment.split("=")
                             if value in attrs.get(key, []):
-                                results.append((dn, attrs))
+                                results.append(("ignored", (dn, attrs)))
 
             return results
 
@@ -369,6 +478,7 @@
 
             self.service = LdapDirectoryService(params)
             self.service.ldap = LdapDirectoryTestWrapper(self.service.ldap)
+            self.patch(ldap, "async", StubAsync())
 
 
         def test_ldapWrapper(self):

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directorybackedaddressbook.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directorybackedaddressbook.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/directorybackedaddressbook.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+# Copyright (c) 2008-2012 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.

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/caldav.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/caldav.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/caldav.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2005-2011 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2012 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.
@@ -45,7 +45,9 @@
 from twistedcaldav.scheduling.processing import ImplicitProcessor, ImplicitProcessorException
 
 """
-Class that handles delivery of scheduling messages via CalDAV.
+Handles the sending of scheduling messages to the server itself. This will cause
+actual processing of the delivery of the message to the recipient's inbox, via the
+L{ImplicitProcessor} class.
 """
 
 __all__ = [

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/imip.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/imip.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/imip.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2012 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.
@@ -31,7 +31,7 @@
 from twistedcaldav.scheduling.itip import iTIPRequestStatus
 
 """
-Class that handles delivery of scheduling messages via iMIP.
+Handles the sending of scheduling messages via iMIP (mail gateway).
 """
 
 __all__ = [

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/implicit.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/implicit.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/implicit.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -88,6 +88,9 @@
             self.calendar = (yield resource.iCalendarForUser(request))
             yield self.checkImplicitState()
         
+        # Once we have collected sufficient information from the calendar data, check validity of organizer and attendees
+        self.checkValidOrganizer()
+
         # Attendees are not allowed to overwrite one type with another
         if (
             not self.internal_request and
@@ -187,6 +190,22 @@
 
         returnValue((self.action != "none", False,))
 
+    def checkValidOrganizer(self):
+        """
+        Make sure the ORGANIZER is allowed to do certain scheduling operations.
+        """
+        
+        # Check to see whether the organizer principal is enabled for scheduling. If not, do not allow them
+        # to create new scheduling resources.
+        if self.action == "create":
+            if self.organizerPrincipal and not self.organizerPrincipal.enabledAsOrganizer():
+                log.err("ORGANIZER not allowed to be an Organizer: %s" % (self.organizer,))
+                raise HTTPError(ErrorResponse(
+                    responsecode.FORBIDDEN,
+                    (caldav_namespace, "organizer-allowed"),
+                    "Organizer cannot schedule",
+                ))
+
     @inlineCallbacks
     def checkSchedulingObjectResource(self, resource):
         
@@ -835,17 +854,17 @@
                 itipmsg = iTipGenerator.generateCancel(self.oldcalendar, (attendee,), rids)
 
             # Send scheduling message
+            if itipmsg:
+                # This is a local CALDAV scheduling operation.
+                scheduler = self.makeScheduler()
+        
+                # Do the PUT processing
+                log.info("Implicit CANCEL - organizer: '%s' to attendee: '%s', UID: '%s', RIDs: '%s'" % (self.organizer, attendee, self.uid, rids))
+                response = (yield scheduler.doSchedulingViaPUT(self.originator, (attendee,), itipmsg, self.internal_request))
+                self.handleSchedulingResponse(response, True)
+                
+                count += 1
             
-            # This is a local CALDAV scheduling operation.
-            scheduler = self.makeScheduler()
-    
-            # Do the PUT processing
-            log.info("Implicit CANCEL - organizer: '%s' to attendee: '%s', UID: '%s', RIDs: '%s'" % (self.organizer, attendee, self.uid, rids))
-            response = (yield scheduler.doSchedulingViaPUT(self.originator, (attendee,), itipmsg, self.internal_request))
-            self.handleSchedulingResponse(response, True)
-            
-            count += 1
-            
         returnValue(count)
             
     @inlineCallbacks

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/ischedule.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/ischedule.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/ischedule.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2005-2010 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2012 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.
@@ -48,7 +48,8 @@
 import OpenSSL
 
 """
-Server to server utility functions and client requests.
+Handles the sending of iSchedule scheduling messages. Used for both cross-domain scheduling,
+as well as internal partitioning or podding.
 """
 
 __all__ = [

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/itip.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/itip.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/itip.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -15,20 +15,17 @@
 ##
 
 """
-iTIP (RFC5546) processing.
+iTIP (RFC5546) scheduling message processing and generation.
+
+
+This is currently used for handling auto-replies to schedule requests arriving
+in an inbox. It is called in a delayed fashion via reactor.callLater.
+
+We assume that all the components/calendars we deal with have been determined
+as being 'valid for CalDAV/iTIP', i.e. they contain UIDs, single component
+types, etc.
 """
 
-#
-# This is currently used for handling auto-replies to schedule requests arriving
-# in an inbox. It is called in a delayed fashion via reactor.callLater.
-#
-# We assume that all the components/calendars we deal with have been determined
-# as being 'valid for CalDAV/iTIP', i.e. they contain UIDs, single component
-# types, etc.
-#
-# The logic for component matching needs a lot more work as it currently does not
-# know how to deal with overridden instances.
-#
 
 from twext.python.log import Logger
 
@@ -616,6 +613,7 @@
             instances = (None,)
 
         tzids = set()
+        added = False
         for instance_rid in instances:
             
             # Create a new component matching the type of the original
@@ -624,11 +622,16 @@
             # Use the master component when the instance is None
             if not instance_rid:
                 instance = original.masterComponent()
+                assert instance is not None, "Need a master component"
             else:
                 instance = original.overriddenComponent(instance_rid)
                 if instance is None:
                     instance = original.deriveInstance(instance_rid)
-            assert instance is not None, "Need a master component"
+                    
+                # If the instance to be cancelled did not exist in the original, then
+                # do nothing
+                if instance is None:
+                    continue
 
             # Add some required properties extracted from the original
             comp.addProperty(Property("DTSTAMP", instance.propertyValue("DTSTAMP")))
@@ -665,19 +668,23 @@
             tzids.update(comp.timezoneIDs())
 
             itip.addComponent(comp)
-            
-        # Now include any referenced tzids
-        for comp in original.subcomponents():
-            if comp.name() == "VTIMEZONE":
-                tzid = comp.propertyValue("TZID")
-                if tzid in tzids:
-                    itip.addComponent(comp)
+            added = True
+        
+        if added:
+            # Now include any referenced tzids
+            for comp in original.subcomponents():
+                if comp.name() == "VTIMEZONE":
+                    tzid = comp.propertyValue("TZID")
+                    if tzid in tzids:
+                        itip.addComponent(comp)
+    
+            # Strip out unwanted bits
+            iTipGenerator.prepareSchedulingMessage(itip)
+    
+            return itip
+        else:
+            return None
 
-        # Strip out unwanted bits
-        iTipGenerator.prepareSchedulingMessage(itip)
-
-        return itip
-
     @staticmethod
     def generateAttendeeRequest(original, attendees, filter_rids):
         """

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/processing.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/processing.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/processing.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -40,6 +40,16 @@
 from pycalendar.datetime import PyCalendarDateTime
 from pycalendar.timezone import PyCalendarTimezone
 
+"""
+CalDAV implicit processing.
+
+This module handles the processing of scheduling messages being delivered to a calendar user's inbox.
+It determines who is scheduling (organizer or attendee) and applies the scheduling message changes
+to the recipient's calendar data as well as depositing the scheduling message in the inbox. For users
+who have an auto-accept option on, it will also handle the automatic response. Also, refreshes of other
+attendees (when one attendee replies) are triggered from here.
+"""
+
 __all__ = [
     "ImplicitProcessor",
     "ImplicitProcessorException",

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/scheduler.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/scheduler.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/scheduler.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -55,6 +55,62 @@
 
 """
 CalDAV/Server-to-Server scheduling behavior.
+
+This module handles the delivery of scheduling messages to organizer and attendees. The basic idea is to first
+confirm the integrity of the incoming scheduling message, check authorization. Appropriate L{DeliveryService}s
+are then used to deliver the message to attendees or organizer. Delivery responses are processed and returned.
+This takes into account partitioning and podding of users by detecting the appropriate host for a calendar
+user and then dispatching the delivery accordingly.
+
+The L{Scheduler} class defines the basic behavior for processing deliveries. Sub-classes are defined for the
+different ways a deliver can be triggered.
+
+L{CalDAVScheduler} - handles deliveries for scheduling messages originating from inside the CalDAV server
+i.e. user PUTs or POSTs.
+
+L{IScheduleScheduler} - handles deliveries for scheduling messages being POSTed to the iSchedule inbox.
+
+L{IMIPScheduler} - handles deliveries for POSTs on the iMIP inbox (coming from the mail gateway).
+
+L{DirectScheduler} - used when doing some internal processing (e.g., inbox item processing during an
+upgrade.
+
+Here is a typical flow of activity for a iTIP between users on the server:
+
+iTIP PUT request
+\
+ \_L{ImplicitScheduler}           - does CalDAV-schedule logic and sends iTIP message
+   \
+    \_L{CalDAVScheduler}          - receives iTIP message
+      \
+       \_L{ScheduleViaCalDAV}     - handles delivery of iTIP message
+         \
+          \_L{ImplicitProcessor}  - dispatches iTIP message (also auto-accept)
+            \
+             \_L{iTipProcessing}  - processes iTIP message
+             
+Here is a typical flow of activity for a iTIP between an organizer on the server and an iMIP attendee:
+
+iTIP PUT request
+\
+ \_L{ImplicitScheduler}
+   \
+    \_L{CalDAVScheduler}
+      \
+       \_L{ScheduleViaIMip}
+
+Here is a typical flow of activity for a iTIP between an organizer not on the server and attendee on the server:
+
+iTIP POST on /ischedule
+\
+ \_L{IScheduleScheduler}
+   \
+    \_L{ScheduleViaCalDAV}
+      \
+       \_L{ImplicitProcessor}
+         \
+          \_L{iTipProcessing}
+
 """
 
 __all__ = [
@@ -671,7 +727,11 @@
             if organizerPrincipal:
                 outboxURL = organizerPrincipal.scheduleOutboxURL()
                 if outboxURL:
-                    if not organizerPrincipal.enabledAsOrganizer():
+                    
+                    # Only do this check for a freebusy request. A check for an invite needs
+                    # to be handled later when we know whether a new invite is being added
+                    # (which we reject) vs an update to an existing one (which we allow).
+                    if self.checkForFreeBusy() and not organizerPrincipal.enabledAsOrganizer():
                         log.err("ORGANIZER not allowed to be an Organizer: %s" % (self.calendar,))
                         raise HTTPError(ErrorResponse(
                             responsecode.FORBIDDEN,

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/test/test_itip.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/test/test_itip.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/scheduling/test/test_itip.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -1894,12 +1894,33 @@
                 ("mailto:user2 at example.com",),
                 (None, ),
             ),
+
+            # Recurring component - cancel non-existent instance
+            (
+                """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-4
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=DAILY;COUNT=10
+SEQUENCE:1
+END:VEVENT
+END:VCALENDAR
+""",
+                "",
+                ("mailto:user2 at example.com",),
+                (PyCalendarDateTime(2008, 12, 14, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)), ),
+            ),
+
         )
         
         for original, filtered, attendees, instances in data:
             component = Component.fromString(original)
             itipped = iTipGenerator.generateCancel(component, attendees, instances)
-            itipped = str(itipped).replace("\r", "")
+            itipped = str(itipped).replace("\r", "") if itipped else ""
             itipped = "".join([line for line in itipped.splitlines(True) if not line.startswith("DTSTAMP:")])
             self.assertEqual(filtered, itipped)
 

Modified: CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/stdconfig.py	2012-03-14 19:44:23 UTC (rev 8867)
+++ CalendarServer/branches/users/gaya/ldapdirectorybacker/twistedcaldav/stdconfig.py	2012-03-14 20:49:10 UTC (rev 8868)
@@ -65,6 +65,9 @@
         "negativeCaching": False,
         "warningThresholdSeconds": 3,
         "batchSize": 500, # for splitting up large queries
+        "requestTimeoutSeconds" : 10,
+        "requestResultsLimit" : 200,
+        "optimizeMultiName" : False,
         "queryLocationsImplicitly": True,
         "restrictEnabledRecords": False,
         "restrictToGroup": "",


Property changes on: CalendarServer/branches/users/gaya/ldapdirectorybacker/txdav/caldav/datastore/index_file.py
___________________________________________________________________
Deleted: svn:mergeinfo
   - /CalendarServer/branches/config-separation/txdav/caldav/datastore/index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/caldav/datastore/index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/caldav/datastore/index_file.py:6167-6191
/CalendarServer/branches/new-store/txdav/caldav/datastore/index_file.py:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/txdav/caldav/datastore/index_file.py:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/caldav/datastore/index_file.py:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/caldav/datastore/index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/caldav/datastore/index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/caldav/datastore/index_file.py:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/caldav/datastore/index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace/txdav/caldav/datastore/index_file.py:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/caldav/datastore/index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/caldav/datastore/index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/caldav/datastore/index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/caldav/datastore/index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes/txdav/caldav/datastore/index_file.py:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/caldav/datastore/index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/caldav/datastore/index_file.py:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/txdav/caldav/datastore/index_file.py:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging/txdav/caldav/datastore/index_file.py:8730-8743
/CalendarServer/branches/users/glyph/case-insensitive-uid/txdav/caldav/datastore/index_file.py:8772-8805
/CalendarServer/branches/users/glyph/conn-limit/txdav/caldav/datastore/index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/caldav/datastore/index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/caldav/datastore/index_file.py:6932-7023
/CalendarServer/branches/users/glyph/deploybuild/txdav/caldav/datastore/index_file.py:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/caldav/datastore/index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/caldav/datastore/index_file.py:6893-6900
/CalendarServer/branches/users/glyph/migrate-merge/txdav/caldav/datastore/index_file.py:8690-8713
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/caldav/datastore/index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/caldav/datastore/index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/index_file.py:6369
/CalendarServer/branches/users/glyph/new-export/txdav/caldav/datastore/index_file.py:7444-7485
/CalendarServer/branches/users/glyph/oracle/txdav/caldav/datastore/index_file.py:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/caldav/datastore/index_file.py:7340-7351
/CalendarServer/branches/users/glyph/parallel-upgrade/txdav/caldav/datastore/index_file.py:8376-8400
/CalendarServer/branches/users/glyph/parallel-upgrade_to_1/txdav/caldav/datastore/index_file.py:8571-8583
/CalendarServer/branches/users/glyph/sendfdport/txdav/caldav/datastore/index_file.py:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-fixes/txdav/caldav/datastore/index_file.py:8436-8443
/CalendarServer/branches/users/glyph/sharedpool/txdav/caldav/datastore/index_file.py:6490-6550
/CalendarServer/branches/users/glyph/skip-lonely-vtimezones/txdav/caldav/datastore/index_file.py:8524-8535
/CalendarServer/branches/users/glyph/sql-store/txdav/caldav/datastore/index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/caldav/datastore/index_file.py:7248-7258
/CalendarServer/branches/users/glyph/table-alias/txdav/caldav/datastore/index_file.py:8651-8664
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/caldav/datastore/index_file.py:5084-5149
/CalendarServer/branches/users/sagen/applepush/txdav/caldav/datastore/index_file.py:8126-8184
/CalendarServer/branches/users/sagen/inboxitems/txdav/caldav/datastore/index_file.py:7380-7381
/CalendarServer/branches/users/sagen/locations-resources/txdav/caldav/datastore/index_file.py:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/caldav/datastore/index_file.py:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events/txdav/caldav/datastore/index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/caldav/datastore/index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/caldav/datastore/index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/caldav/datastore/index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/caldav/datastore/index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/index.py:6322-6394


Property changes on: CalendarServer/branches/users/gaya/ldapdirectorybacker/txdav/caldav/datastore/test/test_index_file.py
___________________________________________________________________
Deleted: svn:mergeinfo
   - /CalendarServer/branches/config-separation/txdav/caldav/datastore/test/test_index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/caldav/datastore/test/test_index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/caldav/datastore/test/test_index_file.py:6167-6191
/CalendarServer/branches/new-store/txdav/caldav/datastore/test/test_index_file.py:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/txdav/caldav/datastore/test/test_index_file.py:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/caldav/datastore/test/test_index_file.py:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/caldav/datastore/test/test_index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/caldav/datastore/test/test_index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/caldav/datastore/test/test_index_file.py:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/caldav/datastore/test/test_index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/caldav/datastore/test/test_index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/caldav/datastore/test/test_index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/caldav/datastore/test/test_index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/caldav/datastore/test/test_index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/caldav/datastore/test/test_index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes/txdav/caldav/datastore/test/test_index_file.py:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/caldav/datastore/test/test_index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/caldav/datastore/test/test_index_file.py:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/txdav/caldav/datastore/test/test_index_file.py:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging/txdav/caldav/datastore/test/test_index_file.py:8730-8743
/CalendarServer/branches/users/glyph/case-insensitive-uid/txdav/caldav/datastore/test/test_index_file.py:8772-8805
/CalendarServer/branches/users/glyph/conn-limit/txdav/caldav/datastore/test/test_index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/caldav/datastore/test/test_index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/caldav/datastore/test/test_index_file.py:6932-7023
/CalendarServer/branches/users/glyph/deploybuild/txdav/caldav/datastore/test/test_index_file.py:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/caldav/datastore/test/test_index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/caldav/datastore/test/test_index_file.py:6893-6900
/CalendarServer/branches/users/glyph/migrate-merge/txdav/caldav/datastore/test/test_index_file.py:8690-8713
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/caldav/datastore/test/test_index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/caldav/datastore/test/test_index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/test_index_file.py:6369
/CalendarServer/branches/users/glyph/new-export/txdav/caldav/datastore/test/test_index_file.py:7444-7485
/CalendarServer/branches/users/glyph/oracle/txdav/caldav/datastore/test/test_index_file.py:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/caldav/datastore/test/test_index_file.py:7340-7351
/CalendarServer/branches/users/glyph/parallel-upgrade/txdav/caldav/datastore/test/test_index_file.py:8376-8400
/CalendarServer/branches/users/glyph/parallel-upgrade_to_1/txdav/caldav/datastore/test/test_index_file.py:8571-8583
/CalendarServer/branches/users/glyph/sendfdport/txdav/caldav/datastore/test/test_index_file.py:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-fixes/txdav/caldav/datastore/test/test_index_file.py:8436-8443
/CalendarServer/branches/users/glyph/sharedpool/txdav/caldav/datastore/test/test_index_file.py:6490-6550
/CalendarServer/branches/users/glyph/skip-lonely-vtimezones/txdav/caldav/datastore/test/test_index_file.py:8524-8535
/CalendarServer/branches/users/glyph/sql-store/txdav/caldav/datastore/test/test_index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/caldav/datastore/test/test_index_file.py:7248-7258
/CalendarServer/branches/users/glyph/table-alias/txdav/caldav/datastore/test/test_index_file.py:8651-8664
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/caldav/datastore/test/test_index_file.py:5084-5149
/CalendarServer/branches/users/sagen/applepush/txdav/caldav/datastore/test/test_index_file.py:8126-8184
/CalendarServer/branches/users/sagen/inboxitems/txdav/caldav/datastore/test/test_index_file.py:7380-7381
/CalendarServer/branches/users/sagen/locations-resources/txdav/caldav/datastore/test/test_index_file.py:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/caldav/datastore/test/test_index_file.py:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events/txdav/caldav/datastore/test/test_index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/caldav/datastore/test/test_index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/caldav/datastore/test/test_index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/caldav/datastore/test/test_index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/caldav/datastore/test/test_index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/test/test_index.py:6322-6394


Property changes on: CalendarServer/branches/users/gaya/ldapdirectorybacker/txdav/carddav/datastore/index_file.py
___________________________________________________________________
Deleted: svn:mergeinfo
   - /CalendarServer/branches/config-separation/txdav/carddav/datastore/index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/carddav/datastore/index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/carddav/datastore/index_file.py:6167-6191
/CalendarServer/branches/new-store/txdav/carddav/datastore/index_file.py:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/txdav/carddav/datastore/index_file.py:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/carddav/datastore/index_file.py:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/carddav/datastore/index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/carddav/datastore/index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/carddav/datastore/index_file.py:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/carddav/datastore/index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace/txdav/carddav/datastore/index_file.py:8137-8141
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/carddav/datastore/index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/carddav/datastore/index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/carddav/datastore/index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/carddav/datastore/index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/carddav/datastore/index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes/txdav/carddav/datastore/index_file.py:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/carddav/datastore/index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/carddav/datastore/index_file.py:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/txdav/carddav/datastore/index_file.py:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging/txdav/carddav/datastore/index_file.py:8730-8743
/CalendarServer/branches/users/glyph/case-insensitive-uid/txdav/carddav/datastore/index_file.py:8772-8805
/CalendarServer/branches/users/glyph/conn-limit/txdav/carddav/datastore/index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/carddav/datastore/index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/carddav/datastore/index_file.py:6932-7023
/CalendarServer/branches/users/glyph/deploybuild/txdav/carddav/datastore/index_file.py:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/carddav/datastore/index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/carddav/datastore/index_file.py:6893-6900
/CalendarServer/branches/users/glyph/migrate-merge/txdav/carddav/datastore/index_file.py:8690-8713
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/carddav/datastore/index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/carddav/datastore/index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/index_file.py:6369
/CalendarServer/branches/users/glyph/new-export/txdav/carddav/datastore/index_file.py:7444-7485
/CalendarServer/branches/users/glyph/oracle/txdav/carddav/datastore/index_file.py:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/carddav/datastore/index_file.py:7340-7351
/CalendarServer/branches/users/glyph/parallel-upgrade/txdav/carddav/datastore/index_file.py:8376-8400
/CalendarServer/branches/users/glyph/parallel-upgrade_to_1/txdav/carddav/datastore/index_file.py:8571-8583
/CalendarServer/branches/users/glyph/sendfdport/txdav/carddav/datastore/index_file.py:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-fixes/txdav/carddav/datastore/index_file.py:8436-8443
/CalendarServer/branches/users/glyph/sharedpool/txdav/carddav/datastore/index_file.py:6490-6550
/CalendarServer/branches/users/glyph/skip-lonely-vtimezones/txdav/carddav/datastore/index_file.py:8524-8535
/CalendarServer/branches/users/glyph/sql-store/txdav/carddav/datastore/index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/carddav/datastore/index_file.py:7248-7258
/CalendarServer/branches/users/glyph/table-alias/txdav/carddav/datastore/index_file.py:8651-8664
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/carddav/datastore/index_file.py:5084-5149
/CalendarServer/branches/users/sagen/applepush/txdav/carddav/datastore/index_file.py:8126-8184
/CalendarServer/branches/users/sagen/inboxitems/txdav/carddav/datastore/index_file.py:7380-7381
/CalendarServer/branches/users/sagen/locations-resources/txdav/carddav/datastore/index_file.py:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/carddav/datastore/index_file.py:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events/txdav/carddav/datastore/index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/carddav/datastore/index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/carddav/datastore/index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/carddav/datastore/index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/carddav/datastore/index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/vcardindex.py:6322-6394


Property changes on: CalendarServer/branches/users/gaya/ldapdirectorybacker/txdav/carddav/datastore/test/test_index_file.py
___________________________________________________________________
Deleted: svn:mergeinfo
   - /CalendarServer/branches/config-separation/txdav/carddav/datastore/test/test_index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/carddav/datastore/test/test_index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/carddav/datastore/test/test_index_file.py:6167-6191
/CalendarServer/branches/new-store/txdav/carddav/datastore/test/test_index_file.py:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/txdav/carddav/datastore/test/test_index_file.py:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/carddav/datastore/test/test_index_file.py:5936-5981
/CalendarServer/branches/users/cdaboo/batchupload-6699/txdav/carddav/datastore/test/test_index_file.py:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/carddav/datastore/test/test_index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes/txdav/carddav/datastore/test/test_index_file.py:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/carddav/datastore/test/test_index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/carddav/datastore/test/test_index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/carddav/datastore/test/test_index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/pods/txdav/carddav/datastore/test/test_index_file.py:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar/txdav/carddav/datastore/test/test_index_file.py:7085-7206
/CalendarServer/branches/users/cdaboo/pycard/txdav/carddav/datastore/test/test_index_file.py:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes/txdav/carddav/datastore/test/test_index_file.py:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/carddav/datastore/test/test_index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/carddav/datastore/test/test_index_file.py:5188-5440
/CalendarServer/branches/users/cdaboo/timezones/txdav/carddav/datastore/test/test_index_file.py:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging/txdav/carddav/datastore/test/test_index_file.py:8730-8743
/CalendarServer/branches/users/glyph/case-insensitive-uid/txdav/carddav/datastore/test/test_index_file.py:8772-8805
/CalendarServer/branches/users/glyph/conn-limit/txdav/carddav/datastore/test/test_index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/carddav/datastore/test/test_index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dalify/txdav/carddav/datastore/test/test_index_file.py:6932-7023
/CalendarServer/branches/users/glyph/deploybuild/txdav/carddav/datastore/test/test_index_file.py:7563-7572
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/carddav/datastore/test/test_index_file.py:6592-6614
/CalendarServer/branches/users/glyph/linux-tests/txdav/carddav/datastore/test/test_index_file.py:6893-6900
/CalendarServer/branches/users/glyph/migrate-merge/txdav/carddav/datastore/test/test_index_file.py:8690-8713
/CalendarServer/branches/users/glyph/misc-portability-fixes/txdav/carddav/datastore/test/test_index_file.py:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/carddav/datastore/test/test_index_file.py:6322-6334
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/test/test_index_file.py:6369
/CalendarServer/branches/users/glyph/new-export/txdav/carddav/datastore/test/test_index_file.py:7444-7485
/CalendarServer/branches/users/glyph/oracle/txdav/carddav/datastore/test/test_index_file.py:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls/txdav/carddav/datastore/test/test_index_file.py:7340-7351
/CalendarServer/branches/users/glyph/parallel-upgrade/txdav/carddav/datastore/test/test_index_file.py:8376-8400
/CalendarServer/branches/users/glyph/parallel-upgrade_to_1/txdav/carddav/datastore/test/test_index_file.py:8571-8583
/CalendarServer/branches/users/glyph/sendfdport/txdav/carddav/datastore/test/test_index_file.py:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-fixes/txdav/carddav/datastore/test/test_index_file.py:8436-8443
/CalendarServer/branches/users/glyph/sharedpool/txdav/carddav/datastore/test/test_index_file.py:6490-6550
/CalendarServer/branches/users/glyph/skip-lonely-vtimezones/txdav/carddav/datastore/test/test_index_file.py:8524-8535
/CalendarServer/branches/users/glyph/sql-store/txdav/carddav/datastore/test/test_index_file.py:5929-6073
/CalendarServer/branches/users/glyph/subtransactions/txdav/carddav/datastore/test/test_index_file.py:7248-7258
/CalendarServer/branches/users/glyph/table-alias/txdav/carddav/datastore/test/test_index_file.py:8651-8664
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/carddav/datastore/test/test_index_file.py:5084-5149
/CalendarServer/branches/users/sagen/applepush/txdav/carddav/datastore/test/test_index_file.py:8126-8184
/CalendarServer/branches/users/sagen/inboxitems/txdav/carddav/datastore/test/test_index_file.py:7380-7381
/CalendarServer/branches/users/sagen/locations-resources/txdav/carddav/datastore/test/test_index_file.py:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/carddav/datastore/test/test_index_file.py:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events/txdav/carddav/datastore/test/test_index_file.py:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/carddav/datastore/test/test_index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/carddav/datastore/test/test_index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/carddav/datastore/test/test_index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/carddav/datastore/test/test_index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/test/test_vcardindex.py:6322-6394
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120314/1e176170/attachment-0001.html>


More information about the calendarserver-changes mailing list