Browse Source

Ticket #460 - support multiple subtrees and filters

Description:
1. Support multiple subtrees
   new config parameter in windwows sync agreement:
   winSyncSubtreePair: <DS Subtree>:<AD Subtree>

   Example:
   winSyncSubtreePair: ou=OU1,dc=DSexample,dc=com:ou=OU1,DC=ADexample,DC=com
   winSyncSubtreePair: ou=OU2,dc=DSexample,dc=com:ou=OU2,DC=ADexample,DC=com
   winSyncSubtreePair: ou=OU3,dc=DSexample,dc=com:ou=OU3,DC=ADexample,DC=com

 . Attribute type "winSyncSubtreePair" is added to the objectclass
   "nsDSWindowsReplicationAgreement".
 . If "winSyncSubtreePair" is not set, there is not behavioral
   difference: the AD subtree "nsds7WindowsReplicaSubtree" and the
   DS subtree "nsds7DirectoryReplicaSubtree" are used for the sync
   target checks.
 . When "winSyncSubtreePair" is set, the above 2 config parameters
   are ignored.
   To determine if an entry is the target of the synchronization,
   the DN is examined whether the DN is a descendent of any of the
   subtrees or not. If it is, the subtree of the counter part is
   retrieved.
   Moving an entry from one subtree to another is synchronized.
   Members of a group is synchronized as long as the member entry
   is in any of the defined subtrees.

2. Support filters
   new config parameters in windwows sync agreement:
   winSyncWindowsFilter: <additional filter on AD>
   winSyncDirectoryFilter: <additional filter on DS>

   Example:
   winSyncWindowsFilter: (|(cn=*user*)(cn=*group*))
   winSyncDirectoryFilter: (|(uid=*user*)(cn=*group*))

 . The filters are set to the windows_userfilter and directory_
   userfilter in the private area in the windows agreement.  And
   when each server is searched the filters are added to the internal
   filter.  For instance, filters shown in the above Example allow
   synchronizing the entries which CN contains "user" or "group".

3. Misc
 . Added slapi_sdn_set_ndn_byref, slapi_sdn_set_ndn_passin, and
   slapi_sdn_common_ancestor to dn.c (see also slapi-plugin.h).
 . Fixed memory leaks.
 . Fixed some of the mixed indentations.

https://fedorahosted.org/389/ticket/460

Reviewed by Rich (Thank you!!)
Noriko Hosoi 12 years ago
parent
commit
c410b87b2b

+ 4 - 1
ldap/schema/02common.ldif

