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
37static const char *
38ldap_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 */
62ldap_sync_t *
63ldap_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 */
84void
85ldap_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 */
130static int
131ldap_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
233done:;
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 */
249static int
250ldap_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 */
271static int
272ldap_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
404done:;
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 */
425static int
426ldap_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
636done:;
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 */
655int
656ldap_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
830done_search:;
831	ldap_msgfree( res );
832
833done:;
834	if ( ber != NULL ) {
835		ber_free( ber, 1 );
836	}
837
838	return rc;
839}
840
841/*
842 * initialize the refreshOnly sync
843 */
844int
845ldap_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 */
853int
854ldap_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 */
862int
863ldap_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
928done_search:;
929	ldap_msgfree( res );
930
931done:;
932	return rc;
933}
934