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