@@ -148,6 +148,9 @@ attributeTypes: ( 2.16.840.1.113730.3.1.1099 NAME 'winSyncInterval' DESC 'Netsca
 attributeTypes: ( 2.16.840.1.113730.3.1.1100 NAME 'oneWaySync' DESC 'Netscape defined attribute type'  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' )
 attributeTypes: ( 2.16.840.1.113730.3.1.1100 NAME 'oneWaySync' DESC 'Netscape defined attribute type'  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' )
 attributeTypes: ( 2.16.840.1.113730.3.1.1101 NAME 'nsRoleScopeDN' DESC 'Scope of a role'  SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE X-ORIGIN '389 Directory Server' )
 attributeTypes: ( 2.16.840.1.113730.3.1.1101 NAME 'nsRoleScopeDN' DESC 'Scope of a role'  SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE X-ORIGIN '389 Directory Server' )
 attributeTypes: ( 2.16.840.1.113730.3.1.2139 NAME 'winSyncMoveAction' DESC 'Netscape defined attribute type'  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' )
 attributeTypes: ( 2.16.840.1.113730.3.1.2139 NAME 'winSyncMoveAction' DESC 'Netscape defined attribute type'  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' )
+attributeTypes: ( 2.16.840.1.113730.3.1.2162 NAME 'winSyncDirectoryFilter' DESC 'Netscape defined attribute type'  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' )
+attributeTypes: ( 2.16.840.1.113730.3.1.2163 NAME 'winSyncWindowsFilter' DESC 'Netscape defined attribute type'  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' )
+attributeTypes: ( 2.16.840.1.113730.3.1.2164 NAME 'winSyncSubtreePair' DESC 'Netscape defined attribute type'  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape Directory Server' )
 attributeTypes: ( 1.3.6.1.1.4 NAME 'vendorName' EQUALITY 1.3.6.1.4.1.1466.109.114.1 SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation X-ORIGIN 'RFC 3045' )
 attributeTypes: ( 1.3.6.1.1.4 NAME 'vendorName' EQUALITY 1.3.6.1.4.1.1466.109.114.1 SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation X-ORIGIN 'RFC 3045' )
 attributeTypes: ( 1.3.6.1.1.5 NAME 'vendorVersion' EQUALITY 1.3.6.1.4.1.1466.109.114.1 SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation X-ORIGIN 'RFC 3045' )
 attributeTypes: ( 1.3.6.1.1.5 NAME 'vendorVersion' EQUALITY 1.3.6.1.4.1.1466.109.114.1 SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation X-ORIGIN 'RFC 3045' )
 attributeTypes: ( 2.16.840.1.113730.3.1.3023 NAME 'nsViewFilter' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape Directory Server' )
 attributeTypes: ( 2.16.840.1.113730.3.1.3023 NAME 'nsViewFilter' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape Directory Server' )
@@ -182,7 +185,7 @@ objectClasses: ( 2.16.840.1.113730.3.2.99 NAME 'cosSuperDefinition' DESC 'Netsca
 objectClasses: ( 2.16.840.1.113730.3.2.100 NAME 'cosClassicDefinition' DESC 'Netscape defined objectclass' SUP cosSuperDefinition MAY ( cosTemplateDn $ cosspecifier ) X-ORIGIN 'Netscape Directory Server' )
 objectClasses: ( 2.16.840.1.113730.3.2.100 NAME 'cosClassicDefinition' DESC 'Netscape defined objectclass' SUP cosSuperDefinition MAY ( cosTemplateDn $ cosspecifier ) X-ORIGIN 'Netscape Directory Server' )
 objectClasses: ( 2.16.840.1.113730.3.2.101 NAME 'cosPointerDefinition' DESC 'Netscape defined objectclass' SUP cosSuperDefinition MAY ( cosTemplateDn ) X-ORIGIN 'Netscape Directory Server' )
 objectClasses: ( 2.16.840.1.113730.3.2.101 NAME 'cosPointerDefinition' DESC 'Netscape defined objectclass' SUP cosSuperDefinition MAY ( cosTemplateDn ) X-ORIGIN 'Netscape Directory Server' )
 objectClasses: ( 2.16.840.1.113730.3.2.102 NAME 'cosIndirectDefinition' DESC 'Netscape defined objectclass' SUP cosSuperDefinition MAY ( cosIndirectSpecifier ) X-ORIGIN 'Netscape Directory Server' )
 objectClasses: ( 2.16.840.1.113730.3.2.102 NAME 'cosIndirectDefinition' DESC 'Netscape defined objectclass' SUP cosSuperDefinition MAY ( cosIndirectSpecifier ) X-ORIGIN 'Netscape Directory Server' )
-objectClasses: ( 2.16.840.1.113730.3.2.503 NAME 'nsDSWindowsReplicationAgreement' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsDS5ReplicaHost $ nsDS5ReplicaPort $ nsDS5ReplicaTransportInfo $ nsDS5ReplicaBindDN $ nsDS5ReplicaCredentials $ nsDS5ReplicaBindMethod $ nsDS5ReplicaRoot $ nsDS5ReplicatedAttributeList $ nsDS5ReplicaUpdateSchedule $ nsds5BeginReplicaRefresh $ description $ nsds50ruv $ nsruvReplicaLastModified $ nsds5ReplicaTimeout $ nsds5replicaChangesSentSinceStartup $ nsds5replicaLastUpdateEnd $ nsds5replicaLastUpdateStart $ nsds5replicaLastUpdateStatus $ nsds5replicaUpdateInProgress $ nsds5replicaLastInitEnd $ nsds5replicaLastInitStart $ nsds5replicaLastInitStatus $ nsds5debugreplicatimeout $ nsds5replicaBusyWaitTime $ nsds5replicaSessionPauseTime $ nsds7WindowsReplicaSubtree $ nsds7DirectoryReplicaSubtree $ nsds7NewWinUserSyncEnabled $ nsds7NewWinGroupSyncEnabled $ nsds7WindowsDomain $ nsds7DirsyncCookie $ winSyncInterval $ oneWaySync $ winSyncMoveAction $ nsds5ReplicaEnabled ) X-ORIGIN 'Netscape Directory Server' )
+objectClasses: ( 2.16.840.1.113730.3.2.503 NAME 'nsDSWindowsReplicationAgreement' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsDS5ReplicaHost $ nsDS5ReplicaPort $ nsDS5ReplicaTransportInfo $ nsDS5ReplicaBindDN $ nsDS5ReplicaCredentials $ nsDS5ReplicaBindMethod $ nsDS5ReplicaRoot $ nsDS5ReplicatedAttributeList $ nsDS5ReplicaUpdateSchedule $ nsds5BeginReplicaRefresh $ description $ nsds50ruv $ nsruvReplicaLastModified $ nsds5ReplicaTimeout $ nsds5replicaChangesSentSinceStartup $ nsds5replicaLastUpdateEnd $ nsds5replicaLastUpdateStart $ nsds5replicaLastUpdateStatus $ nsds5replicaUpdateInProgress $ nsds5replicaLastInitEnd $ nsds5replicaLastInitStart $ nsds5replicaLastInitStatus $ nsds5debugreplicatimeout $ nsds5replicaBusyWaitTime $ nsds5replicaSessionPauseTime $ nsds7WindowsReplicaSubtree $ nsds7DirectoryReplicaSubtree $ nsds7NewWinUserSyncEnabled $ nsds7NewWinGroupSyncEnabled $ nsds7WindowsDomain $ nsds7DirsyncCookie $ winSyncInterval $ oneWaySync $ winSyncMoveAction $ nsds5ReplicaEnabled $ winSyncDirectoryFilter $ winSyncWindowsFilter $ winSyncSubtreePair ) X-ORIGIN 'Netscape Directory Server' )
 objectClasses: ( 2.16.840.1.113730.3.2.128 NAME 'costemplate' DESC 'Netscape defined objectclass' SUP top MAY ( cn $ cospriority ) X-ORIGIN 'Netscape Directory Server' )
 objectClasses: ( 2.16.840.1.113730.3.2.128 NAME 'costemplate' DESC 'Netscape defined objectclass' SUP top MAY ( cn $ cospriority ) X-ORIGIN 'Netscape Directory Server' )
 objectClasses: ( 2.16.840.1.113730.3.2.304 NAME 'nsView' DESC 'Netscape defined objectclass' SUP top AUXILIARY MAY ( nsViewFilter $ description ) X-ORIGIN 'Netscape Directory Server' )
 objectClasses: ( 2.16.840.1.113730.3.2.304 NAME 'nsView' DESC 'Netscape defined objectclass' SUP top AUXILIARY MAY ( nsViewFilter $ description ) X-ORIGIN 'Netscape Directory Server' )
 objectClasses: ( 2.16.840.1.113730.3.2.316 NAME 'nsAttributeEncryption' DESC 'Netscape defined objectclass' SUP top MUST ( cn $ nsEncryptionAlgorithm ) X-ORIGIN 'Netscape Directory Server' )
 objectClasses: ( 2.16.840.1.113730.3.2.316 NAME 'nsAttributeEncryption' DESC 'Netscape defined objectclass' SUP top MUST ( cn $ nsEncryptionAlgorithm ) X-ORIGIN 'Netscape Directory Server' )

+ 3 - 0
ldap/servers/plugins/replication/repl5.h

@@ -182,6 +182,9 @@ extern const char *type_nsds7WindowsDomain;
 extern const char *type_winSyncInterval;
 extern const char *type_winSyncInterval;
 extern const char *type_oneWaySync;
 extern const char *type_oneWaySync;
 extern const char *type_winsyncMoveAction;
 extern const char *type_winsyncMoveAction;
+extern const char *type_winSyncWindowsFilter;
+extern const char *type_winSyncDirectoryFilter;
+extern const char *type_winSyncSubtreePair;
 
 
 /* To Allow Consumer Initialisation when adding an agreement - */
 /* To Allow Consumer Initialisation when adding an agreement - */
 extern const char *type_nsds5BeginReplicaRefresh;
 extern const char *type_nsds5BeginReplicaRefresh;

+ 3 - 0
ldap/servers/plugins/replication/repl_globals.c

@@ -145,6 +145,9 @@ const char *type_nsds7DirsyncCookie = "nsds7DirsyncCookie";
 const char *type_winSyncInterval = "winSyncInterval";
 const char *type_winSyncInterval = "winSyncInterval";
 const char *type_oneWaySync = "oneWaySync";
 const char *type_oneWaySync = "oneWaySync";
 const char *type_winsyncMoveAction = "winSyncMoveAction";
 const char *type_winsyncMoveAction = "winSyncMoveAction";
+const char *type_winSyncWindowsFilter = "winSyncWindowsFilter";
+const char *type_winSyncDirectoryFilter = "winSyncDirectoryFilter";
+const char *type_winSyncSubtreePair = "winSyncSubtreePair";
 
 
 /* To Allow Consumer Initialization when adding an agreement - */
 /* To Allow Consumer Initialization when adding an agreement - */
 const char *type_nsds5BeginReplicaRefresh = "nsds5BeginReplicaRefresh";
 const char *type_nsds5BeginReplicaRefresh = "nsds5BeginReplicaRefresh";

+ 13 - 6
ldap/servers/plugins/replication/windows_connection.c

@@ -846,12 +846,13 @@ send_dirsync_search(Repl_Connection *conn)
 		const char *op_string = NULL;
 		const char *op_string = NULL;
 		int rc;
 		int rc;
 		int scope = LDAP_SCOPE_SUBTREE;
 		int scope = LDAP_SCOPE_SUBTREE;
-		char *filter = slapi_ch_strdup("(objectclass=*)");
+		const char *userfilter = NULL;
+		char *filter = NULL;
 		char **attrs = NULL;
 		char **attrs = NULL;
 		LDAPControl **server_controls = NULL;
 		LDAPControl **server_controls = NULL;
 		int msgid;
 		int msgid;
-		/* need to strip the dn down to dc= */
 		const char *old_dn = slapi_sdn_get_ndn( windows_private_get_windows_subtree(conn->agmt) );
 		const char *old_dn = slapi_sdn_get_ndn( windows_private_get_windows_subtree(conn->agmt) );
+		/* LDAP_SERVER_DIRSYNC_OID requires the search base Naming Context */
 		char *dn = slapi_ch_strdup(strstr(old_dn, "dc="));
 		char *dn = slapi_ch_strdup(strstr(old_dn, "dc="));
 		char **exattrs = NULL;
 		char **exattrs = NULL;
 
 
@@ -861,8 +862,8 @@ send_dirsync_search(Repl_Connection *conn)
 		} else 
 		} else 
 		{
 		{
 			slapi_add_control_ext(&server_controls,
 			slapi_add_control_ext(&server_controls,
-								  windows_private_dirsync_control(conn->agmt),
-								  0 /* no copy - passin */);
+			                      windows_private_dirsync_control(conn->agmt),
+			                      0 /* no copy - passin */);
 		}
 		}
 
 
 		conn->last_operation = CONN_SEARCH;
 		conn->last_operation = CONN_SEARCH;
@@ -870,9 +871,15 @@ send_dirsync_search(Repl_Connection *conn)
 		op_string = "search";
 		op_string = "search";
 
 
 		LDAPDebug( LDAP_DEBUG_REPL, "Calling dirsync search request plugin\n", 0, 0, 0 );
 		LDAPDebug( LDAP_DEBUG_REPL, "Calling dirsync search request plugin\n", 0, 0, 0 );
+		userfilter = windows_private_get_windows_userfilter(conn->agmt);
+		if (userfilter) {
+			filter = slapi_ch_strdup(userfilter);
+		} else {
+			filter = slapi_ch_strdup("(objectclass=*)");
+		}
 
 
 		winsync_plugin_call_dirsync_search_params_cb(conn->agmt, old_dn, &dn, &scope, &filter,
 		winsync_plugin_call_dirsync_search_params_cb(conn->agmt, old_dn, &dn, &scope, &filter,
-													 &attrs, &server_controls);
+		                                             &attrs, &server_controls);
 		exattrs = windows_private_get_range_attrs(conn->agmt);
 		exattrs = windows_private_get_range_attrs(conn->agmt);
 		charray_merge(&attrs, exattrs, 0 /* pass in */);
 		charray_merge(&attrs, exattrs, 0 /* pass in */);
 		slapi_ch_free((void **)&exattrs); /* strings are passed in */
 		slapi_ch_free((void **)&exattrs); /* strings are passed in */
@@ -1038,7 +1045,7 @@ Slapi_Entry * windows_conn_get_search_result(Repl_Connection *conn)
 				{
 				{
 					char **exattrs = NULL;
 					char **exattrs = NULL;
 					slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,"received entry from dirsync: %s\n", dn);
 					slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,"received entry from dirsync: %s\n", dn);
-					lm = ldap_first_entry( conn->ld, res );			
+					lm = ldap_first_entry( conn->ld, res );
 					e = windows_private_get_curr_entry(conn->agmt); /* if range search, e != NULL */
 					e = windows_private_get_curr_entry(conn->agmt); /* if range search, e != NULL */
 					e = windows_LDAPMessage2Entry(e, conn, lm, 0, &exattrs);
 					e = windows_LDAPMessage2Entry(e, conn, lm, 0, &exattrs);
 					ldap_memfree(dn);
 					ldap_memfree(dn);

+ 489 - 92
ldap/servers/plugins/replication/windows_private.c

@@ -52,12 +52,12 @@
 
 
 struct windowsprivate {
 struct windowsprivate {
   
   
-  Slapi_DN *windows_subtree; /* DN of synchronized subtree  (on the windows side) */
+  Slapi_DN *windows_subtree;   /* DN of synchronized subtree  (on the windows side) */
   Slapi_DN *directory_subtree; /* DN of synchronized subtree on directory side */
   Slapi_DN *directory_subtree; /* DN of synchronized subtree on directory side */
-                                /* this simplifies the mapping as it's simply
-								   from the former to the latter container, or
-								   vice versa */
-  ber_int_t dirsync_flags;		
+                               /* this simplifies the mapping as it's simply
+                                  from the former to the latter container, or
+                                  vice versa */
+  ber_int_t dirsync_flags;
   ber_int_t dirsync_maxattributecount;
   ber_int_t dirsync_maxattributecount;
   char *dirsync_cookie; 
   char *dirsync_cookie; 
   int dirsync_cookie_len;
   int dirsync_cookie_len;
@@ -70,6 +70,7 @@ struct windowsprivate {
   /* This filter is used to determine if an entry belongs to this agreement.  We put it here
   /* This filter is used to determine if an entry belongs to this agreement.  We put it here
    * so we only have to allocate each filter once instead of doing it every time we receive a change. */
    * so we only have to allocate each filter once instead of doing it every time we receive a change. */
   Slapi_Filter *directory_filter; /* Used for checking if local entries need to be sync'd to AD */
   Slapi_Filter *directory_filter; /* Used for checking if local entries need to be sync'd to AD */
+  Slapi_Filter *windows_filter; /* Used for checking if remote entries need to be sync'd to DS */
   Slapi_Filter *deleted_filter; /* Used for checking if an entry is an AD tombstone */
   Slapi_Filter *deleted_filter; /* Used for checking if an entry is an AD tombstone */
   Slapi_Entry *raw_entry; /* "raw" un-schema processed last entry read from AD */
   Slapi_Entry *raw_entry; /* "raw" un-schema processed last entry read from AD */
   int keep_raw_entry; /* flag to control when the raw entry is set */
   int keep_raw_entry; /* flag to control when the raw entry is set */
@@ -79,9 +80,20 @@ struct windowsprivate {
   int move_action; /* Indicates what to do with DS entry if AD entry is moved out of scope */
   int move_action; /* Indicates what to do with DS entry if AD entry is moved out of scope */
   Slapi_Entry *curr_entry; /* entry being retrieved; used for the range retrieval */
   Slapi_Entry *curr_entry; /* entry being retrieved; used for the range retrieval */
   char **range_attrs; /* next attributes for the range retrieval */
   char **range_attrs; /* next attributes for the range retrieval */
+  char *windows_userfilter;
+  char *directory_userfilter;
+  struct subtreepair *subtree_pairs; /* Array of subtree pairs (winSyncSubtreePair) */
+  Slapi_DN *windows_treetop;   /* Common subtree top to sync on AD */
+                               /* If winSyncSubtreePair is not set, identical to windows_subtree.
+                                * If set, ancestor node of all AD subtrees. */
+  Slapi_DN *directory_treetop; /* Common subtree top to sync on DS */
+                               /* If winSyncSubtreePair is not set, identical to directory_subtree.
+                                * If set, ancestor node of all DS subtrees. */
 };
 };
 
 
 static void windows_private_set_windows_domain(const Repl_Agmt *ra, char *domain);
 static void windows_private_set_windows_domain(const Repl_Agmt *ra, char *domain);
+static subtreePair *create_subtree_pairs(char **pairs);
+static void free_subtree_pairs(subtreePair **pairs);
 
 
 static int
 static int
 true_value_from_string(char *val)
 true_value_from_string(char *val)
@@ -153,6 +165,10 @@ windows_parse_config_entry(Repl_Agmt *ra, const char *type, Slapi_Entry *e)
 		return retval;
 		return retval;
 	}
 	}
 	
 	
+	/* 
+	 * if winSyncSubtreePair is set, WindowsReplicaArea and DirectoryReplicaArea
+	 * are ignored.
+	 */
 	if (type == NULL || slapi_attr_types_equivalent(type,type_nsds7WindowsReplicaArea))
 	if (type == NULL || slapi_attr_types_equivalent(type,type_nsds7WindowsReplicaArea))
 	{
 	{
 		tmpstr = slapi_entry_attr_get_charptr(e, type_nsds7WindowsReplicaArea);
 		tmpstr = slapi_entry_attr_get_charptr(e, type_nsds7WindowsReplicaArea);
@@ -295,6 +311,29 @@ windows_parse_config_entry(Repl_Agmt *ra, const char *type, Slapi_Entry *e)
 		}
 		}
 		retval = 1;
 		retval = 1;
 	}
 	}
+	if (type == NULL || slapi_attr_types_equivalent(type,type_winSyncWindowsFilter))
+	{
+		tmpstr = slapi_entry_attr_get_charptr(e, type_winSyncWindowsFilter);
+		windows_private_set_windows_userfilter(ra, tmpstr); /* if NULL, set it */
+		retval = 1;
+	}
+	if (type == NULL || slapi_attr_types_equivalent(type,type_winSyncDirectoryFilter))
+	{
+		tmpstr = slapi_entry_attr_get_charptr(e, type_winSyncDirectoryFilter);
+		windows_private_set_directory_userfilter(ra, tmpstr); /* if NULL, set it */
+		retval = 1;
+	}
+	if (type == NULL || slapi_attr_types_equivalent(type, type_winSyncSubtreePair))
+	{
+		char **parray = slapi_entry_attr_get_charray(e, type_winSyncSubtreePair);
+
+		/* If winSyncSubtreePair is not set, subtree_pairs is NULL */
+		windows_private_set_subtreepairs(ra, parray);
+		slapi_ch_array_free(parray);
+		retval = 1;
+	}
+	windows_private_set_windows_treetop(ra, NULL);
+	windows_private_set_directory_treetop(ra, NULL);
 
 
 	return retval;
 	return retval;
 }
 }
@@ -370,8 +409,14 @@ Dirsync_Private* windows_private_new()
 
 
 	dp->dirsync_maxattributecount = -1;
 	dp->dirsync_maxattributecount = -1;
 	dp->directory_filter = NULL;
 	dp->directory_filter = NULL;
+	dp->windows_filter = NULL;
 	dp->deleted_filter = NULL;
 	dp->deleted_filter = NULL;
 	dp->sync_interval = PERIODIC_DIRSYNC_INTERVAL;
 	dp->sync_interval = PERIODIC_DIRSYNC_INTERVAL;
+	dp->windows_userfilter = NULL;
+	dp->directory_userfilter = NULL;
+	dp->subtree_pairs = NULL;
+	dp->windows_treetop = NULL;
+	dp->directory_treetop = NULL;
 
 
 	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_new\n" );
 	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_new\n" );
 	return dp;
 	return dp;
@@ -380,6 +425,7 @@ Dirsync_Private* windows_private_new()
 
 
 void windows_agreement_delete(Repl_Agmt *ra)
 void windows_agreement_delete(Repl_Agmt *ra)
 {
 {
+	const subtreePair *sp;
 
 
 	Dirsync_Private *dp = (Dirsync_Private *) agmt_get_priv(ra);
 	Dirsync_Private *dp = (Dirsync_Private *) agmt_get_priv(ra);
 	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_delete\n" );
 	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_delete\n" );
@@ -394,12 +440,25 @@ void windows_agreement_delete(Repl_Agmt *ra)
 	slapi_sdn_free(&dp->directory_subtree);
 	slapi_sdn_free(&dp->directory_subtree);
 	slapi_sdn_free(&dp->windows_subtree);
 	slapi_sdn_free(&dp->windows_subtree);
 	slapi_filter_free(dp->directory_filter, 1);
 	slapi_filter_free(dp->directory_filter, 1);
+	slapi_filter_free(dp->windows_filter, 1);
 	slapi_filter_free(dp->deleted_filter, 1);
 	slapi_filter_free(dp->deleted_filter, 1);
 	slapi_entry_free(dp->raw_entry);
 	slapi_entry_free(dp->raw_entry);
 	slapi_ch_free_string(&dp->windows_domain);
 	slapi_ch_free_string(&dp->windows_domain);
 	dp->raw_entry = NULL;
 	dp->raw_entry = NULL;
 	dp->api_cookie = NULL;
 	dp->api_cookie = NULL;
-	slapi_ch_free((void **)dp);
+	slapi_ch_free_string(&dp->dirsync_cookie);
+	dp->dirsync_cookie_len = 0;
+
+	slapi_ch_free_string(&dp->windows_userfilter);
+	slapi_ch_free_string(&dp->directory_userfilter);
+	slapi_sdn_free((Slapi_DN **)&dp->windows_treetop);
+	slapi_sdn_free((Slapi_DN **)&dp->directory_treetop);
+	for (sp = dp->subtree_pairs; sp && sp->ADsubtree && sp->DSsubtree; sp++) {
+		slapi_sdn_free((Slapi_DN **)&sp->ADsubtree);
+		slapi_sdn_free((Slapi_DN **)&sp->DSsubtree);
+	}
+	slapi_ch_free((void **)&dp->subtree_pairs);
+	slapi_ch_free((void **)&dp);
 
 
 	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_delete\n" );
 	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_delete\n" );
 
 
@@ -407,183 +466,228 @@ void windows_agreement_delete(Repl_Agmt *ra)
 
 
 int windows_private_get_isnt4(const Repl_Agmt *ra)
 int windows_private_get_isnt4(const Repl_Agmt *ra)
 {
 {
-		Dirsync_Private *dp;
+	Dirsync_Private *dp;
 
 
-		LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_isnt4\n" );
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_isnt4\n" );
 
 
-        PR_ASSERT(ra);
+	PR_ASSERT(ra);
 
 
-		dp = (Dirsync_Private *) agmt_get_priv(ra);
-		PR_ASSERT (dp);
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
 		
 		
-		LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_isnt4\n" );
-	
-		return dp->isnt4;	
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_isnt4\n" );
+
+	return dp->isnt4;
 }
 }
 
 
 void windows_private_set_isnt4(const Repl_Agmt *ra, int isit)
 void windows_private_set_isnt4(const Repl_Agmt *ra, int isit)
 {
 {
-		Dirsync_Private *dp;
+	Dirsync_Private *dp;
 
 
-		LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_set_isnt4\n" );
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_set_isnt4\n" );
 
 
-        PR_ASSERT(ra);
+	PR_ASSERT(ra);
 
 
-		dp = (Dirsync_Private *) agmt_get_priv(ra);
-		PR_ASSERT (dp);
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
 
 
-		dp->isnt4 = isit;
-		
-		LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_set_isnt4\n" );
+	dp->isnt4 = isit;
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_set_isnt4\n" );
 }
 }
 
 
 int windows_private_get_iswin2k3(const Repl_Agmt *ra)
 int windows_private_get_iswin2k3(const Repl_Agmt *ra)
 {
 {
-		Dirsync_Private *dp;
+	Dirsync_Private *dp;
 
 
-		LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_iswin2k3\n" );
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_iswin2k3\n" );
 
 
 	PR_ASSERT(ra);
 	PR_ASSERT(ra);
 
 
-		dp = (Dirsync_Private *) agmt_get_priv(ra);
-		PR_ASSERT (dp);
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
 
 
-		LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_iswin2k3\n" );
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_iswin2k3\n" );
 
 
-		return dp->iswin2k3;
+	return dp->iswin2k3;
 }
 }
 
 
 void windows_private_set_iswin2k3(const Repl_Agmt *ra, int isit)
 void windows_private_set_iswin2k3(const Repl_Agmt *ra, int isit)
 {
 {
-		Dirsync_Private *dp;
+	Dirsync_Private *dp;
 
 
-		LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_set_iswin2k3\n" );
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_set_iswin2k3\n" );
 
 
 	PR_ASSERT(ra);
 	PR_ASSERT(ra);
 
 
-		dp = (Dirsync_Private *) agmt_get_priv(ra);
-		PR_ASSERT (dp);
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
 
 
-		dp->iswin2k3 = isit;
+	dp->iswin2k3 = isit;
 
 
-		LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_set_iswin2k3\n" );
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_set_iswin2k3\n" );
 }
 }
 
 
 /* Returns a copy of the Slapi_Filter pointer.  The caller should not free it */
 /* Returns a copy of the Slapi_Filter pointer.  The caller should not free it */
 Slapi_Filter* windows_private_get_directory_filter(const Repl_Agmt *ra)
 Slapi_Filter* windows_private_get_directory_filter(const Repl_Agmt *ra)
 {
 {
-		Dirsync_Private *dp;
+	Dirsync_Private *dp;
 
 
-		LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_directory_filter\n" );
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_directory_filter\n" );
 
 
 	PR_ASSERT(ra);
 	PR_ASSERT(ra);
 
 
-		dp = (Dirsync_Private *) agmt_get_priv(ra);
-		PR_ASSERT (dp);
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
 
 
-		if (dp->directory_filter == NULL) {
-			char *string_filter = slapi_ch_strdup("(&(|(objectclass=ntuser)(objectclass=ntgroup))(ntUserDomainId=*))");
-			/* The filter gets freed in windows_agreement_delete() */
-                        dp->directory_filter = slapi_str2filter( string_filter );
-			slapi_ch_free_string(&string_filter);
+	if (dp->directory_filter == NULL) {
+		char *string_filter = NULL;
+		const char *userfilter = windows_private_get_directory_userfilter(ra);
+		if (userfilter) {
+			if ('(' == *userfilter) {
+				string_filter = slapi_ch_smprintf("(&(|(objectclass=ntuser)(objectclass=ntgroup))(ntUserDomainId=*)%s)",
+				                                  userfilter);
+			} else {
+				string_filter = slapi_ch_smprintf("(&(|(objectclass=ntuser)(objectclass=ntgroup))(ntUserDomainId=*)(%s))",
+				                                  userfilter);
+			}
+		} else {
+			string_filter = slapi_ch_strdup("(&(|(objectclass=ntuser)(objectclass=ntgroup))(ntUserDomainId=*))");
 		}
 		}
+		/* The filter gets freed in windows_agreement_delete() */
+		dp->directory_filter = slapi_str2filter( string_filter );
+		slapi_ch_free_string(&string_filter);
+	}
 
 
-		LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_directory_filter\n" );
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_directory_filter\n" );
 
 
-		return dp->directory_filter;
+	return dp->directory_filter;
 }
 }
 
 
 /* Returns a copy of the Slapi_Filter pointer.  The caller should not free it */
 /* Returns a copy of the Slapi_Filter pointer.  The caller should not free it */
-Slapi_Filter* windows_private_get_deleted_filter(const Repl_Agmt *ra)
+Slapi_Filter*
+windows_private_get_windows_filter(const Repl_Agmt *ra)
 {
 {
-		Dirsync_Private *dp;
+	Dirsync_Private *dp;
 
 
-		LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_deleted_filter\n" );
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_windows_filter\n" );
 
 
 	PR_ASSERT(ra);
 	PR_ASSERT(ra);
 
 
-		dp = (Dirsync_Private *) agmt_get_priv(ra);
-		PR_ASSERT (dp);
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
 
 
-		if (dp->deleted_filter == NULL) {
-			char *string_filter = slapi_ch_strdup("(isdeleted=*)");
+	if (dp->windows_filter == NULL) {
+		const char *userfilter = windows_private_get_windows_userfilter(ra);
+		if (userfilter) {
+			char *string_filter = NULL;
+			if ('(' == *userfilter) {
+				string_filter = slapi_ch_strdup(userfilter);
+			} else {
+				string_filter = slapi_ch_smprintf("(%s)", userfilter);
+			}
 			/* The filter gets freed in windows_agreement_delete() */
 			/* The filter gets freed in windows_agreement_delete() */
-			dp->deleted_filter = slapi_str2filter( string_filter );
+			dp->windows_filter = slapi_str2filter( string_filter );
 			slapi_ch_free_string(&string_filter);
 			slapi_ch_free_string(&string_filter);
 		}
 		}
+	}
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_windows_filter\n" );
+
+	return dp->windows_filter;
+}
+
+/* Returns a copy of the Slapi_Filter pointer.  The caller should not free it */
+Slapi_Filter* windows_private_get_deleted_filter(const Repl_Agmt *ra)
+{
+	Dirsync_Private *dp;
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_deleted_filter\n" );
+
+	PR_ASSERT(ra);
+
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
+
+	if (dp->deleted_filter == NULL) {
+		char *string_filter = slapi_ch_strdup("(isdeleted=*)");
+		/* The filter gets freed in windows_agreement_delete() */
+		dp->deleted_filter = slapi_str2filter( string_filter );
+		slapi_ch_free_string(&string_filter);
+	}
 
 
-		LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_deleted_filter\n" );
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_deleted_filter\n" );
 
 
-		return dp->deleted_filter;
+	return dp->deleted_filter;
 }
 }
 
 
 /* Returns a copy of the Slapi_DN pointer, no need to free it */
 /* Returns a copy of the Slapi_DN pointer, no need to free it */
 const Slapi_DN* windows_private_get_windows_subtree (const Repl_Agmt *ra)
 const Slapi_DN* windows_private_get_windows_subtree (const Repl_Agmt *ra)
 {
 {
-		Dirsync_Private *dp;
+	Dirsync_Private *dp;
 
 
-		LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_windows_subtree\n" );
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_windows_subtree\n" );
 
 
-        PR_ASSERT(ra);
+	PR_ASSERT(ra);
 
 
-		dp = (Dirsync_Private *) agmt_get_priv(ra);
-		PR_ASSERT (dp);
-		
-		LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_windows_subtree\n" );
-	
-		return dp->windows_subtree;	
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_windows_subtree\n" );
+
+	return dp->windows_subtree;
 }
 }
 
 
 const char *
 const char *
 windows_private_get_windows_domain(const Repl_Agmt *ra)
 windows_private_get_windows_domain(const Repl_Agmt *ra)
 {
 {
-		Dirsync_Private *dp;
+	Dirsync_Private *dp;
 
 
-		LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_windows_domain\n" );
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_windows_domain\n" );
 
 
-        PR_ASSERT(ra);
+	PR_ASSERT(ra);
 
 
-		dp = (Dirsync_Private *) agmt_get_priv(ra);
-		PR_ASSERT (dp);
-		
-		LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_windows_domain\n" );
-	
-		return dp->windows_domain;	
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_windows_domain\n" );
+
+	return dp->windows_domain;	
 }
 }
 
 
 static void
 static void
 windows_private_set_windows_domain(const Repl_Agmt *ra, char *domain)
 windows_private_set_windows_domain(const Repl_Agmt *ra, char *domain)
 {
 {
-		Dirsync_Private *dp;
+	Dirsync_Private *dp;
 
 
-		LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_set_windows_domain\n" );
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_set_windows_domain\n" );
 
 
-        PR_ASSERT(ra);
+	PR_ASSERT(ra);
 
 
-		dp = (Dirsync_Private *) agmt_get_priv(ra);
-		PR_ASSERT (dp);
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
 
 
-		slapi_ch_free_string(&dp->windows_domain);
-		dp->windows_domain = domain;
-		
-		LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_set_windows_domain\n" );
-	}
+	slapi_ch_free_string(&dp->windows_domain);
+	dp->windows_domain = domain;
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_set_windows_domain\n" );
+}
 
 
 /* Returns a copy of the Slapi_DN pointer, no need to free it */
 /* Returns a copy of the Slapi_DN pointer, no need to free it */
 const Slapi_DN* windows_private_get_directory_subtree (const Repl_Agmt *ra)
 const Slapi_DN* windows_private_get_directory_subtree (const Repl_Agmt *ra)
 {
 {
-		Dirsync_Private *dp;
+	Dirsync_Private *dp;
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_directory_replarea\n" );
 
 
-		LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_directory_replarea\n" );
+	PR_ASSERT(ra);
 
 
-        PR_ASSERT(ra);
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
 
 
-		dp = (Dirsync_Private *) agmt_get_priv(ra);
-		PR_ASSERT (dp);
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_directory_replarea\n" );
 
 
-		LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_directory_replarea\n" );
-	
-		return dp->directory_subtree; 
+	return dp->directory_subtree; 
 }
 }
 
 
 /* Takes a copy of the sdn passed in */
 /* Takes a copy of the sdn passed in */
@@ -724,6 +828,299 @@ void windows_private_set_one_way(const Repl_Agmt *ra, int value)
 	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_set_one_way\n" );
 	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_set_one_way\n" );
 }
 }
 
 
+const char* 
+windows_private_get_windows_userfilter(const Repl_Agmt *ra)
+{
+	Dirsync_Private *dp;
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_windows_userfilter\n" );
+
+	PR_ASSERT(ra);
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_windows_userfilter\n" );
+
+	return dp->windows_userfilter;
+}
+
+/* filter is passed in */
+void
+windows_private_set_windows_userfilter(const Repl_Agmt *ra, char *filter)
+{
+	Dirsync_Private *dp;
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_set_windows_userfilter\n" );
+
+	PR_ASSERT(ra);
+
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
+
+	slapi_ch_free_string(&dp->windows_userfilter);
+	dp->windows_userfilter = filter;
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_set_windows_userfilter\n" );
+}
+
+const char* 
+windows_private_get_directory_userfilter(const Repl_Agmt *ra)
+{
+	Dirsync_Private *dp;
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_directory_userfilter\n" );
+
+	PR_ASSERT(ra);
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_directory_userfilter\n" );
+
+	return dp->directory_userfilter;
+}
+
+/* filter is passed in */
+void
+windows_private_set_directory_userfilter(const Repl_Agmt *ra, char *filter)
+{
+	Dirsync_Private *dp;
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_set_directory_userfilter\n" );
+
+	PR_ASSERT(ra);
+
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
+
+	slapi_ch_free_string(&dp->directory_userfilter);
+	dp->directory_userfilter = filter;
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_set_directory_userfilter\n" );
+}
+
+const subtreePair* 
+windows_private_get_subtreepairs(const Repl_Agmt *ra)
+{
+	Dirsync_Private *dp;
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_subtreepairs\n" );
+
+	PR_ASSERT(ra);
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_subtreepairs\n" );
+
+	return dp->subtree_pairs;
+}
+
+/* parray is NOT passed in */
+void
+windows_private_set_subtreepairs(const Repl_Agmt *ra, char **parray)
+{
+	Dirsync_Private *dp;
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_set_subtreepairs\n" );
+
+	PR_ASSERT(ra);
+
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
+
+	free_subtree_pairs(&(dp->subtree_pairs));
+	dp->subtree_pairs = create_subtree_pairs(parray);
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_set_subtreepairs\n" );
+}
+
+/* 
+ * winSyncSubtreePair: <DS_SUBTREE>:<WINDOWS_SUBTREE>
+ * E.g.,
+ * winSyncSubtreePair: ou=people,dc=example,dc=com:CN=users,DC=example,DC=com
+ * winSyncSubtreePair: ou=adminpeople,dc=example,dc=com:CN=adminusers,DC=example,DC=com
+ */
+static subtreePair *
+create_subtree_pairs(char **pairs)
+{
+	subtreePair *subtree_pairs = NULL;
+	subtreePair *spp;
+	char **ptr;
+	char *p0, *p1;
+	char *saveptr;
+	int cnt;
+
+	for (cnt = 0, ptr = pairs; ptr && *ptr; cnt++, ptr++) ;
+	if (0 == cnt) {
+		return NULL;
+	}
+	subtree_pairs = (subtreePair *)slapi_ch_calloc(cnt + 1, sizeof(subtreePair));
+	spp = subtree_pairs;
+
+	for (ptr = pairs; ptr && *ptr; ptr++) {
+		p0 = ldap_utf8strtok_r(*ptr, ":", &saveptr);
+		p1 = ldap_utf8strtok_r(NULL, ":", &saveptr);
+		spp->DSsubtree = slapi_sdn_new_dn_byval(p0);
+		if (NULL == spp->DSsubtree) {
+			LDAPDebug1Arg(LDAP_DEBUG_ANY, 
+			              "create_subtree_pairs: "
+			              "Ignoring invalid DS subtree \"%s\".\n",
+			              p0);
+			continue;
+		}
+		spp->ADsubtree = slapi_sdn_new_dn_byval(p1);
+		if (NULL == spp->ADsubtree) {
+			LDAPDebug1Arg(LDAP_DEBUG_ANY, 
+			              "create_subtree_pairs: "
+			              "Ignoring invalid AD subtree \"%s\".\n",
+			              p1);
+			slapi_sdn_free(&(spp->DSsubtree));
+			continue;
+		}
+		spp++;
+	}
+	return subtree_pairs;
+}
+
+static void
+free_subtree_pairs(subtreePair **pairs)
+{
+	subtreePair *p;
+
+	if (NULL == pairs) {
+		return;
+	}
+	for (p = *pairs; p; p++) {
+		slapi_sdn_free(&(p->ADsubtree));
+		slapi_sdn_free(&(p->DSsubtree));
+	}
+	slapi_ch_free((void **)pairs);
+}
+
+const Slapi_DN* 
+windows_private_get_windows_treetop(const Repl_Agmt *ra)
+{
+	Dirsync_Private *dp;
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_windows_treetop\n" );
+
+	PR_ASSERT(ra);
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_windows_treetop\n" );
+
+	return dp->windows_treetop;
+}
+
+/* treetop is NOT passed in */
+void
+windows_private_set_windows_treetop(const Repl_Agmt *ra, char *treetop)
+{
+	Dirsync_Private *dp;
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_set_windows_treetop\n" );
+
+	PR_ASSERT(ra);
+
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
+
+	slapi_sdn_free(&(dp->windows_treetop));
+	if (treetop) {
+		dp->windows_treetop = slapi_sdn_new_dn_byval(treetop);
+	} else {
+		const subtreePair *subtree_pairs = windows_private_get_subtreepairs(ra);
+		const subtreePair *sp;
+		if (subtree_pairs) {
+			Slapi_DN *treetop_sdn = NULL;
+			for (sp = subtree_pairs; sp && sp->ADsubtree; sp++) {
+				if (NULL == treetop_sdn) {
+					treetop_sdn = slapi_sdn_dup(sp->ADsubtree);
+				} else {
+					Slapi_DN *prev = treetop_sdn;
+					treetop_sdn = slapi_sdn_common_ancestor(prev, sp->ADsubtree);
+					slapi_sdn_free(&prev);
+				}
+			}
+			if (treetop_sdn) {
+				dp->windows_treetop = treetop_sdn;
+			} else {
+				LDAPDebug0Args(LDAP_DEBUG_ANY, 
+				               "windows_private_set_windows_treetop: "
+				               "winSyncSubtreePair contains inconsistent Windows subtrees.\n");
+				dp->windows_treetop = NULL;
+			}
+		} else {
+			const Slapi_DN *windows_subtree = windows_private_get_windows_subtree(ra);
+			dp->windows_treetop = slapi_sdn_dup(windows_subtree);
+		}
+	}
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_set_windows_treetop\n" );
+}
+
+const Slapi_DN* 
+windows_private_get_directory_treetop(const Repl_Agmt *ra)
+{
+	Dirsync_Private *dp;
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_directory_treetop\n" );
+
+	PR_ASSERT(ra);
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_directory_treetop\n" );
+
+	return dp->directory_treetop;
+}
+
+/* treetop is NOT passed in */
+void
+windows_private_set_directory_treetop(const Repl_Agmt *ra, char *treetop)
+{
+	Dirsync_Private *dp;
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_set_directory_treetop\n" );
+
+	PR_ASSERT(ra);
+
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
+
+	slapi_sdn_free(&(dp->directory_treetop));
+	if (treetop) {
+		dp->directory_treetop = slapi_sdn_new_dn_byval(treetop);
+	} else {
+		const subtreePair *subtree_pairs = windows_private_get_subtreepairs(ra);
+		if (subtree_pairs) {
+			const subtreePair *sp;
+			Slapi_DN *treetop_sdn = NULL;
+			for (sp = subtree_pairs; sp && sp->DSsubtree; sp++) {
+				if (NULL == treetop_sdn) {
+					treetop_sdn = slapi_sdn_dup(sp->DSsubtree);
+				} else {
+					Slapi_DN *prev = treetop_sdn;
+					treetop_sdn = slapi_sdn_common_ancestor(prev, sp->DSsubtree);
+					slapi_sdn_free(&prev);
+				}
+			}
+			if (treetop_sdn) {
+				dp->directory_treetop = treetop_sdn;
+			} else {
+				LDAPDebug0Args(LDAP_DEBUG_ANY, 
+				               "windows_private_set_directory_treetop: "
+				               "winSyncSubtreePair contains inconsistent Windows subtrees.\n");
+				dp->directory_treetop = NULL;
+			}
+		} else {
+			const Slapi_DN *directory_subtree = windows_private_get_directory_subtree(ra);
+			dp->directory_treetop = slapi_sdn_dup(directory_subtree);
+		}
+	}
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_set_directory_treetop\n" );
+}
 
 
 /* 
 /* 
 	This function returns the current Dirsync_Private that's inside 
 	This function returns the current Dirsync_Private that's inside 
@@ -744,7 +1141,7 @@ LDAPControl* windows_private_dirsync_control(const Repl_Agmt *ra)
 	
 	
 	dp = (Dirsync_Private *) agmt_get_priv(ra);
 	dp = (Dirsync_Private *) agmt_get_priv(ra);
 	PR_ASSERT (dp);
 	PR_ASSERT (dp);
-	ber = 	ber_alloc();
+	ber = ber_alloc();
 
 
 	ber_printf( ber, "{iio}", dp->dirsync_flags, dp->dirsync_maxattributecount, dp->dirsync_cookie ? dp->dirsync_cookie : "", dp->dirsync_cookie_len );
 	ber_printf( ber, "{iio}", dp->dirsync_flags, dp->dirsync_maxattributecount, dp->dirsync_cookie ? dp->dirsync_cookie : "", dp->dirsync_cookie_len );
 
 

+ 416 - 136
ldap/servers/plugins/replication/windows_protocol_util.c

@@ -1136,10 +1136,33 @@ process_replay_add(Private_Repl_Protocol *prp, Slapi_Entry *add_entry, Slapi_Ent
 
 
 			if (cn_string) {
 			if (cn_string) {
 				char *container_str = NULL;
 				char *container_str = NULL;
-				const char *suffix = slapi_sdn_get_dn(windows_private_get_windows_subtree(prp->agmt));
+				const char *suffix = NULL;
+				const Slapi_DN *local_sdn = NULL;
+				const subtreePair* subtree_pairs = NULL;
+				const subtreePair* sp = NULL;
+
+				/* Figure out the Windows subtree from the local subtree... */
+				local_sdn = slapi_entry_get_sdn_const(local_entry);
+				subtree_pairs = windows_private_get_subtreepairs(prp->agmt);
+				if (subtree_pairs) {
+					for (sp = subtree_pairs; sp && sp->DSsubtree; sp++) {
+						if (slapi_sdn_scope_test(local_sdn, sp->DSsubtree, LDAP_SCOPE_SUBTREE)) {
+							suffix = slapi_sdn_get_dn(sp->ADsubtree);
+							break;
+						}
+					}
+				}
+				if (NULL == suffix) {
+					suffix = slapi_sdn_get_dn(windows_private_get_windows_subtree(prp->agmt));
+				}
 
 
-				container_str = extract_container(slapi_entry_get_sdn_const(local_entry),
-					windows_private_get_directory_subtree(prp->agmt));
+				if (sp && sp->DSsubtree) {
+					container_str = extract_container(slapi_entry_get_sdn_const(local_entry),
+					                                  sp->DSsubtree);
+				} else {
+					container_str = extract_container(slapi_entry_get_sdn_const(local_entry),
+					                                  windows_private_get_directory_subtree(prp->agmt));
+				}
 				new_dn_string = slapi_create_dn_string("cn=\"%s\",%s%s", cn_string, container_str, suffix);
 				new_dn_string = slapi_create_dn_string("cn=\"%s\",%s%s", cn_string, container_str, suffix);
 
 
 				if (new_dn_string) {
 				if (new_dn_string) {
@@ -1311,7 +1334,9 @@ process_replay_rename(Private_Repl_Protocol *prp,
 	char *remote_rdn = NULL;
 	char *remote_rdn = NULL;
 	char *remote_dn = NULL;
 	char *remote_dn = NULL;
 	char *local_pndn = NULL;
 	char *local_pndn = NULL;
-	
+	const subtreePair* subtree_pairs = NULL;
+	const subtreePair* sp = NULL;
+
 	if (NULL == local_origsdn || NULL == local_newentry) {
 	if (NULL == local_origsdn || NULL == local_newentry) {
 		slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
 		slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
 		                "process_replay_rename: %s is empty\n",
 		                "process_replay_rename: %s is empty\n",
@@ -1324,29 +1349,40 @@ process_replay_rename(Private_Repl_Protocol *prp,
 
 
 	/* Generate newsuperior for AD */
 	/* Generate newsuperior for AD */
 	winrepl_agmt = prp->agmt;
 	winrepl_agmt = prp->agmt;
-	remote_subtree =
-		slapi_sdn_get_ndn(windows_private_get_windows_subtree(winrepl_agmt));
-	local_subtree =
-		slapi_sdn_get_ndn(windows_private_get_directory_subtree(winrepl_agmt));
-	if (NULL == remote_subtree || NULL == local_subtree ||
-		'\0' == *remote_subtree || '\0' == *local_subtree) {
+	remote_subtree = slapi_sdn_get_ndn(windows_private_get_windows_subtree(winrepl_agmt));
+	local_subtree = slapi_sdn_get_ndn(windows_private_get_directory_subtree(winrepl_agmt));
+	subtree_pairs = windows_private_get_subtreepairs(winrepl_agmt);
+	if ((NULL == remote_subtree || NULL == local_subtree ||
+		 '\0' == *remote_subtree || '\0' == *local_subtree) &&
+		(NULL == subtree_pairs)) {
 		slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
 		slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
 					"process_replay_rename: local subtree \"%s\" or "
 					"process_replay_rename: local subtree \"%s\" or "
-					"remote subtree \"%s\" is empty\n",
+					"remote subtree \"%s\" and "
+					"subtree_pairs are empty\n",
 					local_subtree?local_subtree:"empty",
 					local_subtree?local_subtree:"empty",
 					remote_subtree?remote_subtree:"empty");
 					remote_subtree?remote_subtree:"empty");
 		goto bail;
 		goto bail;
 	}
 	}
-	/* if given, newparent is already normzlized; just ignore the case */
+	/* if given, newparent is already normalized; just ignore the case */
 	if (newparent) {
 	if (newparent) {
 		norm_newparent = slapi_ch_strdup(newparent);
 		norm_newparent = slapi_ch_strdup(newparent);
+		slapi_dn_ignore_case(norm_newparent);
 	} else {
 	} else {
 		/* newparent is NULL; set the original parent */
 		/* newparent is NULL; set the original parent */
 		/* slapi_dn_parent returns the dup'ed dn */
 		/* slapi_dn_parent returns the dup'ed dn */
 		norm_newparent = slapi_dn_parent(slapi_sdn_get_ndn(local_origsdn));
 		norm_newparent = slapi_dn_parent(slapi_sdn_get_ndn(local_origsdn));
 	}
 	}
-	slapi_dn_ignore_case(norm_newparent);
-	p = strstr(norm_newparent, local_subtree);
+	p = NULL;
+	if (subtree_pairs && subtree_pairs->DSsubtree) {
+		for (sp = subtree_pairs; sp && sp->DSsubtree; sp++) {
+			p = strstr(norm_newparent, slapi_sdn_get_ndn(sp->DSsubtree));
+			if (p) {
+				break;
+			}
+		}
+	} else {
+		p = strstr(norm_newparent, local_subtree);
+	}
 	if (NULL == p) {
 	if (NULL == p) {
 		slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
 		slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
 					"process_replay_rename: new superior \"%s\" is not "
 					"process_replay_rename: new superior \"%s\" is not "
@@ -1354,11 +1390,20 @@ process_replay_rename(Private_Repl_Protocol *prp,
 					norm_newparent, local_subtree);
 					norm_newparent, local_subtree);
 		goto bail; /* not in the subtree */
 		goto bail; /* not in the subtree */
 	}
 	}
-	*p = '\0';
-	if (p == norm_newparent) {
-		newsuperior = PR_smprintf("%s", remote_subtree);
+	if (sp && sp->DSsubtree) {
+		if (p == norm_newparent) {
+			newsuperior = PR_smprintf("%s", slapi_sdn_get_ndn(sp->ADsubtree));
+		} else {
+			*p = '\0';
+			newsuperior = PR_smprintf("%s%s", norm_newparent, slapi_sdn_get_ndn(sp->ADsubtree));
+		}
 	} else {
 	} else {
-		newsuperior = PR_smprintf("%s%s", norm_newparent, remote_subtree);
+		if (p == norm_newparent) {
+			newsuperior = PR_smprintf("%s", remote_subtree);
+		} else {
+			*p = '\0';
+			newsuperior = PR_smprintf("%s%s", norm_newparent, remote_subtree);
+		}
 	}
 	}
 
 
 	if (is_user) {
 	if (is_user) {
@@ -1385,7 +1430,16 @@ process_replay_rename(Private_Repl_Protocol *prp,
 	/* local parent normalized dn */
 	/* local parent normalized dn */
 	local_pndn = /* strdup'ed */
 	local_pndn = /* strdup'ed */
 			slapi_dn_parent((const char *)slapi_sdn_get_ndn(local_origsdn));
 			slapi_dn_parent((const char *)slapi_sdn_get_ndn(local_origsdn));
-	p = strstr(local_pndn, local_subtree);
+	if (subtree_pairs && subtree_pairs->DSsubtree) {
+		for (sp = subtree_pairs; sp && sp->DSsubtree; sp++) {
+			p = strstr(local_pndn, slapi_sdn_get_ndn(sp->DSsubtree));
+			if (p) {
+				break;
+			}
+		}
+	} else {
+		p = strstr(local_pndn, local_subtree);
+	}
 	if (NULL == p) {
 	if (NULL == p) {
 		/* Original entry is not in the subtree.
 		/* Original entry is not in the subtree.
 		 * To add the entry after returning from this function,
 		 * To add the entry after returning from this function,
@@ -1393,10 +1447,23 @@ process_replay_rename(Private_Repl_Protocol *prp,
 		windows_conn_set_error(prp->conn, LDAP_NO_SUCH_OBJECT);
 		windows_conn_set_error(prp->conn, LDAP_NO_SUCH_OBJECT);
 		goto bail;
 		goto bail;
 	}
 	}
-	*p = '\0';
 
 
 	/* generate a remote dn */
 	/* generate a remote dn */
-	remote_dn = PR_smprintf("%s,%s%s", remote_rdn, local_pndn, remote_subtree);
+	if (sp && sp->DSsubtree) {
+		if (p == local_pndn) {
+			remote_dn = PR_smprintf("%s,%s", remote_rdn, slapi_sdn_get_ndn(sp->ADsubtree));
+		} else {
+			*p = '\0';
+			remote_dn = PR_smprintf("%s,%s%s", remote_rdn, local_pndn, slapi_sdn_get_ndn(sp->ADsubtree));
+		}
+	} else {
+		if (p == local_pndn) {
+			remote_dn = PR_smprintf("%s,%s", remote_rdn, remote_subtree);
+		} else {
+			*p = '\0';
+			remote_dn = PR_smprintf("%s,%s%s", remote_rdn, local_pndn, remote_subtree);
+		}
+	}
 	if (!deleteoldrdn) {
 	if (!deleteoldrdn) {
 		/* AD does not accept deleteoldrdn == 0 */
 		/* AD does not accept deleteoldrdn == 0 */
 		/*
 		/*
@@ -1677,7 +1744,7 @@ windows_replay_update(Private_Repl_Protocol *prp, slapi_operation_parameters *op
 		case SLAPI_OPERATION_MODRDN:
 		case SLAPI_OPERATION_MODRDN:
 		/* only move case (newsuperior: ...) comse here since local leaf RDN is
 		/* only move case (newsuperior: ...) comse here since local leaf RDN is
 		 * not identical to the remote leaf RDN. */
 		 * not identical to the remote leaf RDN. */
-			{
+		{
 			return_value = process_replay_rename(prp, local_entry, local_dn,
 			return_value = process_replay_rename(prp, local_entry, local_dn,
 								op->p.p_modrdn.modrdn_newrdn,
 								op->p.p_modrdn.modrdn_newrdn,
 								REPL_GET_DN(&op->p.p_modrdn.modrdn_newsuperior_address),
 								REPL_GET_DN(&op->p.p_modrdn.modrdn_newsuperior_address),
@@ -1696,7 +1763,7 @@ windows_replay_update(Private_Repl_Protocol *prp, slapi_operation_parameters *op
 				}
 				}
 			}
 			}
 			break;
 			break;
-			}
+		}
 		default:
 		default:
 			slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name, "%s: replay_update: Unknown "
 			slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name, "%s: replay_update: Unknown "
 				"operation type %lu found in changelog - skipping change.\n",
 				"operation type %lu found in changelog - skipping change.\n",
@@ -2372,8 +2439,12 @@ windows_get_superior_change(Private_Repl_Protocol *prp,
 	char *local_pndn = NULL;     /* Normalized parent dn of the local entry */
 	char *local_pndn = NULL;     /* Normalized parent dn of the local entry */
 	const char *remote_subtree = NULL; /* Normalized subtree of the remote entry */
 	const char *remote_subtree = NULL; /* Normalized subtree of the remote entry */
 	const char *local_subtree = NULL;  /* Normalized subtree of the local entry */
 	const char *local_subtree = NULL;  /* Normalized subtree of the local entry */
-	char *ptr = NULL;
+	char *mptr = NULL;
+	char *lptr = NULL;
 	int rc = -1;
 	int rc = -1;
+	const subtreePair* subtree_pairs = NULL;
+	const subtreePair* msp = NULL;
+	const subtreePair* lsp = NULL;
 
 
 	if (NULL == newsuperior) {
 	if (NULL == newsuperior) {
 		slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
 		slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
@@ -2389,15 +2460,16 @@ windows_get_superior_change(Private_Repl_Protocol *prp,
 
 
 	/* Check if modrdn with superior has happened on AD */
 	/* Check if modrdn with superior has happened on AD */
 	winrepl_agmt = prp->agmt;
 	winrepl_agmt = prp->agmt;
-	remote_subtree =
-		slapi_sdn_get_ndn(windows_private_get_windows_subtree(winrepl_agmt));
-	local_subtree =
-		slapi_sdn_get_ndn(windows_private_get_directory_subtree(winrepl_agmt));
-	if (NULL == remote_subtree || NULL == local_subtree ||
-		'\0' == *remote_subtree || '\0' == *local_subtree) {
+	remote_subtree = slapi_sdn_get_ndn(windows_private_get_windows_subtree(winrepl_agmt));
+	local_subtree = slapi_sdn_get_ndn(windows_private_get_directory_subtree(winrepl_agmt));
+	subtree_pairs = windows_private_get_subtreepairs(winrepl_agmt);
+	if ((NULL == remote_subtree || NULL == local_subtree ||
+		 '\0' == *remote_subtree || '\0' == *local_subtree) &&
+		(NULL == subtree_pairs)) {
 		slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
 		slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
 					"windows_get_superior_change: local subtree \"%s\" or "
 					"windows_get_superior_change: local subtree \"%s\" or "
-					"remote subtree \"%s\" is empty\n",
+					"remote subtree \"%s\" and "
+					"subtree_pairs are empty\n",
 					local_subtree?local_subtree:"empty",
 					local_subtree?local_subtree:"empty",
 					remote_subtree?remote_subtree:"empty");
 					remote_subtree?remote_subtree:"empty");
 		goto bail;
 		goto bail;
@@ -2423,18 +2495,69 @@ windows_get_superior_change(Private_Repl_Protocol *prp,
 					mapped_pndn?mapped_pndn:"empty");
 					mapped_pndn?mapped_pndn:"empty");
 		goto bail;
 		goto bail;
 	}
 	}
-	ptr = strstr(mapped_pndn, local_subtree);
-	if (ptr) {
-		*ptr = '\0'; /* if ptr != mapped_pndn, mapped_pndn ends with ',' */
-		ptr = strstr(local_pndn, local_subtree);
-		if (ptr) {
-			*ptr = '\0'; /* if ptr != local_pndn, local_pndn ends with ',' */
+	if (subtree_pairs && subtree_pairs->DSsubtree) {
+		for (msp = subtree_pairs; msp && msp->DSsubtree; msp++) {
+			mptr = strstr(mapped_pndn, slapi_sdn_get_ndn(msp->DSsubtree));
+			if (mptr) {
+				break;
+			}
+		}
+	} else {
+		mptr = strstr(mapped_pndn, local_subtree);
+	}
+	if (mptr) {
+		/* mapped DN (originally from AD) is in the DS subtree(s) defined in the agreement */
+		if (subtree_pairs && subtree_pairs->DSsubtree) {
+			for (lsp = subtree_pairs; lsp && lsp->DSsubtree; lsp++) {
+				lptr = strstr(local_pndn, slapi_sdn_get_ndn(lsp->DSsubtree));
+				if (lptr) {
+					break;
+				}
+			}
+		} else {
+			lptr = strstr(local_pndn, local_subtree);
+		}
+		if (lptr) {
+			/* local DN is in the DS subtree(s) defined in the agreement*/
 			if (0 != strcmp(mapped_pndn, local_pndn)) {
 			if (0 != strcmp(mapped_pndn, local_pndn)) {
-				/* the remote parent is different from the local parent */
-				if (to_windows) {
-					*newsuperior = slapi_create_dn_string("%s%s", local_pndn, remote_subtree);
-				} else {
-					*newsuperior = slapi_create_dn_string("%s%s", mapped_pndn, local_subtree);
+				/* 
+				 * The mapped remote parent is different from the local parent.
+				 * we need to move the entry to the new superior.
+				 */
+				if (to_windows) { /* from DS to AD */
+					if (lsp && lsp->ADsubtree) {
+						if (lptr == local_pndn) {
+							*newsuperior = slapi_ch_strdup(slapi_sdn_get_ndn(lsp->ADsubtree));
+						} else {
+							*lptr = '\0';
+							*newsuperior = slapi_ch_smprintf("%s%s", local_pndn,
+							                                 slapi_sdn_get_ndn(lsp->ADsubtree));
+						}
+					} else {
+						if (lptr == local_pndn) {
+							*newsuperior = slapi_ch_smprintf("%s", remote_subtree);
+						} else {
+							*lptr = '\0';
+							*newsuperior = slapi_ch_smprintf("%s%s", local_pndn, remote_subtree);
+						}
+					}
+				} else { /* from AD to DS */
+					if (msp && msp->DSsubtree) {
+						if (mptr == mapped_pndn) {
+							*newsuperior = slapi_ch_strdup(slapi_sdn_get_ndn(msp->DSsubtree));
+						} else {
+							*mptr = '\0';
+							*newsuperior = slapi_ch_smprintf("%s%s", mapped_pndn, 
+							                                 slapi_sdn_get_ndn(msp->DSsubtree));
+						}
+					} else {
+						if (mptr == mapped_pndn) {
+							*newsuperior = slapi_ch_strdup(local_subtree);
+						} else {
+							*mptr = '\0';
+							*newsuperior = slapi_ch_smprintf("%s%s", mapped_pndn, local_subtree);
+						}
+					}
 				}
 				}
 				rc = 0;
 				rc = 0;
 			}
 			}
@@ -2642,6 +2765,7 @@ windows_map_mods_for_replay(Private_Repl_Protocol *prp,
 	const Slapi_Entry *ad_entry = NULL;
 	const Slapi_Entry *ad_entry = NULL;
 	Slapi_Entry *ad_entry_copy = NULL;
 	Slapi_Entry *ad_entry_copy = NULL;
 	const Slapi_DN *windows_subtree = NULL;
 	const Slapi_DN *windows_subtree = NULL;
+	const subtreePair* subtree_pairs = NULL;
 
 
 	LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_map_mods_for_replay\n", 0, 0, 0 );
 	LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_map_mods_for_replay\n", 0, 0, 0 );
 	if (NULL == prp) {
 	if (NULL == prp) {
@@ -2650,7 +2774,8 @@ windows_map_mods_for_replay(Private_Repl_Protocol *prp,
 		return;
 		return;
 	}
 	}
 	windows_subtree = windows_private_get_windows_subtree(prp->agmt);
 	windows_subtree = windows_private_get_windows_subtree(prp->agmt);
-	if (NULL == windows_subtree) {
+	subtree_pairs = windows_private_get_subtreepairs(prp->agmt);
+	if ((NULL == windows_subtree) && (NULL == subtree_pairs)) {
 		LDAPDebug(LDAP_DEBUG_TRACE,
 		LDAPDebug(LDAP_DEBUG_TRACE,
 		          "<= windows_map_mods_for_replay; NULL agreement subtree; NOOP\n", 0, 0, 0);
 		          "<= windows_map_mods_for_replay; NULL agreement subtree; NOOP\n", 0, 0, 0);
 		return;
 		return;
@@ -2770,8 +2895,17 @@ windows_map_mods_for_replay(Private_Repl_Protocol *prp,
 									const char *strval = slapi_value_get_string(valp); /* no dup */
 									const char *strval = slapi_value_get_string(valp); /* no dup */
 									if (strval) {
 									if (strval) {
 										slapi_sdn_set_dn_byref(sdn, strval);
 										slapi_sdn_set_dn_byref(sdn, strval);
-										is_in_subtree = slapi_sdn_scope_test(sdn, windows_subtree,
-										                                     LDAP_SCOPE_SUBTREE);
+										if (subtree_pairs) {
+											const subtreePair* sp;
+											for (sp = subtree_pairs; sp && sp->ADsubtree; sp++) {
+												is_in_subtree = slapi_sdn_scope_test(sdn, sp->ADsubtree, LDAP_SCOPE_SUBTREE);
+												if (is_in_subtree) {
+													break;
+												}
+											}
+										} else {
+											is_in_subtree = slapi_sdn_scope_test(sdn, windows_subtree, LDAP_SCOPE_SUBTREE);
+										}
 										if (is_in_subtree) {
 										if (is_in_subtree) {
 											/* 
 											/* 
 											 * If delete all on DS, 
 											 * If delete all on DS, 
@@ -3015,18 +3149,18 @@ find_entry_by_attr_value_remote(const char *attribute, const char *value, Slapi_
 	int retval = 0;
 	int retval = 0;
 	ConnResult cres = 0;
 	ConnResult cres = 0;
 	char *filter = NULL;
 	char *filter = NULL;
-	const char *searchbase = NULL;
+	const Slapi_DN *searchbase = NULL;
 	Slapi_Entry *found_entry = NULL;
 	Slapi_Entry *found_entry = NULL;
 
 
 	/* should not have to escape attribute names */
 	/* should not have to escape attribute names */
 	filter = slapi_filter_sprintf("(%s=%s%s)",attribute, ESC_NEXT_VAL, value);
 	filter = slapi_filter_sprintf("(%s=%s%s)",attribute, ESC_NEXT_VAL, value);
-	searchbase = slapi_sdn_get_dn(windows_private_get_windows_subtree(prp->agmt));
-	cres = windows_search_entry(prp->conn, (char*)searchbase, filter, &found_entry);
+	searchbase = windows_private_get_windows_treetop(prp->agmt);
+	cres = windows_search_entry(prp->conn, (char*)slapi_sdn_get_dn(searchbase), filter, &found_entry);
 	if (cres)
 	if (cres)
 	{
 	{
 		retval = -1;
 		retval = -1;
 	} else
 	} else
-		{
+	{
 		if (found_entry)
 		if (found_entry)
 		{
 		{
 			*e = found_entry;
 			*e = found_entry;
@@ -3136,7 +3270,7 @@ find_entry_by_attr_value(const char *attribute, const char *value, Slapi_Entry *
     Slapi_Entry **entries = NULL, **ep = NULL;
     Slapi_Entry **entries = NULL, **ep = NULL;
     Slapi_Entry *entry_found = NULL;
     Slapi_Entry *entry_found = NULL;
     LDAPControl **server_controls = NULL;
     LDAPControl **server_controls = NULL;
-    const char *subtree_dn = NULL;
+    const Slapi_DN *subtree_sdn = NULL;
     char *subtree_dn_copy = NULL;
     char *subtree_dn_copy = NULL;
     char **attrs = NULL;
     char **attrs = NULL;
     char *query = NULL;
     char *query = NULL;
@@ -3152,10 +3286,10 @@ find_entry_by_attr_value(const char *attribute, const char *value, Slapi_Entry *
     query = slapi_filter_sprintf("(%s=%s%s)", attribute, ESC_NEXT_VAL, value);
     query = slapi_filter_sprintf("(%s=%s%s)", attribute, ESC_NEXT_VAL, value);
 
 
     if (query == NULL)
     if (query == NULL)
-	    goto done;
+        goto done;
 
 
-    subtree_dn = slapi_sdn_get_dn(windows_private_get_directory_subtree(ra));
-    subtree_dn_copy = slapi_ch_strdup(subtree_dn);
+    subtree_sdn = windows_private_get_directory_treetop(ra);
+    subtree_dn_copy = slapi_ch_strdup(slapi_sdn_get_ndn(subtree_sdn));
 
 
     winsync_plugin_call_pre_ds_search_entry_cb(ra, NULL, &subtree_dn_copy, &scope, &query,
     winsync_plugin_call_pre_ds_search_entry_cb(ra, NULL, &subtree_dn_copy, &scope, &query,
                                                &attrs, &server_controls);
                                                &attrs, &server_controls);
@@ -3391,9 +3525,10 @@ map_windows_tombstone_dn(Slapi_Entry *e, Slapi_DN **dn, Private_Repl_Protocol *p
 
 
 	/* The tombstone suffix discards any containers, so we need
 	/* The tombstone suffix discards any containers, so we need
 	 * to trim the DN to only dc components. */
 	 * to trim the DN to only dc components. */
-	if ((suffix = slapi_sdn_get_dn(windows_private_get_windows_subtree(prp->agmt)))) {
+	suffix = slapi_sdn_get_ndn(windows_private_get_windows_treetop(prp->agmt));
+	if (suffix) {
 		/* If this isn't found, it is treated as an error below. */
 		/* If this isn't found, it is treated as an error below. */
-		suffix = (const char *) PL_strcasestr(suffix,"dc=");
+		suffix = (const char *)PL_strstr(suffix, "dc=");
 	}
 	}
 
 
 	if (cn && guid && suffix) {
 	if (cn && guid && suffix) {
@@ -3566,7 +3701,18 @@ map_entry_dn_outbound(Slapi_Entry *e, Slapi_DN **dn, Private_Repl_Protocol *prp,
 	char *guid = NULL;
 	char *guid = NULL;
 	Slapi_DN *new_dn = NULL;
 	Slapi_DN *new_dn = NULL;
 	int is_nt4 = windows_private_get_isnt4(prp->agmt);
 	int is_nt4 = windows_private_get_isnt4(prp->agmt);
-	const char *suffix = slapi_sdn_get_dn(windows_private_get_windows_subtree(prp->agmt));
+	const char *suffix = NULL;
+	const Slapi_DN *local_sdn = NULL;
+	const subtreePair* subtree_pairs = NULL;
+	const subtreePair* sp = NULL;
+
+	if (NULL == e) {
+		slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
+		                "%s: map_entry_dn_outbound: NULL entry.\n",
+		                agmt_get_long_name(prp->agmt));
+		return -1;
+	}
+
 	/* To find the DN of the peer entry we first look for an ntUniqueId attribute
 	/* To find the DN of the peer entry we first look for an ntUniqueId attribute
 	 * on the local entry. If that's present, we generate a GUID-form DN.
 	 * on the local entry. If that's present, we generate a GUID-form DN.
 	 * If there's no GUID, then we look for an ntUserDomainId attribute
 	 * If there's no GUID, then we look for an ntUserDomainId attribute
@@ -3585,6 +3731,25 @@ map_entry_dn_outbound(Slapi_Entry *e, Slapi_DN **dn, Private_Repl_Protocol *prp,
 	*dn = NULL;
 	*dn = NULL;
 	*missing_entry = 0;
 	*missing_entry = 0;
 
 
+	local_sdn = slapi_entry_get_sdn_const(e);
+	subtree_pairs = windows_private_get_subtreepairs(prp->agmt);
+	if (subtree_pairs) {
+		for (sp = subtree_pairs; sp && sp->DSsubtree; sp++) {
+			if (slapi_sdn_scope_test(local_sdn, sp->DSsubtree, LDAP_SCOPE_SUBTREE)) {
+				suffix = slapi_sdn_get_dn(sp->ADsubtree);
+				break;
+			}
+		}
+	} else {
+		suffix = slapi_sdn_get_dn(windows_private_get_windows_subtree(prp->agmt));
+	}
+	if (NULL == suffix) {
+		slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
+		                "%s: map_entry_dn_outbound: Failed to get the AD suffix of %s.\n",
+		                agmt_get_long_name(prp->agmt), slapi_sdn_get_dn(local_sdn));
+		return -1;
+	}
+
 	guid = slapi_entry_attr_get_charptr(e,"ntUniqueId");
 	guid = slapi_entry_attr_get_charptr(e,"ntUniqueId");
 	slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
 	slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
 			"%s: map_entry_dn_outbound: looking for AD entry for DS "
 			"%s: map_entry_dn_outbound: looking for AD entry for DS "
@@ -3646,8 +3811,12 @@ map_entry_dn_outbound(Slapi_Entry *e, Slapi_DN **dn, Private_Repl_Protocol *prp,
 				if (cn_string) {
 				if (cn_string) {
 					char *container_str = NULL;
 					char *container_str = NULL;
 
 
-					container_str = extract_container(slapi_entry_get_sdn_const(e),
-						windows_private_get_directory_subtree(prp->agmt));
+					if (sp) {
+						container_str = extract_container(slapi_entry_get_sdn_const(e), sp->DSsubtree);
+					} else {
+						container_str = extract_container(slapi_entry_get_sdn_const(e),
+						                                  windows_private_get_directory_subtree(prp->agmt));
+					}
 					new_dn_string = slapi_create_dn_string("cn=\"%s\",%s%s", cn_string, container_str, suffix);
 					new_dn_string = slapi_create_dn_string("cn=\"%s\",%s%s", cn_string, container_str, suffix);
 
 
 					if (new_dn_string) {
 					if (new_dn_string) {
@@ -3714,7 +3883,12 @@ map_entry_dn_outbound(Slapi_Entry *e, Slapi_DN **dn, Private_Repl_Protocol *prp,
 						char *rdnstr = NULL;
 						char *rdnstr = NULL;
 						char *container_str = NULL;
 						char *container_str = NULL;
 					
 					
-						container_str = extract_container(slapi_entry_get_sdn_const(e), windows_private_get_directory_subtree(prp->agmt));
+						if (sp) {
+							container_str = extract_container(slapi_entry_get_sdn_const(e), sp->DSsubtree);
+						} else {
+							container_str = extract_container(slapi_entry_get_sdn_const(e),
+							                                  windows_private_get_directory_subtree(prp->agmt));
+						}
 						
 						
 						rdnstr = is_nt4 ? "samaccountname=\"%s\",%s%s" : "cn=\"%s\",%s%s";
 						rdnstr = is_nt4 ? "samaccountname=\"%s\",%s%s" : "cn=\"%s\",%s%s";
 
 
@@ -3843,6 +4017,14 @@ map_entry_dn_inbound_ext(Slapi_Entry *e, Slapi_DN **dn, const Repl_Agmt *ra, int
 	int is_user = 0;
 	int is_user = 0;
 	int is_group = 0;
 	int is_group = 0;
 	int is_nt4 = windows_private_get_isnt4(ra);
 	int is_nt4 = windows_private_get_isnt4(ra);
+	char *container_str = NULL;
+
+	if (NULL == e) {
+		slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
+		                "%s: map_entry_dn_inbound: entry is NULL.\n",
+					    agmt_get_long_name(ra));
+		return -1;
+	}
 
 
 	/* To map a non-tombstone's DN we need to first try to look it up by GUID.
 	/* To map a non-tombstone's DN we need to first try to look it up by GUID.
 	 * If we do not find it, then we need to generate the DN that it would have if added as a new entry.
 	 * If we do not find it, then we need to generate the DN that it would have if added as a new entry.
@@ -3967,10 +4149,34 @@ map_entry_dn_inbound_ext(Slapi_Entry *e, Slapi_DN **dn, const Repl_Agmt *ra, int
 		char *new_dn_string = NULL;
 		char *new_dn_string = NULL;
 		if (username) 
 		if (username) 
 		{
 		{
-			const char *suffix = slapi_sdn_get_dn(windows_private_get_directory_subtree(ra));
-			char *container_str = NULL;
+			const char *suffix = NULL;
+			const subtreePair* subtree_pairs = windows_private_get_subtreepairs(ra);
+			const subtreePair* sp = NULL;
+			const Slapi_DN *remote_sdn = slapi_entry_get_sdn_const(e);
+
+			if (subtree_pairs) {
+				for (sp = subtree_pairs; sp && sp->ADsubtree; sp++) {
+					if (slapi_sdn_scope_test(remote_sdn, sp->ADsubtree, LDAP_SCOPE_SUBTREE)) {
+						suffix = slapi_sdn_get_dn(sp->DSsubtree);
+						break;
+					}
+				}
+			} else {
+				suffix = slapi_sdn_get_dn(windows_private_get_directory_subtree(ra));
+			}
 
 
-			container_str = extract_container(slapi_entry_get_sdn_const(e), windows_private_get_windows_subtree(ra));
+			if (sp) {
+				container_str = extract_container(slapi_entry_get_sdn_const(e), sp->ADsubtree);
+			} else {
+				container_str = extract_container(slapi_entry_get_sdn_const(e), 
+				                                  windows_private_get_windows_subtree(ra));
+			}
+			if (NULL == suffix) {
+				slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
+								"%s: map_entry_dn_inbound: Failed to retrieve local suiffx from %s\n",
+								agmt_get_long_name(ra), slapi_sdn_get_dn(remote_sdn));
+				goto error;
+			}
 			/* Local DNs for users and groups are different */
 			/* Local DNs for users and groups are different */
 			if (is_user)
 			if (is_user)
 			{
 			{
@@ -3979,8 +4185,10 @@ map_entry_dn_inbound_ext(Slapi_Entry *e, Slapi_DN **dn, const Repl_Agmt *ra, int
 														  windows_private_get_raw_entry(ra),
 														  windows_private_get_raw_entry(ra),
 														  e,
 														  e,
 														  &new_dn_string,
 														  &new_dn_string,
-														  windows_private_get_directory_subtree(ra),
-														  windows_private_get_windows_subtree(ra));
+														  sp?sp->DSsubtree:
+														     windows_private_get_directory_subtree(ra),
+														  sp?sp->ADsubtree:
+														     windows_private_get_windows_subtree(ra));
 			} else
 			} else
 			{
 			{
 				new_dn_string = slapi_create_dn_string("cn=\"%s\",%s%s",username,container_str,suffix);
 				new_dn_string = slapi_create_dn_string("cn=\"%s\",%s%s",username,container_str,suffix);
@@ -3989,8 +4197,10 @@ map_entry_dn_inbound_ext(Slapi_Entry *e, Slapi_DN **dn, const Repl_Agmt *ra, int
 															   windows_private_get_raw_entry(ra),
 															   windows_private_get_raw_entry(ra),
 															   e,
 															   e,
 															   &new_dn_string,
 															   &new_dn_string,
-															   windows_private_get_directory_subtree(ra),
-															   windows_private_get_windows_subtree(ra));
+															   sp?sp->DSsubtree:
+															      windows_private_get_directory_subtree(ra),
+															   sp?sp->ADsubtree:
+															      windows_private_get_windows_subtree(ra));
 				}
 				}
 			}
 			}
 			/* 
 			/* 
@@ -3998,7 +4208,6 @@ map_entry_dn_inbound_ext(Slapi_Entry *e, Slapi_DN **dn, const Repl_Agmt *ra, int
 			 * which is normalized. Thus, we can use _normdn_.
 			 * which is normalized. Thus, we can use _normdn_.
 			 */
 			 */
 			new_dn = slapi_sdn_new_normdn_passin(new_dn_string);
 			new_dn = slapi_sdn_new_normdn_passin(new_dn_string);
-			slapi_ch_free_string(&container_str);
 		} else 
 		} else 
 		{
 		{
 			/* Error, no username */
 			/* Error, no username */
@@ -4015,14 +4224,9 @@ error:
 	{
 	{
 		PR_smprintf_free(guid);
 		PR_smprintf_free(guid);
 	}
 	}
-	if (matching_entry)
-	{
-		slapi_entry_free(matching_entry);
-	}
-	if (username)
-	{
-		slapi_ch_free_string(&username);
-	}
+	slapi_entry_free(matching_entry);
+	slapi_ch_free_string(&username);
+	slapi_ch_free_string(&container_str);
 	return retval;
 	return retval;
 }
 }
 
 
@@ -4034,30 +4238,41 @@ is_subject_of_agreement_local(const Slapi_Entry *local_entry, const Repl_Agmt *r
 {
 {
 	int retval = 0;
 	int retval = 0;
 	int is_in_subtree = 0;
 	int is_in_subtree = 0;
-	const Slapi_DN *agreement_subtree = NULL;
+	const Slapi_DN *local_sdn = NULL;
+	const subtreePair* subtree_pairs = NULL;
 	
 	
-	/* First test for the sync'ed subtree */
-	agreement_subtree = windows_private_get_directory_subtree(ra);
-	if (NULL == agreement_subtree)
-	{
+	if (!local_entry) {
+		/* Error: couldn't find the entry */
+		slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
+		                "failed to find entry in is_subject_of_agreement_local\n");
 		goto error;
 		goto error;
 	}
 	}
-	is_in_subtree = slapi_sdn_scope_test(slapi_entry_get_sdn_const(local_entry), agreement_subtree, LDAP_SCOPE_SUBTREE);
-	if (is_in_subtree) 
-	{
-		/* Next test for the correct kind of entry */
-		if (local_entry) {
-			if (slapi_filter_test_simple( (Slapi_Entry*)local_entry,
-					(Slapi_Filter*)windows_private_get_directory_filter(ra)) == 0)
-			{
-				retval = 1;
+
+	/* First test for the sync'ed subtree */
+	local_sdn = slapi_entry_get_sdn_const(local_entry);
+	subtree_pairs = windows_private_get_subtreepairs(ra);
+	if (subtree_pairs) {
+		const subtreePair* sp;
+		for (sp = subtree_pairs; sp && sp->DSsubtree; sp++) {
+			is_in_subtree = slapi_sdn_scope_test(local_sdn,
+			                                     sp->DSsubtree, LDAP_SCOPE_SUBTREE);
+			if (is_in_subtree) {
+				break;
 			}
 			}
-		} else 
-		{
-			/* Error: couldn't find the entry */
-			slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
-				"failed to find entry in is_subject_of_agreement_local: %d\n", retval);
-			retval = 0;
+		}
+	} else {
+		const Slapi_DN *agreement_subtree = NULL;
+		agreement_subtree = windows_private_get_directory_subtree(ra);
+		if (agreement_subtree) {
+			is_in_subtree = slapi_sdn_scope_test(local_sdn,
+			                                     agreement_subtree, LDAP_SCOPE_SUBTREE);
+		}
+	}
+	if (is_in_subtree) {
+		/* Next test for the correct kind of entry */
+		if (slapi_filter_test_simple((Slapi_Entry*)local_entry,
+		                             windows_private_get_directory_filter(ra)) == 0) {
+			retval = 1;
 		}
 		}
 	}
 	}
 error:
 error:
@@ -4069,19 +4284,26 @@ static int
 is_dn_subject_of_agreement_local(const Slapi_DN *sdn, const Repl_Agmt *ra)
 is_dn_subject_of_agreement_local(const Slapi_DN *sdn, const Repl_Agmt *ra)
 {
 {
 	int retval = 0;
 	int retval = 0;
-	const Slapi_DN *agreement_subtree = NULL;
+	const subtreePair* subtree_pairs = NULL;
 
 
 	/* Get the subtree from the agreement */
 	/* Get the subtree from the agreement */
-	agreement_subtree = windows_private_get_directory_subtree(ra);
-	if (NULL == agreement_subtree)
-	{
-		goto error;
+	subtree_pairs = windows_private_get_subtreepairs(ra);
+	if (subtree_pairs) {
+		const subtreePair* sp;
+		for (sp = subtree_pairs; sp && sp->DSsubtree; sp++) {
+			retval = slapi_sdn_scope_test(sdn, sp->DSsubtree, LDAP_SCOPE_SUBTREE);
+			if (retval) {
+				break;
+			}
+		}
+	} else {
+		const Slapi_DN *agreement_subtree = NULL;
+		agreement_subtree = windows_private_get_directory_subtree(ra);
+		if (agreement_subtree) {
+			/* Check if the DN is within the subtree */
+			retval = slapi_sdn_scope_test(sdn, agreement_subtree, LDAP_SCOPE_SUBTREE);
+		}
 	}
 	}
-
-	/* Check if the DN is within the subtree */
-	retval = slapi_sdn_scope_test(sdn, agreement_subtree, LDAP_SCOPE_SUBTREE);
-
-error:
 	return retval;
 	return retval;
 }
 }
 
 
@@ -4097,20 +4319,41 @@ is_subject_of_agreement_remote(Slapi_Entry *e, const Repl_Agmt *ra)
 	int retval = 0;
 	int retval = 0;
 	int is_in_subtree = 0;
 	int is_in_subtree = 0;
 	const Slapi_DN *agreement_subtree = NULL;
 	const Slapi_DN *agreement_subtree = NULL;
-	const Slapi_DN *sdn;
-	
-	/* First test for the sync'ed subtree */
-	agreement_subtree = windows_private_get_windows_subtree(ra);
-	if (NULL == agreement_subtree) 
-	{
+	const Slapi_DN *sdn = NULL;
+	const subtreePair* subtree_pairs = NULL;
+
+	if (!e) {
 		goto error;
 		goto error;
 	}
 	}
 	sdn = slapi_entry_get_sdn_const(e);
 	sdn = slapi_entry_get_sdn_const(e);
-	is_in_subtree = slapi_sdn_scope_test(sdn, agreement_subtree, LDAP_SCOPE_SUBTREE);
+	/* First test for the sync'ed subtree */
+	/* check the subtree pairs; if not set, check windows_subtree */
+	subtree_pairs = windows_private_get_subtreepairs(ra);
+	if (subtree_pairs) {
+		const subtreePair* sp;
+		for (sp = subtree_pairs; sp && sp->ADsubtree; sp++) {
+			is_in_subtree = slapi_sdn_scope_test(sdn, sp->ADsubtree, LDAP_SCOPE_SUBTREE);
+			if (is_in_subtree) {
+				agreement_subtree = sp->ADsubtree;
+				break;
+			}
+		}
+	} else {
+		agreement_subtree = windows_private_get_windows_subtree(ra);
+		if (agreement_subtree) {
+			is_in_subtree = slapi_sdn_scope_test(sdn, agreement_subtree, LDAP_SCOPE_SUBTREE);
+		}
+	}
 	if (is_in_subtree) 
 	if (is_in_subtree) 
 	{
 	{
 		Slapi_DN psdn = {0};
 		Slapi_DN psdn = {0};
 		Slapi_Entry *pentry = NULL;
 		Slapi_Entry *pentry = NULL;
+
+		if (windows_private_get_windows_filter(ra) &&
+		    slapi_filter_test_simple(e, windows_private_get_windows_filter(ra))) {
+			/* type_winSyncWindowsFilter is set and the remote entry does not match the filter */
+			goto error;
+		}
 		/*
 		/*
 		 * Check whether the parent of the entry exists or not.
 		 * Check whether the parent of the entry exists or not.
 		 * If it does not, treat the entry e is out of scope.
 		 * If it does not, treat the entry e is out of scope.
@@ -4357,9 +4600,16 @@ windows_generate_dn_value_mods(char *local_type, const Slapi_Attr *attr, Slapi_M
 	return ret;
 	return ret;
 }
 }
 
 
-/* Generate the mods for an update in either direction.  Be careful... the "remote" entry is the DS entry in the to_windows case, but the AD entry in the other case. */
+/* 
+ * Generate the mods for an update in either direction.  
+ */
 static int
 static int
-windows_generate_update_mods(Private_Repl_Protocol *prp,Slapi_Entry *remote_entry,Slapi_Entry *local_entry, int to_windows, Slapi_Mods *smods, int *do_modify)
+windows_generate_update_mods(Private_Repl_Protocol *prp,
+                             Slapi_Entry *remote_entry,
+                             Slapi_Entry *local_entry,
+                             int to_windows,
+                             Slapi_Mods *smods,
+                             int *do_modify)
 {
 {
 	int retval = 0;
 	int retval = 0;
 	Slapi_Attr *attr = NULL;
 	Slapi_Attr *attr = NULL;
@@ -4369,6 +4619,10 @@ windows_generate_update_mods(Private_Repl_Protocol *prp,Slapi_Entry *remote_entr
 	int rc = 0;
 	int rc = 0;
 	int is_nt4 = windows_private_get_isnt4(prp->agmt);
 	int is_nt4 = windows_private_get_isnt4(prp->agmt);
 	const Slapi_DN *local_subtree = NULL;
 	const Slapi_DN *local_subtree = NULL;
+	const Slapi_DN *local_sdn = NULL;
+	const subtreePair* subtree_pairs = NULL;
+	Slapi_Entry *target_entry = NULL;
+
 	/* Iterate over the attributes on the remote entry, updating the local entry where appropriate */
 	/* Iterate over the attributes on the remote entry, updating the local entry where appropriate */
 	LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_generate_update_mods\n", 0, 0, 0 );
 	LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_generate_update_mods\n", 0, 0, 0 );
 
 
@@ -4385,14 +4639,16 @@ windows_generate_update_mods(Private_Repl_Protocol *prp,Slapi_Entry *remote_entr
 
 
 	if (to_windows)
 	if (to_windows)
 	{
 	{
-		windows_is_local_entry_user_or_group(remote_entry,&is_user,&is_group);
+		windows_is_local_entry_user_or_group(local_entry,&is_user,&is_group);
+		target_entry = local_entry;
 	} else
 	} else
 	{
 	{
 		windows_is_remote_entry_user_or_group(remote_entry,&is_user,&is_group);
 		windows_is_remote_entry_user_or_group(remote_entry,&is_user,&is_group);
+		target_entry = remote_entry;
 	}
 	}
 
 
-	for (rc = slapi_entry_first_attr(remote_entry, &attr); rc == 0;
-	     rc = slapi_entry_next_attr(remote_entry, attr, &attr)) 
+	for (rc = slapi_entry_first_attr(target_entry, &attr); rc == 0;
+	     rc = slapi_entry_next_attr(target_entry, attr, &attr)) 
 	{
 	{
 		int is_present_local = 0;
 		int is_present_local = 0;
 		char *type = NULL;
 		char *type = NULL;
@@ -4432,7 +4688,7 @@ windows_generate_update_mods(Private_Repl_Protocol *prp,Slapi_Entry *remote_entr
 		}
 		}
 
 
 		if (to_windows && (0 == slapi_attr_type_cmp(local_type, "streetAddress", SLAPI_TYPE_CMP_SUBTYPE))) {
 		if (to_windows && (0 == slapi_attr_type_cmp(local_type, "streetAddress", SLAPI_TYPE_CMP_SUBTYPE))) {
-			slapi_entry_attr_find(local_entry,FAKE_STREET_ATTR_NAME,&local_attr);
+			slapi_entry_attr_find(remote_entry,FAKE_STREET_ATTR_NAME,&local_attr);
 		} else {
 		} else {
 			slapi_entry_attr_find(local_entry,local_type,&local_attr);
 			slapi_entry_attr_find(local_entry,local_type,&local_attr);
 		}
 		}
@@ -4474,8 +4730,8 @@ windows_generate_update_mods(Private_Repl_Protocol *prp,Slapi_Entry *remote_entr
 				if (!values_equal)
 				if (!values_equal)
 				{
 				{
 					slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
 					slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
-					"windows_generate_update_mods: %s, %s : values are different\n",
-					slapi_sdn_get_dn(slapi_entry_get_sdn_const(local_entry)), local_type);
+					                "windows_generate_update_mods: %s, %s : values are different\n",
+					                slapi_entry_get_dn_const(local_entry), local_type);
 
 
 					if (to_windows && ((0 == slapi_attr_type_cmp(local_type, "streetAddress", SLAPI_TYPE_CMP_SUBTYPE)) ||
 					if (to_windows && ((0 == slapi_attr_type_cmp(local_type, "streetAddress", SLAPI_TYPE_CMP_SUBTYPE)) ||
 						(0 == slapi_attr_type_cmp(local_type, "telephoneNumber", SLAPI_TYPE_CMP_SUBTYPE)) ||
 						(0 == slapi_attr_type_cmp(local_type, "telephoneNumber", SLAPI_TYPE_CMP_SUBTYPE)) ||
@@ -4531,7 +4787,8 @@ windows_generate_update_mods(Private_Repl_Protocol *prp,Slapi_Entry *remote_entr
 				} else
 				} else
 				{
 				{
 					slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
 					slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
-					"windows_generate_update_mods: %s, %s : values are equal\n", slapi_sdn_get_dn(slapi_entry_get_sdn_const(local_entry)), local_type);
+					                "windows_generate_update_mods: %s, %s : values are equal\n",
+					                slapi_entry_get_dn_const(local_entry), local_type);
 				}
 				}
 			} else {
 			} else {
 				/* A dn-valued attribute : need to take special steps */
 				/* A dn-valued attribute : need to take special steps */
@@ -4702,7 +4959,22 @@ windows_generate_update_mods(Private_Repl_Protocol *prp,Slapi_Entry *remote_entr
 
 
 	/* Check if any attributes were deleted from the remote entry */
 	/* Check if any attributes were deleted from the remote entry */
 	entry_first_deleted_attribute(remote_entry, &del_attr);
 	entry_first_deleted_attribute(remote_entry, &del_attr);
-	local_subtree = windows_private_get_directory_subtree(prp->agmt);
+	local_sdn = slapi_entry_get_sdn_const(local_entry);
+	subtree_pairs = windows_private_get_subtreepairs(prp->agmt);
+	if (subtree_pairs) {
+		const subtreePair* sp;
+		for (sp = subtree_pairs; sp && sp->DSsubtree; sp++) {
+			if (slapi_sdn_scope_test(local_sdn, sp->DSsubtree, LDAP_SCOPE_SUBTREE)) {
+				local_subtree = sp->DSsubtree;
+				break;
+			}
+		}
+	} else {
+		local_subtree = windows_private_get_directory_subtree(prp->agmt);
+	}
+	if (NULL == local_subtree) {
+		goto bail;
+	}
 	while (del_attr != NULL) {
 	while (del_attr != NULL) {
 		Slapi_Attr *local_attr = NULL;
 		Slapi_Attr *local_attr = NULL;
 		char *type = NULL;
 		char *type = NULL;
@@ -4832,14 +5104,14 @@ bail:
 static int
 static int
 windows_update_remote_entry(Private_Repl_Protocol *prp,Slapi_Entry *remote_entry,Slapi_Entry *local_entry, int is_user)
 windows_update_remote_entry(Private_Repl_Protocol *prp,Slapi_Entry *remote_entry,Slapi_Entry *local_entry, int is_user)
 {
 {
-    Slapi_Mods smods = {0};
+	Slapi_Mods smods = {0};
 	int retval = 0;
 	int retval = 0;
 	int do_modify = 0;
 	int do_modify = 0;
 	int ldap_op = 0;
 	int ldap_op = 0;
 	int ldap_result_code = 0;
 	int ldap_result_code = 0;
 
 
-    slapi_mods_init (&smods, 0);
-	retval = windows_generate_update_mods(prp,local_entry,remote_entry,1,&smods,&do_modify);
+	slapi_mods_init (&smods, 0);
+	retval = windows_generate_update_mods(prp, remote_entry, local_entry, 1, &smods, &do_modify);
 	/* Now perform the modify if we need to */
 	/* Now perform the modify if we need to */
 	if (0 == retval && do_modify)
 	if (0 == retval && do_modify)
 	{
 	{
@@ -4943,7 +5215,7 @@ windows_update_local_entry(Private_Repl_Protocol *prp,Slapi_Entry *remote_entry,
 		slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name, "renaming entry \"%s\" - "
 		slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name, "renaming entry \"%s\" - "
 				"(newrdn: \"%s\", newsuperior: \"%s\"\n", newdn,
 				"(newrdn: \"%s\", newsuperior: \"%s\"\n", newdn,
 				newrdn ? newrdn:"NULL", newsuperior ? newsuperior:"NULL");
 				newrdn ? newrdn:"NULL", newsuperior ? newsuperior:"NULL");
-		slapi_sdn_init_dn_byref(&newsuperior_sdn, newsuperior);
+		slapi_sdn_init_ndn_byref(&newsuperior_sdn, newsuperior);
 		slapi_rename_internal_set_pb_ext (pb,
 		slapi_rename_internal_set_pb_ext (pb,
 				   slapi_entry_get_sdn(local_entry),
 				   slapi_entry_get_sdn(local_entry),
 				   newrdn, &newsuperior_sdn, 1 /* delete old RDNS */,
 				   newrdn, &newsuperior_sdn, 1 /* delete old RDNS */,
@@ -5076,7 +5348,7 @@ windows_process_total_add(Private_Repl_Protocol *prp,Slapi_Entry *e, Slapi_DN* r
 		(void)slapi_entry2mods (mapped_entry , NULL /* &entrydn : We don't need it */, &entryattrs);
 		(void)slapi_entry2mods (mapped_entry , NULL /* &entrydn : We don't need it */, &entryattrs);
 		if (NULL == entryattrs)
 		if (NULL == entryattrs)
 		{
 		{
-			slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,"%s: windows_replay_update: Cannot convert entry to LDAPMods.\n",agmt_get_long_name(prp->agmt));
+			slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,"%s: windows_process_total_add: Cannot convert entry to LDAPMods.\n",agmt_get_long_name(prp->agmt));
 			retval = CONN_LOCAL_ERROR;
 			retval = CONN_LOCAL_ERROR;
 		}
 		}
 		else
 		else
@@ -5112,7 +5384,7 @@ windows_process_total_add(Private_Repl_Protocol *prp,Slapi_Entry *e, Slapi_DN* r
 			/* It's possible that the entry already exists in AD, in which case we fall back to modify it */
 			/* It's possible that the entry already exists in AD, in which case we fall back to modify it */
 			if (retval)
 			if (retval)
 			{
 			{
-				slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,"%s: windows_replay_update: Cannot replay add operation.\n",agmt_get_long_name(prp->agmt));
+				slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,"%s: windows_process_total_add: Cannot replay add operation.\n",agmt_get_long_name(prp->agmt));
 			}
 			}
 			ldap_mods_free(entryattrs, 1);
 			ldap_mods_free(entryattrs, 1);
 			entryattrs = NULL;
 			entryattrs = NULL;
@@ -5183,7 +5455,7 @@ int windows_process_total_entry(Private_Repl_Protocol *prp,Slapi_Entry *e)
 		if (retval || NULL == remote_dn) 
 		if (retval || NULL == remote_dn) 
 		{
 		{
 			slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
 			slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
-				"%s: windows_replay_update: failed map dn for total update dn=\"%s\"\n",
+				"%s: windows_process_total_entry: failed map dn for total update dn=\"%s\"\n",
 				agmt_get_long_name(prp->agmt), slapi_sdn_get_dn(local_dn));
 				agmt_get_long_name(prp->agmt), slapi_sdn_get_dn(local_dn));
 			goto error;
 			goto error;
 		}
 		}
@@ -5198,19 +5470,27 @@ error:
 }
 }
 
 
 static int
 static int
-windows_search_local_entry_by_uniqueid(Private_Repl_Protocol *prp, const char *uniqueid, char ** attrs, Slapi_Entry **ret_entry, int tombstone, void * component_identity, int is_global)
+windows_search_local_entry_by_uniqueid(Private_Repl_Protocol *prp,
+                                       const char *uniqueid,
+                                       char ** attrs,
+                                       Slapi_Entry **ret_entry,
+                                       int tombstone,
+                                       void * component_identity,
+                                       int is_global)
 {
 {
-    Slapi_Entry **entries = NULL;
-    Slapi_PBlock *int_search_pb = NULL;
-    int rc = 0;
+	Slapi_Entry **entries = NULL;
+	Slapi_PBlock *int_search_pb = NULL;
+	int rc = 0;
 	char *filter_string = NULL;
 	char *filter_string = NULL;
 	const Slapi_DN *local_subtree = NULL;
 	const Slapi_DN *local_subtree = NULL;
+	const Slapi_DN *local_subtree_sdn = NULL;
     
     
-    *ret_entry = NULL;
+	*ret_entry = NULL;
 	if (is_global) { /* Search from the suffix (rename case) */
 	if (is_global) { /* Search from the suffix (rename case) */
 		local_subtree = agmt_get_replarea(prp->agmt); 
 		local_subtree = agmt_get_replarea(prp->agmt); 
+		local_subtree_sdn = local_subtree;
 	} else {
 	} else {
-		local_subtree = windows_private_get_directory_subtree(prp->agmt);
+		local_subtree_sdn = windows_private_get_directory_treetop(prp->agmt);
 	}
 	}
 
 
 	/* Searching for tombstones can be expensive, so the caller needs to specify if
 	/* Searching for tombstones can be expensive, so the caller needs to specify if
@@ -5222,7 +5502,7 @@ windows_search_local_entry_by_uniqueid(Private_Repl_Protocol *prp, const char *u
 	}
 	}
 
 
     int_search_pb = slapi_pblock_new ();
     int_search_pb = slapi_pblock_new ();
-	slapi_search_internal_set_pb ( int_search_pb,  slapi_sdn_get_dn(local_subtree), LDAP_SCOPE_SUBTREE, filter_string,
+	slapi_search_internal_set_pb ( int_search_pb,  slapi_sdn_get_dn(local_subtree_sdn), LDAP_SCOPE_SUBTREE, filter_string,
 								   attrs ,
 								   attrs ,
 								   0 /* attrsonly */, NULL /* controls */,
 								   0 /* attrsonly */, NULL /* controls */,
 								   NULL /* uniqueid */,
 								   NULL /* uniqueid */,

+ 78 - 47
ldap/servers/plugins/replication/windows_tot_protocol.c

@@ -85,6 +85,57 @@ static void get_result (int rc, void *cb_data);
 static int send_entry (Slapi_Entry *e, void *callback_data);
 static int send_entry (Slapi_Entry *e, void *callback_data);
 static void windows_tot_delete(Private_Repl_Protocol **prp);
 static void windows_tot_delete(Private_Repl_Protocol **prp);
 
 
+static void
+_windows_tot_send_entry(const Repl_Agmt *ra, callback_data *cbp, const Slapi_DN *local_sdn)
+{
+	Slapi_PBlock *pb = NULL;
+	char* dn = NULL;
+	int scope = LDAP_SCOPE_SUBTREE;
+	char *filter = NULL;
+	const char *userfilter = NULL;
+	char **attrs = NULL;
+	LDAPControl **server_controls = NULL;
+
+	if ((NULL == ra) || (NULL == cbp) || (NULL == local_sdn)) {
+		return;
+	}
+	dn = slapi_ch_strdup(slapi_sdn_get_dn(local_sdn));
+	userfilter = windows_private_get_directory_userfilter(ra);
+	if (userfilter) {
+		if ('(' == *userfilter) {
+			filter = slapi_ch_smprintf("(&(|(objectclass=ntuser)(objectclass=ntgroup))%s)",
+			                           userfilter);
+		} else {
+			filter = slapi_ch_smprintf("(&(|(objectclass=ntuser)(objectclass=ntgroup))(%s))",
+			                           userfilter);
+		}
+	} else {
+		filter = slapi_ch_strdup("(|(objectclass=ntuser)(objectclass=ntgroup))");
+	}
+
+	winsync_plugin_call_pre_ds_search_all_cb(ra, NULL, &dn, &scope, &filter,
+	                                         &attrs, &server_controls);
+
+	pb = slapi_pblock_new ();
+	/* Perform a subtree search for any ntuser or ntgroup entries underneath the
+	* suffix defined in the sync agreement. */
+	slapi_search_internal_set_pb(pb, dn, scope, filter, attrs, 0, server_controls, NULL, 
+	                             repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION), 0);
+
+	slapi_search_internal_callback_pb(pb, cbp /* callback data */,
+	                                  get_result /* result callback */,
+	                                  send_entry /* entry callback */,
+	                                  NULL /* referral callback */);
+
+	slapi_ch_free_string(&dn);
+	slapi_ch_free_string(&filter);
+	slapi_ch_array_free(attrs);
+	attrs = NULL;
+	ldap_controls_free(server_controls);
+	server_controls = NULL;
+	slapi_pblock_destroy (pb);
+}
+
 /*
 /*
  * Completely refresh a replica. The basic protocol interaction goes
  * Completely refresh a replica. The basic protocol interaction goes
  * like this:
  * like this:
@@ -98,16 +149,10 @@ windows_tot_run(Private_Repl_Protocol *prp)
 {
 {
 	int rc;
 	int rc;
 	callback_data cb_data;
 	callback_data cb_data;
-	Slapi_PBlock *pb = NULL;
-	char* dn = NULL;
 	RUV *ruv = NULL;
 	RUV *ruv = NULL;
 	RUV *starting_ruv = NULL;
 	RUV *starting_ruv = NULL;
 	Replica *replica = NULL;
 	Replica *replica = NULL;
 	Object *local_ruv_obj = NULL;
 	Object *local_ruv_obj = NULL;
-	int scope = LDAP_SCOPE_SUBTREE;
-	char *filter = slapi_ch_strdup("(|(objectclass=ntuser)(objectclass=ntgroup))");
-	char **attrs = NULL;
-	LDAPControl **server_controls = NULL;
 	int one_way;
 	int one_way;
 	
 	
 	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_tot_run\n" );
 	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_tot_run\n" );
@@ -160,9 +205,9 @@ windows_tot_run(Private_Repl_Protocol *prp)
 
 
 	/* call begin total update callback */
 	/* call begin total update callback */
 	winsync_plugin_call_begin_update_cb(prp->agmt,
 	winsync_plugin_call_begin_update_cb(prp->agmt,
-										windows_private_get_directory_subtree(prp->agmt),
-										windows_private_get_windows_subtree(prp->agmt),
-										1 /* is_total == TRUE */);
+	                                    windows_private_get_directory_treetop(prp->agmt),
+	                                    windows_private_get_windows_treetop(prp->agmt),
+	                                    1 /* is_total == TRUE */);
 
 
 	if ((one_way == ONE_WAY_SYNC_DISABLED) || (one_way == ONE_WAY_SYNC_FROM_AD)) {
 	if ((one_way == ONE_WAY_SYNC_DISABLED) || (one_way == ONE_WAY_SYNC_FROM_AD)) {
 		/* get everything */
 		/* get everything */
@@ -178,10 +223,10 @@ windows_tot_run(Private_Repl_Protocol *prp)
 	 * the incremental sync protocol ( send_updates() ).  We will
 	 * the incremental sync protocol ( send_updates() ).  We will
 	 * use this value for setting the consumer RUV if the total
 	 * use this value for setting the consumer RUV if the total
 	 * update succeeds. */
 	 * update succeeds. */
-        replica = object_get_data(prp->replica_object);
-        local_ruv_obj = replica_get_ruv (replica);
-        starting_ruv = ruv_dup((RUV*)  object_get_data ( local_ruv_obj ));
-        object_release (local_ruv_obj);
+	replica = object_get_data(prp->replica_object);
+	local_ruv_obj = replica_get_ruv (replica);
+	starting_ruv = ruv_dup((RUV*)object_get_data ( local_ruv_obj ));
+	object_release (local_ruv_obj);
 	
 	
 	/* Set up the callback data. */
 	/* Set up the callback data. */
 	cb_data.prp = prp;
 	cb_data.prp = prp;
@@ -190,44 +235,30 @@ windows_tot_run(Private_Repl_Protocol *prp)
 	cb_data.sleep_on_busy = 0UL;
 	cb_data.sleep_on_busy = 0UL;
 	cb_data.last_busy = current_time ();
 	cb_data.last_busy = current_time ();
 
 
-	/* Don't send anything if one-way is set. */
+	/* Don't send anything if one-way (ONE_WAY_SYNC_FROM_AD) is set. */
 	if ((one_way == ONE_WAY_SYNC_DISABLED) || (one_way == ONE_WAY_SYNC_TO_AD)) {
 	if ((one_way == ONE_WAY_SYNC_DISABLED) || (one_way == ONE_WAY_SYNC_TO_AD)) {
 		/* send everything */
 		/* send everything */
-		dn = slapi_ch_strdup(slapi_sdn_get_dn( windows_private_get_directory_subtree(prp->agmt)));
-
-		winsync_plugin_call_pre_ds_search_all_cb(prp->agmt, NULL, &dn, &scope, &filter,
-											 &attrs, &server_controls);
-
-		pb = slapi_pblock_new ();
-		/* Perform a subtree search for any ntuser or ntgroup entries underneath the
-		 * suffix defined in the sync agreement. */
-		slapi_search_internal_set_pb (pb, dn, scope, filter, attrs, 0, server_controls, NULL, 
-				repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0);
-
-		slapi_search_internal_callback_pb (pb, &cb_data /* callback data */,
-				get_result /* result callback */,
-				send_entry /* entry callback */,
-				NULL /* referral callback*/);
+		const subtreePair* subtree_pairs = NULL;
+		const subtreePair* sp = NULL;
+
+		subtree_pairs = windows_private_get_subtreepairs(prp->agmt);
+		if (subtree_pairs) {
+			for (sp = subtree_pairs; sp && sp->DSsubtree; sp++) {
+				_windows_tot_send_entry(prp->agmt, &cb_data, sp->DSsubtree);
+			}
+		} else {
+			_windows_tot_send_entry(prp->agmt, &cb_data, windows_private_get_directory_subtree(prp->agmt));
+		}
 	}
 	}
-
-    slapi_ch_free_string(&dn);
-    slapi_ch_free_string(&filter);
-    slapi_ch_array_free(attrs);
-    attrs = NULL;
-    ldap_controls_free(server_controls);
-    server_controls = NULL;
-
-    slapi_pblock_destroy (pb);
 	rc = cb_data.rc;
 	rc = cb_data.rc;
 	windows_release_replica(prp);
 	windows_release_replica(prp);
 		
 		
-    if (rc != LDAP_SUCCESS)
-    {
-        slapi_log_error (SLAPI_LOG_REPL, windows_repl_plugin_name, "%s: windows_tot_run: "
-                         "failed to obtain data to send to the consumer; LDAP error - %d\n", 
-                         agmt_get_long_name(prp->agmt), rc);
+	if (rc != LDAP_SUCCESS) {
+		slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name, "%s: windows_tot_run: "
+		                "failed to obtain data to send to the consumer; LDAP error - %d\n", 
+		                agmt_get_long_name(prp->agmt), rc);
 		agmt_set_last_init_status(prp->agmt, rc, 0, "Total update aborted");
 		agmt_set_last_init_status(prp->agmt, rc, 0, "Total update aborted");
-    } else {
+	} else {
 		slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name, "Finished total update of replica "
 		slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name, "Finished total update of replica "
 						"\"%s\". Sent %lu entries.\n", agmt_get_long_name(prp->agmt), cb_data.num_entries);
 						"\"%s\". Sent %lu entries.\n", agmt_get_long_name(prp->agmt), cb_data.num_entries);
 		agmt_set_last_init_status(prp->agmt, 0, 0, "Total update succeeded");
 		agmt_set_last_init_status(prp->agmt, 0, 0, "Total update succeeded");
@@ -256,9 +287,9 @@ windows_tot_run(Private_Repl_Protocol *prp)
 
 
 	/* call end total update callback */
 	/* call end total update callback */
 	winsync_plugin_call_end_update_cb(prp->agmt,
 	winsync_plugin_call_end_update_cb(prp->agmt,
-									  windows_private_get_directory_subtree(prp->agmt),
-									  windows_private_get_windows_subtree(prp->agmt),
-									  1 /* is_total == TRUE */);
+	                                  windows_private_get_directory_treetop(prp->agmt),
+	                                  windows_private_get_windows_treetop(prp->agmt),
+	                                  1 /* is_total == TRUE */);
 
 
 done:
 done:
 	if (starting_ruv)
 	if (starting_ruv)

+ 18 - 0
ldap/servers/plugins/replication/windowsrepl.h

@@ -43,6 +43,11 @@
 
 
 /* windows_private.c */
 /* windows_private.c */
 typedef struct windowsprivate Dirsync_Private;
 typedef struct windowsprivate Dirsync_Private;
+typedef struct subtreepair {
+	Slapi_DN *ADsubtree;
+	Slapi_DN *DSsubtree;
+} subtreePair;
+
 Dirsync_Private* windows_private_new();
 Dirsync_Private* windows_private_new();
 void windows_private_set_windows_subtree (const Repl_Agmt *ra,Slapi_DN* sdn );
 void windows_private_set_windows_subtree (const Repl_Agmt *ra,Slapi_DN* sdn );
 const Slapi_DN* windows_private_get_windows_subtree (const Repl_Agmt *ra);
 const Slapi_DN* windows_private_get_windows_subtree (const Repl_Agmt *ra);
@@ -68,6 +73,7 @@ void windows_private_set_isnt4(const Repl_Agmt *ra, int isit);
 int windows_private_get_iswin2k3(const Repl_Agmt *ra);
 int windows_private_get_iswin2k3(const Repl_Agmt *ra);
 void windows_private_set_iswin2k3(const Repl_Agmt *ra, int isit);
 void windows_private_set_iswin2k3(const Repl_Agmt *ra, int isit);
 Slapi_Filter* windows_private_get_directory_filter(const Repl_Agmt *ra);
 Slapi_Filter* windows_private_get_directory_filter(const Repl_Agmt *ra);
+Slapi_Filter* windows_private_get_windows_filter(const Repl_Agmt *ra);
 Slapi_Filter* windows_private_get_deleted_filter(const Repl_Agmt *ra);
 Slapi_Filter* windows_private_get_deleted_filter(const Repl_Agmt *ra);
 const char* windows_private_get_purl(const Repl_Agmt *ra);
 const char* windows_private_get_purl(const Repl_Agmt *ra);
 /*
 /*
@@ -94,6 +100,18 @@ void windows_private_set_curr_entry(const Repl_Agmt *ra, Slapi_Entry *e);
 char **windows_private_get_range_attrs(const Repl_Agmt *ra);
 char **windows_private_get_range_attrs(const Repl_Agmt *ra);
 void windows_private_set_range_attrs(const Repl_Agmt *ra, char **attrs);
 void windows_private_set_range_attrs(const Repl_Agmt *ra, char **attrs);
 
 
+void windows_private_set_directory_userfilter(const Repl_Agmt *ra, char *filter);
+void windows_private_set_windows_userfilter(const Repl_Agmt *ra, char *filter);
+const char* windows_private_get_directory_userfilter(const Repl_Agmt *ra);
+const char* windows_private_get_windows_userfilter(const Repl_Agmt *ra);
+
+const subtreePair* windows_private_get_subtreepairs(const Repl_Agmt *ra);
+void windows_private_set_subtreepairs(const Repl_Agmt *ra, char **parray);
+const Slapi_DN* windows_private_get_windows_treetop(const Repl_Agmt *ra);
+void windows_private_set_windows_treetop(const Repl_Agmt *ra, char *treetop);
+const Slapi_DN* windows_private_get_directory_treetop(const Repl_Agmt *ra);
+void windows_private_set_directory_treetop(const Repl_Agmt *ra, char *treetop);
+
 /* in windows_connection.c */
 /* in windows_connection.c */
 ConnResult windows_conn_connect(Repl_Connection *conn);
 ConnResult windows_conn_connect(Repl_Connection *conn);
 void windows_conn_disconnect(Repl_Connection *conn);
 void windows_conn_disconnect(Repl_Connection *conn);

+ 93 - 0
ldap/servers/slapd/dn.c

@@ -2011,6 +2011,17 @@ slapi_sdn_new_ndn_byref(const char *ndn)
     return sdn;
     return sdn;
 }
 }
 
 
+/* use when dn is already fully normalized */
+Slapi_DN *
+slapi_sdn_new_ndn_passin(const char *ndn)
+{
+    Slapi_DN *sdn = slapi_sdn_new();
+    slapi_sdn_set_ndn_passin(sdn, ndn);
+    SDN_DUMP( sdn, "slapi_sdn_new_ndn_passin");
+    return sdn;
+}
+
+
 /* use when dn is already normalized */
 /* use when dn is already normalized */
 Slapi_DN *
 Slapi_DN *
 slapi_sdn_new_normdn_byref(const char *normdn)
 slapi_sdn_new_normdn_byref(const char *normdn)
@@ -2154,6 +2165,20 @@ slapi_sdn_set_ndn_byref(Slapi_DN *sdn, const char *ndn)
     return sdn;
     return sdn;
 }
 }
 
 
+Slapi_DN *
+slapi_sdn_set_ndn_passin(Slapi_DN *sdn, const char *ndn)
+{
+    slapi_sdn_done(sdn);
+    sdn->flag = slapi_setbit_uchar(sdn->flag, FLAG_NDN);
+    sdn->ndn = ndn;
+    if (ndn == NULL) {
+        sdn->ndn_len = 0;
+    } else {
+        sdn->ndn_len = strlen(ndn);
+    }
+    return sdn;
+}
+
 /*
 /*
  * Set the RDN of the DN.
  * Set the RDN of the DN.
  */
  */
@@ -3017,3 +3042,71 @@ ndn_cache_get_stats(PRUint64 *hits, PRUint64 *tries, size_t *size, size_t *max_s
     slapi_rwlock_unlock(ndn_cache_lock);
     slapi_rwlock_unlock(ndn_cache_lock);
 }
 }
 
 
+/* Common ancestor sdn is allocated.
+ * caller is responsible to free it */
+Slapi_DN *
+slapi_sdn_common_ancestor(Slapi_DN *dn1, Slapi_DN *dn2)
+{
+    const char *dn1str = NULL;
+    const char *dn2str = NULL;
+    char **dns1 = NULL;
+    char **dns2 = NULL;
+    char **dn1p, **dn2p;
+    char **dn1end;
+    int dn1len = 0;
+    int dn2len = 0;
+    char *common = NULL;
+    char *cp = 0;
+    if ((NULL == dn1) || (NULL == dn2)) {
+        return NULL;
+    }
+    dn1str = slapi_sdn_get_ndn(dn1);
+    dn2str = slapi_sdn_get_ndn(dn2);
+    if (0 == strcmp(dn1str, dn2str)) {
+        /* identical */
+        return slapi_sdn_dup(dn1);
+    }
+    dn1len = strlen(dn1str);
+    dn2len = strlen(dn2str);
+    if (dn1len > dn2len) {
+        if (slapi_sdn_isparent(dn2, dn1)) {
+            /* dn2 is dn1's parent */
+            return slapi_sdn_dup(dn2);
+        }
+    } else if (dn1len < dn2len) {
+        if (slapi_sdn_isparent(dn1, dn2)) {
+            /* dn1 is dn2's parent */
+            return slapi_sdn_dup(dn1);
+        }
+    }
+    dns1 = slapi_ldap_explode_dn(slapi_sdn_get_ndn(dn1), 0);
+    dns2 = slapi_ldap_explode_dn(slapi_sdn_get_ndn(dn2), 0);
+    for (dn1p = dns1; dn1p && *dn1p; dn1p++) ;
+    for (dn2p = dns2; dn2p && *dn2p; dn2p++) ;
+    dn1end = dn1p;
+    while (--dn1p && --dn2p && (dn1p >= dns1) && (dn2p >= dns2)) {
+        if (strcmp(*dn1p, *dn2p)) {
+            break;
+        }
+    }
+    if (dn1end == ++dn1p) {
+        /* No common ancestor */
+        charray_free(dns1);
+        charray_free(dns2);
+        return NULL;
+    }
+    dn1len += 1;
+    cp = common = slapi_ch_malloc(dn1len);
+    *common = '\0';
+    do {
+        PR_snprintf(cp, dn1len, "%s,", *dn1p);
+        cp += strlen(*dn1p) + 1/*,*/;
+    } while (++dn1p < dn1end);
+    dn1len = strlen(common);
+    if (',' == *(common + dn1len - 1)) {
+        *(common + dn1len - 1) = '\0';
+    }
+    charray_free(dns1);
+    charray_free(dns2);
+    return slapi_sdn_new_ndn_passin(common);
+}

+ 45 - 0
ldap/servers/slapd/slapi-plugin.h

@@ -2262,6 +2262,7 @@ Slapi_DN *slapi_sdn_new_dn_byval(const char *dn);
  * \warning The \c ndn value is copied by the function itself.  The caller
  * \warning The \c ndn value is copied by the function itself.  The caller
  *          is still responsible for the memory used by \c ndn.
  *          is still responsible for the memory used by \c ndn.
  * \see slapi_sdn_new_ndn_byref()
  * \see slapi_sdn_new_ndn_byref()
+ * \see slapi_sdn_new_ndn_passin()
  * \see slapi_sdn_free()
  * \see slapi_sdn_free()
  * \see slapi_sdn_copy()
  * \see slapi_sdn_copy()
  * \see slapi_sdn_done()
  * \see slapi_sdn_done()
@@ -2301,12 +2302,30 @@ Slapi_DN *slapi_sdn_new_dn_byref(const char *dn);
  *          memory should not be freed until the returned \c Slapi_DN has been
  *          memory should not be freed until the returned \c Slapi_DN has been
  *          disposed of or reinitialized.
  *          disposed of or reinitialized.
  * \see slapi_sdn_new_ndn_byval()
  * \see slapi_sdn_new_ndn_byval()
+ * \see slapi_sdn_new_ndn_passin()
  * \see slapi_sdn_free()
  * \see slapi_sdn_free()
  * \see slapi_sdn_copy()
  * \see slapi_sdn_copy()
  * \see slapi_sdn_done()
  * \see slapi_sdn_done()
  */
  */
 Slapi_DN *slapi_sdn_new_ndn_byref(const char *ndn);
 Slapi_DN *slapi_sdn_new_ndn_byref(const char *ndn);
 
 
+/**
+ * Creates a new \c Slapi_DN structure and intializes it's normalized and case ignored DN to a requested value.
+ *
+ * The normalized and case ignored DN of the new structure will point to the same string pointed to by \c ndn.
+ * Ownership of the memory pointed to by \c ndn is tranferred to the Slapi_DN.
+ *
+ * \param ndn The normalized and case ignored DN value to be set in the new \c Slapi_DN structure.
+ * \return A pointer to the newly allocated \c Slapi_DN structure with
+ *         the normalized and case ignored DN value set to the content of \c ndn.
+ * \see slapi_sdn_new_ndn_byval()
+ * \see slapi_sdn_new_ndn_byref()
+ * \see slapi_sdn_free()
+ * \see slapi_sdn_copy()
+ * \see slapi_sdn_done()
+ */
+Slapi_DN *slapi_sdn_new_ndn_passin(const char *ndn);
+
 /**
 /**
  * Creates a new \c Slapi_DN structure and intializes it's DN to a requested value.
  * Creates a new \c Slapi_DN structure and intializes it's DN to a requested value.
  *
  *
@@ -2494,6 +2513,7 @@ Slapi_DN *slapi_sdn_set_normdn_byval(Slapi_DN *sdn, const char *dn);
  * \warning The \c ndn value is copied by the function itself.  The caller
  * \warning The \c ndn value is copied by the function itself.  The caller
  *          is still responsible for the memory used by \c ndn.
  *          is still responsible for the memory used by \c ndn.
  * \see slapi_sdn_set_ndn_byref()
  * \see slapi_sdn_set_ndn_byref()
+ * \see slapi_sdn_set_ndn_passin()
  */
  */
 Slapi_DN *slapi_sdn_set_ndn_byval(Slapi_DN *sdn, const char *ndn);
 Slapi_DN *slapi_sdn_set_ndn_byval(Slapi_DN *sdn, const char *ndn);
 
 
@@ -2510,9 +2530,25 @@ Slapi_DN *slapi_sdn_set_ndn_byval(Slapi_DN *sdn, const char *ndn);
  *          memory should not be freed until the returned \c Slapi_DN has been
  *          memory should not be freed until the returned \c Slapi_DN has been
  *          disposed of or reinitialized.
  *          disposed of or reinitialized.
  * \see slapi_sdn_set_ndn_byval()
  * \see slapi_sdn_set_ndn_byval()
+ * \see slapi_sdn_set_ndn_passin()
  */
  */
 Slapi_DN *slapi_sdn_set_ndn_byref(Slapi_DN *sdn, const char *ndn);
 Slapi_DN *slapi_sdn_set_ndn_byref(Slapi_DN *sdn, const char *ndn);
 
 
+/**
+ * Sets a normalized DN value in a \c Slapi_DN structure.
+ *
+ * The normalized DN of the structure will point to the same string pointed to
+ * by \c ndn.  
+ * Ownership of the memory pointed to by \c ndn is tranferred to the Slapi_DN.
+ *
+ * \param sdn The target \c Slapi_DN structure.
+ * \param ndn The normalized DN value to be set in \c sdn.
+ * \return A pointer to the \c Slapi_DN structure containing the new normalized DN value.
+ * \see slapi_sdn_set_ndn_byval()
+ * \see slapi_sdn_set_ndn_byref()
+ */
+Slapi_DN *slapi_sdn_set_ndn_passin(Slapi_DN *sdn, const char *ndn);
+
 /**
 /**
  * Clears the contents of a Slapi_DN structure.
  * Clears the contents of a Slapi_DN structure.
  *
  *
@@ -3631,6 +3667,15 @@ int slapi_rdn2typeval( char *rdn, char **type, struct berval *bv );
  */
  */
 char *slapi_dn_plus_rdn(const char *dn, const char *rdn);
 char *slapi_dn_plus_rdn(const char *dn, const char *rdn);
 
 
+/**
+ * Create an Slapi_DN that is a common ancestor of given 2 Slapi_DN's
+ *
+ * \param dn1 The first DN
+ * \param dn2 the second DN
+ * \return A pointer to the new Slapi_DN that is a common ancestor of dn1 and dn2
+ * \warning The caller must free the returnd Slapi_DN
+ */
+Slapi_DN *slapi_sdn_common_ancestor(Slapi_DN *dn1, Slapi_DN *dn2);
 
 
 /*
 /*
  * thread safe random functions
  * thread safe random functions

+ 1 - 1
ldap/servers/slapd/util.c

@@ -604,7 +604,7 @@ int slapi_mods2entry (Slapi_Entry **e, const char *idn, LDAPMod **iattrs)
         if (rc != LDAP_SUCCESS)
         if (rc != LDAP_SUCCESS)
         {
         {
             LDAPDebug2Args(LDAP_DEBUG_ANY,
             LDAPDebug2Args(LDAP_DEBUG_ANY,
-                "slapi_add_internal: add_values for type %s failed (rc: %d)\n",
+                "slapi_mods2entry: add_values for type %s failed (rc: %d)\n",
                 normtype, rc );
                 normtype, rc );
             slapi_entry_free (*e);
             slapi_entry_free (*e);
             *e = NULL;
             *e = NULL;