From bf1f2e8ae6e6f23f09f5303631ed602cc2830c93 Mon Sep 17 00:00:00 2001
From: Ludwig Krispenz <lkrispen@redhat.com>
Date: Tue, 14 Mar 2017 11:46:47 +0100
Subject: [PATCH 3/4] ticket 49043 - manage replication conflicts

implement new management of replication conflicts
- hide conflict entries by adding objectclass ldapsubentry
- move children to valid enries
- track changed dns by  creating cenotapohs

design doc:
---
 ldap/servers/plugins/replication/repl5_plugins.c |  16 +-
 ldap/servers/plugins/replication/urp.c           | 780 ++++++++++++++++++++---
 ldap/servers/plugins/replication/urp.h           |   1 +
 3 files changed, 707 insertions(+), 90 deletions(-)

diff --git a/ldap/servers/plugins/replication/repl5_plugins.c b/ldap/servers/plugins/replication/repl5_plugins.c
index 357c093..60d6de4 100644
--- a/ldap/servers/plugins/replication/repl5_plugins.c
+++ b/ldap/servers/plugins/replication/repl5_plugins.c
@@ -811,7 +811,20 @@ multimaster_bepreop_modrdn (Slapi_PBlock *pb)
 	return rc;
 }
 
-int 
+int
+multimaster_bepostop_add (Slapi_PBlock *pb)
+{
+	Slapi_Operation *op;
+
+	slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+	if ( ! operation_is_flag_set (op, OP_FLAG_REPL_FIXUP) )
+	{
+		urp_post_add_operation (pb);
+	}
+	return SLAPI_PLUGIN_SUCCESS;
+}
+
+int
 multimaster_bepostop_modrdn (Slapi_PBlock *pb)
 {
 	Slapi_Operation *op;
@@ -921,6 +934,7 @@ multimaster_be_betxnpostop_add (Slapi_PBlock *pb)
     int rc = 0;
     /* original betxnpost */
     rc = write_changelog_and_ruv(pb);
+    rc |= multimaster_bepostop_add(pb);
     return rc;
 }
 
diff --git a/ldap/servers/plugins/replication/urp.c b/ldap/servers/plugins/replication/urp.c
index 88723ef..c551fa9 100644
--- a/ldap/servers/plugins/replication/urp.c
+++ b/ldap/servers/plugins/replication/urp.c
@@ -24,12 +24,22 @@
 extern int slapi_log_urp;
 
 static int urp_add_resolve_parententry (Slapi_PBlock *pb, char *sessionid, Slapi_Entry *entry, Slapi_Entry *parententry, CSN *opcsn);
-static int urp_annotate_dn (char *sessionid, const Slapi_Entry *entry, CSN *opcsn, const char *optype);
+static int urp_add_check_tombstone (char *sessionid, Slapi_Entry *entry, CSN *opcsn);
+static int urp_add_new_entry_to_conflict (Slapi_PBlock *pb, char *sessionid, Slapi_Entry *addentry, CSN *opcsn);
+static int urp_annotate_dn (char *sessionid, const Slapi_Entry *entry, CSN *opcsn, const char *optype, char **conflict_dn);
+static int urp_conflict_to_glue (char *sessionid, const Slapi_Entry *entry, Slapi_DN *parentdn, CSN *opcsn);
+static char *urp_find_tombstone_for_glue (Slapi_PBlock *pb, char *sessionid, const Slapi_Entry *entry, Slapi_DN *parentdn, CSN *opcsn);
 static int urp_naming_conflict_removal (Slapi_PBlock *pb, char *sessionid, CSN *opcsn, const char *optype);
 static int mod_namingconflict_attr (const char *uniqueid, const Slapi_DN *entrysdn, const Slapi_DN *conflictsdn, CSN *opcsn, const char *optype);
+static int mod_objectclass_attr (const char *uniqueid, const Slapi_DN *entrysdn, const Slapi_DN *conflictsdn, CSN *opcsn, const char *optype);
 static int del_replconflict_attr (const Slapi_Entry *entry, CSN *opcsn, int opflags);
 static char *get_dn_plus_uniqueid(char *sessionid,const Slapi_DN *oldsdn,const char *uniqueid);
 static int is_suffix_entry (Slapi_PBlock *pb, Slapi_Entry *entry, Slapi_DN **parenddn);
+static int urp_fixup_add_cenotaph (Slapi_PBlock *pb, char *sessionid, CSN *opcsn);
+static int is_conflict_entry(Slapi_Entry *entry);
+static char * urp_get_valid_parent_nsuniqueid ( Slapi_DN *parentdn );
+static char * urp_get_conflict_dn_for_nsuniqueid ( char *nsuniqueid, Slapi_DN *parentdn );
+static int urp_rename_conflict_children(char *old_parent, Slapi_DN *new_parent);
 
 /*
  * Return 0 for OK, -1 for Error.
@@ -89,14 +99,12 @@ urp_add_operation( Slapi_PBlock *pb )
 	Slapi_Entry	*existing_uniqueid_entry;
 	Slapi_Entry	*existing_dn_entry;
 	Slapi_Entry	*addentry;
-	const char *adduniqueid;
 	CSN *opcsn;
 	const char *basedn;
 	char sessionid[REPL_SESSION_ID_SIZE];
 	int r;
 	int op_result= 0;
 	int rc= 0; /* OK */
-	Slapi_DN *sdn = NULL;
 
 	if ( slapi_op_abandoned(pb) )
 	{
@@ -131,6 +139,25 @@ urp_add_operation( Slapi_PBlock *pb )
 
 	slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &addentry );
 	slapi_pblock_get( pb, SLAPI_ADD_EXISTING_DN_ENTRY, &existing_dn_entry );
+
+	slapi_log_err (SLAPI_LOG_ERR, sessionid,
+		          "urp_add_operation - handling add of (%s).\n",
+			slapi_entry_get_dn_const(addentry));
+	/* first check if there was an existing entry at the time of this add 
+	 * in that case the new entry will become a conflict entry independent 
+	 * of the case if the other entry still exists
+	 */
+	if (urp_add_check_tombstone(sessionid, addentry, opcsn)) {
+		/* the new entry is conflicting with an already deleted entry
+		 * transform to conflict entry
+		 */
+		rc = urp_add_new_entry_to_conflict(pb, sessionid, addentry, opcsn);
+		slapi_log_err (SLAPI_LOG_ERR, sessionid,
+		          "urp_add_operation - add conflictentry (%s).\n",
+			slapi_entry_get_dn_const(addentry));
+		goto bailout;
+	}
+
 	if (existing_dn_entry==NULL) /* The target DN does not exist */
 	{
 		/* Check for parent entry... this could be an orphan. */
@@ -138,6 +165,7 @@ urp_add_operation( Slapi_PBlock *pb )
 		slapi_pblock_get( pb, SLAPI_ADD_PARENT_ENTRY, &parententry );
 		rc = urp_add_resolve_parententry (pb, sessionid, addentry, parententry, opcsn);
 		PROFILE_POINT; /* Add Entry */
+
 		goto bailout;
 	}
 
