Home | History | Annotate | Line # | Download | only in libldap
      1 /*	$NetBSD: ldap_sync.c,v 1.4 2025/09/05 21:16:21 christos Exp $	*/
      2 
      3 /* $OpenLDAP$ */
      4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      5  *
      6  * Copyright 2006-2024 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.4 2025/09/05 21:16:21 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 *
     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 *
     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
     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
    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
    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
    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
    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
    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
    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
    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
    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