]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/syncrepl.c
implement search timeout when the remote server does not respond in the specified...
[openldap] / servers / slapd / syncrepl.c
index 9ae1cfbf09a87166878d644a047ce1cad968abe7..72374ec545cee7451b0037308f8ed4808249959f 100644 (file)
@@ -2,7 +2,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 2003-2006 The OpenLDAP Foundation.
+ * Copyright 2003-2007 The OpenLDAP Foundation.
  * Portions Copyright 2003 by IBM Corporation.
  * Portions Copyright 2003 by Howard Chu, Symas Corporation.
  * All rights reserved.
@@ -828,9 +828,10 @@ do_syncrep2(
                                                                struct berval *syncuuid_bv;
                                                                syncuuid_bv = ber_dupbv( NULL, &syncUUIDs[i] );
                                                                slap_sl_free( syncUUIDs[i].bv_val,op->o_tmpmemctx );
-                                                               avl_insert( &si->si_presentlist,
+                                                               if ( avl_insert( &si->si_presentlist,
                                                                        (caddr_t) syncuuid_bv,
-                                                                       syncuuid_cmp, avl_dup_error );
+                                                                       syncuuid_cmp, avl_dup_error ))
+                                                                       ber_bvfree( syncuuid_bv );
                                                        }
                                                        slap_sl_free( syncUUIDs, op->o_tmpmemctx );
                                                }
