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