@@ -149,78 +177,17 @@ urp_add_operation( Slapi_PBlock *pb )
 	 * ATTR_NSDS5_REPLCONFLIC.
 	 */
 	basedn = slapi_entry_get_ndn (addentry);
-	adduniqueid = slapi_entry_get_uniqueid (addentry);
 	r = csn_compare (entry_get_dncsn(existing_dn_entry), opcsn);
 	if (r<0)
 	{
 		/* Entry to be added is a loser */
-		char *newdn= get_dn_plus_uniqueid (sessionid, (const Slapi_DN *)addentry, adduniqueid);
-		if(newdn==NULL)
-		{
-			op_result= LDAP_OPERATIONS_ERROR;
-			slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
-			rc = SLAPI_PLUGIN_NOOP; /* Abort this Operation */
-			slapi_log_err (slapi_log_urp, sessionid,
-			      "urp_add_operation - %s - Add conflict; Unique ID (%s) already in RDN\n",
-			      basedn, adduniqueid);
-			PROFILE_POINT; /* Add Conflict; Entry Exists; Unique ID already in RDN - Abort this update. */
-		}
-		else
-		{
-			/* Add the nsds5ReplConflict attribute in the mods */
-			Slapi_Attr *attr = NULL;
-			Slapi_Value **vals = NULL;
-			Slapi_RDN *rdn;
-			char buf[BUFSIZ];
-
-#ifdef DEBUG
-			PR_snprintf(buf, BUFSIZ, "%s (add) %s", REASON_ANNOTATE_DN, basedn);
-#else
-			PR_snprintf(buf, BUFSIZ, "%s %s", REASON_ANNOTATE_DN, basedn);
-#endif
-			if (slapi_entry_attr_find (addentry, ATTR_NSDS5_REPLCONFLICT, &attr) == 0)
-			{
-				/* ATTR_NSDS5_REPLCONFLICT exists */
-				slapi_log_err(SLAPI_LOG_ERR, sessionid,
-				          "urp_add_operation - New entry has nsds5ReplConflict already\n");
-				vals = attr_get_present_values (attr); /* this returns a pointer to the contents */
-			}
-			if ( vals == NULL || *vals == NULL )
-			{
-				/* Add new attribute */
-				slapi_entry_add_string (addentry, ATTR_NSDS5_REPLCONFLICT, buf);
-			}
-			else
-			{
-				/*
-				 * Replace old attribute. We don't worry about the index
-				 * change here since the entry is yet to be added.
-				 */
-				slapi_value_set_string (*vals, buf);
-			}
-			/* slapi_pblock_get(pb, SLAPI_ADD_TARGET, &dn); */
-			slapi_pblock_get(pb, SLAPI_ADD_TARGET_SDN, &sdn);
-			slapi_sdn_free(&sdn);
-
-			slapi_entry_set_normdn(addentry, newdn); /* dn: passin */
-
-			sdn = slapi_sdn_dup(slapi_entry_get_sdn_const(addentry));
-			slapi_pblock_set(pb, SLAPI_ADD_TARGET_SDN, sdn);
-
-			rdn = slapi_rdn_new_sdn ( slapi_entry_get_sdn_const(addentry) );
-			slapi_log_err (slapi_log_urp, sessionid,
-			                 "urp_add_operation - Naming conflict ADD. Add %s instead\n",
-			                 slapi_rdn_get_rdn(rdn));
-			slapi_rdn_free(&rdn);
-
-			rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY);
-			PROFILE_POINT; /* Add Conflict; Entry Exists; Rename Operation Entry */
-		}
+		rc = urp_add_new_entry_to_conflict(pb, sessionid, addentry, opcsn);
 	}
 	else if(r>0)
 	{
+		char *conflict_dn = NULL;
 		/* Existing entry is a loser */
-		if (!urp_annotate_dn(sessionid, existing_dn_entry, opcsn, "ADD"))
+		if (!urp_annotate_dn(sessionid, existing_dn_entry, opcsn, "ADD", &conflict_dn))
 		{
 			op_result= LDAP_OPERATIONS_ERROR;
 			slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
@@ -234,6 +201,7 @@ urp_add_operation( Slapi_PBlock *pb )
 			/* The backend add code should now search for the existing entry again. */
 			rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY);
 			rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_PARENT_ENTRY);
+			slapi_pblock_set(pb, SLAPI_URP_NAMING_COLLISION_DN, conflict_dn);
 		}
 		PROFILE_POINT; /* Add Conflict; Entry Exists; Rename Existing Entry */
 	}
@@ -444,6 +412,7 @@ urp_modrdn_operation( Slapi_PBlock *pb )
 				goto bailout;
 			}
 			mod_namingconflict_attr (op_uniqueid, target_sdn, existing_sdn, opcsn, "MODRDN");