@@ -1298,7 +1299,7 @@ syncrepl_message_to_op(
                } else {
                        op->orm_modlist = modlist;
                        rc = op->o_bd->be_modify( op, &rs );
-                       Debug( LDAP_DEBUG_SYNC,
+                       Debug( rc ? LDAP_DEBUG_ANY : LDAP_DEBUG_SYNC,
                                "syncrepl_message_to_op: rid %03ld be_modify %s (%d)\n", 
                                si->si_rid, op->o_req_dn.bv_val, rc );
                }
@@ -1325,15 +1326,23 @@ syncrepl_message_to_op(
                op->orr_modlist = NULL;
                if ( slap_modrdn2mods( op, &rs ))
                        goto done;
+               /* Append modlist for operational attrs */
+               {
+                       Modifications *m;
+
+                       for ( m = op->orr_modlist; m->sml_next; m = m->sml_next ) ;
+                       m->sml_next = modlist;
+                       modlist = NULL;
+               }
                rc = op->o_bd->be_modrdn( op, &rs );
                slap_mods_free( op->orr_modlist, 1 );
-               Debug( LDAP_DEBUG_SYNC,
+               Debug( rc ? LDAP_DEBUG_ANY : LDAP_DEBUG_SYNC,
                        "syncrepl_message_to_op: rid %03ld be_modrdn %s (%d)\n", 
                        si->si_rid, op->o_req_dn.bv_val, rc );
                break;
        case LDAP_REQ_DELETE:
                rc = op->o_bd->be_delete( op, &rs );
-               Debug( LDAP_DEBUG_SYNC,
+               Debug( rc ? LDAP_DEBUG_ANY : LDAP_DEBUG_SYNC,
                        "syncrepl_message_to_op: rid %03ld be_delete %s (%d)\n", 
                        si->si_rid, op->o_req_dn.bv_val, rc );
                break;
@@ -1382,7 +1391,7 @@ syncrepl_message_to_entry(
        char txtbuf[SLAP_TEXT_BUFLEN];
        size_t textlen = sizeof txtbuf;
 
-       struct berval   bdn = {0, NULL}, dn, ndn;
+       struct berval   bdn = BER_BVNULL, dn, ndn;
        int             rc;
 
        *modlist = NULL;
@@ -1397,7 +1406,6 @@ syncrepl_message_to_entry(
        op->o_tag = LDAP_REQ_ADD;
 
        rc = ldap_get_dn_ber( si->si_ld, msg, &ber, &bdn );
-
        if ( rc != LDAP_SUCCESS ) {
                Debug( LDAP_DEBUG_ANY,
                        "syncrepl_message_to_entry: rid %03ld dn get failed (%d)",
@@ -1405,22 +1413,24 @@ syncrepl_message_to_entry(
                return rc;
        }
 
-       dnPrettyNormal( NULL, &bdn, &dn, &ndn, op->o_tmpmemctx );
-       ber_dupbv( &op->o_req_dn, &dn );
-       ber_dupbv( &op->o_req_ndn, &ndn );
-       slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
-       slap_sl_free( dn.bv_val, op->o_tmpmemctx );
-
        if ( syncstate == LDAP_SYNC_PRESENT || syncstate == LDAP_SYNC_DELETE ) {
-               if ( entry )
-                       *entry = NULL;
-               return LDAP_SUCCESS;
+               /* NOTE: this could be done even before decoding the DN,
+                * although encoding errors wouldn't be detected */
+               rc = LDAP_SUCCESS;
+               goto done;
        }
 
        if ( entry == NULL ) {
-               return -1;
+               rc = -1;
+               goto done;
        }
 
+       dnPrettyNormal( NULL, &bdn, &dn, &ndn, op->o_tmpmemctx );
+       ber_dupbv( &op->o_req_dn, &dn );
+       ber_dupbv( &op->o_req_ndn, &ndn );
+       slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
+       slap_sl_free( dn.bv_val, op->o_tmpmemctx );
+
        e = entry_alloc();
        e->e_name = op->o_req_dn;
        e->e_nname = op->o_req_ndn;
@@ -1485,7 +1495,7 @@ syncrepl_message_to_entry(
                        modtail = &mod->sml_next;
                }
        }
-       
+
        rc = slap_mods2entry( *modlist, &e, 1, 1, &text, txtbuf, textlen);
        if( rc != LDAP_SUCCESS ) {
                Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_entry: rid %03ld mods2entry (%s)\n",
@@ -1493,7 +1503,7 @@ syncrepl_message_to_entry(
        }
 
 done:
-       ber_free ( ber, 0 );
+       ber_free( ber, 0 );
        if ( rc != LDAP_SUCCESS ) {
                if ( e ) {
                        entry_free( e );
@@ -1522,17 +1532,18 @@ static struct berval generic_filterstr = BER_BVC("(objectclass=*)");
  * the new ones. This avoids the issue of trying to delete/add a non-leaf
  * entry.
  *
- * We don't try to otherwise distinguish ModDN from Modify; in the case of
- * a ModDN we will issue both operations on the local database.
+ * We otherwise distinguish ModDN from Modify; in the case of
+ * a ModDN we just use the CSN, modifyTimestamp and modifiersName
+ * operational attributes from the entry, and do a regular ModDN.
  */
 typedef struct dninfo {
        Entry *new_entry;
        struct berval dn;
        struct berval ndn;
        int renamed;    /* Was an existing entry renamed? */
-       int wasChanged; /* are the attributes changed? */
-       int attrs;              /* how many attribute types are in the ads list */
-       AttributeDescription **ads;
+       int delOldRDN;  /* Was old RDN deleted? */
+       Modifications **modlist;        /* the modlist we received */
+       Modifications *mods;    /* the modlist we compared */
 } dninfo;
 
 static int
@@ -1598,8 +1609,11 @@ syncrepl_entry(
        if (( syncstate == LDAP_SYNC_PRESENT || syncstate == LDAP_SYNC_ADD )) {
                if ( !si->si_refreshPresent ) {
                        syncuuid_bv = ber_dupbv( NULL, syncUUID );
-                       avl_insert( &si->si_presentlist, (caddr_t) syncuuid_bv,
-                               syncuuid_cmp, avl_dup_error );
+                       if ( avl_insert( &si->si_presentlist, (caddr_t) syncuuid_bv,
+                               syncuuid_cmp, avl_dup_error )) {
+                               ber_bvfree( syncuuid_bv );
+                               syncuuid_bv = NULL;
+                       }
                }
        }
 
@@ -1611,12 +1625,31 @@ syncrepl_entry(
                }
        }
 
+       (void)slap_uuidstr_from_normalized( &syncUUID_strrep, syncUUID, op->o_tmpmemctx );
+       if ( syncstate != LDAP_SYNC_DELETE ) {
+               Attribute       *a = attr_find( entry->e_attrs, slap_schema.si_ad_entryUUID );
+
+               if ( a == NULL ) {
+                       /* add if missing */
+                       attr_merge_one( entry, slap_schema.si_ad_entryUUID,
+                               &syncUUID_strrep, syncUUID );
+
+               } else if ( !bvmatch( &a->a_nvals[0], syncUUID ) ) {
+                       /* replace only if necessary */
+                       if ( a->a_nvals != a->a_vals ) {
+                               ber_memfree( a->a_nvals[0].bv_val );
+                               ber_dupbv( &a->a_nvals[0], syncUUID );
+                       }
+                       ber_memfree( a->a_vals[0].bv_val );
+                       ber_dupbv( &a->a_vals[0], &syncUUID_strrep );
+               }
+       }
+
        f.f_choice = LDAP_FILTER_EQUALITY;
        f.f_ava = &ava;
        ava.aa_desc = slap_schema.si_ad_entryUUID;
        ava.aa_value = *syncUUID;
 
-       (void)slap_uuidstr_from_normalized( &syncUUID_strrep, syncUUID, op->o_tmpmemctx );
        if ( syncuuid_bv ) {
                Debug( LDAP_DEBUG_SYNC, "syncrepl_entry: rid %03ld inserted UUID %s\n",
                        si->si_rid, syncUUID_strrep.bv_val, 0 );
@@ -1652,6 +1685,7 @@ syncrepl_entry(
        cb.sc_response = dn_callback;
        cb.sc_private = &dni;
        dni.new_entry = entry;
+       dni.modlist = modlist;
 
        if ( limits_check( op, &rs_search ) == 0 ) {
                rc = be->be_search( op, &rs_search );
@@ -1677,37 +1711,19 @@ syncrepl_entry(
                                si->si_rid, dni.dn.bv_val ? dni.dn.bv_val : "(null)", 0 );
        }
 
-       if ( syncstate != LDAP_SYNC_DELETE ) {
-               Attribute       *a = attr_find( entry->e_attrs, slap_schema.si_ad_entryUUID );
-
-               if ( a == NULL ) {
-                       /* add if missing */
-                       attr_merge_one( entry, slap_schema.si_ad_entryUUID,
-                               &syncUUID_strrep, syncUUID );
-
-               } else if ( !bvmatch( &a->a_nvals[0], syncUUID ) ) {
-                       /* replace only if necessary */
-                       if ( a->a_nvals != a->a_vals ) {
-                               ber_memfree( a->a_nvals[0].bv_val );
-                               ber_dupbv( &a->a_nvals[0], syncUUID );
-                       }
-                       ber_memfree( a->a_vals[0].bv_val );
-                       ber_dupbv( &a->a_vals[0], &syncUUID_strrep );
-               }
-               /* Don't save the contextCSN on the inooming context entry,
-                * we'll write it when syncrepl_updateCookie eventually
-                * gets called. (ITS#4622)
-                */
-               if ( syncstate == LDAP_SYNC_ADD && dn_match( &entry->e_nname,
-                       &be->be_nsuffix[0] )) {
-                       Attribute **ap;
-                       for ( ap = &entry->e_attrs; *ap; ap=&(*ap)->a_next ) {
-                               a = *ap;
-                               if ( a->a_desc == slap_schema.si_ad_contextCSN ) {
-                                       *ap = a->a_next;
-                                       attr_free( a );
-                                       break;
-                               }
+       /* Don't save the contextCSN on the inooming context entry,
+        * we'll write it when syncrepl_updateCookie eventually
+        * gets called. (ITS#4622)
+        */
+       if ( syncstate == LDAP_SYNC_ADD && dn_match( &entry->e_nname,
+               &be->be_nsuffix[0] )) {
+               Attribute *a, **ap;
+               for ( ap = &entry->e_attrs; *ap; ap=&(*ap)->a_next ) {
+                       a = *ap;
+                       if ( a->a_desc == slap_schema.si_ad_contextCSN ) {
+                               *ap = a->a_next;
+                               attr_free( a );
+                               break;
                        }
                }
        }
@@ -1825,7 +1841,7 @@ retry_add:;
                                op->orr_newSup = NULL;
                                op->orr_nnewSup = NULL;
                        }
-                       op->orr_deleteoldrdn = 0;
+                       op->orr_deleteoldrdn = dni.delOldRDN;
                        op->orr_modlist = NULL;
                        if (( rc = slap_modrdn2mods( op, &rs_modify ))) {
                                goto done;
@@ -1837,6 +1853,34 @@ retry_add:;
                        noldp = op->orr_nnewrdn;
                        ber_dupbv_x( &op->orr_nnewrdn, &noldp, op->o_tmpmemctx );
 
+                       /* Setup opattrs too */
+                       {
+                               AttributeDescription *opattrs[] = {
+                                       slap_schema.si_ad_entryCSN,
+                                       slap_schema.si_ad_modifiersName,
+                                       slap_schema.si_ad_modifyTimestamp,
+                                       NULL
+                               };
+                               Modifications *mod, **modtail, **ml;
+                               int i;
+
+                               for (mod=op->orr_modlist; mod->sml_next; mod=mod->sml_next)
+                                       ;
+                               modtail = &mod->sml_next;
+
+                               /* pull mod off incoming modlist, append to orr_modlist */
+                               for (i=0; opattrs[i]; i++) {
+                                       for (ml = modlist; *ml; ml = &(*ml)->sml_next)
+                                               if ( (*ml)->sml_desc == opattrs[i] ) {
+                                                       mod = *ml;
+                                                       *ml = mod->sml_next;
+                                                       mod->sml_next = NULL;
+                                                       *modtail = mod;
+                                                       modtail = &mod->sml_next;
+                                                       break;
+                                               }
+                               }
+                       }
                        rc = be->be_modrdn( op, &rs_modify );
                        op->o_tmpfree( op->orr_nnewrdn.bv_val, op->o_tmpmemctx );
                        op->o_tmpfree( op->orr_newrdn.bv_val, op->o_tmpmemctx );
@@ -1845,68 +1889,14 @@ retry_add:;
                        Debug( LDAP_DEBUG_SYNC,
                                        "syncrepl_entry: rid %03ld be_modrdn (%d)\n", 
                                        si->si_rid, rc, 0 );
-                       if ( rs_modify.sr_err == LDAP_SUCCESS ) {
-                               op->o_req_dn = entry->e_name;
-                               op->o_req_ndn = entry->e_nname;
-                       } else {
-                               goto done;
-                       }
-                       if ( dni.wasChanged )
-                               slap_op_time( &op->o_time, &op->o_tincr );
+                       goto done;
                }
-               if ( dni.wasChanged ) {
-                       Modifications *mod, *modhead = NULL;
-                       Modifications *modtail = NULL;
-                       int i;
-
-                       op->o_tag = LDAP_REQ_MODIFY;
-
-                       assert( *modlist != NULL );
-
-                       /* Delete all the old attrs */
-                       for ( i = 0; i < dni.attrs; i++ ) {
-                               mod = ch_malloc( sizeof( Modifications ) );
-                               mod->sml_op = LDAP_MOD_DELETE;
-                               mod->sml_flags = 0;
-                               mod->sml_desc = dni.ads[i];
-                               mod->sml_type = mod->sml_desc->ad_cname;
-                               mod->sml_values = NULL;
-                               mod->sml_nvalues = NULL;
-                               if ( !modhead ) modhead = mod;
-                               if ( modtail ) {
-                                       modtail->sml_next = mod;
-                               }
-                               modtail = mod;
-                       }
-
-                       /* Append passed in list to ours */
-                       if ( modtail ) {
-                               modtail->sml_next = *modlist;
-                               *modlist = modhead;
-                       } else {
-                               mod = *modlist;
-                       }
-
-                       /* Find end of this list */
-                       for ( ; mod != NULL; mod = mod->sml_next ) {
-                               modtail = mod;
-                       }
-
-                       mod = (Modifications *)ch_calloc(1, sizeof(Modifications));
-                       mod->sml_op = LDAP_MOD_REPLACE;
-                       mod->sml_flags = 0;
-                       mod->sml_desc = slap_schema.si_ad_entryUUID;
-                       mod->sml_type = mod->sml_desc->ad_cname;
-                       ber_dupbv( &uuid_bv, &syncUUID_strrep );
-                       ber_bvarray_add( &mod->sml_values, &uuid_bv );
-                       ber_dupbv( &uuid_bv, syncUUID );
-                       ber_bvarray_add( &mod->sml_nvalues, &uuid_bv );
-                       modtail->sml_next = mod;
-                                       
+               if ( dni.mods ) {
                        op->o_tag = LDAP_REQ_MODIFY;
-                       op->orm_modlist = *modlist;
+                       op->orm_modlist = dni.mods;
 
                        rc = be->be_modify( op, &rs_modify );
+                       slap_mods_free( op->orm_modlist, 1 );
                        Debug( LDAP_DEBUG_SYNC,
                                        "syncrepl_entry: rid %03ld be_modify (%d)\n", 
                                        si->si_rid, rc, 0 );
@@ -1956,9 +1946,6 @@ done:
                slap_sl_free( syncUUID_strrep.bv_val, op->o_tmpmemctx );
                BER_BVZERO( &syncUUID_strrep );
        }
-       if ( dni.ads ) {
-               op->o_tmpfree( dni.ads, op->o_tmpmemctx );
-       }
        if ( !BER_BVISNULL( &dni.ndn ) ) {
                op->o_tmpfree( dni.ndn.bv_val, op->o_tmpmemctx );
        }
@@ -2303,7 +2290,7 @@ syncrepl_updateCookie(
        Modifications mod = { { 0 } };
        struct berval vals[ 2 ];
 
-       int rc;
+       int rc, flags;
 
        slap_callback cb = { NULL };
        SlapReply       rs_modify = {REP_RESULT};
@@ -2331,7 +2318,10 @@ syncrepl_updateCookie(
        /* update contextCSN */
        op->o_msgid = SLAP_SYNC_UPDATE_MSGID;
        op->orm_modlist = &mod;
+       flags = SLAP_DBFLAGS( op->o_bd );
+       SLAP_DBFLAGS( op->o_bd ) |= SLAP_DBFLAG_NOLASTMOD;
        rc = be->be_modify( op, &rs_modify );
+       SLAP_DBFLAGS( op->o_bd ) = flags;
        op->o_msgid = 0;
 
        if ( rs_modify.sr_err == LDAP_SUCCESS ) {
@@ -2351,6 +2341,130 @@ syncrepl_updateCookie(
        return rc;
 }
 
+static void
+attr_cmp( Operation *op, Attribute *old, Attribute *new,
+       Modifications ***mret, Modifications ***mcur )
+{
+       int i, j, doadd = 0;
+       Modifications *mod, **modtail;
+
+       modtail = *mret;
+
+       if ( old ) {
+               int n, o, d, a, *adds, *dels;
+               /* count old and new */
+               for ( o=0; old->a_vals[o].bv_val; o++ ) ;
+               for ( n=0; new->a_vals[n].bv_val; n++ ) ;
+
+               adds = op->o_tmpalloc( sizeof(int) * n, op->o_tmpmemctx );
+               dels = op->o_tmpalloc( sizeof(int) * o, op->o_tmpmemctx );
+               d = 0;
+               a = 0;
+               i = 0;
+               j = 0;
+
+               while ( i < o && j < n ) {
+                       int k;
+                       if ( bvmatch( &old->a_vals[i], &new->a_vals[j] )) {
+                               i++;
+                               j++;
+                               continue;
+                       }
+                       for ( k=j+1; k<n; k++ ) {
+                               if ( bvmatch( &old->a_vals[i], &new->a_vals[k] )) {
+                                       break;
+                               }
+                       }
+                       /* an old value was deleted */
+                       if ( k == n ) {
+                               dels[d++] = i++;
+                               continue;
+                       }
+                       for ( k=i+1; k<o; k++ ) {
+                               if ( bvmatch( &old->a_vals[k], &new->a_vals[j] )) {
+                                       break;
+                               }
+                       }
+                       if ( k == o ) {
+                               adds[a++] = j++;
+                       }
+               }
+               while ( i < o )
+                       dels[d++] = i++;
+               while ( j < n )
+                       adds[a++] = j++;
+
+               /* all old values were deleted, just use the replace op */
+               if ( d == o ) {
+                       i = j-1;
+               } else if ( d ) {
+               /* delete some values */
+                       mod = ch_malloc( sizeof( Modifications ) );
+                       mod->sml_op = LDAP_MOD_DELETE;
+                       mod->sml_flags = 0;
+                       mod->sml_desc = old->a_desc;
+                       mod->sml_type = mod->sml_desc->ad_cname;
+                       mod->sml_values = ch_malloc(( d+1) * sizeof(struct berval));
+                       if ( old->a_vals != old->a_nvals )
+                               mod->sml_nvalues = ch_malloc(( d+1) * sizeof(struct berval));
+                       else
+                               mod->sml_nvalues = NULL;
+                       for ( i=0; i<d; i++ ) {
+                               ber_dupbv( &mod->sml_values[i], &old->a_vals[dels[i]] );
+                               if ( mod->sml_nvalues )
+                                       ber_dupbv( &mod->sml_nvalues[i], &old->a_nvals[dels[i]] );
+                       }
+                       BER_BVZERO( &mod->sml_values[i] );
+                       if ( mod->sml_nvalues )
+                               BER_BVZERO( &mod->sml_nvalues[i] );
+                       *modtail = mod;
+                       modtail = &mod->sml_next;
+                       i = j;
+               }
+               op->o_tmpfree( dels, op->o_tmpmemctx );
+               /* some values were added */
+               if ( a && d < o ) {
+                       mod = ch_malloc( sizeof( Modifications ) );
+                       mod->sml_op = LDAP_MOD_ADD;
+                       mod->sml_flags = 0;
+                       mod->sml_desc = old->a_desc;
+                       mod->sml_type = mod->sml_desc->ad_cname;
+                       mod->sml_values = ch_malloc(( a+1) * sizeof(struct berval));
+                       if ( old->a_vals != old->a_nvals )
+                               mod->sml_nvalues = ch_malloc(( a+1) * sizeof(struct berval));
+                       else
+                               mod->sml_nvalues = NULL;
+                       for ( i=0; i<a; i++ ) {
+                               ber_dupbv( &mod->sml_values[i], &new->a_vals[adds[i]] );
+                               if ( mod->sml_nvalues )
+                                       ber_dupbv( &mod->sml_nvalues[i], &new->a_nvals[adds[i]] );
+                       }
+                       BER_BVZERO( &mod->sml_values[i] );
+                       if ( mod->sml_nvalues )
+                               BER_BVZERO( &mod->sml_nvalues[i] );
+                       *modtail = mod;
+                       modtail = &mod->sml_next;
+                       i = j;
+               }
+               op->o_tmpfree( adds, op->o_tmpmemctx );
+       } else {
+               /* new attr, just use the new mod */
+               i = 0;
+               j = 1;
+       }
+       /* advance to next element */
+       mod = **mcur;
+       if ( i != j ) {
+               **mcur = mod->sml_next;
+               *modtail = mod;
+               modtail = &mod->sml_next;
+       } else {
+               if ( mod )
+                       *mcur = &mod->sml_next;
+       }
+       *mret = modtail;
+}
+
 static int
 dn_callback(
        Operation*      op,
@@ -2371,72 +2485,96 @@ dn_callback(
                         * in the provider are always propagated.
                         */
                        if ( dni->new_entry ) {
+                               Modifications **modtail, **ml;
                                Attribute *old, *new;
                                int i;
 
-                               /* Did the DN change? Note that we don't explicitly try to
-                                * discover if the deleteOldRdn argument applies here. It
-                                * would save an unnecessary Modify if we detected it, but
-                                * that's a fair amount of trouble to compare the two attr
-                                * lists in detail. (Just test normalized DN; we ignore
-                                * insignificant changes here.)
+                               /* Did the DN change?
                                 */
-                               if ( !dn_match( &rs->sr_entry->e_nname,
-                                               &dni->new_entry->e_nname ) )
+                               if ( !dn_match( &rs->sr_entry->e_name,
+                                               &dni->new_entry->e_name ) )
                                {
+                                       struct berval oldRDN, oldVal;
+                                       AttributeDescription *ad = NULL;
+                                       Attribute *a;
+
                                        dni->renamed = 1;
+                                       /* See if the oldRDN was deleted */
+                                       dnRdn( &rs->sr_entry->e_nname, &oldRDN );
+                                       oldVal.bv_val = strchr(oldRDN.bv_val, '=') + 1;
+                                       oldVal.bv_len = oldRDN.bv_len - ( oldVal.bv_val -
+                                               oldRDN.bv_val );
+                                       oldRDN.bv_len -= oldVal.bv_len + 2;
+                                       slap_bv2ad( &oldRDN, &ad, &rs->sr_text );
+                                       a = attr_find( dni->new_entry->e_attrs, ad );
+                                       if ( !a || value_find_ex( ad,
+                                               SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH |
+                                               SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+                                               SLAP_MR_VALUE_OF_SYNTAX, a->a_nvals,
+                                               &oldVal, op->o_tmpmemctx ) != LDAP_SUCCESS )
+                                       {
+                                               dni->delOldRDN = 1;
+                                       }
+                                       /* OK, this was just a modDN, we're done */
+                                       return LDAP_SUCCESS;
                                }
 
-                               for ( i = 0, old = rs->sr_entry->e_attrs;
-                                               old;
-                                               i++, old = old->a_next )
-                                       ;
-
-                               dni->attrs = i;
+                               modtail = &dni->mods;
+                               ml = dni->modlist;
 
                                /* We assume that attributes are saved in the same order
                                 * in the remote and local databases. So if we walk through
                                 * the attributeDescriptions one by one they should match in
-                                * lock step. If not, we signal a change. Otherwise we test
-                                * all the values...
+                                * lock step. If not, look for an add or delete.
                                 */
                                for ( old = rs->sr_entry->e_attrs, new = dni->new_entry->e_attrs;
-                                               old && new;
-                                               old = old->a_next, new = new->a_next )
+                                               old && new; )
                                {
-                                       if ( old->a_desc != new->a_desc ) {
-                                               dni->wasChanged = 1;
-                                               break;
+#if 1
+                                       if ( new->a_flags & SLAP_ATTR_IXADD ) {
+                                               new = new->a_next;
+                                               continue;
                                        }
-                                       for ( i = 0; ; i++ ) {
-                                               int nold, nnew;
-                                               nold = BER_BVISNULL( &old->a_vals[i] );
-                                               nnew = BER_BVISNULL( &new->a_vals[i] );
-                                               /* If both are empty, stop looking */
-                                               if ( nold && nnew ) {
-                                                       break;
-                                               }
-                                               /* If they are different, stop looking */
-                                               if ( nold != nnew ) {
-                                                       dni->wasChanged = 1;
-                                                       break;
-                                               }
-                                               if ( ber_bvcmp( &old->a_vals[i], &new->a_vals[i] )) {
-                                                       dni->wasChanged = 1;
-                                                       break;
+#endif
+                                       if ( old->a_desc != new->a_desc ) {
+                                               Modifications *mod;
+                                               Attribute *tmp;
+
+                                               /* If it's just been re-added later,
+                                                * remember that we've seen it.
+                                                */
+                                               tmp = attr_find( new, old->a_desc );
+                                               if ( tmp ) {
+                                                       tmp->a_flags |= SLAP_ATTR_IXADD;
+                                               } else {
+                                                       /* If it's a new attribute, pull it in.
+                                                        */
+                                                       tmp = attr_find( old, new->a_desc );
+                                                       if ( !tmp ) {
+                                                               attr_cmp( op, NULL, new, &modtail, &ml );
+                                                               new = new->a_next;
+                                                               continue;
+                                                       }
+                                                       /* Delete old attr */
+                                                       mod = ch_malloc( sizeof( Modifications ) );
+                                                       mod->sml_op = LDAP_MOD_DELETE;
+                                                       mod->sml_flags = 0;
+                                                       mod->sml_desc = old->a_desc;
+                                                       mod->sml_type = mod->sml_desc->ad_cname;
+                                                       mod->sml_values = NULL;
+                                                       mod->sml_nvalues = NULL;
+                                                       *modtail = mod;
+                                                       modtail = &mod->sml_next;
                                                }
+                                               old = old->a_next;
+                                               continue;
                                        }
-                                       if ( dni->wasChanged ) break;
-                               }
-                               if ( dni->wasChanged ) {
-                                       dni->ads = op->o_tmpalloc( dni->attrs *
-                                               sizeof(AttributeDescription *), op->o_tmpmemctx );
-                                       i = 0;
-                                       for ( old = rs->sr_entry->e_attrs; old; old = old->a_next ) {
-                                               dni->ads[i] = old->a_desc;
-                                               i++;
-                                       }
+                                       attr_cmp( op, old, new, &modtail, &ml );
+                                       new = new->a_next;
+                                       old = old->a_next;
                                }
+                               *modtail = *ml;
+                               *ml = NULL;
                        }
                }
        } else if ( rs->sr_type == REP_RESULT ) {
@@ -3242,12 +3380,42 @@ add_syncrepl(
                }
 
                si->si_be = c->be;
-               init_syncrepl( si );
-               si->si_re = ldap_pvt_runqueue_insert( &slapd_rq, si->si_interval,
-                       do_syncrepl, si, "do_syncrepl", c->be->be_suffix[0].bv_val );
-               if ( !si->si_re )
-                       rc = -1;
+               if ( slapMode & SLAP_SERVER_MODE ) {
+                       Listener **l = slapd_get_listeners();
+                       int isMe = 0;
+
+                       /* check if URL points to current server. If so, ignore
+                        * this configuration. We require an exact match. Just
+                        * in case they really want to do this, they can vary
+                        * the case of the URL to allow it.
+                        */
+                       if ( l && !SLAP_DBHIDDEN( c->be )) {
+                               int i;
+                               for ( i=0; l[i]; i++ ) {
+                                       if ( bvmatch( &l[i]->sl_url, &si->si_bindconf.sb_uri )) {
+                                               isMe = 1;
+                                               break;
+                                       }
+                               }
+                       }
+
+                       if ( !isMe ) {
+                               init_syncrepl( si );
+                               si->si_re = ldap_pvt_runqueue_insert( &slapd_rq,
+                                       si->si_interval, do_syncrepl, si, "do_syncrepl",
+                                       c->be->be_suffix[0].bv_val );
+                               if ( si->si_re )
+                                       rc = config_sync_shadow( c ) ? -1 : 0;
+                               else
+                                       rc = -1;
+                       }
+               }
        }
+
+#ifdef HAVE_TLS
+       /* Use main slapd defaults */
+       bindconf_tls_defaults( &si->si_bindconf );
+#endif
        if ( rc < 0 ) {
                Debug( LDAP_DEBUG_ANY, "failed to add syncinfo\n", 0, 0, 0 );
                syncinfo_free( si );    
@@ -3452,8 +3620,7 @@ syncrepl_config( ConfigArgs *c )
                        "syncrepl: database already shadowed.\n",
                        c->log, 0, 0);
                return(1);
-       } else if ( add_syncrepl( c ) ) {
-               return(1);
+       } else {
+               return add_syncrepl( c );
        }
-       return config_sync_shadow( c );
 }