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