+		mod_objectclass_attr (op_uniqueid, target_sdn, target_sdn, opcsn, "MODRDN");
 			slapi_pblock_set(pb, SLAPI_MODRDN_NEWRDN, newrdn_with_uniqueid); 
 			slapi_log_err (slapi_log_urp, sessionid,
 			                "urp_modrdn_operation - Naming conflict MODRDN. Rename target entry from %s to %s\n",
@@ -457,7 +426,7 @@ urp_modrdn_operation( Slapi_PBlock *pb )
 		{
 			/* The existing entry is a loser */
 
-			int resolve = urp_annotate_dn (sessionid, existing_entry, opcsn, "MODRDN");
+			int resolve = urp_annotate_dn (sessionid, existing_entry, opcsn, "MODRDN", NULL);
 			if(!resolve)
 			{
 				op_result= LDAP_OPERATIONS_ERROR;
@@ -578,7 +547,7 @@ bailout:
 /*
  * Return 0 for OK, -1 for Error
  */
-int 
+int
 urp_delete_operation( Slapi_PBlock *pb )
 {
 	const Slapi_Entry *deleteentry;
@@ -651,6 +620,64 @@ urp_delete_operation( Slapi_PBlock *pb )
 	return rc;
 }
 
+/*
+ * Return 0 for OK, -1 for Error
+ */
+int
+urp_post_add_operation( Slapi_PBlock *pb )
+{
+	char sessionid[REPL_SESSION_ID_SIZE];
+	Slapi_Operation *op;
+	CSN *opcsn;
+	Slapi_Entry *existing_dn_entry;
+	Slapi_Entry *addentry;
+	Slapi_DN *parentdn = NULL;
+	char *nsuid = NULL;
+	char *conflict_dn = NULL;
+	char *valid_dn = NULL;
+
+	slapi_pblock_get( pb, SLAPI_URP_NAMING_COLLISION_DN, &conflict_dn );
+	if (conflict_dn == NULL) {
+		/* there was no conflicting entry, done */
+		return 0;
+	}
+#if 0
+	if( is_suffix_entry (pb, existing_dn_entry, &parentdn) )
+	{
+		/* should not happen, just used to get parentdn */
+		return 0;
+	}
+	/* there was a conflict, but we lost info if the new or existing entry
+	 * was transformed into a conflict entry
+	 * if it was the new entry we are done.
+	 * if it was the existing entry we need to check for child entries and
+	 * move them to the valid entry
+	 */
+
+	nsuid = slapi_entry_attr_get_charptr(existing_dn_entry, "nsuniqueid");
+
+	/* search the entry */
+	conflict_dn = urp_get_conflict_dn_for_nsuniqueid(nsuid, parentdn);
+
+	if (conflict_dn == NULL) {
+		/* existing entry is not a conflict dn, done */
+		return 0;
+	}
+#endif
+
+	slapi_pblock_get( pb, SLAPI_OPERATION, &op);
+	get_repl_session_id (pb, sessionid, &opcsn);
+	slapi_log_err(SLAPI_LOG_ERR, sessionid,
+			                "urp_post_add_operation - Entry %s is conflict entry, check for children\n",
+			                conflict_dn);
+	slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &addentry );
+	valid_dn = slapi_entry_get_dn_const(addentry);
+	slapi_log_err(SLAPI_LOG_ERR, sessionid,
+			                "urp_post_add_operation - Entry %s is valid entry, check for children\n",
+			                valid_dn);
+	urp_rename_conflict_children(conflict_dn, slapi_entry_get_sdn_const(addentry));
+}
+
 int urp_post_modrdn_operation (Slapi_PBlock *pb)
 {
 	CSN *opcsn;
@@ -685,6 +712,11 @@ int urp_post_modrdn_operation (Slapi_PBlock *pb)
 		{
 			get_repl_session_id (pb, sessionid, &opcsn);
 			urp_naming_conflict_removal (pb, sessionid, opcsn, "MODRDN");
+
+			/* keep track of the entry's lifetime befor renaming
+			 * by creating a cenotaph
+			 */
+			urp_fixup_add_cenotaph(pb, sessionid, opcsn);
 		}
 	}
 	else
@@ -757,6 +789,71 @@ urp_post_delete_operation( Slapi_PBlock *pb )
 	return 0;
 }
 
+static int
+urp_fixup_add_cenotaph (Slapi_PBlock *pb, char *sessionid, CSN *opcsn)
+{
+	Slapi_PBlock *add_pb = slapi_pblock_new();
+	Slapi_Entry *cenotaph = NULL;
+	Slapi_Entry *pre_entry = NULL;
+	int ret = 0;
+	Slapi_DN *pre_sdn = NULL;
+	Slapi_RDN *rdn = slapi_rdn_new();
+	char *parentdn = NULL;
+	char *newdn;
+	const char *entrydn;
+	const char *uniqueid = NULL;
+	CSN *dncsn = NULL;
+	char csnstr[CSN_STRSIZE+1];
+
+	slapi_pblock_get ( pb, SLAPI_ENTRY_PRE_OP, &pre_entry );
+	if (pre_entry == NULL) {
+		slapi_log_err (SLAPI_LOG_ERR, sessionid,
+				"urp_fixup_add_cenotaph - failed to get preop entry\n");
+		return -1;
+	}
+
+	pre_sdn = slapi_entry_get_sdn(pre_entry);
+	uniqueid = slapi_entry_get_uniqueid (pre_entry);
+	entrydn = slapi_entry_get_ndn (pre_entry);
+	parentdn = slapi_dn_parent(entrydn);
+	slapi_sdn_get_rdn(pre_sdn, rdn);
+	slapi_rdn_add(rdn, "cenotaphID", uniqueid);
+	newdn = slapi_ch_smprintf("%s,%s", slapi_rdn_get_rdn(rdn), parentdn);
+	slapi_ch_free_string(&parentdn);
+	/* slapi_sdn_free(&pre_sdn); */
+
+	cenotaph = slapi_entry_alloc();
+	slapi_entry_init(cenotaph, newdn, NULL);
+
+	dncsn = (CSN *)entry_get_dncsn (pre_entry);
+	slapi_entry_add_string(cenotaph, SLAPI_ATTR_OBJECTCLASS, "extensibleobject");
+	slapi_entry_add_string(cenotaph, SLAPI_ATTR_OBJECTCLASS, "nstombstone");
+	slapi_entry_add_string(cenotaph,"cenotaphfrom", csn_as_string(dncsn, PR_FALSE, csnstr));
+	slapi_entry_add_string(cenotaph,"cenotaphto", csn_as_string(opcsn, PR_FALSE, csnstr));
+	slapi_entry_add_string(cenotaph,"nstombstonecsn", csn_as_string(opcsn, PR_FALSE, csnstr));
+	slapi_entry_add_string(cenotaph, SLAPI_ATTR_NSCP_ENTRYDN, entrydn);
+
+	slapi_log_err (SLAPI_LOG_ERR, sessionid,
+			"urp_fixup_add_cenotaph - addinng cenotaph: %s \n", newdn);
+	slapi_pblock_init(add_pb);
+
+	slapi_add_entry_internal_set_pb(add_pb,
+			cenotaph,
+			NULL,
+			repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),
+			OP_FLAG_REPL_FIXUP);
+	slapi_add_internal_pb(add_pb);
+	slapi_pblock_get(add_pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+
+	if (ret != LDAP_SUCCESS) {
+		slapi_log_err (SLAPI_LOG_ERR, sessionid,
+			"urp_fixup_add_cenotaph - failed to add cenotaph, err= %d\n", ret);
+	}
+	slapi_pblock_destroy(add_pb);
+
+	return ret;
+}
+
 int
 urp_fixup_add_entry (Slapi_Entry *e, const char *target_uniqueid, const char *parentuniqueid, CSN *opcsn, int opflags)
 {
@@ -839,6 +936,44 @@ urp_fixup_rename_entry (const Slapi_Entry *entry, const char *newrdn, const char
 }
 
 int
+urp_fixup_move_entry (const Slapi_Entry *entry, Slapi_DN *newsuperior, int opflags)
+{
+	Slapi_PBlock *newpb;
+    Slapi_Operation *op;
+	CSN *opcsn;
+	int op_result;
+
+	newpb = slapi_pblock_new();
+
+	/*
+	 * Must mark this operation as replicated,
+	 * so that the frontend doesn't add extra attributes.
+	 */
+	slapi_rename_internal_set_pb_ext (
+					newpb,
+					slapi_entry_get_sdn_const (entry),
+					slapi_entry_get_rdn_const(entry), /*NewRDN*/
+					newsuperior, /*NewSuperior*/
+					0, /* !Delete Old RDNS */
+					NULL, /*Controls*/
+					slapi_entry_get_uniqueid (entry), /*uniqueid*/
+					repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),
+					OP_FLAG_REPLICATED | OP_FLAG_REPL_FIXUP | opflags);
+
+    /* set operation csn to the entry's dncsn */
+	/* TBD check which csn to use 
+	opcsn = (CSN *)entry_get_dncsn (entry);
+    slapi_pblock_get (newpb, SLAPI_OPERATION, &op);
+    operation_set_csn (op, opcsn);
+TBD */
+	slapi_modrdn_internal_pb(newpb); 
+    slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_RESULT, &op_result);
+
+	slapi_pblock_destroy(newpb);
+	return op_result;
+}
+
+int
 urp_fixup_delete_entry (const char *uniqueid, const char *dn, CSN *opcsn, int opflags)
 {
 	Slapi_PBlock *newpb;
@@ -966,6 +1101,47 @@ urp_add_resolve_parententry (Slapi_PBlock *pb, char *sessionid, Slapi_Entry *ent
 		goto bailout;
 	}
 
+	if(is_conflict_entry(parententry))
+	{
+	/* The parent is a conflict entry
+	 * Check if a valid entry exists and make this the new parent
+	 *
+	 * or if no valid entry exists
+	 * - check if also a tombstone for the parentdn exists
+	 * - compare csns of tombstone and conflict generation
+	 * - turn the latest into a valid glue
+	 */
+		char *valid_nsuniqueid = urp_get_valid_parent_nsuniqueid(parentdn);
+		if (valid_nsuniqueid) {
+			slapi_log_err(SLAPI_LOG_ERR, sessionid,
+				"urp_resolve parent entry: found valid parent %s\n", slapi_sdn_get_dn(parentdn));
+			struct slapi_operation_parameters *op_params;
+			slapi_pblock_get( pb, SLAPI_OPERATION_PARAMETERS, &op_params );
+			/* free it ? */
+			op_params->p.p_add.parentuniqueid = valid_nsuniqueid;
+		} else {
+			int op_result;
+			char *tombstone_nsuniqueid = urp_find_tombstone_for_glue(pb, sessionid, parententry, parentdn, opcsn);
+			slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &op_result);
+			if (tombstone_nsuniqueid) {
+				struct slapi_operation_parameters *op_params;
+				slapi_pblock_get( pb, SLAPI_OPERATION_PARAMETERS, &op_params );
+					op_params->p.p_add.parentuniqueid = tombstone_nsuniqueid;
+					rc = LDAP_SUCCESS;
+			} else if (0 == op_result) {
+				rc = urp_conflict_to_glue(sessionid, parententry, parentdn, opcsn);
+				if (rc == 0) {
+					slapi_log_err(SLAPI_LOG_ERR, sessionid,
+					"urp_resolve parent entry: created valid parent from conflict %s\n", slapi_sdn_get_dn(parentdn));
+				} else {
+					slapi_log_err(SLAPI_LOG_ERR, sessionid,
+					"urp_resolve parent entry: cannot resolve parent %s\n", slapi_sdn_get_dn(parentdn));
+				}
+			}
+		}
+		goto bailout;
+	}
+
 	/* The parent is healthy */
 	/* Now we need to check that the parent has the correct DN */
 	if (slapi_sdn_isparent(slapi_entry_get_sdn(parententry), slapi_entry_get_sdn(entry)))
