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