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