1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3 *
4 * Copyright 2006-2021 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 program was originally developed by Pierangelo Masarati
17 * for inclusion in OpenLDAP Software.
18 */
19
20 /*
21 * Proof-of-concept API that implement the client-side
22 * of the "LDAP Content Sync Operation" (RFC 4533)
23 */
24
25 #include "portable.h"
26
27 #include <ac/time.h>
28
29 #include "ldap-int.h"
30
31 #ifdef LDAP_SYNC_TRACE
32 static const char *
ldap_sync_state2str(int state)33 ldap_sync_state2str( int state )
34 {
35 switch ( state ) {
36 case LDAP_SYNC_PRESENT:
37 return "LDAP_SYNC_PRESENT";
38
39 case LDAP_SYNC_ADD:
40 return "LDAP_SYNC_ADD";
41
42 case LDAP_SYNC_MODIFY:
43 return "LDAP_SYNC_MODIFY";
44
45 case LDAP_SYNC_DELETE:
46 return "LDAP_SYNC_DELETE";
47
48 default:
49 return "(unknown)";
50 }
51 }
52 #endif
53
54 /*
55 * initialize the persistent search structure
56 */
57 ldap_sync_t *
ldap_sync_initialize(ldap_sync_t * ls_in)58 ldap_sync_initialize( ldap_sync_t *ls_in )
59 {
60 ldap_sync_t *ls = ls_in;
61
62 if ( ls == NULL ) {
63 ls = ldap_memalloc( sizeof( ldap_sync_t ) );
64 if ( ls == NULL ) {
65 return NULL;
66 }
67 }
68 memset( ls, 0, sizeof( ldap_sync_t ) );
69
70 ls->ls_scope = LDAP_SCOPE_SUBTREE;
71 ls->ls_timeout = -1;
72
73 return ls;
74 }
75
76 /*
77 * destroy the persistent search structure
78 */
79 void
ldap_sync_destroy(ldap_sync_t * ls,int freeit)80 ldap_sync_destroy( ldap_sync_t *ls, int freeit )
81 {
82 assert( ls != NULL );
83
84 if ( ls->ls_base != NULL ) {
85 ldap_memfree( ls->ls_base );
86 ls->ls_base = NULL;
87 }
88
89 if ( ls->ls_filter != NULL ) {
90 ldap_memfree( ls->ls_filter );
91 ls->ls_filter = NULL;
92 }
93
94 if ( ls->ls_attrs != NULL ) {
95 int i;
96
97 for ( i = 0; ls->ls_attrs[ i ] != NULL; i++ ) {
98 ldap_memfree( ls->ls_attrs[ i ] );
99 }
100 ldap_memfree( ls->ls_attrs );
101 ls->ls_attrs = NULL;
102 }
103
104 if ( ls->ls_ld != NULL ) {
105 (void)ldap_unbind_ext( ls->ls_ld, NULL, NULL );
106 #ifdef LDAP_SYNC_TRACE
107 fprintf( stderr, "ldap_unbind_ext()\n" );
108 #endif /* LDAP_SYNC_TRACE */
109 ls->ls_ld = NULL;
110 }
111
112 if ( ls->ls_cookie.bv_val != NULL ) {
113 ldap_memfree( ls->ls_cookie.bv_val );
114 ls->ls_cookie.bv_val = NULL;
115 }
116
117 if ( freeit ) {
118 ldap_memfree( ls );
119 }
120 }
121
122 /*
123 * handle the LDAP_RES_SEARCH_ENTRY response
124 */
125 static int
ldap_sync_search_entry(ldap_sync_t * ls,LDAPMessage * res)126 ldap_sync_search_entry( ldap_sync_t *ls, LDAPMessage *res )
127 {
128 LDAPControl **ctrls = NULL;
129 int rc = LDAP_OTHER,
130 i;
131 BerElement *ber = NULL;
132 struct berval entryUUID = { 0 },
133 cookie = { 0 };
134 int state = -1;
135 ber_len_t len;
136 ldap_sync_refresh_t phase;
137
138 #ifdef LDAP_SYNC_TRACE
139 fprintf( stderr, "\tgot LDAP_RES_SEARCH_ENTRY\n" );
140 #endif /* LDAP_SYNC_TRACE */
141
142 assert( ls != NULL );
143 assert( res != NULL );
144
145 phase = ls->ls_refreshPhase;
146
147 /* OK */
148
149 /* extract:
150 * - data
151 * - entryUUID
152 *
153 * check that:
154 * - Sync State Control is "add"
155 */
156
157 /* the control MUST be present */
158
159 /* extract controls */
160 ldap_get_entry_controls( ls->ls_ld, res, &ctrls );
161 if ( ctrls == NULL ) {
162 goto done;
163 }
164
165 /* lookup the sync state control */
166 for ( i = 0; ctrls[ i ] != NULL; i++ ) {
167 if ( strcmp( ctrls[ i ]->ldctl_oid, LDAP_CONTROL_SYNC_STATE ) == 0 ) {
168 break;
169 }
170 }
171
172 /* control must be present; there might be other... */
173 if ( ctrls[ i ] == NULL ) {
174 goto done;
175 }
176
177 /* extract data */
178 ber = ber_init( &ctrls[ i ]->ldctl_value );
179 if ( ber == NULL ) {
180 goto done;
181 }
182 /* scan entryUUID in-place ("m") */
183 if ( ber_scanf( ber, "{em" /*"}"*/, &state, &entryUUID ) == LBER_ERROR
184 || entryUUID.bv_len == 0 )
185 {
186 goto done;
187 }
188
189 if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
190 /* scan cookie in-place ("m") */
191 if ( ber_scanf( ber, /*"{"*/ "m}", &cookie ) == LBER_ERROR ) {
192 goto done;
193 }
194 if ( cookie.bv_val != NULL ) {
195 ber_bvreplace( &ls->ls_cookie, &cookie );
196 }
197 #ifdef LDAP_SYNC_TRACE
198 fprintf( stderr, "\t\tgot cookie=%s\n",
199 cookie.bv_val ? cookie.bv_val : "(null)" );
200 #endif /* LDAP_SYNC_TRACE */
201 }
202
203 switch ( state ) {
204 case LDAP_SYNC_PRESENT:
205 case LDAP_SYNC_DELETE:
206 case LDAP_SYNC_ADD:
207 case LDAP_SYNC_MODIFY:
208 /* NOTE: ldap_sync_refresh_t is defined
209 * as the corresponding LDAP_SYNC_*
210 * for the 4 above cases */
211 phase = state;
212 #ifdef LDAP_SYNC_TRACE
213 fprintf( stderr, "\t\tgot syncState=%s\n", ldap_sync_state2str( state ) );
214 #endif /* LDAP_SYNC_TRACE */
215 break;
216
217 default:
218 #ifdef LDAP_SYNC_TRACE
219 fprintf( stderr, "\t\tgot unknown syncState=%d\n", state );
220 #endif /* LDAP_SYNC_TRACE */
221 goto done;
222 }
223
224 rc = ls->ls_search_entry
225 ? ls->ls_search_entry( ls, res, &entryUUID, phase )
226 : LDAP_SUCCESS;
227
228 done:;
229 if ( ber != NULL ) {
230 ber_free( ber, 1 );
231 }
232
233 if ( ctrls != NULL ) {
234 ldap_controls_free( ctrls );
235 }
236
237 return rc;
238 }
239
240 /*
241 * handle the LDAP_RES_SEARCH_REFERENCE response
242 * (to be implemented yet)
243 */
244 static int
ldap_sync_search_reference(ldap_sync_t * ls,LDAPMessage * res)245 ldap_sync_search_reference( ldap_sync_t *ls, LDAPMessage *res )
246 {
247 int rc = 0;
248
249 #ifdef LDAP_SYNC_TRACE
250 fprintf( stderr, "\tgot LDAP_RES_SEARCH_REFERENCE\n" );
251 #endif /* LDAP_SYNC_TRACE */
252
253 assert( ls != NULL );
254 assert( res != NULL );
255
256 if ( ls->ls_search_reference ) {
257 rc = ls->ls_search_reference( ls, res );
258 }
259
260 return rc;
261 }
262
263 /*
264 * handle the LDAP_RES_SEARCH_RESULT response
265 */
266 static int
ldap_sync_search_result(ldap_sync_t * ls,LDAPMessage * res)267 ldap_sync_search_result( ldap_sync_t *ls, LDAPMessage *res )
268 {
269 int err;
270 char *matched = NULL,
271 *msg = NULL;
272 LDAPControl **ctrls = NULL;
273 int rc;
274 int refreshDeletes = -1;
275
276 #ifdef LDAP_SYNC_TRACE
277 fprintf( stderr, "\tgot LDAP_RES_SEARCH_RESULT\n" );
278 #endif /* LDAP_SYNC_TRACE */
279
280 assert( ls != NULL );
281 assert( res != NULL );
282
283 /* should not happen in refreshAndPersist... */
284 rc = ldap_parse_result( ls->ls_ld,
285 res, &err, &matched, &msg, NULL, &ctrls, 0 );
286 #ifdef LDAP_SYNC_TRACE
287 fprintf( stderr,
288 "\tldap_parse_result(%d, \"%s\", \"%s\") == %d\n",
289 err,
290 matched ? matched : "",
291 msg ? msg : "",
292 rc );
293 #endif /* LDAP_SYNC_TRACE */
294 if ( rc == LDAP_SUCCESS ) {
295 rc = err;
296 }
297
298 ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE;
299
300 switch ( rc ) {
301 case LDAP_SUCCESS: {
302 int i;
303 BerElement *ber = NULL;
304 ber_len_t len;
305 struct berval cookie = { 0 };
306
307 rc = LDAP_OTHER;
308
309 /* deal with control; then fallthru to handler */
310 if ( ctrls == NULL ) {
311 goto done;
312 }
313
314 /* lookup the sync state control */
315 for ( i = 0; ctrls[ i ] != NULL; i++ ) {
316 if ( strcmp( ctrls[ i ]->ldctl_oid,
317 LDAP_CONTROL_SYNC_DONE ) == 0 )
318 {
319 break;
320 }
321 }
322
323 /* control must be present; there might be other... */
324 if ( ctrls[ i ] == NULL ) {
325 goto done;
326 }
327
328 /* extract data */
329 ber = ber_init( &ctrls[ i ]->ldctl_value );
330 if ( ber == NULL ) {
331 goto done;
332 }
333
334 if ( ber_scanf( ber, "{" /*"}"*/) == LBER_ERROR ) {
335 goto ber_done;
336 }
337 if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
338 if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) {
339 goto ber_done;
340 }
341 if ( cookie.bv_val != NULL ) {
342 ber_bvreplace( &ls->ls_cookie, &cookie );
343 }
344 #ifdef LDAP_SYNC_TRACE
345 fprintf( stderr, "\t\tgot cookie=%s\n",
346 cookie.bv_val ? cookie.bv_val : "(null)" );
347 #endif /* LDAP_SYNC_TRACE */
348 }
349
350 refreshDeletes = 0;
351 if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDELETES ) {
352 if ( ber_scanf( ber, "b", &refreshDeletes ) == LBER_ERROR ) {
353 goto ber_done;
354 }
355 if ( refreshDeletes ) {
356 refreshDeletes = 1;
357 }
358 }
359
360 if ( ber_scanf( ber, /*"{"*/ "}" ) != LBER_ERROR ) {
361 rc = LDAP_SUCCESS;
362 }
363
364 ber_done:;
365 ber_free( ber, 1 );
366 if ( rc != LDAP_SUCCESS ) {
367 break;
368 }
369
370 #ifdef LDAP_SYNC_TRACE
371 fprintf( stderr, "\t\tgot refreshDeletes=%s\n",
372 refreshDeletes ? "TRUE" : "FALSE" );
373 #endif /* LDAP_SYNC_TRACE */
374
375 /* FIXME: what should we do with the refreshDelete? */
376 switch ( refreshDeletes ) {
377 case 0:
378 ls->ls_refreshPhase = LDAP_SYNC_CAPI_PRESENTS;
379 break;
380
381 default:
382 ls->ls_refreshPhase = LDAP_SYNC_CAPI_DELETES;
383 break;
384 }
385
386 } /* fallthru */
387
388 case LDAP_SYNC_REFRESH_REQUIRED:
389 /* TODO: check for Sync Done Control */
390 /* FIXME: perhaps the handler should be called
391 * also in case of failure; we'll deal with this
392 * later when implementing refreshOnly */
393 if ( ls->ls_search_result ) {
394 err = ls->ls_search_result( ls, res, refreshDeletes );
395 }
396 break;
397 }
398
399 done:;
400 if ( matched != NULL ) {
401 ldap_memfree( matched );
402 }
403
404 if ( msg != NULL ) {
405 ldap_memfree( msg );
406 }
407
408 if ( ctrls != NULL ) {
409 ldap_controls_free( ctrls );
410 }
411
412 ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE;
413
414 return rc;
415 }
416
417 /*
418 * handle the LDAP_RES_INTERMEDIATE response
419 */
420 static int
ldap_sync_search_intermediate(ldap_sync_t * ls,LDAPMessage * res,int * refreshDone)421 ldap_sync_search_intermediate( ldap_sync_t *ls, LDAPMessage *res, int *refreshDone )
422 {
423 int rc;
424 char *retoid = NULL;
425 struct berval *retdata = NULL;
426 BerElement *ber = NULL;
427 ber_len_t len;
428 ber_tag_t syncinfo_tag;
429 struct berval cookie;
430 int refreshDeletes = 0;
431 BerVarray syncUUIDs = NULL;
432 ldap_sync_refresh_t phase;
433
434 #ifdef LDAP_SYNC_TRACE
435 fprintf( stderr, "\tgot LDAP_RES_INTERMEDIATE\n" );
436 #endif /* LDAP_SYNC_TRACE */
437
438 assert( ls != NULL );
439 assert( res != NULL );
440 assert( refreshDone != NULL );
441
442 *refreshDone = 0;
443
444 rc = ldap_parse_intermediate( ls->ls_ld, res,
445 &retoid, &retdata, NULL, 0 );
446 #ifdef LDAP_SYNC_TRACE
447 fprintf( stderr, "\t%sldap_parse_intermediate(%s) == %d\n",
448 rc != LDAP_SUCCESS ? "!!! " : "",
449 retoid == NULL ? "\"\"" : retoid,
450 rc );
451 #endif /* LDAP_SYNC_TRACE */
452 /* parsing must be successful, and yield the OID
453 * of the sync info intermediate response */
454 if ( rc != LDAP_SUCCESS ) {
455 goto done;
456 }
457
458 rc = LDAP_OTHER;
459
460 if ( retoid == NULL || strcmp( retoid, LDAP_SYNC_INFO ) != 0 ) {
461 goto done;
462 }
463
464 /* init ber using the value in the response */
465 ber = ber_init( retdata );
466 if ( ber == NULL ) {
467 goto done;
468 }
469
470 syncinfo_tag = ber_peek_tag( ber, &len );
471 switch ( syncinfo_tag ) {
472 case LDAP_TAG_SYNC_NEW_COOKIE:
473 if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) {
474 goto done;
475 }
476 if ( cookie.bv_val != NULL ) {
477 ber_bvreplace( &ls->ls_cookie, &cookie );
478 }
479 #ifdef LDAP_SYNC_TRACE
480 fprintf( stderr, "\t\tgot cookie=%s\n",
481 cookie.bv_val ? cookie.bv_val : "(null)" );
482 #endif /* LDAP_SYNC_TRACE */
483 break;
484
485 case LDAP_TAG_SYNC_REFRESH_DELETE:
486 case LDAP_TAG_SYNC_REFRESH_PRESENT:
487 if ( syncinfo_tag == LDAP_TAG_SYNC_REFRESH_DELETE ) {
488 #ifdef LDAP_SYNC_TRACE
489 fprintf( stderr, "\t\tgot refreshDelete\n" );
490 #endif /* LDAP_SYNC_TRACE */
491 switch ( ls->ls_refreshPhase ) {
492 case LDAP_SYNC_CAPI_NONE:
493 case LDAP_SYNC_CAPI_PRESENTS:
494 ls->ls_refreshPhase = LDAP_SYNC_CAPI_DELETES;
495 break;
496
497 default:
498 /* TODO: impossible; handle */
499 goto done;
500 }
501
502 } else {
503 #ifdef LDAP_SYNC_TRACE
504 fprintf( stderr, "\t\tgot refreshPresent\n" );
505 #endif /* LDAP_SYNC_TRACE */
506 switch ( ls->ls_refreshPhase ) {
507 case LDAP_SYNC_CAPI_NONE:
508 ls->ls_refreshPhase = LDAP_SYNC_CAPI_PRESENTS;
509 break;
510
511 default:
512 /* TODO: impossible; handle */
513 goto done;
514 }
515 }
516
517 if ( ber_scanf( ber, "{" /*"}"*/ ) == LBER_ERROR ) {
518 goto done;
519 }
520 if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
521 if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) {
522 goto done;
523 }
524 if ( cookie.bv_val != NULL ) {
525 ber_bvreplace( &ls->ls_cookie, &cookie );
526 }
527 #ifdef LDAP_SYNC_TRACE
528 fprintf( stderr, "\t\tgot cookie=%s\n",
529 cookie.bv_val ? cookie.bv_val : "(null)" );
530 #endif /* LDAP_SYNC_TRACE */
531 }
532
533 *refreshDone = 1;
534 if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDONE ) {
535 if ( ber_scanf( ber, "b", refreshDone ) == LBER_ERROR ) {
536 goto done;
537 }
538 }
539
540 #ifdef LDAP_SYNC_TRACE
541 fprintf( stderr, "\t\tgot refreshDone=%s\n",
542 *refreshDone ? "TRUE" : "FALSE" );
543 #endif /* LDAP_SYNC_TRACE */
544
545 if ( ber_scanf( ber, /*"{"*/ "}" ) == LBER_ERROR ) {
546 goto done;
547 }
548
549 if ( *refreshDone ) {
550 ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE;
551 }
552
553 if ( ls->ls_intermediate ) {
554 ls->ls_intermediate( ls, res, NULL, ls->ls_refreshPhase );
555 }
556
557 break;
558
559 case LDAP_TAG_SYNC_ID_SET:
560 #ifdef LDAP_SYNC_TRACE
561 fprintf( stderr, "\t\tgot syncIdSet\n" );
562 #endif /* LDAP_SYNC_TRACE */
563 if ( ber_scanf( ber, "{" /*"}"*/ ) == LBER_ERROR ) {
564 goto done;
565 }
566 if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
567 if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) {
568 goto done;
569 }
570 if ( cookie.bv_val != NULL ) {
571 ber_bvreplace( &ls->ls_cookie, &cookie );
572 }
573 #ifdef LDAP_SYNC_TRACE
574 fprintf( stderr, "\t\tgot cookie=%s\n",
575 cookie.bv_val ? cookie.bv_val : "(null)" );
576 #endif /* LDAP_SYNC_TRACE */
577 }
578
579 if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDELETES ) {
580 if ( ber_scanf( ber, "b", &refreshDeletes ) == LBER_ERROR ) {
581 goto done;
582 }
583 }
584
585 if ( ber_scanf( ber, /*"{"*/ "[W]}", &syncUUIDs ) == LBER_ERROR
586 || syncUUIDs == NULL )
587 {
588 goto done;
589 }
590
591 #ifdef LDAP_SYNC_TRACE
592 {
593 int i;
594
595 fprintf( stderr, "\t\tgot refreshDeletes=%s\n",
596 refreshDeletes ? "TRUE" : "FALSE" );
597 for ( i = 0; syncUUIDs[ i ].bv_val != NULL; i++ ) {
598 char buf[ BUFSIZ ];
599 fprintf( stderr, "\t\t%s\n",
600 lutil_uuidstr_from_normalized(
601 syncUUIDs[ i ].bv_val, syncUUIDs[ i ].bv_len,
602 buf, sizeof( buf ) ) );
603 }
604 }
605 #endif /* LDAP_SYNC_TRACE */
606
607 if ( refreshDeletes ) {
608 phase = LDAP_SYNC_CAPI_DELETES_IDSET;
609
610 } else {
611 phase = LDAP_SYNC_CAPI_PRESENTS_IDSET;
612 }
613
614 /* FIXME: should touch ls->ls_refreshPhase? */
615 if ( ls->ls_intermediate ) {
616 ls->ls_intermediate( ls, res, syncUUIDs, phase );
617 }
618
619 ber_bvarray_free( syncUUIDs );
620 break;
621
622 default:
623 #ifdef LDAP_SYNC_TRACE
624 fprintf( stderr, "\t\tunknown tag!\n" );
625 #endif /* LDAP_SYNC_TRACE */
626 goto done;
627 }
628
629 rc = LDAP_SUCCESS;
630
631 done:;
632 if ( ber != NULL ) {
633 ber_free( ber, 1 );
634 }
635
636 if ( retoid != NULL ) {
637 ldap_memfree( retoid );
638 }
639
640 if ( retdata != NULL ) {
641 ber_bvfree( retdata );
642 }
643
644 return rc;
645 }
646
647 /*
648 * initialize the sync
649 */
650 int
ldap_sync_init(ldap_sync_t * ls,int mode)651 ldap_sync_init( ldap_sync_t *ls, int mode )
652 {
653 LDAPControl ctrl = { 0 },
654 *ctrls[ 2 ];
655 BerElement *ber = NULL;
656 int rc;
657 struct timeval tv = { 0 },
658 *tvp = NULL;
659 LDAPMessage *res = NULL;
660
661 #ifdef LDAP_SYNC_TRACE
662 fprintf( stderr, "ldap_sync_init(%s)...\n",
663 mode == LDAP_SYNC_REFRESH_AND_PERSIST ?
664 "LDAP_SYNC_REFRESH_AND_PERSIST" :
665 ( mode == LDAP_SYNC_REFRESH_ONLY ?
666 "LDAP_SYNC_REFRESH_ONLY" : "unknown" ) );
667 #endif /* LDAP_SYNC_TRACE */
668
669 assert( ls != NULL );
670 assert( ls->ls_ld != NULL );
671
672 /* support both refreshOnly and refreshAndPersist */
673 switch ( mode ) {
674 case LDAP_SYNC_REFRESH_AND_PERSIST:
675 case LDAP_SYNC_REFRESH_ONLY:
676 break;
677
678 default:
679 fprintf( stderr, "ldap_sync_init: unknown mode=%d\n", mode );
680 return LDAP_PARAM_ERROR;
681 }
682
683 /* check consistency of cookie and reloadHint at initial refresh */
684 if ( ls->ls_cookie.bv_val == NULL && ls->ls_reloadHint != 0 ) {
685 fprintf( stderr, "ldap_sync_init: inconsistent cookie/rhint\n" );
686 return LDAP_PARAM_ERROR;
687 }
688
689 ctrls[ 0 ] = &ctrl;
690 ctrls[ 1 ] = NULL;
691
692 /* prepare the Sync Request control */
693 ber = ber_alloc_t( LBER_USE_DER );
694 #ifdef LDAP_SYNC_TRACE
695 fprintf( stderr, "%sber_alloc_t() %s= NULL\n",
696 ber == NULL ? "!!! " : "",
697 ber == NULL ? "=" : "!" );
698 #endif /* LDAP_SYNC_TRACE */
699 if ( ber == NULL ) {
700 rc = LDAP_NO_MEMORY;
701 goto done;
702 }
703
704 ls->ls_refreshPhase = LDAP_SYNC_CAPI_NONE;
705
706 if ( ls->ls_cookie.bv_val != NULL ) {
707 ber_printf( ber, "{eOb}", mode,
708 &ls->ls_cookie, ls->ls_reloadHint );
709
710 } else {
711 ber_printf( ber, "{eb}", mode, ls->ls_reloadHint );
712 }
713
714 rc = ber_flatten2( ber, &ctrl.ldctl_value, 0 );
715 #ifdef LDAP_SYNC_TRACE
716 fprintf( stderr,
717 "%sber_flatten2() == %d\n",
718 rc ? "!!! " : "",
719 rc );
720 #endif /* LDAP_SYNC_TRACE */
721 if ( rc < 0 ) {
722 rc = LDAP_OTHER;
723 goto done;
724 }
725
726 /* make the control critical, as we cannot proceed without */
727 ctrl.ldctl_oid = LDAP_CONTROL_SYNC;
728 ctrl.ldctl_iscritical = 1;
729
730 /* timelimit? */
731 if ( ls->ls_timelimit ) {
732 tv.tv_sec = ls->ls_timelimit;
733 tvp = &tv;
734 }
735
736 /* actually run the search */
737 rc = ldap_search_ext( ls->ls_ld,
738 ls->ls_base, ls->ls_scope, ls->ls_filter,
739 ls->ls_attrs, 0, ctrls, NULL,
740 tvp, ls->ls_sizelimit, &ls->ls_msgid );
741 #ifdef LDAP_SYNC_TRACE
742 fprintf( stderr,
743 "%sldap_search_ext(\"%s\", %d, \"%s\") == %d\n",
744 rc ? "!!! " : "",
745 ls->ls_base, ls->ls_scope, ls->ls_filter, rc );
746 #endif /* LDAP_SYNC_TRACE */
747 if ( rc != LDAP_SUCCESS ) {
748 goto done;
749 }
750
751 /* initial content/content update phase */
752 for ( ; ; ) {
753 LDAPMessage *msg = NULL;
754
755 /* NOTE: this very short timeout is just to let
756 * ldap_result() yield long enough to get something */
757 tv.tv_sec = 0;
758 tv.tv_usec = 100000;
759
760 rc = ldap_result( ls->ls_ld, ls->ls_msgid,
761 LDAP_MSG_RECEIVED, &tv, &res );
762 #ifdef LDAP_SYNC_TRACE
763 fprintf( stderr,
764 "\t%sldap_result(%d) == %d\n",
765 rc == -1 ? "!!! " : "",
766 ls->ls_msgid, rc );
767 #endif /* LDAP_SYNC_TRACE */
768 switch ( rc ) {
769 case 0:
770 /*
771 * timeout
772 *
773 * TODO: can do something else in the meanwhile)
774 */
775 break;
776
777 case -1:
778 /* smtg bad! */
779 goto done;
780
781 default:
782 for ( msg = ldap_first_message( ls->ls_ld, res );
783 msg != NULL;
784 msg = ldap_next_message( ls->ls_ld, msg ) )
785 {
786 int refreshDone;
787
788 switch ( ldap_msgtype( msg ) ) {
789 case LDAP_RES_SEARCH_ENTRY:
790 rc = ldap_sync_search_entry( ls, res );
791 break;
792
793 case LDAP_RES_SEARCH_REFERENCE:
794 rc = ldap_sync_search_reference( ls, res );
795 break;
796
797 case LDAP_RES_SEARCH_RESULT:
798 rc = ldap_sync_search_result( ls, res );
799 goto done_search;
800
801 case LDAP_RES_INTERMEDIATE:
802 rc = ldap_sync_search_intermediate( ls, res, &refreshDone );
803 if ( rc != LDAP_SUCCESS || refreshDone ) {
804 goto done_search;
805 }
806 break;
807
808 default:
809 #ifdef LDAP_SYNC_TRACE
810 fprintf( stderr, "\tgot something unexpected...\n" );
811 #endif /* LDAP_SYNC_TRACE */
812
813 ldap_msgfree( res );
814
815 rc = LDAP_OTHER;
816 goto done;
817 }
818 }
819 ldap_msgfree( res );
820 res = NULL;
821 break;
822 }
823 }
824
825 done_search:;
826 ldap_msgfree( res );
827
828 done:;
829 if ( ber != NULL ) {
830 ber_free( ber, 1 );
831 }
832
833 return rc;
834 }
835
836 /*
837 * initialize the refreshOnly sync
838 */
839 int
ldap_sync_init_refresh_only(ldap_sync_t * ls)840 ldap_sync_init_refresh_only( ldap_sync_t *ls )
841 {
842 return ldap_sync_init( ls, LDAP_SYNC_REFRESH_ONLY );
843 }
844
845 /*
846 * initialize the refreshAndPersist sync
847 */
848 int
ldap_sync_init_refresh_and_persist(ldap_sync_t * ls)849 ldap_sync_init_refresh_and_persist( ldap_sync_t *ls )
850 {
851 return ldap_sync_init( ls, LDAP_SYNC_REFRESH_AND_PERSIST );
852 }
853
854 /*
855 * poll for new responses
856 */
857 int
ldap_sync_poll(ldap_sync_t * ls)858 ldap_sync_poll( ldap_sync_t *ls )
859 {
860 struct timeval tv,
861 *tvp = NULL;
862 LDAPMessage *res = NULL,
863 *msg;
864 int rc = 0;
865
866 #ifdef LDAP_SYNC_TRACE
867 fprintf( stderr, "ldap_sync_poll...\n" );
868 #endif /* LDAP_SYNC_TRACE */
869
870 assert( ls != NULL );
871 assert( ls->ls_ld != NULL );
872
873 if ( ls->ls_timeout != -1 ) {
874 tv.tv_sec = ls->ls_timeout;
875 tv.tv_usec = 0;
876 tvp = &tv;
877 }
878
879 rc = ldap_result( ls->ls_ld, ls->ls_msgid,
880 LDAP_MSG_RECEIVED, tvp, &res );
881 if ( rc <= 0 ) {
882 return rc;
883 }
884
885 for ( msg = ldap_first_message( ls->ls_ld, res );
886 msg;
887 msg = ldap_next_message( ls->ls_ld, msg ) )
888 {
889 int refreshDone;
890
891 switch ( ldap_msgtype( msg ) ) {
892 case LDAP_RES_SEARCH_ENTRY:
893 rc = ldap_sync_search_entry( ls, res );
894 break;
895
896 case LDAP_RES_SEARCH_REFERENCE:
897 rc = ldap_sync_search_reference( ls, res );
898 break;
899
900 case LDAP_RES_SEARCH_RESULT:
901 rc = ldap_sync_search_result( ls, res );
902 goto done_search;
903
904 case LDAP_RES_INTERMEDIATE:
905 rc = ldap_sync_search_intermediate( ls, res, &refreshDone );
906 if ( rc != LDAP_SUCCESS || refreshDone ) {
907 goto done_search;
908 }
909 break;
910
911 default:
912 #ifdef LDAP_SYNC_TRACE
913 fprintf( stderr, "\tgot something unexpected...\n" );
914 #endif /* LDAP_SYNC_TRACE */
915
916 ldap_msgfree( res );
917
918 rc = LDAP_OTHER;
919 goto done;
920 }
921 }
922
923 done_search:;
924 ldap_msgfree( res );
925
926 done:;
927 return rc;
928 }
929