@@ -1004,7 +1180,264 @@ bailout:
 	slapi_rdn_free(&add_rdn);
 	return rc;
 }
+static int
+urp_add_new_entry_to_conflict (Slapi_PBlock *pb, char *sessionid, Slapi_Entry *addentry, CSN *opcsn)
+{
+	int rc = 0;
+	int op_result;
+
+	/* Entry to be added is a loser */
+	char *basedn = slapi_entry_get_ndn (addentry);
+	const char *adduniqueid = slapi_entry_get_uniqueid (addentry);
+	char *newdn= get_dn_plus_uniqueid (sessionid, (const Slapi_DN *)addentry, adduniqueid);
+	if(newdn==NULL)
+	{
+		op_result= LDAP_OPERATIONS_ERROR;
+		slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
+		rc = SLAPI_PLUGIN_NOOP; /* Abort this Operation */
+		slapi_log_err (slapi_log_urp, sessionid,
+		      "urp_add_operation - %s - Add conflict; Unique ID (%s) already in RDN\n",
+		      basedn, adduniqueid);
+	}
+	else
+	{
+		/* Add the nsds5ReplConflict attribute in the mods */
+		Slapi_Attr *attr = NULL;
+		Slapi_Value **vals = NULL;
+		Slapi_Value **csn_vals = NULL;
+		Slapi_DN *sdn = NULL;
+		Slapi_RDN *rdn;
+		char buf[BUFSIZ];
+		char csnstr[CSN_STRSIZE+1];
+
+		PR_snprintf(buf, BUFSIZ, "%s (ADD) %s", REASON_ANNOTATE_DN, basedn);
+		if (slapi_entry_attr_find (addentry, ATTR_NSDS5_REPLCONFLICT, &attr) == 0)
+		{
+			/* ATTR_NSDS5_REPLCONFLICT exists */
+			slapi_log_err(SLAPI_LOG_ERR, sessionid,
+			          "urp_add_operation - New entry has nsds5ReplConflict already\n");
+			vals = attr_get_present_values (attr); /* this returns a pointer to the contents */
+		}
+		if ( vals == NULL || *vals == NULL )
+		{
+			/* Add new attribute */
+			slapi_entry_add_string (addentry, ATTR_NSDS5_REPLCONFLICT, buf);
+		}
+		else
+		{
+			/*
+			 * Replace old attribute. We don't worry about the index
+			 * change here since the entry is yet to be added.
+			 */
+			slapi_value_set_string (*vals, buf);
+		}
+
+		/* add the ldapsubentry objectclass if not present */
+		slapi_entry_attr_find(addentry, "objectclass", &attr);
+		if (attr != NULL) {
+			struct berval bv;
+			bv.bv_val = "ldapsubentry";
+			bv.bv_len = strlen(bv.bv_val);
+			if (slapi_attr_value_find(attr, &bv) != 0) {
+				Slapi_Value *new_v = slapi_value_new();
+				slapi_value_init_berval(new_v, &bv);
+				slapi_attr_add_value(attr, new_v);
+				slapi_entry_set_flag(addentry, SLAPI_ENTRY_LDAPSUBENTRY);
+			}
+		}
+		/* add or replace the conflict csn */
+		if (slapi_entry_attr_find (addentry, "conflictcsn", &attr) == 0) {
+			csn_vals = attr_get_present_values (attr);
+		}
+		if (csn_vals == NULL || *csn_vals == NULL ) {
+			slapi_entry_add_string(addentry,"conflictcsn", csn_as_string(opcsn, PR_FALSE, csnstr));
+		} else {
+
+			slapi_value_set_string (*csn_vals, csn_as_string(opcsn, PR_FALSE, csnstr));
+		}
+
+		/* slapi_pblock_get(pb, SLAPI_ADD_TARGET, &dn); */
+		slapi_pblock_get(pb, SLAPI_ADD_TARGET_SDN, &sdn);
+		slapi_sdn_free(&sdn);
+
+		slapi_entry_set_normdn(addentry, newdn); /* dn: passin */
+
+		sdn = slapi_sdn_dup(slapi_entry_get_sdn_const(addentry));
+		slapi_pblock_set(pb, SLAPI_ADD_TARGET_SDN, sdn);
+
+		rdn = slapi_rdn_new_sdn ( slapi_entry_get_sdn_const(addentry) );
+		slapi_log_err (SLAPI_LOG_ERR, sessionid,
+		                 "urp_add_operation - Naming conflict ADD. Add %s instead\n",
+		                 slapi_rdn_get_rdn(rdn));
+		slapi_rdn_free(&rdn);
+		rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY);
+
+	}
+	return rc;
+}
+
+static int
+urp_add_check_tombstone (char *sessionid, Slapi_Entry *entry, CSN *opcsn)
+{
+	int rc = 0;
+	int op_result;
+	Slapi_Entry **entries = NULL;
+	Slapi_PBlock *newpb;
+	char *basedn = slapi_entry_get_ndn (entry);
+
+	char *filter = slapi_filter_sprintf("(&(objectclass=nstombstone)(nscpentrydn=%s))", basedn);
+	newpb = slapi_pblock_new();
+	char *parent_dn = slapi_dn_parent (basedn);
+	slapi_search_internal_set_pb(newpb,
+								 parent_dn, /* Base DN */
+								 LDAP_SCOPE_SUBTREE,
+								 filter,
+								 NULL, /* Attrs */
+								 0, /* AttrOnly */
+								 NULL, /* Controls */
+								 NULL, /* UniqueID */
+								 repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),
+								 0);
+	slapi_search_internal_pb(newpb);
+	slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_RESULT, &op_result);
+	slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+	if ( (op_result != LDAP_SUCCESS) || (entries == NULL) )
+	{
+		/* Log a message */
+		goto done;
+	}
+	for (int i = 0; entries && (entries[i] != NULL); i++) {
+		/* we need to distinguish between normal tombstones abd cenotaphs */
+		CSN *from_csn = NULL;
+		CSN *to_csn = NULL;
+		char *to_value = NULL;
+		char *from_value = slapi_entry_attr_get_charptr(entries[i], "cenotaphfrom");
+		if (from_value) {
+			from_csn = csn_new_by_string(from_value);
+			to_value = slapi_entry_attr_get_charptr(entries[i], "cenotaphto");
+			to_csn = csn_new_by_string(to_value);
+			slapi_ch_free_string(&from_value);
+		} else {
+			from_csn = csn_dup(entry_get_dncsn(entries[i]));
+			to_value = slapi_entry_attr_get_charptr(entries[i], "nstombstonecsn");
+			to_csn = csn_new_by_string(to_value);
+		}
+		if (csn_compare(from_csn, opcsn) < 0 &&
+			csn_compare(to_csn, opcsn) > 0 ) {
+		slapi_log_err (SLAPI_LOG_ERR, sessionid,
+		          "urp_add_check_tombstone - found conflicting tombstone (%s).\n",
+			slapi_entry_get_dn_const(entries[i]));
+			rc = 1;
+		}
+		slapi_ch_free_string(&to_value);
+		csn_free(&from_csn);
+		csn_free(&to_csn);
+
+		if (rc) break;
+	}
+
+done:
+	return rc;
+}
 
