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