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