+static char*
+urp_find_tombstone_for_glue (Slapi_PBlock *pb, char *sessionid, const Slapi_Entry *entry, Slapi_DN *parentdn, CSN *opcsn)
+{
+	char *tombstone_nsuniqueid = NULL;
+	int op_result;
+	int rc = LDAP_SUCCESS;;
+	Slapi_Entry **entries = NULL;
+	Slapi_PBlock *newpb;
+	char *basedn = slapi_sdn_get_dn(parentdn);
+	char *conflict_csnstr = slapi_entry_attr_get_charptr(entry, "conflictcsn");
+	CSN *conflict_csn = csn_new_by_string(conflict_csnstr);
+
+	char *filter = slapi_filter_sprintf("(&(objectclass=nstombstone)(nscpentrydn=%s))", basedn);
+	newpb = slapi_pblock_new();
+	char *parent_dn = slapi_dn_parent (basedn);
+	slapi_search_internal_set_pb(newpb,
+								 parent_dn, /* Base DN */
+								 LDAP_SCOPE_SUBTREE,
+								 filter,
+								 NULL, /* Attrs */
+								 0, /* AttrOnly */
+								 NULL, /* Controls */
+								 NULL, /* UniqueID */
+								 repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),
+								 0);
+	slapi_search_internal_pb(newpb);
+	slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_RESULT, &op_result);
+	slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+	if ( (op_result != LDAP_SUCCESS) || (entries == NULL) )
+	{
+		/* Log a message */
+		goto done;
+	}
+	for (int i = 0; entries && (entries[i] != NULL); i++) {
+		CSN *tombstone_csn = NULL;
+		char *tombstone_csn_value = slapi_entry_attr_get_charptr(entries[i], "nstombstonecsn");
+		if (tombstone_csn_value) {
+			tombstone_csn = csn_new_by_string(tombstone_csn_value);
+			if( csn_compare(tombstone_csn, conflict_csn) > 0 ) {
+				slapi_log_err (SLAPI_LOG_ERR, sessionid,
+					"urp_find_tombstone_for_glue - found tombstone newer than conflict (%s).\n",
+					slapi_entry_get_dn_const(entries[i]));
+				tombstone_nsuniqueid = slapi_entry_attr_get_charptr(entries[i], "nsuniqueid");
+				rc = tombstone_to_glue (pb, sessionid, entries[i], parentdn, REASON_RESURRECT_ENTRY, opcsn, NULL);
+				if (rc) {
+					slapi_log_err(SLAPI_LOG_ERR, sessionid,
+					"urp_resolve parent entry: failed to create glue from tombstone %s\n", slapi_sdn_get_dn(parentdn));
+					tombstone_nsuniqueid = NULL;
+				}
+				break;
+			}
+		}
+	}
+
+done:
+	slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+	return tombstone_nsuniqueid;
+}
+
+static int
+urp_conflict_to_glue (char *sessionid, const Slapi_Entry *entry, Slapi_DN *parentdn, CSN *opcsn)
+{
+	int rc = 0;
+	int op_result;
+	char *newrdn;
+	const char *uniqueid;
+	Slapi_RDN *parentrdn = slapi_rdn_new();
+	const Slapi_DN *basesdn;
+	const char *basedn;
+
+	uniqueid = slapi_entry_get_uniqueid (entry);
+	basesdn = slapi_entry_get_sdn_const (entry);
+	basedn = slapi_entry_get_dn_const (entry);
+	slapi_sdn_get_rdn(parentdn, parentrdn);
+	newrdn = slapi_rdn_get_rdn(parentrdn);
+	if(newrdn) {
+		del_replconflict_attr(entry, opcsn, NULL);
+		slapi_log_err (slapi_log_urp, sessionid,
+		                "urp_conflict_to_glue - %s --> %s\n", basedn, newrdn);
+		op_result = urp_fixup_rename_entry ( entry, newrdn, NULL, 0 );
+		switch(op_result)
+		{
+		case LDAP_SUCCESS:
+			break;
+		case LDAP_NO_SUCH_OBJECT:
+			slapi_log_err (slapi_log_urp, sessionid,
+		                "urp_conflict_to_glue failed(%d) - %s --> %s\n", op_result, basedn, newrdn);
+			rc = LDAP_NO_SUCH_OBJECT;
+			break;
+		default: 
+			slapi_log_err (slapi_log_urp, sessionid,
+		                "urp_conflict_to_glue failed(%d) - %s --> %s\n", op_result, basedn, newrdn);
+			rc = 1;
+		}
+		slapi_ch_free ( (void**)&newrdn );
+	}
+	return rc;
+}
 /* 
  * urp_annotate_dn:
  * Returns 0 on failure
@@ -1014,7 +1447,7 @@ bailout:
  * a new entry (the operation entry) see urp_add_operation.
  */
 static int
