]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/syncprov.c
Serialize multiple modifies of the same entry (ala seqmod.c)
[openldap] / servers / slapd / overlays / syncprov.c
1 /* syncprov.c - syncrepl provider */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 2004 The OpenLDAP Foundation.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted only as authorized by the OpenLDAP
9  * Public License.
10  *
11  * A copy of this license is available in the file LICENSE in the
12  * top-level directory of the distribution or, alternatively, at
13  * <http://www.OpenLDAP.org/license.html>.
14  */
15 /* ACKNOWLEDGEMENTS:
16  * This work was initially developed by Howard Chu for inclusion in
17  * OpenLDAP Software.
18  */
19
20 #include "portable.h"
21
22 #ifdef SLAPD_OVER_SYNCPROV
23
24 #include <ac/string.h>
25 #include "lutil.h"
26 #include "slap.h"
27
28 /* A modify request on a particular entry */
29 typedef struct modinst {
30         struct modinst *mi_next;
31         Operation *mi_op;
32 } modinst;
33
34 typedef struct modtarget {
35         struct modinst *mt_mods;
36         struct modinst *mt_tail;
37         Operation *mt_op;
38         ldap_pvt_thread_mutex_t mt_mutex;
39 } modtarget;
40
41         
42
43 /* A queued result of a persistent search */
44 typedef struct syncres {
45         struct syncres *s_next;
46         struct berval s_dn;
47         struct berval s_ndn;
48         struct berval s_uuid;
49         struct berval s_csn;
50         char s_mode;
51         char s_isreference;
52 } syncres;
53
54 /* Record of a persistent search */
55 typedef struct syncops {
56         struct syncops *s_next;
57         struct berval   s_base;         /* ndn of search base */
58         ID              s_eid;          /* entryID of search base */
59         Operation       *s_op;          /* search op */
60         long    s_sid;
61         long    s_rid;
62         struct berval s_filterstr;
63         int             s_flags;        /* search status */
64         int             s_inuse;        /* reference count */
65         struct syncres *s_res;
66         struct syncres *s_restail;
67         ldap_pvt_thread_mutex_t s_mutex;
68 } syncops;
69
70 static int      sync_cid;
71
72 /* A received sync control */
73 typedef struct sync_control {
74         struct sync_cookie sr_state;
75         int sr_rhint;
76 } sync_control;
77
78 /* o_sync_mode uses data bits of o_sync */
79 #define o_sync  o_ctrlflag[sync_cid]
80 #define o_sync_mode     o_ctrlflag[sync_cid]
81
82 #define SLAP_SYNC_NONE                                  (LDAP_SYNC_NONE<<SLAP_CONTROL_SHIFT)
83 #define SLAP_SYNC_REFRESH                               (LDAP_SYNC_REFRESH_ONLY<<SLAP_CONTROL_SHIFT)
84 #define SLAP_SYNC_PERSIST                               (LDAP_SYNC_RESERVED<<SLAP_CONTROL_SHIFT)
85 #define SLAP_SYNC_REFRESH_AND_PERSIST   (LDAP_SYNC_REFRESH_AND_PERSIST<<SLAP_CONTROL_SHIFT)
86
87 #define PS_IS_REFRESHING        0x01
88
89 /* Record of which searches matched at premodify step */
90 typedef struct syncmatches {
91         struct syncmatches *sm_next;
92         syncops *sm_op;
93 } syncmatches;
94
95 typedef struct syncprov_info_t {
96         syncops         *si_ops;
97         struct berval   si_ctxcsn;      /* ldapsync context */
98         int             si_gotcsn;      /* is our ctxcsn up to date? */
99         Avlnode *si_mods;       /* entries being modified */
100         ldap_pvt_thread_mutex_t si_csn_mutex;
101         ldap_pvt_thread_mutex_t si_ops_mutex;
102         ldap_pvt_thread_mutex_t si_mods_mutex;
103         char            si_ctxcsnbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
104 } syncprov_info_t;
105
106 typedef struct opcookie {
107         slap_overinst *son;
108         syncmatches *smatches;
109         struct berval sdn;      /* DN of entry, for deletes */
110         struct berval sndn;
111         struct berval suuid;    /* UUID of entry */
112         struct berval sctxcsn;
113         int sreference; /* Is the entry a reference? */
114 } opcookie;
115
116 typedef struct fbase_cookie {
117         struct berval *fdn;     /* DN of a modified entry, for scope testing */
118         syncops *fss;   /* persistent search we're testing against */
119         int fbase;      /* if TRUE we found the search base and it's still valid */
120         int fscope;     /* if TRUE then fdn is within the psearch scope */
121 } fbase_cookie;
122
123 static AttributeName csn_anlist[2];
124 static AttributeName uuid_anlist[2];
125
126 static int
127 syncprov_state_ctrl(
128         Operation       *op,
129         SlapReply       *rs,
130         Entry           *e,
131         int                     entry_sync_state,
132         LDAPControl     **ctrls,
133         int                     num_ctrls,
134         int                     send_cookie,
135         struct berval   *cookie)
136 {
137         Attribute* a;
138         int ret;
139         int res;
140         const char *text = NULL;
141
142         BerElementBuffer berbuf;
143         BerElement *ber = (BerElement *)&berbuf;
144
145         struct berval entryuuid_bv      = BER_BVNULL;
146
147         ber_init2( ber, 0, LBER_USE_DER );
148         ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
149
150         ctrls[num_ctrls] = slap_sl_malloc ( sizeof ( LDAPControl ), op->o_tmpmemctx );
151
152         for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
153                 AttributeDescription *desc = a->a_desc;
154                 if ( desc == slap_schema.si_ad_entryUUID ) {
155                         entryuuid_bv = a->a_nvals[0];
156                         break;
157                 }
158         }
159
160         if ( send_cookie && cookie ) {
161                 ber_printf( ber, "{eOON}",
162                         entry_sync_state, &entryuuid_bv, cookie );
163         } else {
164                 ber_printf( ber, "{eON}",
165                         entry_sync_state, &entryuuid_bv );
166         }
167
168         ctrls[num_ctrls]->ldctl_oid = LDAP_CONTROL_SYNC_STATE;
169         ctrls[num_ctrls]->ldctl_iscritical = (op->o_sync == SLAP_CONTROL_CRITICAL);
170         ret = ber_flatten2( ber, &ctrls[num_ctrls]->ldctl_value, 1 );
171
172         ber_free_buf( ber );
173
174         if ( ret < 0 ) {
175                 Debug( LDAP_DEBUG_TRACE,
176                         "slap_build_sync_ctrl: ber_flatten2 failed\n",
177                         0, 0, 0 );
178                 send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
179                 return ret;
180         }
181
182         return LDAP_SUCCESS;
183 }
184
185 static int
186 syncprov_done_ctrl(
187         Operation       *op,
188         SlapReply       *rs,
189         LDAPControl     **ctrls,
190         int                     num_ctrls,
191         int                     send_cookie,
192         struct berval *cookie,
193         int                     refreshDeletes )
194 {
195         int ret;
196         BerElementBuffer berbuf;
197         BerElement *ber = (BerElement *)&berbuf;
198
199         ber_init2( ber, NULL, LBER_USE_DER );
200         ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
201
202         ctrls[num_ctrls] = ch_malloc ( sizeof ( LDAPControl ) );
203
204         ber_printf( ber, "{" );
205         if ( send_cookie && cookie ) {
206                 ber_printf( ber, "O", cookie );
207         }
208         if ( refreshDeletes == LDAP_SYNC_REFRESH_DELETES ) {
209                 ber_printf( ber, "b", refreshDeletes );
210         }
211         ber_printf( ber, "N}" );        
212
213         ctrls[num_ctrls]->ldctl_oid = LDAP_CONTROL_SYNC_DONE;
214         ctrls[num_ctrls]->ldctl_iscritical = (op->o_sync == SLAP_CONTROL_CRITICAL);
215         ret = ber_flatten2( ber, &ctrls[num_ctrls]->ldctl_value, 1 );
216
217         ber_free_buf( ber );
218
219         if ( ret < 0 ) {
220                 Debug( LDAP_DEBUG_TRACE,
221                         "syncprov_done_ctrl: ber_flatten2 failed\n",
222                         0, 0, 0 );
223                 send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
224                 return ret;
225         }
226
227         return LDAP_SUCCESS;
228 }
229
230
231 static int
232 syncprov_state_ctrl_from_slog(
233         Operation       *op,
234         SlapReply       *rs,
235         struct slog_entry *slog_e,
236         int                     entry_sync_state,
237         LDAPControl     **ctrls,
238         int                     num_ctrls,
239         int                     send_cookie,
240         struct berval   *cookie)
241 {
242         Attribute* a;
243         int ret;
244         int res;
245         const char *text = NULL;
246
247         BerElementBuffer berbuf;
248         BerElement *ber = (BerElement *)&berbuf;
249
250         struct berval entryuuid_bv      = BER_BVNULL;
251
252         ber_init2( ber, NULL, LBER_USE_DER );
253         ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
254
255         ctrls[num_ctrls] = ch_malloc ( sizeof ( LDAPControl ) );
256
257         entryuuid_bv = slog_e->sl_uuid;
258
259         if ( send_cookie && cookie ) {
260                 ber_printf( ber, "{eOON}",
261                         entry_sync_state, &entryuuid_bv, cookie );
262         } else {
263                 ber_printf( ber, "{eON}",
264                         entry_sync_state, &entryuuid_bv );
265         }
266
267         ctrls[num_ctrls]->ldctl_oid = LDAP_CONTROL_SYNC_STATE;
268         ctrls[num_ctrls]->ldctl_iscritical = (op->o_sync == SLAP_CONTROL_CRITICAL);
269         ret = ber_flatten2( ber, &ctrls[num_ctrls]->ldctl_value, 1 );
270
271         ber_free_buf( ber );
272
273         if ( ret < 0 ) {
274                 Debug( LDAP_DEBUG_TRACE,
275                         "slap_build_sync_ctrl: ber_flatten2 failed\n",
276                         0, 0, 0 );
277                 send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
278                 return ret;
279         }
280
281         return LDAP_SUCCESS;
282 }
283
284 static int
285 syncprov_sendinfo(
286         Operation       *op,
287         SlapReply       *rs,
288         int                     type,
289         struct berval *cookie,
290         int                     refreshDone,
291         BerVarray       syncUUIDs,
292         int                     refreshDeletes )
293 {
294         BerElementBuffer berbuf;
295         BerElement *ber = (BerElement *)&berbuf;
296         struct berval rspdata;
297
298         int ret;
299
300         ber_init2( ber, NULL, LBER_USE_DER );
301         ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
302
303         if ( type ) {
304                 switch ( type ) {
305                 case LDAP_TAG_SYNC_NEW_COOKIE:
306                         ber_printf( ber, "tO", type, cookie );
307                         break;
308                 case LDAP_TAG_SYNC_REFRESH_DELETE:
309                 case LDAP_TAG_SYNC_REFRESH_PRESENT:
310                         ber_printf( ber, "t{", type );
311                         if ( cookie ) {
312                                 ber_printf( ber, "O", cookie );
313                         }
314                         if ( refreshDone == 0 ) {
315                                 ber_printf( ber, "b", refreshDone );
316                         }
317                         ber_printf( ber, "N}" );
318                         break;
319                 case LDAP_TAG_SYNC_ID_SET:
320                         ber_printf( ber, "t{", type );
321                         if ( cookie ) {
322                                 ber_printf( ber, "O", cookie );
323                         }
324                         if ( refreshDeletes == 1 ) {
325                                 ber_printf( ber, "b", refreshDeletes );
326                         }
327                         ber_printf( ber, "[W]", syncUUIDs );
328                         ber_printf( ber, "N}" );
329                         break;
330                 default:
331                         Debug( LDAP_DEBUG_TRACE,
332                                 "syncprov_sendinfo: invalid syncinfo type (%d)\n",
333                                 type, 0, 0 );
334                         return LDAP_OTHER;
335                 }
336         }
337
338         ret = ber_flatten2( ber, &rspdata, 0 );
339
340         if ( ret < 0 ) {
341                 Debug( LDAP_DEBUG_TRACE,
342                         "syncprov_sendinfo: ber_flatten2 failed\n",
343                         0, 0, 0 );
344                 send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
345                 return ret;
346         }
347
348         rs->sr_rspdata = &rspdata;
349         send_ldap_intermediate( op, rs );
350         rs->sr_rspdata = NULL;
351         ber_free_buf( ber );
352
353         return LDAP_SUCCESS;
354 }
355
356 static int
357 sp_avl_cmp( const void *c1, const void *c2 )
358 {
359         const modtarget *m1, *m2;
360         int rc;
361
362         m1 = c1; m2 = c2;
363         rc = m1->mt_op->o_req_ndn.bv_len - m2->mt_op->o_req_ndn.bv_len;
364
365         if ( rc ) return rc;
366         return ber_bvcmp( &m1->mt_op->o_req_ndn, &m2->mt_op->o_req_ndn );
367 }
368
369 /* syncprov_findbase:
370  *   finds the true DN of the base of a search (with alias dereferencing) and
371  * checks to make sure the base entry doesn't get replaced with a different
372  * entry (e.g., swapping trees via ModDN, or retargeting an alias). If a
373  * change is detected, any persistent search on this base must be terminated /
374  * reloaded.
375  *   On the first call, we just save the DN and entryID. On subsequent calls
376  * we compare the DN and entryID with the saved values.
377  */
378 static int
379 findbase_cb( Operation *op, SlapReply *rs )
380 {
381         slap_callback *sc = op->o_callback;
382
383         if ( rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS ) {
384                 fbase_cookie *fc = sc->sc_private;
385
386                 /* If no entryID, we're looking for the first time.
387                  * Just store whatever we got.
388                  */
389                 if ( fc->fss->s_eid == NOID ) {
390                         fc->fbase = 1;
391                         fc->fss->s_eid = rs->sr_entry->e_id;
392                         ber_dupbv( &fc->fss->s_base, &rs->sr_entry->e_nname );
393
394                 } else if ( rs->sr_entry->e_id == fc->fss->s_eid &&
395                         dn_match( &rs->sr_entry->e_nname, &fc->fss->s_base )) {
396
397                 /* OK, the DN is the same and the entryID is the same. Now
398                  * see if the fdn resides in the scope.
399                  */
400                         fc->fbase = 1;
401                         switch ( fc->fss->s_op->ors_scope ) {
402                         case LDAP_SCOPE_BASE:
403                                 fc->fscope = dn_match( fc->fdn, &rs->sr_entry->e_nname );
404                                 break;
405                         case LDAP_SCOPE_ONELEVEL: {
406                                 struct berval pdn;
407                                 dnParent( fc->fdn, &pdn );
408                                 fc->fscope = dn_match( &pdn, &rs->sr_entry->e_nname );
409                                 break; }
410                         case LDAP_SCOPE_SUBTREE:
411                                 fc->fscope = dnIsSuffix( fc->fdn, &rs->sr_entry->e_nname );
412                                 break;
413 #ifdef LDAP_SCOPE_SUBORDINATE
414                         case LDAP_SCOPE_SUBORDINATE:
415                                 fc->fscope = dnIsSuffix( fc->fdn, &rs->sr_entry->e_nname ) &&
416                                         !dn_match( fc->fdn, &rs->sr_entry->e_nname );
417                                 break;
418 #endif
419                         }
420                 }
421         }
422         return LDAP_SUCCESS;
423 }
424
425 static int
426 syncprov_findbase( Operation *op, fbase_cookie *fc )
427 {
428         opcookie *opc = op->o_callback->sc_private;
429         slap_overinst *on = opc->son;
430         syncprov_info_t         *si = on->on_bi.bi_private;
431
432         slap_callback cb = {0};
433         Operation fop;
434         SlapReply frs = { REP_RESULT };
435         int rc;
436
437         fop = *op;
438
439         cb.sc_response = findbase_cb;
440         cb.sc_private = fc;
441
442         fop.o_sync_mode &= SLAP_CONTROL_MASK;   /* turn off sync mode */
443         fop.o_callback = &cb;
444         fop.o_tag = LDAP_REQ_SEARCH;
445         fop.ors_scope = LDAP_SCOPE_BASE;
446         fop.ors_deref = fc->fss->s_op->ors_deref;
447         fop.ors_slimit = 1;
448         fop.ors_tlimit = SLAP_NO_LIMIT;
449         fop.ors_attrs = slap_anlist_no_attrs;
450         fop.ors_attrsonly = 1;
451         fop.ors_filter = fc->fss->s_op->ors_filter;
452         fop.ors_filterstr = fc->fss->s_op->ors_filterstr;
453
454         fop.o_req_ndn = fc->fss->s_op->o_req_ndn;
455
456         fop.o_bd->bd_info = on->on_info->oi_orig;
457         rc = fop.o_bd->be_search( &fop, &frs );
458         fop.o_bd->bd_info = (BackendInfo *)on;
459
460         if ( fc->fbase ) return LDAP_SUCCESS;
461
462         /* If entryID has changed, then the base of this search has
463          * changed. Invalidate the psearch.
464          */
465         return LDAP_NO_SUCH_OBJECT;
466 }
467
468 /* syncprov_findcsn:
469  *   This function has three different purposes, but they all use a search
470  * that filters on entryCSN so they're combined here.
471  * 1: when the current contextCSN is unknown (i.e., at server start time)
472  * and a syncrepl search has arrived with a cookie, we search for all entries
473  * with CSN >= the cookie CSN, and store the maximum as our contextCSN. Also,
474  * we expect to find the cookie CSN in the search results, and note if we did
475  * or not. If not, we assume the cookie is stale. (This may be too restrictive,
476  * notice case 2.)
477  *
478  * 2: when the current contextCSN is known and we have a sync cookie, we search
479  * for one entry with CSN <= the cookie CSN. (Used to search for =.) If an
480  * entry is found, the cookie CSN is valid, otherwise it is stale. Case 1 is
481  * considered a special case of case 2, and both are generally called the
482  * "find CSN" task.
483  *
484  * 3: during a refresh phase, we search for all entries with CSN <= the cookie
485  * CSN, and generate Present records for them. We always collect this result
486  * in SyncID sets, even if there's only one match.
487  */
488 #define FIND_CSN        1
489 #define FIND_PRESENT    2
490
491 typedef struct fcsn_cookie {
492         struct berval maxcsn;
493         int gotmatch;
494 } fcsn_cookie;
495
496 static int
497 findcsn_cb( Operation *op, SlapReply *rs )
498 {
499         slap_callback *sc = op->o_callback;
500
501         if ( rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS ) {
502                 /* If the private pointer is set, it points to an fcsn_cookie
503                  * and we want to record the maxcsn and match state.
504                  */
505                 if ( sc->sc_private ) {
506                         int i;
507                         fcsn_cookie *fc = sc->sc_private;
508                         sync_control *srs = op->o_controls[sync_cid];
509                         Attribute *a = attr_find(rs->sr_entry->e_attrs,
510                                 slap_schema.si_ad_entryCSN );
511                         i = ber_bvcmp( &a->a_vals[0], srs->sr_state.ctxcsn );
512                         if ( i == 0 ) fc->gotmatch = 1;
513                         i = ber_bvcmp( &a->a_vals[0], &fc->maxcsn );
514                         if ( i > 0 ) {
515                                 fc->maxcsn.bv_len = a->a_vals[0].bv_len;
516                                 strcpy(fc->maxcsn.bv_val, a->a_vals[0].bv_val );
517                         }
518                 } else {
519                 /* Otherwise, if the private pointer is not set, we just
520                  * want to know if any entry matched the filter.
521                  */
522                         sc->sc_private = (void *)1;
523                 }
524         }
525         return LDAP_SUCCESS;
526 }
527
528 /* Build a list of entryUUIDs for sending in a SyncID set */
529
530 typedef struct fpres_cookie {
531         int num;
532         BerVarray uuids;
533 } fpres_cookie;
534
535 static int
536 findpres_cb( Operation *op, SlapReply *rs )
537 {
538         slap_callback *sc = op->o_callback;
539         fpres_cookie *pc = sc->sc_private;
540         int ret = SLAP_CB_CONTINUE;
541
542         if ( rs->sr_type == REP_SEARCH ) {
543                 ret = slap_build_syncUUID_set( op, &pc->uuids, rs->sr_entry );
544                 if ( ret > 0 ) {
545                         pc->num++;
546                         ret = LDAP_SUCCESS;
547                         if ( pc->num == SLAP_SYNCUUID_SET_SIZE ) {
548                                 rs->sr_rspoid = LDAP_SYNC_INFO;
549                                 ret = syncprov_sendinfo( op, rs, LDAP_TAG_SYNC_ID_SET, NULL,
550                                         0, pc->uuids, 0 );
551                                 ber_bvarray_free_x( pc->uuids, op->o_tmpmemctx );
552                                 pc->uuids = NULL;
553                                 pc->num = 0;
554                         }
555                 } else {
556                         ret = LDAP_OTHER;
557                 }
558         } else if ( rs->sr_type == REP_RESULT ) {
559                 ret = rs->sr_err;
560                 if ( pc->num ) {
561                         rs->sr_rspoid = LDAP_SYNC_INFO;
562                         ret = syncprov_sendinfo( op, rs, LDAP_TAG_SYNC_ID_SET, NULL,
563                                 0, pc->uuids, 0 );
564                         ber_bvarray_free_x( pc->uuids, op->o_tmpmemctx );
565                         pc->uuids = NULL;
566                         pc->num = 0;
567                 }
568         }
569         return ret;
570 }
571
572
573 static int
574 syncprov_findcsn( Operation *op, int mode )
575 {
576         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
577         syncprov_info_t         *si = on->on_bi.bi_private;
578
579         slap_callback cb = {0};
580         Operation fop;
581         SlapReply frs = { REP_RESULT };
582         char buf[LDAP_LUTIL_CSNSTR_BUFSIZE + STRLENOF("(entryCSN<=)")];
583         char cbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
584         struct berval fbuf;
585         Filter cf;
586         AttributeAssertion eq;
587         int rc;
588         fcsn_cookie fcookie;
589         fpres_cookie pcookie;
590         int locked = 0;
591         sync_control *srs = op->o_controls[sync_cid];
592
593         if ( srs->sr_state.ctxcsn->bv_len >= LDAP_LUTIL_CSNSTR_BUFSIZE ) {
594                 return LDAP_OTHER;
595         }
596
597         fop = *op;
598         fop.o_sync_mode &= SLAP_CONTROL_MASK;   /* turn off sync_mode */
599
600         fbuf.bv_val = buf;
601         if ( mode == FIND_CSN ) {
602                 if ( !si->si_gotcsn ) {
603                         /* If we don't know the current ctxcsn, find it */
604                         ldap_pvt_thread_mutex_lock( &si->si_csn_mutex );
605                         locked = 1;
606                 }
607                 if ( !si->si_gotcsn ) {
608                         cf.f_choice = LDAP_FILTER_GE;
609                         fop.ors_attrsonly = 0;
610                         fop.ors_attrs = csn_anlist;
611                         fop.ors_slimit = SLAP_NO_LIMIT;
612                         cb.sc_private = &fcookie;
613                         fcookie.maxcsn.bv_val = cbuf;
614                         fcookie.maxcsn.bv_len = 0;
615                         fcookie.gotmatch = 0;
616                         fbuf.bv_len = sprintf( buf, "(entryCSN>=%s)", srs->sr_state.ctxcsn->bv_val );
617                 } else {
618                         if ( locked ) {
619                                 ldap_pvt_thread_mutex_unlock( &si->si_csn_mutex );
620                                 locked = 0;
621                         }
622                         cf.f_choice = LDAP_FILTER_LE;
623                         fop.ors_attrsonly = 1;
624                         fop.ors_attrs = slap_anlist_no_attrs;
625                         fop.ors_slimit = 1;
626                         cb.sc_private = NULL;
627                         fbuf.bv_len = sprintf( buf, "(entryCSN<=%s)", srs->sr_state.ctxcsn->bv_val );
628                 }
629                 cb.sc_response = findcsn_cb;
630
631         } else if ( mode == FIND_PRESENT ) {
632                 cf.f_choice = LDAP_FILTER_LE;
633                 fop.ors_attrsonly = 0;
634                 fop.ors_attrs = uuid_anlist;
635                 fop.ors_slimit = SLAP_NO_LIMIT;
636                 /* We want pure entries, not referrals */
637                 fop.o_managedsait = SLAP_CONTROL_CRITICAL;
638                 cb.sc_private = &pcookie;
639                 cb.sc_response = findpres_cb;
640                 pcookie.num = 0;
641                 pcookie.uuids = NULL;
642                 fbuf.bv_len = sprintf( buf, "(entryCSN<=%s)", srs->sr_state.ctxcsn->bv_val );
643         }
644         cf.f_ava = &eq;
645         cf.f_av_desc = slap_schema.si_ad_entryCSN;
646         cf.f_av_value = *srs->sr_state.ctxcsn;
647         cf.f_next = NULL;
648
649         fop.o_callback = &cb;
650         fop.ors_tlimit = SLAP_NO_LIMIT;
651         fop.ors_filter = &cf;
652         fop.ors_filterstr = fbuf;
653
654         fop.o_bd->bd_info = on->on_info->oi_orig;
655         rc = fop.o_bd->be_search( &fop, &frs );
656         fop.o_bd->bd_info = (BackendInfo *)on;
657
658         if ( mode == FIND_CSN ) {
659                 if ( !si->si_gotcsn ) {
660                         strcpy(si->si_ctxcsnbuf, fcookie.maxcsn.bv_val);
661                         si->si_ctxcsn.bv_len = fcookie.maxcsn.bv_len;
662                         si->si_gotcsn = 1;
663                         ldap_pvt_thread_mutex_unlock( &si->si_csn_mutex );
664                         if ( fcookie.gotmatch ) return LDAP_SUCCESS;
665                         
666                 } else {
667                         if ( cb.sc_private ) return LDAP_SUCCESS;
668                 }
669         } else if ( mode == FIND_PRESENT ) {
670                 return LDAP_SUCCESS;
671         }
672
673         /* If matching CSN was not found, invalidate the context. */
674         return LDAP_NO_SUCH_OBJECT;
675 }
676
677 /* Queue a persistent search response if still in Refresh stage */
678 static int
679 syncprov_qresp( opcookie *opc, syncops *so, int mode )
680 {
681         syncres *sr;
682
683         sr = ch_malloc(sizeof(syncres) + opc->suuid.bv_len + 1 +
684                 opc->sdn.bv_len + 1 + opc->sndn.bv_len + 1 + opc->sctxcsn.bv_len + 1 );
685         sr->s_next = NULL;
686         sr->s_dn.bv_val = (char *)(sr + 1);
687         sr->s_mode = mode;
688         sr->s_isreference = opc->sreference;
689         sr->s_ndn.bv_val = lutil_strcopy( sr->s_dn.bv_val, opc->sdn.bv_val );
690         *(sr->s_ndn.bv_val++) = '\0';
691         sr->s_uuid.bv_val = lutil_strcopy( sr->s_ndn.bv_val, opc->sndn.bv_val );
692         *(sr->s_uuid.bv_val++) = '\0';
693         sr->s_csn.bv_val = lutil_strcopy( sr->s_uuid.bv_val, opc->suuid.bv_val );
694
695         if ( !so->s_res ) {
696                 so->s_res = sr;
697         } else {
698                 so->s_restail->s_next = sr;
699         }
700         so->s_restail = sr;
701         ldap_pvt_thread_mutex_unlock( &so->s_mutex );
702         return LDAP_SUCCESS;
703 }
704
705 /* Send a persistent search response */
706 static int
707 syncprov_sendresp( Operation *op, opcookie *opc, syncops *so, Entry *e, int mode, int queue )
708 {
709         slap_overinst *on = opc->son;
710         syncprov_info_t *si = on->on_bi.bi_private;
711
712         SlapReply rs = { REP_SEARCH };
713         LDAPControl *ctrls[2];
714         struct berval cookie;
715         Entry e_uuid = {0};
716         Attribute a_uuid = {0};
717         Operation sop = *so->s_op;
718         Opheader ohdr;
719
720         ohdr = *sop.o_hdr;
721         sop.o_hdr = &ohdr;
722         sop.o_tmpmemctx = op->o_tmpmemctx;
723         sop.o_bd = op->o_bd;
724         sop.o_controls = op->o_controls;
725
726         if ( queue && (so->s_flags & PS_IS_REFRESHING) ) {
727                 ldap_pvt_thread_mutex_lock( &so->s_mutex );
728                 if ( so->s_flags & PS_IS_REFRESHING )
729                         return syncprov_qresp( opc, so, mode );
730                 ldap_pvt_thread_mutex_unlock( &so->s_mutex );
731         }
732
733         ctrls[1] = NULL;
734         slap_compose_sync_cookie( op, &cookie, &opc->sctxcsn,
735                 so->s_sid, so->s_rid );
736
737         e_uuid.e_attrs = &a_uuid;
738         a_uuid.a_desc = slap_schema.si_ad_entryUUID;
739         a_uuid.a_nvals = &opc->suuid;
740         rs.sr_err = syncprov_state_ctrl( &sop, &rs, &e_uuid,
741                 mode, ctrls, 0, 1, &cookie );
742
743         rs.sr_entry = e;
744         rs.sr_ctrls = ctrls;
745         switch( mode ) {
746         case LDAP_SYNC_ADD:
747                 if ( opc->sreference ) {
748                         rs.sr_ref = get_entry_referrals( &sop, e );
749                         send_search_reference( &sop, &rs );
750                         ber_bvarray_free( rs.sr_ref );
751                         break;
752                 }
753                 /* fallthru */
754         case LDAP_SYNC_MODIFY:
755                 rs.sr_attrs = sop.ors_attrs;
756                 send_search_entry( &sop, &rs );
757                 break;
758         case LDAP_SYNC_DELETE:
759                 e_uuid.e_attrs = NULL;
760                 e_uuid.e_name = opc->sdn;
761                 e_uuid.e_nname = opc->sndn;
762                 rs.sr_entry = &e_uuid;
763                 if ( opc->sreference ) {
764                         struct berval bv;
765                         bv.bv_val = NULL;
766                         bv.bv_len = 0;
767                         rs.sr_ref = &bv;
768                         send_search_reference( &sop, &rs );
769                 } else {
770                         send_search_entry( &sop, &rs );
771                 }
772                 break;
773         default:
774                 assert(0);
775         }
776         free( rs.sr_ctrls[0] );
777         return rs.sr_err;
778 }
779
780 static void
781 syncprov_matchops( Operation *op, opcookie *opc, int saveit )
782 {
783         slap_overinst *on = opc->son;
784         syncprov_info_t         *si = on->on_bi.bi_private;
785
786         fbase_cookie fc;
787         syncops *ss;
788         Entry *e;
789         Attribute *a;
790         int rc;
791         struct berval newdn;
792
793         fc.fdn = &op->o_req_ndn;
794         /* compute new DN */
795         if ( op->o_tag == LDAP_REQ_MODRDN && !saveit ) {
796                 struct berval pdn;
797                 if ( op->orr_nnewSup ) pdn = *op->orr_nnewSup;
798                 else dnParent( fc.fdn, &pdn );
799                 build_new_dn( &newdn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
800                 fc.fdn = &newdn;
801         }
802         if ( op->o_tag != LDAP_REQ_ADD ) {
803                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
804                 rc = be_entry_get_rw( op, fc.fdn, NULL, NULL, 0, &e );
805                 op->o_bd->bd_info = (BackendInfo *)on;
806                 if ( rc ) return;
807         } else {
808                 e = op->ora_e;
809         }
810
811         if ( saveit ) {
812                 ber_dupbv_x( &opc->sdn, &e->e_name, op->o_tmpmemctx );
813                 ber_dupbv_x( &opc->sndn, &e->e_nname, op->o_tmpmemctx );
814                 opc->sreference = is_entry_referral( e );
815         }
816         if ( saveit || op->o_tag == LDAP_REQ_ADD ) {
817                 a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
818                 if ( a )
819                         ber_dupbv_x( &opc->suuid, &a->a_nvals[0], op->o_tmpmemctx );
820         }
821
822         ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
823         for (ss = si->si_ops; ss; ss=ss->s_next)
824         {
825                 syncmatches *sm;
826                 int found = 0;
827
828                 /* validate base */
829                 fc.fss = ss;
830                 fc.fbase = 0;
831                 fc.fscope = 0;
832                 rc = syncprov_findbase( op, &fc );
833                 if ( rc != LDAP_SUCCESS ) continue;
834
835                 /* If we're sending results now, look for this op in old matches */
836                 if ( !saveit ) {
837                         syncmatches *old;
838                         for ( sm=opc->smatches, old=(syncmatches *)&opc->smatches; sm;
839                                 old=sm, sm=sm->sm_next ) {
840                                 if ( sm->sm_op == ss ) {
841                                         found = 1;
842                                         old->sm_next = sm->sm_next;
843                                         op->o_tmpfree( sm, op->o_tmpmemctx );
844                                         break;
845                                 }
846                         }
847                 }
848
849                 /* check if current o_req_dn is in scope and matches filter */
850                 if ( fc.fscope && test_filter( op, e, ss->s_op->ors_filter ) ==
851                         LDAP_COMPARE_TRUE ) {
852                         if ( saveit ) {
853                                 sm = op->o_tmpalloc( sizeof(syncmatches), op->o_tmpmemctx );
854                                 sm->sm_next = opc->smatches;
855                                 sm->sm_op = ss;
856                                 ss->s_inuse++;
857                                 opc->smatches = sm;
858                         } else {
859                                 /* if found send UPDATE else send ADD */
860                                 syncprov_sendresp( op, opc, ss, e,
861                                         found ? LDAP_SYNC_MODIFY : LDAP_SYNC_ADD, 1 );
862                         }
863                 } else if ( !saveit && found ) {
864                         /* send DELETE */
865                         syncprov_sendresp( op, opc, ss, NULL, LDAP_SYNC_DELETE, 1 );
866                 }
867         }
868         ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
869         if ( op->o_tag != LDAP_REQ_ADD ) {
870                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
871                 be_entry_release_r( op, e );
872                 op->o_bd->bd_info = (BackendInfo *)on;
873         }
874 }
875
876 static void
877 syncprov_free_syncop( syncops *so )
878 {
879         syncres *sr, *srnext;
880
881         ldap_pvt_thread_mutex_lock( &so->s_mutex );
882         so->s_inuse--;
883         if ( so->s_inuse > 0 ) {
884                 ldap_pvt_thread_mutex_unlock( &so->s_mutex );
885                 return;
886         }
887         ldap_pvt_thread_mutex_unlock( &so->s_mutex );
888         filter_free( so->s_op->ors_filter );
889         ch_free( so->s_op );
890         ch_free( so->s_base.bv_val );
891         for ( sr=so->s_res; sr; sr=srnext ) {
892                 srnext = sr->s_next;
893                 ch_free( sr );
894         }
895         ldap_pvt_thread_mutex_destroy( &so->s_mutex );
896         ch_free( so );
897 }
898
899 static int
900 syncprov_op_cleanup( Operation *op, SlapReply *rs )
901 {
902         slap_callback *cb = op->o_callback;
903         opcookie *opc = cb->sc_private;
904         slap_overinst *on = opc->son;
905         syncprov_info_t         *si = on->on_bi.bi_private;
906         syncmatches *sm, *snext;
907         modtarget *mt, mtdummy;
908
909         for (sm = opc->smatches; sm; sm=snext) {
910                 snext = sm->sm_next;
911                 syncprov_free_syncop( sm->sm_op );
912                 op->o_tmpfree( sm, op->o_tmpmemctx );
913         }
914
915         /* Remove op from lock table */
916         mtdummy.mt_op = op;
917         ldap_pvt_thread_mutex_lock( &si->si_mods_mutex );
918         mt = avl_find( si->si_mods, &mtdummy, sp_avl_cmp );
919         if ( mt ) {
920                 modinst *mi = mt->mt_mods;
921                 
922                 /* If there are more, promote the next one */
923                 ldap_pvt_thread_mutex_lock( &mt->mt_mutex );
924                 if ( mi->mi_next ) {
925                         mt->mt_mods = mi->mi_next;
926                         mt->mt_op = mt->mt_mods->mi_op;
927                         ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
928                 } else {
929                         avl_delete( &si->si_mods, mt, sp_avl_cmp );
930                         ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
931                         ldap_pvt_thread_mutex_destroy( &mt->mt_mutex );
932                         ch_free( mt );
933                 }
934         }
935         ldap_pvt_thread_mutex_unlock( &si->si_mods_mutex );
936         op->o_callback = cb->sc_next;
937         op->o_tmpfree(cb, op->o_tmpmemctx);
938 }
939
940 static int
941 syncprov_op_response( Operation *op, SlapReply *rs )
942 {
943         opcookie *opc = op->o_callback->sc_private;
944         slap_overinst *on = opc->son;
945         syncprov_info_t         *si = on->on_bi.bi_private;
946         syncmatches *sm;
947
948         if ( rs->sr_err == LDAP_SUCCESS )
949         {
950                 struct berval maxcsn;
951                 char cbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
952
953                 cbuf[0] = '\0';
954                 ldap_pvt_thread_mutex_lock( &si->si_csn_mutex );
955                 slap_get_commit_csn( op, &maxcsn );
956                 if ( maxcsn.bv_val ) {
957                         strcpy( cbuf, maxcsn.bv_val );
958                         if ( ber_bvcmp( &maxcsn, &si->si_ctxcsn ) > 0 ) {
959                                 strcpy( si->si_ctxcsnbuf, cbuf );
960                                 si->si_ctxcsn.bv_len = maxcsn.bv_len;
961                         }
962                         si->si_gotcsn = 1;
963                 }
964                 ldap_pvt_thread_mutex_unlock( &si->si_csn_mutex );
965
966                 opc->sctxcsn.bv_len = maxcsn.bv_len;
967                 opc->sctxcsn.bv_val = cbuf;
968
969                 if ( si->si_ops ) {
970                         switch(op->o_tag) {
971                         case LDAP_REQ_ADD:
972                         case LDAP_REQ_MODIFY:
973                         case LDAP_REQ_MODRDN:
974                         case LDAP_REQ_EXTENDED:
975                                 syncprov_matchops( op, opc, 0 );
976                                 break;
977                         case LDAP_REQ_DELETE:
978                                 /* for each match in opc->smatches:
979                                  *   send DELETE msg
980                                  */
981                                 ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
982                                 for ( sm = opc->smatches; sm; sm=sm->sm_next ) {
983                                         if ( sm->sm_op->s_op->o_abandon )
984                                                 continue;
985                                         syncprov_sendresp( op, opc, sm->sm_op, NULL,
986                                                 LDAP_SYNC_DELETE, 1 );
987                                 }
988                                 ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
989                                 break;
990                         }
991                 }
992
993         }
994         return SLAP_CB_CONTINUE;
995 }
996
997 static int
998 syncprov_op_abandon( Operation *op, SlapReply *rs )
999 {
1000         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1001         syncprov_info_t         *si = on->on_bi.bi_private;
1002         syncops *so, *soprev;
1003
1004         ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
1005         for ( so=si->si_ops, soprev = (syncops *)&si->si_ops; so;
1006                 soprev=so, so=so->s_next ) {
1007                 if ( so->s_op->o_connid == op->o_connid &&
1008                         so->s_op->o_msgid == op->orn_msgid ) {
1009                                 so->s_op->o_abandon = 1;
1010                                 soprev->s_next = so->s_next;
1011                                 break;
1012                 }
1013         }
1014         ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
1015         if ( so ) {
1016                 ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
1017                 op->o_conn->c_n_ops_executing--;
1018                 op->o_conn->c_n_ops_completed++;
1019                 ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
1020                 /* Is this really a Cancel exop? */
1021                 if ( op->o_tag != LDAP_REQ_ABANDON ) {
1022                         rs->sr_err = LDAP_CANCELLED;
1023                         send_ldap_result( so->s_op, rs );
1024                 }
1025                 syncprov_free_syncop( so );
1026         }
1027         return SLAP_CB_CONTINUE;
1028 }
1029
1030 #if 0
1031 static int
1032 syncprov_op_compare( Operation *op, SlapReply *rs )
1033 {
1034         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1035         syncprov_info_t         *si = on->on_bi.bi_private;
1036         int rc = SLAP_CB_CONTINUE;
1037
1038         if ( dn_match( &op->o_req_ndn, &si->si_e->e_nname ) )
1039         {
1040                 Attribute *a;
1041
1042                 ldap_pvt_thread_mutex_lock( &si->si_e_mutex );
1043
1044                 if ( get_assert( op ) &&
1045                         ( test_filter( op, si->si_e, get_assertion( op ) ) != LDAP_COMPARE_TRUE ) )
1046                 {
1047                         rs->sr_err = LDAP_ASSERTION_FAILED;
1048                         goto return_results;
1049                 }
1050
1051                 rs->sr_err = access_allowed( op, si->si_e, op->oq_compare.rs_ava->aa_desc,
1052                         &op->oq_compare.rs_ava->aa_value, ACL_COMPARE, NULL );
1053                 if ( ! rs->sr_err ) {
1054                         rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1055                         goto return_results;
1056                 }
1057
1058                 rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
1059
1060                 for ( a = attr_find( si->si_e->e_attrs, op->oq_compare.rs_ava->aa_desc );
1061                         a != NULL;
1062                         a = attr_find( a->a_next, op->oq_compare.rs_ava->aa_desc ) )
1063                 {
1064                         rs->sr_err = LDAP_COMPARE_FALSE;
1065
1066                         if ( value_find_ex( op->oq_compare.rs_ava->aa_desc,
1067                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1068                                         SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1069                                 a->a_nvals, &op->oq_compare.rs_ava->aa_value, op->o_tmpmemctx ) == 0 )
1070                         {
1071                                 rs->sr_err = LDAP_COMPARE_TRUE;
1072                                 break;
1073                         }
1074                 }
1075
1076 return_results:;
1077
1078                 ldap_pvt_thread_mutex_unlock( &si->si_e_mutex );
1079
1080                 send_ldap_result( op, rs );
1081
1082                 if( rs->sr_err == LDAP_COMPARE_FALSE || rs->sr_err == LDAP_COMPARE_TRUE ) {
1083                         rs->sr_err = LDAP_SUCCESS;
1084                 }
1085                 rc = rs->sr_err;
1086         }
1087
1088         return SLAP_CB_CONTINUE;
1089 }
1090 #endif
1091         
1092 static int
1093 syncprov_op_mod( Operation *op, SlapReply *rs )
1094 {
1095         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1096         syncprov_info_t         *si = on->on_bi.bi_private;
1097
1098         slap_callback *cb = op->o_tmpcalloc(1, sizeof(slap_callback)+
1099                 sizeof(opcookie) +
1100                 (si->si_ops ? sizeof(modinst) : 0 ),
1101                 op->o_tmpmemctx);
1102         opcookie *opc = (opcookie *)(cb+1);
1103         opc->son = on;
1104         cb->sc_response = syncprov_op_response;
1105         cb->sc_cleanup = syncprov_op_cleanup;
1106         cb->sc_private = opc;
1107         cb->sc_next = op->o_callback;
1108         op->o_callback = cb;
1109
1110         /* If there are active persistent searches, lock this operation.
1111          * See seqmod.c for the locking logic on its own.
1112          */
1113         if ( si->si_ops ) {
1114                 modtarget *mt, mtdummy;
1115                 modinst *mi;
1116
1117                 mi = (modinst *)(opc+1);
1118                 mi->mi_op = op;
1119
1120                 /* See if we're already modifying this entry... */
1121                 mtdummy.mt_op = op;
1122                 ldap_pvt_thread_mutex_lock( &si->si_mods_mutex );
1123                 mt = avl_find( si->si_mods, &mtdummy, sp_avl_cmp );
1124                 if ( mt ) {
1125                         ldap_pvt_thread_mutex_lock( &mt->mt_mutex );
1126                         ldap_pvt_thread_mutex_unlock( &si->si_mods_mutex );
1127                         mt->mt_tail->mi_next = mi;
1128                         mt->mt_tail = mi;
1129                         /* wait for this op to get to head of list */
1130                         while ( mt->mt_mods != mi ) {
1131                                 ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
1132                                 ldap_pvt_thread_yield();
1133                                 ldap_pvt_thread_mutex_lock( &mt->mt_mutex );
1134                         }
1135                 } else {
1136                         /* Record that we're modifying this entry now */
1137                         mt = malloc( sizeof(modtarget) );
1138                         mt->mt_mods = mi;
1139                         mt->mt_tail = mi;
1140                         mt->mt_op = mi->mi_op;
1141                         ldap_pvt_thread_mutex_init( &mt->mt_mutex );
1142                         avl_insert( &si->si_mods, mt, sp_avl_cmp, avl_dup_error );
1143                         ldap_pvt_thread_mutex_unlock( &si->si_mods_mutex );
1144                 }
1145
1146                 if ( op->o_tag != LDAP_REQ_ADD )
1147                         syncprov_matchops( op, opc, 1 );
1148         }
1149
1150         return SLAP_CB_CONTINUE;
1151 }
1152
1153 static int
1154 syncprov_op_extended( Operation *op, SlapReply *rs )
1155 {
1156         if ( exop_is_write( op ))
1157                 return syncprov_op_mod( op, rs );
1158
1159         return SLAP_CB_CONTINUE;
1160 }
1161
1162 typedef struct searchstate {
1163         slap_overinst *ss_on;
1164         syncops *ss_so;
1165 } searchstate;
1166
1167 static int
1168 syncprov_search_cleanup( Operation *op, SlapReply *rs )
1169 {
1170         searchstate *ss = op->o_callback->sc_private;
1171         if ( rs->sr_ctrls ) {
1172                 free( rs->sr_ctrls[0] );
1173                 op->o_tmpfree( rs->sr_ctrls, op->o_tmpmemctx );
1174         }
1175         return 0;
1176 }
1177
1178 static void
1179 syncprov_detach_op( Operation *op, syncops *so )
1180 {
1181         Operation *op2;
1182         int i, alen = 0;
1183         size_t size;
1184         char *ptr;
1185
1186         /* count the search attrs */
1187         for (i=0; op->ors_attrs && op->ors_attrs[i].an_name.bv_val; i++) {
1188                 alen += op->ors_attrs[i].an_name.bv_len + 1;
1189         }
1190         /* Make a new copy of the operation */
1191         size = sizeof(Operation) + sizeof(Opheader) +
1192                 (i ? ( (i+1) * sizeof(AttributeName) + alen) : 0) +
1193                 op->o_req_dn.bv_len + 1 +
1194                 op->o_req_ndn.bv_len + 1 +
1195                 op->o_ndn.bv_len + 1 +
1196                 so->s_filterstr.bv_len + 1;
1197         op2 = (Operation *)ch_malloc( size );
1198         *op2 = *op;
1199         op2->o_hdr = (Opheader *)(op2+1);
1200         *op2->o_hdr = *op->o_hdr;
1201         if ( i ) {
1202                 op2->ors_attrs = (AttributeName *)(op2->o_hdr + 1);
1203                 ptr = (char *)(op2->ors_attrs+i+1);
1204                 for (i=0; op->ors_attrs[i].an_name.bv_val; i++) {
1205                         op2->ors_attrs[i] = op->ors_attrs[i];
1206                         op2->ors_attrs[i].an_name.bv_val = ptr;
1207                         ptr = lutil_strcopy( ptr, op->ors_attrs[i].an_name.bv_val ) + 1;
1208                 }
1209                 op2->ors_attrs[i].an_name.bv_val = NULL;
1210                 op2->ors_attrs[i].an_name.bv_len = 0;
1211         } else {
1212                 ptr = (char *)(op2->o_hdr + 1);
1213         }
1214         op2->o_ndn.bv_val = ptr;
1215         ptr = lutil_strcopy(ptr, op->o_ndn.bv_val) + 1;
1216         op2->o_dn = op2->o_ndn;
1217         op2->o_req_dn.bv_val = ptr;
1218         ptr = lutil_strcopy(ptr, op->o_req_dn.bv_val) + 1;
1219         op2->o_req_ndn.bv_val = ptr;
1220         ptr = lutil_strcopy(ptr, op->o_req_ndn.bv_val) + 1;
1221         op2->ors_filterstr.bv_val = ptr;
1222         strcpy( ptr, so->s_filterstr.bv_val );
1223         op2->ors_filterstr.bv_len = so->s_filterstr.bv_len;
1224         op2->ors_filter = str2filter( ptr );
1225         op2->o_controls = NULL;
1226         op2->o_callback = NULL;
1227         so->s_op = op2;
1228
1229         /* Increment number of ops so that idletimeout ignores us */
1230         ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
1231         op->o_conn->c_n_ops_executing++;
1232         op->o_conn->c_n_ops_completed--;
1233         ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
1234 }
1235
1236 static int
1237 syncprov_search_response( Operation *op, SlapReply *rs )
1238 {
1239         searchstate *ss = op->o_callback->sc_private;
1240         slap_overinst *on = ss->ss_on;
1241         syncprov_info_t         *si = on->on_bi.bi_private;
1242         sync_control *srs = op->o_controls[sync_cid];
1243
1244         if ( rs->sr_type == REP_SEARCH || rs->sr_type == REP_SEARCHREF ) {
1245                 int i;
1246                 if ( srs->sr_state.ctxcsn ) {
1247                         Attribute *a = attr_find( rs->sr_entry->e_attrs,
1248                                 slap_schema.si_ad_entryCSN );
1249                         /* Don't send the ctx entry twice */
1250                         if ( bvmatch( &a->a_nvals[0], srs->sr_state.ctxcsn ))
1251                                 return LDAP_SUCCESS;
1252                 }
1253                 rs->sr_ctrls = op->o_tmpalloc( sizeof(LDAPControl *)*2,
1254                         op->o_tmpmemctx );
1255                 rs->sr_ctrls[1] = NULL;
1256                 rs->sr_err = syncprov_state_ctrl( op, rs, rs->sr_entry,
1257                         LDAP_SYNC_ADD, rs->sr_ctrls, 0, 0, NULL );
1258         } else if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS ) {
1259                 struct berval cookie;
1260
1261                 slap_compose_sync_cookie( op, &cookie,
1262                         &op->ors_filter->f_and->f_ava->aa_value,
1263                         srs->sr_state.sid, srs->sr_state.rid );
1264
1265                 /* Is this a regular refresh? */
1266                 if ( !ss->ss_so ) {
1267                         rs->sr_ctrls = op->o_tmpalloc( sizeof(LDAPControl *)*2,
1268                                 op->o_tmpmemctx );
1269                         rs->sr_ctrls[1] = NULL;
1270                         rs->sr_err = syncprov_done_ctrl( op, rs, rs->sr_ctrls,
1271                                 0, 1, &cookie, LDAP_SYNC_REFRESH_PRESENTS );
1272                 } else {
1273                         int locked = 0;
1274                 /* It's RefreshAndPersist, transition to Persist phase */
1275                         rs->sr_rspoid = LDAP_SYNC_INFO;
1276                         syncprov_sendinfo( op, rs, rs->sr_nentries ?
1277                                 LDAP_TAG_SYNC_REFRESH_PRESENT : LDAP_TAG_SYNC_REFRESH_DELETE,
1278                                 &cookie, 1, NULL, 0 );
1279                         /* Flush any queued persist messages */
1280                         if ( ss->ss_so->s_res ) {
1281                                 syncres *sr, *srnext;
1282                                 Entry *e;
1283                                 opcookie opc;
1284
1285                                 opc.son = on;
1286                                 ldap_pvt_thread_mutex_lock( &ss->ss_so->s_mutex );
1287                                 locked = 1;
1288                                 for (sr = ss->ss_so->s_res; sr; sr=srnext) {
1289                                         int rc = LDAP_SUCCESS;
1290                                         srnext = sr->s_next;
1291                                         opc.sdn = sr->s_dn;
1292                                         opc.sndn = sr->s_ndn;
1293                                         opc.suuid = sr->s_uuid;
1294                                         opc.sctxcsn = sr->s_csn;
1295                                         opc.sreference = sr->s_isreference;
1296                                         e = NULL;
1297                                         
1298                                         if ( sr->s_mode != LDAP_SYNC_DELETE ) {
1299                                                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1300                                                 rc = be_entry_get_rw( op, &opc.sndn, NULL, NULL, 0, &e );
1301                                                 op->o_bd->bd_info = (BackendInfo *)on;
1302                                         }
1303                                         if ( rc == LDAP_SUCCESS )
1304                                                 syncprov_sendresp( op, &opc, ss->ss_so, e,
1305                                                         sr->s_mode, 0 );
1306
1307                                         if ( e ) {
1308                                                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1309                                                 be_entry_release_r( op, e );
1310                                                 op->o_bd->bd_info = (BackendInfo *)on;
1311                                         }
1312                                         ch_free( sr );
1313                                 }
1314                                 ss->ss_so->s_res = NULL;
1315                                 ss->ss_so->s_restail = NULL;
1316                         }
1317
1318                         /* Turn off the refreshing flag */
1319                         ss->ss_so->s_flags ^= PS_IS_REFRESHING;
1320                         if ( locked )
1321                                 ldap_pvt_thread_mutex_unlock( &ss->ss_so->s_mutex );
1322
1323                         /* Detach this Op from frontend control */
1324                         syncprov_detach_op( op, ss->ss_so );
1325
1326                         return LDAP_SUCCESS;
1327                 }
1328         }
1329
1330         return SLAP_CB_CONTINUE;
1331 }
1332
1333 static int
1334 syncprov_op_search( Operation *op, SlapReply *rs )
1335 {
1336         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1337         syncprov_info_t         *si = (syncprov_info_t *)on->on_bi.bi_private;
1338         slap_callback   *cb;
1339         int gotstate = 0, nochange = 0;
1340         Filter *fand, *fava;
1341         syncops *sop = NULL;
1342         searchstate *ss;
1343         sync_control *srs;
1344
1345         if ( !(op->o_sync_mode & SLAP_SYNC_REFRESH) ) return SLAP_CB_CONTINUE;
1346
1347         if ( op->ors_deref & LDAP_DEREF_SEARCHING ) {
1348                 send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR, "illegal value for derefAliases" );
1349                 return rs->sr_err;
1350         }
1351
1352         srs = op->o_controls[sync_cid];
1353
1354         /* If this is a persistent search, set it up right away */
1355         if ( op->o_sync_mode & SLAP_SYNC_PERSIST ) {
1356                 syncops so = {0};
1357                 fbase_cookie fc;
1358                 opcookie opc;
1359                 slap_callback sc;
1360
1361                 fc.fss = &so;
1362                 fc.fbase = 0;
1363                 so.s_eid = NOID;
1364                 so.s_op = op;
1365                 so.s_flags = PS_IS_REFRESHING;
1366                 /* syncprov_findbase expects to be called as a callback... */
1367                 sc.sc_private = &opc;
1368                 opc.son = on;
1369                 cb = op->o_callback;
1370                 op->o_callback = &sc;
1371                 rs->sr_err = syncprov_findbase( op, &fc );
1372                 op->o_callback = cb;
1373
1374                 if ( rs->sr_err != LDAP_SUCCESS ) {
1375                         send_ldap_result( op, rs );
1376                         return rs->sr_err;
1377                 }
1378                 sop = ch_malloc( sizeof( syncops ));
1379                 *sop = so;
1380                 ldap_pvt_thread_mutex_init( &sop->s_mutex );
1381                 ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
1382                 sop->s_sid = srs->sr_state.sid;
1383                 sop->s_rid = srs->sr_state.rid;
1384                 sop->s_next = si->si_ops;
1385                 sop->s_inuse = 1;
1386                 si->si_ops = sop;
1387                 ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
1388         }
1389
1390         /* If we have a cookie, handle the PRESENT lookups
1391          */
1392         if ( srs->sr_state.ctxcsn ) {
1393                 /* Is the CSN in a valid format? */
1394                 if ( srs->sr_state.ctxcsn->bv_len >= LDAP_LUTIL_CSNSTR_BUFSIZE ) {
1395                         send_ldap_error( op, rs, LDAP_OTHER, "invalid sync cookie" );
1396                         return rs->sr_err;
1397                 }
1398                 /* Is the CSN still present in the database? */
1399                 if ( syncprov_findcsn( op, FIND_CSN ) != LDAP_SUCCESS ) {
1400                         /* No, so a reload is required */
1401 #if 0           /* the consumer doesn't seem to send this hint */
1402                         if ( op->o_sync_rhint == 0 ) {
1403                                 send_ldap_error( op, rs, LDAP_SYNC_REFRESH_REQUIRED, "sync cookie is stale" );
1404                                 return rs->sr_err;
1405                         }
1406 #endif
1407                 } else {
1408                         gotstate = 1;
1409                         /* If just Refreshing and nothing has changed, shortcut it */
1410                         if ( bvmatch( srs->sr_state.ctxcsn, &si->si_ctxcsn )) {
1411                                 nochange = 1;
1412                                 if ( !(op->o_sync_mode & SLAP_SYNC_PERSIST) ) {
1413                                         LDAPControl     *ctrls[2];
1414
1415                                         ctrls[0] = NULL;
1416                                         ctrls[1] = NULL;
1417                                         syncprov_done_ctrl( op, rs, ctrls, 0, 0,
1418                                                 NULL, LDAP_SYNC_REFRESH_DELETES );
1419                                         rs->sr_ctrls = ctrls;
1420                                         rs->sr_err = LDAP_SUCCESS;
1421                                         send_ldap_result( op, rs );
1422                                         return rs->sr_err;
1423                                 }
1424                                 goto shortcut;
1425                         } else 
1426                         /* If context has changed, check for Present UUIDs */
1427                         if ( syncprov_findcsn( op, FIND_PRESENT ) != LDAP_SUCCESS ) {
1428                                 send_ldap_result( op, rs );
1429                                 return rs->sr_err;
1430                         }
1431                 }
1432         }
1433
1434         /* If we didn't get a cookie and we don't know our contextcsn, try to
1435          * find it anyway.
1436          */
1437         if ( !gotstate && !si->si_gotcsn ) {
1438                 struct berval bv = BER_BVC("1"), *old;
1439                 
1440                 old = srs->sr_state.ctxcsn;
1441                 srs->sr_state.ctxcsn = &bv;
1442                 syncprov_findcsn( op, FIND_CSN );
1443                 srs->sr_state.ctxcsn = old;
1444         }
1445
1446         /* Append CSN range to search filter, save original filter
1447          * for persistent search evaluation
1448          */
1449         if ( sop ) {
1450                 sop->s_filterstr= op->ors_filterstr;
1451         }
1452
1453         fand = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
1454         fand->f_choice = LDAP_FILTER_AND;
1455         fand->f_next = NULL;
1456         fava = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
1457         fava->f_choice = LDAP_FILTER_LE;
1458         fava->f_ava = op->o_tmpalloc( sizeof(AttributeAssertion), op->o_tmpmemctx );
1459         fava->f_ava->aa_desc = slap_schema.si_ad_entryCSN;
1460         ber_dupbv_x( &fava->f_ava->aa_value, &si->si_ctxcsn, op->o_tmpmemctx );
1461         fand->f_and = fava;
1462         if ( gotstate ) {
1463                 fava->f_next = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
1464                 fava = fava->f_next;
1465                 fava->f_choice = LDAP_FILTER_GE;
1466                 fava->f_ava = op->o_tmpalloc( sizeof(AttributeAssertion), op->o_tmpmemctx );
1467                 fava->f_ava->aa_desc = slap_schema.si_ad_entryCSN;
1468                 ber_dupbv_x( &fava->f_ava->aa_value, srs->sr_state.ctxcsn, op->o_tmpmemctx );
1469         }
1470         fava->f_next = op->ors_filter;
1471         op->ors_filter = fand;
1472         filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
1473
1474 shortcut:
1475         /* Let our callback add needed info to returned entries */
1476         cb = op->o_tmpcalloc(1, sizeof(slap_callback)+sizeof(searchstate), op->o_tmpmemctx);
1477         ss = (searchstate *)(cb+1);
1478         ss->ss_on = on;
1479         ss->ss_so = sop;
1480         cb->sc_response = syncprov_search_response;
1481         cb->sc_cleanup = syncprov_search_cleanup;
1482         cb->sc_private = ss;
1483         cb->sc_next = op->o_callback;
1484         op->o_callback = cb;
1485
1486         op->o_sync_mode &= SLAP_CONTROL_MASK;
1487
1488         /* If this is a persistent search and no changes were reported during
1489          * the refresh phase, just invoke the response callback to transition
1490          * us into persist phase
1491          */
1492         if ( nochange ) {
1493                 rs->sr_err = LDAP_SUCCESS;
1494                 rs->sr_nentries = 0;
1495                 send_ldap_result( op, rs );
1496                 return rs->sr_err;
1497         }
1498         return SLAP_CB_CONTINUE;
1499 }
1500
1501 static int
1502 syncprov_db_config(
1503         BackendDB       *be,
1504         const char      *fname,
1505         int             lineno,
1506         int             argc,
1507         char    **argv
1508 )
1509 {
1510         slap_overinst           *on = (slap_overinst *)be->bd_info;
1511         syncprov_info_t         *si = (syncprov_info_t *)on->on_bi.bi_private;
1512
1513 #if 0
1514         if ( strcasecmp( argv[ 0 ], "syncprov-checkpoint" ) == 0 ) {
1515                 if ( argc != 3 ) {
1516                         fprintf( stderr, "%s: line %d: wrong number of arguments in "
1517                                 "\"syncprov-checkpoint <ops> <minutes>\"\n", fname, lineno );
1518                         return -1;
1519                 }
1520                 si->si_chkops = atoi( argv[1] );
1521                 si->si_chktime = atoi( argv[2] ) * 60;
1522
1523         } else {
1524                 return SLAP_CONF_UNKNOWN;
1525         }
1526 #endif
1527
1528         return SLAP_CONF_UNKNOWN;
1529 }
1530
1531 static int
1532 syncprov_db_init(
1533         BackendDB *be
1534 )
1535 {
1536         slap_overinst   *on = (slap_overinst *)be->bd_info;
1537         syncprov_info_t *si;
1538
1539         si = ch_calloc(1, sizeof(syncprov_info_t));
1540         on->on_bi.bi_private = si;
1541         ldap_pvt_thread_mutex_init( &si->si_csn_mutex );
1542         ldap_pvt_thread_mutex_init( &si->si_ops_mutex );
1543         si->si_ctxcsn.bv_val = si->si_ctxcsnbuf;
1544
1545         csn_anlist[0].an_desc = slap_schema.si_ad_entryCSN;
1546         csn_anlist[0].an_name = slap_schema.si_ad_entryCSN->ad_cname;
1547
1548         uuid_anlist[0].an_desc = slap_schema.si_ad_entryUUID;
1549         uuid_anlist[0].an_name = slap_schema.si_ad_entryUUID->ad_cname;
1550
1551         return 0;
1552 }
1553
1554 static int
1555 syncprov_db_destroy(
1556         BackendDB *be
1557 )
1558 {
1559         slap_overinst   *on = (slap_overinst *)be->bd_info;
1560         syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
1561
1562         if ( si ) {
1563                 ldap_pvt_thread_mutex_destroy( &si->si_ops_mutex );
1564                 ldap_pvt_thread_mutex_destroy( &si->si_csn_mutex );
1565                 ch_free( si );
1566         }
1567
1568         return 0;
1569 }
1570
1571 static int syncprov_parseCtrl (
1572         Operation *op,
1573         SlapReply *rs,
1574         LDAPControl *ctrl )
1575 {
1576         ber_tag_t tag;
1577         BerElement *ber;
1578         ber_int_t mode;
1579         ber_len_t len;
1580         struct berval cookie = BER_BVNULL;
1581         sync_control *sr;
1582         int rhint = 0;
1583
1584         if ( op->o_sync != SLAP_CONTROL_NONE ) {
1585                 rs->sr_text = "Sync control specified multiple times";
1586                 return LDAP_PROTOCOL_ERROR;
1587         }
1588
1589         if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
1590                 rs->sr_text = "Sync control specified with pagedResults control";
1591                 return LDAP_PROTOCOL_ERROR;
1592         }
1593
1594         if ( ctrl->ldctl_value.bv_len == 0 ) {
1595                 rs->sr_text = "Sync control value is empty (or absent)";
1596                 return LDAP_PROTOCOL_ERROR;
1597         }
1598
1599         /* Parse the control value
1600          *      syncRequestValue ::= SEQUENCE {
1601          *              mode   ENUMERATED {
1602          *                      -- 0 unused
1603          *                      refreshOnly             (1),
1604          *                      -- 2 reserved
1605          *                      refreshAndPersist       (3)
1606          *              },
1607          *              cookie  syncCookie OPTIONAL
1608          *      }
1609          */
1610
1611         ber = ber_init( &ctrl->ldctl_value );
1612         if( ber == NULL ) {
1613                 rs->sr_text = "internal error";
1614                 return LDAP_OTHER;
1615         }
1616
1617         if ( (tag = ber_scanf( ber, "{i" /*}*/, &mode )) == LBER_ERROR ) {
1618                 rs->sr_text = "Sync control : mode decoding error";
1619                 return LDAP_PROTOCOL_ERROR;
1620         }
1621
1622         switch( mode ) {
1623         case LDAP_SYNC_REFRESH_ONLY:
1624                 mode = SLAP_SYNC_REFRESH;
1625                 break;
1626         case LDAP_SYNC_REFRESH_AND_PERSIST:
1627                 mode = SLAP_SYNC_REFRESH_AND_PERSIST;
1628                 break;
1629         default:
1630                 rs->sr_text = "Sync control : unknown update mode";
1631                 return LDAP_PROTOCOL_ERROR;
1632         }
1633
1634         tag = ber_peek_tag( ber, &len );
1635
1636         if ( tag == LDAP_TAG_SYNC_COOKIE ) {
1637                 if (( ber_scanf( ber, /*{*/ "o", &cookie )) == LBER_ERROR ) {
1638                         rs->sr_text = "Sync control : cookie decoding error";
1639                         return LDAP_PROTOCOL_ERROR;
1640                 }
1641         }
1642         if ( tag == LDAP_TAG_RELOAD_HINT ) {
1643                 if (( ber_scanf( ber, /*{*/ "b", &rhint )) == LBER_ERROR ) {
1644                         rs->sr_text = "Sync control : rhint decoding error";
1645                         return LDAP_PROTOCOL_ERROR;
1646                 }
1647         }
1648         if (( ber_scanf( ber, /*{*/ "}")) == LBER_ERROR ) {
1649                         rs->sr_text = "Sync control : decoding error";
1650                         return LDAP_PROTOCOL_ERROR;
1651         }
1652         sr = op->o_tmpcalloc( 1, sizeof(struct sync_control), op->o_tmpmemctx );
1653         sr->sr_rhint = rhint;
1654         if (!BER_BVISNULL(&cookie)) {
1655                 ber_bvarray_add( &sr->sr_state.octet_str, &cookie );
1656                 slap_parse_sync_cookie( &sr->sr_state );
1657         }
1658
1659         op->o_controls[sync_cid] = sr;
1660
1661         (void) ber_free( ber, 1 );
1662
1663         op->o_sync = ctrl->ldctl_iscritical
1664                 ? SLAP_CONTROL_CRITICAL
1665                 : SLAP_CONTROL_NONCRITICAL;
1666
1667         op->o_sync_mode |= mode;        /* o_sync_mode shares o_sync */
1668
1669         return LDAP_SUCCESS;
1670 }
1671
1672 /* This overlay is set up for dynamic loading via moduleload. For static
1673  * configuration, you'll need to arrange for the slap_overinst to be
1674  * initialized and registered by some other function inside slapd.
1675  */
1676
1677 static slap_overinst            syncprov;
1678
1679 int
1680 syncprov_init()
1681 {
1682         int rc;
1683
1684         rc = register_supported_control( LDAP_CONTROL_SYNC,
1685                 SLAP_CTRL_HIDE|SLAP_CTRL_SEARCH, NULL,
1686                 syncprov_parseCtrl, &sync_cid );
1687         if ( rc != LDAP_SUCCESS ) {
1688                 fprintf( stderr, "Failed to register control %d\n", rc );
1689                 return rc;
1690         }
1691
1692         syncprov.on_bi.bi_type = "syncprov";
1693         syncprov.on_bi.bi_db_init = syncprov_db_init;
1694         syncprov.on_bi.bi_db_config = syncprov_db_config;
1695         syncprov.on_bi.bi_db_destroy = syncprov_db_destroy;
1696
1697         syncprov.on_bi.bi_op_abandon = syncprov_op_abandon;
1698         syncprov.on_bi.bi_op_cancel = syncprov_op_abandon;
1699
1700         syncprov.on_bi.bi_op_add = syncprov_op_mod;
1701 #if 0
1702         syncprov.on_bi.bi_op_compare = syncprov_op_compare;
1703 #endif
1704         syncprov.on_bi.bi_op_delete = syncprov_op_mod;
1705         syncprov.on_bi.bi_op_modify = syncprov_op_mod;
1706         syncprov.on_bi.bi_op_modrdn = syncprov_op_mod;
1707         syncprov.on_bi.bi_op_search = syncprov_op_search;
1708         syncprov.on_bi.bi_extended = syncprov_op_extended;
1709
1710 #if 0
1711         syncprov.on_response = syncprov_response;
1712 #endif
1713
1714         return overlay_register( &syncprov );
1715 }
1716
1717 #if SLAPD_OVER_SYNCPROV == SLAPD_MOD_DYNAMIC
1718 int
1719 init_module( int argc, char *argv[] )
1720 {
1721         return syncprov_init();
1722 }
1723 #endif /* SLAPD_OVER_SYNCPROV == SLAPD_MOD_DYNAMIC */
1724
1725 #endif /* defined(SLAPD_OVER_SYNCPROV) */