1
/* $OpenLDAP: pkg/ldap/libraries/libldap/ldap_sync.c,v 1.2.2.3 2008/02/11 23:26:41 kurt Exp $ */
2
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4
* Copyright 2006-2008 The OpenLDAP Foundation.
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted only as authorized by the OpenLDAP
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>.
16
* This program was originally developed by Pierangelo Masarati
17
* for inclusion in OpenLDAP Software.
21
* Proof-of-concept API that implement the client-side
22
* of the "LDAP Content Sync Operation" (RFC 4533)
31
#ifdef LDAP_SYNC_TRACE
33
ldap_sync_state2str( int state )
36
case LDAP_SYNC_PRESENT:
37
return "LDAP_SYNC_PRESENT";
40
return "LDAP_SYNC_ADD";
42
case LDAP_SYNC_MODIFY:
43
return "LDAP_SYNC_MODIFY";
45
case LDAP_SYNC_DELETE:
46
return "LDAP_SYNC_DELETE";
55
* initialize the persistent search structure
58
ldap_sync_initialize( ldap_sync_t *ls_in )
60
ldap_sync_t *ls = ls_in;
63
ls = ldap_memalloc( sizeof( ldap_sync_t ) );
69
memset( ls, 0, sizeof( ldap_sync_t ) );
72
ls->ls_scope = LDAP_SCOPE_SUBTREE;
79
* destroy the persistent search structure
82
ldap_sync_destroy( ldap_sync_t *ls, int freeit )
86
if ( ls->ls_base != NULL ) {
87
ldap_memfree( ls->ls_base );
91
if ( ls->ls_filter != NULL ) {
92
ldap_memfree( ls->ls_filter );
96
if ( ls->ls_attrs != NULL ) {
99
for ( i = 0; ls->ls_attrs[ i ] != NULL; i++ ) {
100
ldap_memfree( ls->ls_attrs[ i ] );
102
ldap_memfree( ls->ls_attrs );
106
if ( ls->ls_ld != NULL ) {
107
(void)ldap_unbind_ext( ls->ls_ld, NULL, NULL );
108
#ifdef LDAP_SYNC_TRACE
109
fprintf( stderr, "ldap_unbind_ext()\n" );
110
#endif /* LDAP_SYNC_TRACE */
114
if ( ls->ls_cookie.bv_val != NULL ) {
115
ldap_memfree( ls->ls_cookie.bv_val );
116
ls->ls_cookie.bv_val = NULL;
125
* handle the LDAP_RES_SEARCH_ENTRY response
128
ldap_sync_search_entry( ldap_sync_t *ls, LDAPMessage *res )
130
LDAPControl **ctrls = NULL;
131
int rc = LDAP_SUCCESS,
133
BerElement *ber = NULL;
134
struct berval entryUUID = { 0 },
138
ldap_sync_refresh_t phase = ls->ls_refreshPhase;
140
#ifdef LDAP_SYNC_TRACE
141
fprintf( stderr, "\tgot LDAP_RES_SEARCH_ENTRY\n" );
142
#endif /* LDAP_SYNC_TRACE */
144
assert( ls != NULL );
145
assert( res != NULL );
154
* - Sync State Control is "add"
157
/* the control MUST be present */
159
/* extract controls */
160
ldap_get_entry_controls( ls->ls_ld, res, &ctrls );
161
if ( ctrls == NULL ) {
166
/* lookup the sync state control */
167
for ( i = 0; ctrls[ i ] != NULL; i++ ) {
168
if ( strcmp( ctrls[ i ]->ldctl_oid, LDAP_CONTROL_SYNC_STATE ) == 0 ) {
173
/* control must be present; there might be other... */
174
if ( ctrls[ i ] == NULL ) {
180
ber = ber_init( &ctrls[ i ]->ldctl_value );
181
/* scan entryUUID in-place ("m") */
182
ber_scanf( ber, "{em" /*"}"*/, &state, &entryUUID );
183
if ( entryUUID.bv_len == 0 ) {
188
if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
189
/* scan cookie in-place ("m") */
190
ber_scanf( ber, /*"{"*/ "m}", &cookie );
191
if ( cookie.bv_val != NULL ) {
192
ber_bvreplace( &ls->ls_cookie, &cookie );
194
#ifdef LDAP_SYNC_TRACE
195
fprintf( stderr, "\t\tgot cookie=%s\n",
196
cookie.bv_val ? cookie.bv_val : "(null)" );
197
#endif /* LDAP_SYNC_TRACE */
201
case LDAP_SYNC_PRESENT:
202
case LDAP_SYNC_DELETE:
204
case LDAP_SYNC_MODIFY:
205
/* NOTE: ldap_sync_refresh_t is defined
206
* as the corresponding LDAP_SYNC_*
207
* for the 4 above cases */
209
#ifdef LDAP_SYNC_TRACE
210
fprintf( stderr, "\t\tgot syncState=%s\n", ldap_sync_state2str( state ) );
211
#endif /* LDAP_SYNC_TRACE */
216
#ifdef LDAP_SYNC_TRACE
217
fprintf( stderr, "\t\tgot unknown syncState=%d\n", state );
218
#endif /* LDAP_SYNC_TRACE */
222
if ( ls->ls_search_entry ) {
223
rc = ls->ls_search_entry( ls, res, &entryUUID, phase );
231
if ( ctrls != NULL ) {
232
ldap_controls_free( ctrls );
239
* handle the LDAP_RES_SEARCH_REFERENCE response
240
* (to be implemented yet)
243
ldap_sync_search_reference( ldap_sync_t *ls, LDAPMessage *res )
247
#ifdef LDAP_SYNC_TRACE
248
fprintf( stderr, "\tgot LDAP_RES_SEARCH_REFERENCE\n" );
249
#endif /* LDAP_SYNC_TRACE */
251
assert( ls != NULL );
252
assert( res != NULL );
254
if ( ls->ls_search_reference ) {
255
rc = ls->ls_search_reference( ls, res );
262
* handle the LDAP_RES_SEARCH_RESULT response
265
ldap_sync_search_result( ldap_sync_t *ls, LDAPMessage *res )
268
char *matched = NULL,
270
LDAPControl **ctrls = NULL;
272
int refreshDeletes = -1;
274
#ifdef LDAP_SYNC_TRACE
275
fprintf( stderr, "\tgot LDAP_RES_SEARCH_RESULT\n" );
276
#endif /* LDAP_SYNC_TRACE */
278
assert( ls != NULL );
279
assert( res != NULL );
281
/* should not happen in refreshAndPersist... */
282
rc = ldap_parse_result( ls->ls_ld,
283
res, &err, &matched, &msg, NULL, &ctrls, 0 );
284
#ifdef LDAP_SYNC_TRACE
286
"\tldap_parse_result(%d, \"%s\", \"%s\") == %d\n",
288
matched ? matched : "",
291
#endif /* LDAP_SYNC_TRACE */
292
if ( rc == LDAP_SUCCESS ) {
296
ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE;
301
BerElement *ber = NULL;
303
struct berval cookie = { 0 };
305
/* deal with control; then fallthru to handler */
306
if ( ctrls == NULL ) {
311
/* lookup the sync state control */
312
for ( i = 0; ctrls[ i ] != NULL; i++ ) {
313
if ( strcmp( ctrls[ i ]->ldctl_oid,
314
LDAP_CONTROL_SYNC_DONE ) == 0 )
320
/* control must be present; there might be other... */
321
if ( ctrls[ i ] == NULL ) {
327
ber = ber_init( &ctrls[ i ]->ldctl_value );
329
ber_scanf( ber, "{" /*"}"*/);
330
if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
331
ber_scanf( ber, "m", &cookie );
332
if ( cookie.bv_val != NULL ) {
333
ber_bvreplace( &ls->ls_cookie, &cookie );
335
#ifdef LDAP_SYNC_TRACE
336
fprintf( stderr, "\t\tgot cookie=%s\n",
337
cookie.bv_val ? cookie.bv_val : "(null)" );
338
#endif /* LDAP_SYNC_TRACE */
342
if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDELETES ) {
343
ber_scanf( ber, "b", &refreshDeletes );
344
if ( refreshDeletes ) {
349
ber_scanf( ber, /*"{"*/ "}" );
351
/* NOTE: if any goto/return between ber_init() and here
352
* is introduced, don't forget to ber_free() */
355
#ifdef LDAP_SYNC_TRACE
356
fprintf( stderr, "\t\tgot refreshDeletes=%s\n",
357
refreshDeletes ? "TRUE" : "FALSE" );
358
#endif /* LDAP_SYNC_TRACE */
360
/* FIXME: what should we do with the refreshDelete? */
361
switch ( refreshDeletes ) {
363
ls->ls_refreshPhase = LDAP_SYNC_CAPI_PRESENTS;
367
ls->ls_refreshPhase = LDAP_SYNC_CAPI_DELETES;
373
case LDAP_SYNC_REFRESH_REQUIRED:
374
/* TODO: check for Sync Done Control */
375
/* FIXME: perhaps the handler should be called
376
* also in case of failure; we'll deal with this
377
* later when implementing refreshOnly */
378
if ( ls->ls_search_result ) {
379
err = ls->ls_search_result( ls, res, refreshDeletes );
388
if ( matched != NULL ) {
389
ldap_memfree( matched );
396
if ( ctrls != NULL ) {
397
ldap_controls_free( ctrls );
400
ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE;
406
* handle the LDAP_RES_INTERMEDIATE response
409
ldap_sync_search_intermediate( ldap_sync_t *ls, LDAPMessage *res, int *refreshDone )
413
struct berval *retdata = NULL;
414
BerElement *ber = NULL;
418
struct berval cookie;
419
int refreshDeletes = 0;
420
BerVarray syncUUIDs = NULL;
421
ldap_sync_refresh_t phase;
423
#ifdef LDAP_SYNC_TRACE
424
fprintf( stderr, "\tgot LDAP_RES_INTERMEDIATE\n" );
425
#endif /* LDAP_SYNC_TRACE */
427
assert( ls != NULL );
428
assert( res != NULL );
429
assert( refreshDone != NULL );
433
rc = ldap_parse_intermediate( ls->ls_ld, res,
434
&retoid, &retdata, NULL, 0 );
435
#ifdef LDAP_SYNC_TRACE
436
fprintf( stderr, "\t%sldap_parse_intermediate(%s) == %d\n",
437
rc != LDAP_SUCCESS ? "!!! " : "",
438
retoid == NULL ? "\"\"" : retoid,
440
#endif /* LDAP_SYNC_TRACE */
441
/* parsing must be successful, and yield the OID
442
* of the sync info intermediate response */
443
if ( rc != LDAP_SUCCESS ) {
447
if ( retoid == NULL || strcmp( retoid, LDAP_SYNC_INFO ) != 0 ) {
452
/* init ber using the value in the response */
453
ber = ber_init( retdata );
458
syncinfo_tag = ber_peek_tag( ber, &len );
459
switch ( syncinfo_tag ) {
460
case LDAP_TAG_SYNC_NEW_COOKIE:
461
ber_scanf( ber, "tm", &tag, &cookie );
462
if ( cookie.bv_val != NULL ) {
463
ber_bvreplace( &ls->ls_cookie, &cookie );
465
#ifdef LDAP_SYNC_TRACE
466
fprintf( stderr, "\t\tgot cookie=%s\n",
467
cookie.bv_val ? cookie.bv_val : "(null)" );
468
#endif /* LDAP_SYNC_TRACE */
471
case LDAP_TAG_SYNC_REFRESH_DELETE:
472
case LDAP_TAG_SYNC_REFRESH_PRESENT:
473
if ( syncinfo_tag == LDAP_TAG_SYNC_REFRESH_DELETE ) {
474
#ifdef LDAP_SYNC_TRACE
475
fprintf( stderr, "\t\tgot refreshDelete\n" );
476
#endif /* LDAP_SYNC_TRACE */
477
switch ( ls->ls_refreshPhase ) {
478
case LDAP_SYNC_CAPI_NONE:
479
case LDAP_SYNC_CAPI_PRESENTS:
480
ls->ls_refreshPhase = LDAP_SYNC_CAPI_DELETES;
484
/* TODO: impossible; handle */
490
#ifdef LDAP_SYNC_TRACE
491
fprintf( stderr, "\t\tgot refreshPresent\n" );
492
#endif /* LDAP_SYNC_TRACE */
493
switch ( ls->ls_refreshPhase ) {
494
case LDAP_SYNC_CAPI_NONE:
495
ls->ls_refreshPhase = LDAP_SYNC_CAPI_PRESENTS;
499
/* TODO: impossible; handle */
505
ber_scanf( ber, "t{" /*"}"*/, &tag );
506
if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
507
ber_scanf( ber, "m", &cookie );
508
if ( cookie.bv_val != NULL ) {
509
ber_bvreplace( &ls->ls_cookie, &cookie );
511
#ifdef LDAP_SYNC_TRACE
512
fprintf( stderr, "\t\tgot cookie=%s\n",
513
cookie.bv_val ? cookie.bv_val : "(null)" );
514
#endif /* LDAP_SYNC_TRACE */
518
if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDONE ) {
519
ber_scanf( ber, "b", refreshDone );
522
#ifdef LDAP_SYNC_TRACE
523
fprintf( stderr, "\t\tgot refreshDone=%s\n",
524
*refreshDone ? "TRUE" : "FALSE" );
525
#endif /* LDAP_SYNC_TRACE */
527
ber_scanf( ber, /*"{"*/ "}" );
529
if ( *refreshDone ) {
530
ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE;
533
if ( ls->ls_intermediate ) {
534
ls->ls_intermediate( ls, res, NULL, ls->ls_refreshPhase );
539
case LDAP_TAG_SYNC_ID_SET:
540
#ifdef LDAP_SYNC_TRACE
541
fprintf( stderr, "\t\tgot syncIdSet\n" );
542
#endif /* LDAP_SYNC_TRACE */
543
ber_scanf( ber, "t{" /*"}"*/, &tag );
544
if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
545
ber_scanf( ber, "m", &cookie );
546
if ( cookie.bv_val != NULL ) {
547
ber_bvreplace( &ls->ls_cookie, &cookie );
549
#ifdef LDAP_SYNC_TRACE
550
fprintf( stderr, "\t\tgot cookie=%s\n",
551
cookie.bv_val ? cookie.bv_val : "(null)" );
552
#endif /* LDAP_SYNC_TRACE */
555
if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDELETES ) {
556
ber_scanf( ber, "b", &refreshDeletes );
559
ber_scanf( ber, "[W]", &syncUUIDs );
560
ber_scanf( ber, /*"{"*/ "}" );
561
if ( syncUUIDs == NULL ) {
566
#ifdef LDAP_SYNC_TRACE
570
fprintf( stderr, "\t\tgot refreshDeletes=%s\n",
571
refreshDeletes ? "TRUE" : "FALSE" );
572
for ( i = 0; syncUUIDs[ i ].bv_val != NULL; i++ ) {
574
fprintf( stderr, "\t\t%s\n",
575
lutil_uuidstr_from_normalized(
576
syncUUIDs[ i ].bv_val, syncUUIDs[ i ].bv_len,
577
buf, sizeof( buf ) ) );
580
#endif /* LDAP_SYNC_TRACE */
582
if ( refreshDeletes ) {
583
phase = LDAP_SYNC_CAPI_DELETES_IDSET;
586
phase = LDAP_SYNC_CAPI_PRESENTS_IDSET;
589
/* FIXME: should touch ls->ls_refreshPhase? */
590
if ( ls->ls_intermediate ) {
591
ls->ls_intermediate( ls, res, syncUUIDs, phase );
594
ber_bvarray_free( syncUUIDs );
598
#ifdef LDAP_SYNC_TRACE
599
fprintf( stderr, "\t\tunknown tag!\n" );
600
#endif /* LDAP_SYNC_TRACE */
609
if ( retoid != NULL ) {
610
ldap_memfree( retoid );
613
if ( retdata != NULL ) {
614
ber_bvfree( retdata );
621
* initialize the sync
624
ldap_sync_init( ldap_sync_t *ls, int mode )
626
LDAPControl ctrl = { 0 },
628
BerElement *ber = NULL;
630
struct timeval tv = { 0 },
632
LDAPMessage *res = NULL;
634
#ifdef LDAP_SYNC_TRACE
635
fprintf( stderr, "ldap_sync_init(%s)...\n",
636
mode == LDAP_SYNC_REFRESH_AND_PERSIST ?
637
"LDAP_SYNC_REFRESH_AND_PERSIST" :
638
( mode == LDAP_SYNC_REFRESH_ONLY ?
639
"LDAP_SYNC_REFRESH_ONLY" : "unknown" ) );
640
#endif /* LDAP_SYNC_TRACE */
642
assert( ls != NULL );
643
assert( ls->ls_ld != NULL );
645
/* support both refreshOnly and refreshAndPersist */
647
case LDAP_SYNC_REFRESH_AND_PERSIST:
648
case LDAP_SYNC_REFRESH_ONLY:
652
fprintf( stderr, "ldap_sync_init: unknown mode=%d\n", mode );
653
return LDAP_PARAM_ERROR;
656
/* check consistency of cookie and reloadHint at initial refresh */
657
if ( ls->ls_cookie.bv_val == NULL && ls->ls_reloadHint != 0 ) {
658
fprintf( stderr, "ldap_sync_init: inconsistent cookie/rhint\n" );
659
return LDAP_PARAM_ERROR;
665
/* prepare the Sync Request control */
666
ber = ber_alloc_t( LBER_USE_DER );
667
#ifdef LDAP_SYNC_TRACE
668
fprintf( stderr, "%sber_alloc_t() %s= NULL\n",
669
ber == NULL ? "!!! " : "",
670
ber == NULL ? "=" : "!" );
671
#endif /* LDAP_SYNC_TRACE */
677
ls->ls_refreshPhase = LDAP_SYNC_CAPI_NONE;
679
if ( ls->ls_cookie.bv_val != NULL ) {
680
ber_printf( ber, "{eOb}", mode,
681
&ls->ls_cookie, ls->ls_reloadHint );
684
ber_printf( ber, "{eb}", mode, ls->ls_reloadHint );
687
rc = ber_flatten2( ber, &ctrl.ldctl_value, 0 );
688
#ifdef LDAP_SYNC_TRACE
690
"%sber_flatten2() == %d\n",
693
#endif /* LDAP_SYNC_TRACE */
694
if ( rc == LBER_ERROR ) {
699
/* make the control critical, as we cannot proceed without */
700
ctrl.ldctl_oid = LDAP_CONTROL_SYNC;
701
ctrl.ldctl_iscritical = 1;
704
if ( ls->ls_timelimit ) {
705
tv.tv_sec = ls->ls_timelimit;
709
/* actually run the search */
710
rc = ldap_search_ext( ls->ls_ld,
711
ls->ls_base, ls->ls_scope, ls->ls_filter,
712
ls->ls_attrs, 0, ctrls, NULL,
713
tvp, ls->ls_sizelimit, &ls->ls_msgid );
714
#ifdef LDAP_SYNC_TRACE
716
"%sldap_search_ext(\"%s\", %d, \"%s\") == %d\n",
718
ls->ls_base, ls->ls_scope, ls->ls_filter, rc );
719
#endif /* LDAP_SYNC_TRACE */
720
if ( rc != LDAP_SUCCESS ) {
724
/* initial content/content update phase */
726
LDAPMessage *msg = NULL;
728
/* NOTE: this very short timeout is just to let
729
* ldap_result() yield long enough to get something */
733
rc = ldap_result( ls->ls_ld, ls->ls_msgid,
734
LDAP_MSG_RECEIVED, &tv, &res );
735
#ifdef LDAP_SYNC_TRACE
737
"\t%sldap_result(%d) == %d\n",
738
rc == -1 ? "!!! " : "",
740
#endif /* LDAP_SYNC_TRACE */
746
* TODO: can do something else in the meanwhile)
755
for ( msg = ldap_first_message( ls->ls_ld, res );
757
msg = ldap_next_message( ls->ls_ld, msg ) )
761
switch ( ldap_msgtype( msg ) ) {
762
case LDAP_RES_SEARCH_ENTRY:
763
rc = ldap_sync_search_entry( ls, res );
766
case LDAP_RES_SEARCH_REFERENCE:
767
rc = ldap_sync_search_reference( ls, res );
770
case LDAP_RES_SEARCH_RESULT:
771
rc = ldap_sync_search_result( ls, res );
774
case LDAP_RES_INTERMEDIATE:
775
rc = ldap_sync_search_intermediate( ls, res, &refreshDone );
776
if ( rc != LDAP_SUCCESS || refreshDone ) {
782
#ifdef LDAP_SYNC_TRACE
783
fprintf( stderr, "\tgot something unexpected...\n" );
784
#endif /* LDAP_SYNC_TRACE */
810
* initialize the refreshOnly sync
813
ldap_sync_init_refresh_only( ldap_sync_t *ls )
815
return ldap_sync_init( ls, LDAP_SYNC_REFRESH_ONLY );
819
* initialize the refreshAndPersist sync
822
ldap_sync_init_refresh_and_persist( ldap_sync_t *ls )
824
return ldap_sync_init( ls, LDAP_SYNC_REFRESH_AND_PERSIST );
828
* poll for new responses
831
ldap_sync_poll( ldap_sync_t *ls )
835
LDAPMessage *res = NULL,
839
#ifdef LDAP_SYNC_TRACE
840
fprintf( stderr, "ldap_sync_poll...\n" );
841
#endif /* LDAP_SYNC_TRACE */
843
assert( ls != NULL );
844
assert( ls->ls_ld != NULL );
846
if ( ls->ls_timeout != -1 ) {
847
tv.tv_sec = ls->ls_timeout;
852
rc = ldap_result( ls->ls_ld, ls->ls_msgid,
853
LDAP_MSG_RECEIVED, tvp, &res );
858
for ( msg = ldap_first_message( ls->ls_ld, res );
860
msg = ldap_next_message( ls->ls_ld, msg ) )
864
switch ( ldap_msgtype( msg ) ) {
865
case LDAP_RES_SEARCH_ENTRY:
866
rc = ldap_sync_search_entry( ls, res );
869
case LDAP_RES_SEARCH_REFERENCE:
870
rc = ldap_sync_search_reference( ls, res );
873
case LDAP_RES_SEARCH_RESULT:
874
rc = ldap_sync_search_result( ls, res );
877
case LDAP_RES_INTERMEDIATE:
878
rc = ldap_sync_search_intermediate( ls, res, &refreshDone );
879
if ( rc != LDAP_SUCCESS || refreshDone ) {
885
#ifdef LDAP_SYNC_TRACE
886
fprintf( stderr, "\tgot something unexpected...\n" );
887
#endif /* LDAP_SYNC_TRACE */