-urp_annotate_dn (char *sessionid, const Slapi_Entry *entry, CSN *opcsn, const char *optype)
+urp_annotate_dn (char *sessionid, const Slapi_Entry *entry, CSN *opcsn, const char *optype, char **conflict_dn)
 {
 	int rc = 0; /* Fail */
 	int op_result;
@@ -1027,8 +1460,10 @@ urp_annotate_dn (char *sessionid, const Slapi_Entry *entry, CSN *opcsn, const ch
 	basesdn = slapi_entry_get_sdn_const (entry);
 	basedn = slapi_entry_get_dn_const (entry);
 	newrdn = get_rdn_plus_uniqueid ( sessionid, basedn, uniqueid );
+	if (conflict_dn) *conflict_dn = NULL;
 	if(newrdn) {
 		mod_namingconflict_attr (uniqueid, basesdn, basesdn, opcsn, optype);
+		mod_objectclass_attr (uniqueid, basesdn, basesdn, opcsn, optype);
 		slapi_log_err (slapi_log_urp, sessionid,
 		                "urp_annotate_dn - %s --> %s\n", basedn, newrdn);
 		op_result = urp_fixup_rename_entry ( entry, newrdn, NULL, 0 );
@@ -1038,6 +1473,9 @@ urp_annotate_dn (char *sessionid, const Slapi_Entry *entry, CSN *opcsn, const ch
 			slapi_log_err (slapi_log_urp, sessionid,
 			                "urp_annotate_dn - Naming conflict %s. Renamed existing entry to %s\n",
 			                optype, newrdn);
+			if (conflict_dn) {
+				*conflict_dn = slapi_ch_smprintf("%s,%s",newrdn,slapi_dn_find_parent(basedn));
+			}
 			rc = 1;
 			break;
 		case LDAP_NO_SUCH_OBJECT:
@@ -1075,6 +1513,132 @@ urp_annotate_dn (char *sessionid, const Slapi_Entry *entry, CSN *opcsn, const ch
 	}
 	return rc;
 }
+static char *
+urp_get_conflict_dn_for_nsuniqueid ( char *nsuniqueid, Slapi_DN *parentdn )
+{
+	Slapi_PBlock *newpb = NULL;
+	Slapi_Entry **entries = NULL;
+	int op_result = LDAP_SUCCESS;
+	char *conflict_dn = NULL;
+	char *filter = NULL;
+
+	filter = slapi_filter_sprintf("((&(objectclass=ldapsubentry)(nsuniqueid=%s))", nsuniqueid);
+	newpb = slapi_pblock_new();
+	slapi_search_internal_set_pb(newpb,
+					 slapi_sdn_get_dn(parentdn),
+					 LDAP_SCOPE_ONELEVEL,
+					 filter,
+					 NULL, /* Attrs */
+					 0, /* AttrOnly */
+					 NULL, /* Controls */
+					 NULL, /* UniqueID */
+					repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),
+					 0);
+	slapi_search_internal_pb(newpb);
+	slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_RESULT, &op_result);
+	slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+
+	if ( (op_result != LDAP_SUCCESS) || (entries == NULL) )
+	{
+		/* Log a message */
+		goto done;
+	}
+
+	conflict_dn = slapi_entry_attr_get_charptr(entries[0], "nsuniqueid");
+done:
+	slapi_free_search_results_internal(newpb);
+	slapi_pblock_destroy(newpb);
+	newpb = NULL;
+	if (filter) {
+		PR_smprintf_free(filter);
+	}
+
+	return conflict_dn;
+
+}
+static char *
+urp_get_valid_parent_nsuniqueid ( Slapi_DN *parentdn )
+{
+	Slapi_PBlock *newpb = NULL;
+	Slapi_Entry **entries = NULL;
+	int op_result = LDAP_SUCCESS;
+	char *nsuid = NULL;
+
+	newpb = slapi_pblock_new();
+	slapi_search_internal_set_pb(newpb,
+					 slapi_sdn_get_dn(parentdn),
+					 LDAP_SCOPE_BASE,
+					 "objectclass=*",
+					 NULL, /* Attrs */
+					 0, /* AttrOnly */
+					 NULL, /* Controls */
+					 NULL, /* UniqueID */
+					repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),
+					 0);
+	slapi_search_internal_pb(newpb);
+	slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_RESULT, &op_result);
+	slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+
+	if ( (op_result != LDAP_SUCCESS) || (entries == NULL) )
+	{
+		/* Log a message */
+		goto done;
+	}
+
+	nsuid = slapi_entry_attr_get_charptr(entries[0], "nsuniqueid");
+done:
+	slapi_free_search_results_internal(newpb);
+	slapi_pblock_destroy(newpb);
+	newpb = NULL;
+
+	return nsuid;
+}
+
+static int
+urp_rename_conflict_children(char *old_parent, Slapi_DN *new_parent)
+{
+	Slapi_PBlock *newpb = NULL;
+	Slapi_Entry **entries = NULL;
+	int op_result = LDAP_SUCCESS;
+	int i;
+
+	newpb = slapi_pblock_new();
+	slapi_search_internal_set_pb(newpb,
+					 old_parent,
+					 LDAP_SCOPE_ONELEVEL,
+					 "objectclass=*",
+					 NULL, /* Attrs */
+					 0, /* AttrOnly */
+					 NULL, /* Controls */
+					 NULL, /* UniqueID */
+					repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),
+					 0);
+	slapi_search_internal_pb(newpb);
+	slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_RESULT, &op_result);
+	slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+
+	if ( (op_result != LDAP_SUCCESS) || (entries == NULL) )
+	{
+		/* Log a message */
+		goto done;
+	}
+	for (i = 0; NULL != entries[i]; i++) {
+		slapi_log_err(SLAPI_LOG_ERR, "session test",
+			                "urp_rename_conflict children - Renaming: %s\n",
+					slapi_entry_get_dn_const(entries[i]));
+		op_result = urp_fixup_move_entry(entries[i],new_parent,0);
+		slapi_log_err(SLAPI_LOG_ERR, "session test",
+			                "urp_rename_conflict children - Renam: %s, Result: %d\n",
+					slapi_entry_get_dn_const(entries[i]), op_result);
+	}
+
+done:
+	slapi_free_search_results_internal(newpb);
+	slapi_pblock_destroy(newpb);
+	newpb = NULL;
+
+	return op_result;
+}
 
 /*
  * An URP Naming Collision helper function. Retrieves a list of entries
@@ -1106,7 +1670,7 @@ urp_get_min_naming_conflict_entry ( Slapi_PBlock *pb, char *sessionid, CSN *opcs
 	slapi_log_err(SLAPI_LOG_REPL, sessionid,
 		"urp_get_min_naming_conflict_entry - %s\n", basedn);
 
-	filter = slapi_filter_sprintf("(%s=%s %s%s)", ATTR_NSDS5_REPLCONFLICT, REASON_ANNOTATE_DN,
+	filter = slapi_filter_sprintf("(&(objectclass=ldapsubentry)(%s=%s (ADD) %s%s))", ATTR_NSDS5_REPLCONFLICT, REASON_ANNOTATE_DN,
 			ESC_NEXT_VAL, basedn);
 
 	/* server_ctrls will be freed when newpb is destroyed */
