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 */