]> git.sur5r.net Git - openldap/blob - libraries/libldap/ldap_sync.c
3cbc1687844da19f3b972d124cd99577dca558b8
[openldap] / libraries / libldap / ldap_sync.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 2006-2017 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 *
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 *
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
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
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
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
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
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
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
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
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
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 }