@@ -1139,7 +1703,7 @@ urp_get_min_naming_conflict_entry ( Slapi_PBlock *pb, char *sessionid, CSN *opcs
 	{
 		const CSN *dncsn;
 		dncsn = entry_get_dncsn(entries[i]);
-		if ((dncsn != opcsn) && 
+		if ((dncsn != opcsn) && (csn_compare(dncsn, opcsn) > 0 ) &&
 			((min_csn == NULL) || (csn_compare(dncsn, min_csn) < 0)) &&
 			!is_tombstone_entry (entries[i]))
 		{
@@ -1201,11 +1765,27 @@ urp_naming_conflict_removal ( Slapi_PBlock *pb, char *sessionid, CSN *opcsn, con
 		return 0;
 	}
 
+	/* EXPERIMENT - do step 2 before 1 */
+
+	/* Step2: Remove ATTR_NSDS5_REPLCONFLICT from the winning entry */
+	/*
+	 * A fixup op will not invoke urp_modrdn_operation(). Even it does,
+	 * urp_modrdn_operation() will do nothing because of the same CSN.
+	 */
+	op_result = del_replconflict_attr (min_naming_conflict_entry, opcsn, OP_FLAG_ACTION_INVOKE_FOR_REPLOP);
+	if (op_result != LDAP_SUCCESS) {
+		slapi_log_err(SLAPI_LOG_REPL, sessionid,
+			"urp_naming_conflict_removal - Failed to remove nsds5ReplConflict for %s, err=%d\n",
+			newrdnstr, op_result);
+	}
+	/* end Step 2 */
 	/* Step 1: Restore the entry's original DN */
 
 	oldrdn = slapi_rdn_new_sdn(slapi_entry_get_sdn_const(min_naming_conflict_entry));
 	oldrdnstr = slapi_rdn_get_rdn ( oldrdn );
 
+	    slapi_log_err (SLAPI_LOG_ERR, sessionid,
+			"urp_naming_conflict_removal - Found %s\n", slapi_entry_get_ndn(min_naming_conflict_entry));
 	/* newrdnstr is the old rdn of the entry minus the nsuniqueid part */
 	newrdn = slapi_rdn_new_rdn ( oldrdn );
 	slapi_rdn_remove_attr (newrdn, SLAPI_ATTR_UNIQUEID );
@@ -1226,17 +1806,7 @@ urp_naming_conflict_removal ( Slapi_PBlock *pb, char *sessionid, CSN *opcsn, con
 	slapi_log_err (slapi_log_urp, sessionid,
 		"urp_naming_conflict_removal - Naming conflict removed by %s. RDN of %s was restored\n", optype, oldrdnstr);
 			
-	/* Step2: Remove ATTR_NSDS5_REPLCONFLICT from the winning entry */
-	/*
-	 * A fixup op will not invoke urp_modrdn_operation(). Even it does,
-	 * urp_modrdn_operation() will do nothing because of the same CSN.
-	 */
-	op_result = del_replconflict_attr (min_naming_conflict_entry, opcsn, OP_FLAG_ACTION_INVOKE_FOR_REPLOP);
-	if (op_result != LDAP_SUCCESS) {
-		slapi_log_err(SLAPI_LOG_REPL, sessionid,
-			"urp_naming_conflict_removal - Failed to remove nsds5ReplConflict for %s, err=%d\n",
-			newrdnstr, op_result);
-	}
+	/* end Step 1 */
 
 bailout:
 	slapi_entry_free(min_naming_conflict_entry);
@@ -1323,6 +1893,19 @@ bail:
 }
 
 static int
+is_conflict_entry(Slapi_Entry *entry)
+{
+	int is_conflict = 0;
+	char *replconflict = slapi_entry_attr_get_charptr(entry,ATTR_NSDS5_REPLCONFLICT );
+	if (replconflict) {
+		is_conflict = 1;
+		slapi_ch_free_string(&replconflict);
+	}
+
+	return is_conflict;
+}
+
+static int
 is_suffix_entry ( Slapi_PBlock *pb, Slapi_Entry *entry, Slapi_DN **parentdn )
 {
 	return is_suffix_dn ( pb, slapi_entry_get_sdn(entry), parentdn );
@@ -1360,13 +1943,8 @@ mod_namingconflict_attr (const char *uniqueid, const Slapi_DN *entrysdn,
 	char buf[BUFSIZ];
 	int op_result;
 
-#ifdef DEBUG
 	PR_snprintf (buf, sizeof(buf), "%s (%s) %s",
 	             REASON_ANNOTATE_DN, optype, slapi_sdn_get_dn(conflictsdn));
-#else
-	PR_snprintf (buf, sizeof(buf), "%s %s",
-	             REASON_ANNOTATE_DN, slapi_sdn_get_dn(conflictsdn));
-#endif
 	slapi_mods_init (&smods, 2);
 	if ( strncmp (slapi_sdn_get_dn(entrysdn), SLAPI_ATTR_UNIQUEID,
 	     strlen(SLAPI_ATTR_UNIQUEID)) != 0 )
@@ -1388,6 +1966,29 @@ mod_namingconflict_attr (const char *uniqueid, const Slapi_DN *entrysdn,
 }
 
 static int
+mod_objectclass_attr (const char *uniqueid, const Slapi_DN *entrysdn,
+                         const Slapi_DN *conflictsdn, CSN *opcsn,
+                         const char *optype)
+{
+	Slapi_Mods smods;
+	char buf[BUFSIZ];
+	int op_result;
+	char csnstr[CSN_STRSIZE+1];
+
+	slapi_mods_init (&smods, 3);
+	slapi_mods_add (&smods, LDAP_MOD_ADD, "objectclass", strlen("ldapsubentry"),"ldapsubentry");
+	slapi_mods_add (&smods, LDAP_MOD_REPLACE, "conflictcsn", strlen(csnstr),csn_as_string(opcsn, PR_FALSE, csnstr));
+	op_result = urp_fixup_modify_entry (uniqueid, entrysdn, opcsn, &smods, 0);
+	slapi_mods_done (&smods);
+	if (op_result == LDAP_TYPE_OR_VALUE_EXISTS) {
+		/* the objectclass was already present */
+		op_result = LDAP_SUCCESS;
+	}
+	return op_result;
+}
+
+static int
+
 del_replconflict_attr (const Slapi_Entry *entry, CSN *opcsn, int opflags)
 {
 	Slapi_Attr *attr;
@@ -1401,8 +2002,9 @@ del_replconflict_attr (const Slapi_Entry *entry, CSN *opcsn, int opflags)
 
 		uniqueid = slapi_entry_get_uniqueid (entry);
 		entrysdn = slapi_entry_get_sdn_const (entry);
-		slapi_mods_init (&smods, 2);
+		slapi_mods_init (&smods, 3);
 		slapi_mods_add (&smods, LDAP_MOD_DELETE, ATTR_NSDS5_REPLCONFLICT, 0, NULL);
+		slapi_mods_add (&smods, LDAP_MOD_DELETE, "objectclass", strlen("ldapsubentry"),"ldapsubentry");
 		op_result = urp_fixup_modify_entry (uniqueid, entrysdn, opcsn, &smods, opflags);
 		slapi_mods_done (&smods);
 	}
diff --git a/ldap/servers/plugins/replication/urp.h b/ldap/servers/plugins/replication/urp.h
index 0d1cc05..ee50664 100644
--- a/ldap/servers/plugins/replication/urp.h
+++ b/ldap/servers/plugins/replication/urp.h
@@ -23,6 +23,7 @@
 int urp_modify_operation( Slapi_PBlock *pb );
 int urp_add_operation( Slapi_PBlock *pb );
 int urp_delete_operation( Slapi_PBlock *pb );
+int urp_post_add_operation( Slapi_PBlock *pb );
 int urp_post_delete_operation( Slapi_PBlock *pb );
 int urp_modrdn_operation( Slapi_PBlock *pb );
 int urp_post_modrdn_operation( Slapi_PBlock *pb );
-- 
2.5.5

