#define LDAP_URL_ERR_BADFILTER 0x09 /* bad or missing filter */
#define LDAP_URL_ERR_BADEXTS 0x0a /* bad or missing extensions */
+/*
+ * LDAP sync (RFC4533) API
+ */
+
+typedef struct ldap_sync_t ldap_sync_t;
+
+typedef enum {
+ /* these are private - the client should never see them */
+ LDAP_SYNC_CAPI_NONE = -1,
+
+ LDAP_SYNC_CAPI_PHASE_FLAG = 0x10U,
+ LDAP_SYNC_CAPI_IDSET_FLAG = 0x20U,
+ LDAP_SYNC_CAPI_DONE_FLAG = 0x40U,
+
+ /* these are passed to ls_search_entry() */
+ LDAP_SYNC_CAPI_PRESENT = LDAP_SYNC_PRESENT,
+ LDAP_SYNC_CAPI_ADD = LDAP_SYNC_ADD,
+ LDAP_SYNC_CAPI_MODIFY = LDAP_SYNC_MODIFY,
+ LDAP_SYNC_CAPI_DELETE = LDAP_SYNC_DELETE,
+
+ /* these are passed to ls_intermediate() */
+ LDAP_SYNC_CAPI_PRESENTS = ( LDAP_SYNC_CAPI_PHASE_FLAG | LDAP_SYNC_CAPI_PRESENT ),
+ LDAP_SYNC_CAPI_DELETES = ( LDAP_SYNC_CAPI_PHASE_FLAG | LDAP_SYNC_CAPI_DELETE ),
+
+ LDAP_SYNC_CAPI_PRESENTS_IDSET = ( LDAP_SYNC_CAPI_PRESENTS | LDAP_SYNC_CAPI_IDSET_FLAG ),
+ LDAP_SYNC_CAPI_DELETES_IDSET = ( LDAP_SYNC_CAPI_DELETES | LDAP_SYNC_CAPI_IDSET_FLAG ),
+
+ LDAP_SYNC_CAPI_DONE = ( LDAP_SYNC_CAPI_DONE_FLAG | LDAP_SYNC_CAPI_PRESENTS )
+} ldap_sync_refresh_t;
+
+/*
+ * Called when an entry is returned by ldap_result().
+ * If phase is LDAP_SYNC_CAPI_ADD or LDAP_SYNC_CAPI_MODIFY,
+ * the entry has been either added or modified, and thus
+ * the complete view of the entry should be in the LDAPMessage.
+ * If phase is LDAP_SYNC_CAPI_PRESENT or LDAP_SYNC_CAPI_DELETE,
+ * only the DN should be in the LDAPMessage.
+ */
+typedef int (*ldap_sync_search_entry_f) LDAP_P((
+ ldap_sync_t *ls,
+ LDAPMessage *msg,
+ struct berval *entryUUID,
+ ldap_sync_refresh_t phase ));
+
+/*
+ * Called when a reference is returned; the client should know
+ * what to do with it.
+ */
+typedef int (*ldap_sync_search_reference_f) LDAP_P((
+ ldap_sync_t *ls,
+ LDAPMessage *msg ));
+
+/*
+ * Called when specific intermediate/final messages are returned.
+ * If phase is LDAP_SYNC_CAPI_PRESENTS or LDAP_SYNC_CAPI_DELETES,
+ * a "presents" or "deletes" phase begins.
+ * If phase is LDAP_SYNC_CAPI_DONE, a special "presents" phase
+ * with refreshDone set to "TRUE" has been returned, to indicate
+ * that the refresh phase of a refreshAndPersist is complete.
+ * In the above cases, syncUUIDs is NULL.
+ *
+ * If phase is LDAP_SYNC_CAPI_PRESENTS_IDSET or
+ * LDAP_SYNC_CAPI_DELETES_IDSET, syncUUIDs is an array of UUIDs
+ * that are either present or have been deleted.
+ */
+typedef int (*ldap_sync_intermediate_f) LDAP_P((
+ ldap_sync_t *ls,
+ LDAPMessage *msg,
+ BerVarray syncUUIDs,
+ ldap_sync_refresh_t phase ));
+
+/*
+ * Called when a searchResultDone is returned. In refreshAndPersist,
+ * this can only occur if the search for any reason is being terminated
+ * by the server.
+ */
+typedef int (*ldap_sync_search_result_f) LDAP_P((
+ ldap_sync_t *ls,
+ LDAPMessage *msg,
+ int refreshDeletes ));
+
+/*
+ * This structure contains all information about the persistent search;
+ * the caller is responsible for connecting, setting version, binding, tls...
+ */
+struct ldap_sync_t {
+ /* conf search params */
+ char *ls_base;
+ int ls_scope;
+ char *ls_filter;
+ char **ls_attrs;
+ int ls_timelimit;
+ int ls_sizelimit;
+
+ /* poll timeout */
+ int ls_timeout;
+
+ /* helpers - add as appropriate */
+ ldap_sync_search_entry_f ls_search_entry;
+ ldap_sync_search_reference_f ls_search_reference;
+ ldap_sync_intermediate_f ls_intermediate;
+ ldap_sync_search_result_f ls_search_result;
+
+ /* set by the caller as appropriate */
+ void *ls_private;
+
+ /* conn stuff */
+ LDAP *ls_ld;
+
+ /* --- the parameters below are private - do not modify --- */
+
+ /* FIXME: make the structure opaque, and provide an interface
+ * to modify the public values? */
+
+ /* result stuff */
+ int ls_msgid;
+
+ /* sync stuff */
+ /* needed by refreshOnly */
+ int ls_reloadHint;
+
+ /* opaque - need to pass between sessions, updated by the API */
+ struct berval ls_cookie;
+
+ /* state variable - do not modify */
+ ldap_sync_refresh_t ls_refreshPhase;
+};
+
+/*
+ * End of LDAP sync (RFC4533) API
+ */
+
/*
* The API draft spec says we should declare (or cause to be declared)
* 'struct timeval'. We don't. See IETF LDAPext discussions.
int *retidp ));
#endif
+/*
+ * in ldap_sync.c
+ */
+
+/*
+ * initialize the persistent search structure
+ */
+LDAP_F( ldap_sync_t * )
+ldap_sync_initialize LDAP_P((
+ ldap_sync_t *ls ));
+
+/*
+ * destroy the persistent search structure
+ */
+LDAP_F( void )
+ldap_sync_destroy LDAP_P((
+ ldap_sync_t *ls,
+ int freeit ));
+
+/*
+ * initialize a refreshOnly sync
+ */
+LDAP_F( int )
+ldap_sync_init LDAP_P((
+ ldap_sync_t *ls,
+ int mode ));
+
+/*
+ * initialize a refreshOnly sync
+ */
+LDAP_F( int )
+ldap_sync_init_refresh_only LDAP_P((
+ ldap_sync_t *ls ));
+
+/*
+ * initialize a refreshAndPersist sync
+ */
+LDAP_F( int )
+ldap_sync_init_refresh_and_persist LDAP_P((
+ ldap_sync_t *ls ));
+
+/*
+ * poll for new responses
+ */
+LDAP_F( int )
+ldap_sync_poll LDAP_P((
+ ldap_sync_t *ls ));
+
LDAP_END_DECL
#endif /* _LDAP_H */
--- /dev/null
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2006-2007 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This program was originally developed by Pierangelo Masarati
+ * for inclusion in OpenLDAP Software.
+ */
+
+/*
+ * Proof-of-concept API that implement the client-side
+ * of the "LDAP Content Sync Operation" (RFC 4533)
+ */
+
+#include "portable.h"
+
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+#ifdef LDAP_SYNC_TRACE
+/*
+ * used for debug purposes
+ */
+static char *
+print_UUID( char *buf, size_t len, unsigned char *UUID )
+{
+ snprintf( buf, len,
+ "%02x%02x%02x%02x-%02x%02x-%02x%02x-"
+ "%02x%02x-%02x%02x%02x%02x%02x%02x",
+ UUID[0],
+ UUID[1],
+ UUID[2],
+ UUID[3],
+ UUID[4],
+ UUID[5],
+ UUID[6],
+ UUID[7],
+ UUID[8],
+ UUID[9],
+ UUID[10],
+ UUID[11],
+ UUID[12],
+ UUID[13],
+ UUID[14],
+ UUID[15] );
+ return buf;
+}
+
+static const char *
+ldap_sync_state2str( int state )
+{
+ switch ( state ) {
+ case LDAP_SYNC_PRESENT:
+ return "LDAP_SYNC_PRESENT";
+
+ case LDAP_SYNC_ADD:
+ return "LDAP_SYNC_ADD";
+
+ case LDAP_SYNC_MODIFY:
+ return "LDAP_SYNC_MODIFY";
+
+ case LDAP_SYNC_DELETE:
+ return "LDAP_SYNC_DELETE";
+
+ default:
+ return "(unknown)";
+ }
+}
+#endif
+
+/*
+ * initialize the persistent search structure
+ */
+ldap_sync_t *
+ldap_sync_initialize( ldap_sync_t *ls_in )
+{
+ ldap_sync_t *ls = ls_in;
+
+ if ( ls == NULL ) {
+ ls = ldap_memalloc( sizeof( ldap_sync_t ) );
+ if ( ls == NULL ) {
+ return NULL;
+ }
+
+ } else {
+ memset( ls, 0, sizeof( ldap_sync_t ) );
+ }
+
+ ls->ls_scope = LDAP_SCOPE_SUBTREE;
+ ls->ls_timeout = -1;
+
+ return ls;
+}
+
+/*
+ * destroy the persistent search structure
+ */
+void
+ldap_sync_destroy( ldap_sync_t *ls, int freeit )
+{
+ assert( ls != NULL );
+
+ if ( ls->ls_base != NULL ) {
+ ldap_memfree( ls->ls_base );
+ ls->ls_base = NULL;
+ }
+
+ if ( ls->ls_filter != NULL ) {
+ ldap_memfree( ls->ls_filter );
+ ls->ls_filter = NULL;
+ }
+
+ if ( ls->ls_attrs != NULL ) {
+ int i;
+
+ for ( i = 0; ls->ls_attrs[ i ] != NULL; i++ ) {
+ ldap_memfree( ls->ls_attrs[ i ] );
+ }
+ ldap_memfree( ls->ls_attrs );
+ ls->ls_attrs = NULL;
+ }
+
+ if ( ls->ls_ld != NULL ) {
+ (void)ldap_unbind_ext( ls->ls_ld, NULL, NULL );
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "ldap_unbind_ext()\n" );
+#endif /* LDAP_SYNC_TRACE */
+ ls->ls_ld = NULL;
+ }
+
+ if ( ls->ls_cookie.bv_val != NULL ) {
+ ldap_memfree( ls->ls_cookie.bv_val );
+ ls->ls_cookie.bv_val = NULL;
+ }
+
+ if ( freeit ) {
+ ldap_memfree( ls );
+ }
+}
+
+/*
+ * handle the LDAP_RES_SEARCH_ENTRY response
+ */
+static int
+ldap_sync_search_entry( ldap_sync_t *ls, LDAPMessage *res )
+{
+ LDAPControl **ctrls = NULL;
+ int rc = LDAP_SUCCESS,
+ i;
+ BerElement *ber = NULL;
+ struct berval entryUUID = { 0 },
+ cookie = { 0 };
+ int state = -1;
+ ber_len_t len;
+ ldap_sync_refresh_t phase = ls->ls_refreshPhase;
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\tgot LDAP_RES_SEARCH_ENTRY\n" );
+#endif /* LDAP_SYNC_TRACE */
+
+ assert( ls != NULL );
+ assert( res != NULL );
+
+ /* OK */
+
+ /* extract:
+ * - data
+ * - entryUUID
+ *
+ * check that:
+ * - Sync State Control is "add"
+ */
+
+ /* the control MUST be present */
+
+ /* extract controls */
+ ldap_get_entry_controls( ls->ls_ld, res, &ctrls );
+ if ( ctrls == NULL ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ /* lookup the sync state control */
+ for ( i = 0; ctrls[ i ] != NULL; i++ ) {
+ if ( strcmp( ctrls[ i ]->ldctl_oid, LDAP_CONTROL_SYNC_STATE ) == 0 ) {
+ break;
+ }
+ }
+
+ /* control must be present; there might be other... */
+ if ( ctrls[ i ] == NULL ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ /* extract data */
+ ber = ber_init( &ctrls[ i ]->ldctl_value );
+ /* scan entryUUID in-place ("m") */
+ ber_scanf( ber, "{em" /*"}"*/, &state, &entryUUID );
+ if ( entryUUID.bv_len == 0 ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
+ /* scan cookie in-place ("m") */
+ ber_scanf( ber, /*"{"*/ "m}", &cookie );
+ if ( cookie.bv_val != NULL ) {
+ ber_bvreplace( &ls->ls_cookie, &cookie );
+ }
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot cookie=%s\n",
+ cookie.bv_val ? cookie.bv_val : "(null)" );
+#endif /* LDAP_SYNC_TRACE */
+ }
+
+ switch ( state ) {
+ case LDAP_SYNC_PRESENT:
+ case LDAP_SYNC_DELETE:
+ case LDAP_SYNC_ADD:
+ case LDAP_SYNC_MODIFY:
+ /* NOTE: ldap_sync_refresh_t is defined
+ * as the corresponding LDAP_SYNC_*
+ * for the 4 above cases */
+ phase = state;
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot syncState=%s\n", ldap_sync_state2str( state ) );
+#endif /* LDAP_SYNC_TRACE */
+ break;
+
+ default:
+ rc = LDAP_OTHER;
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot unknown syncState=%d\n", state );
+#endif /* LDAP_SYNC_TRACE */
+ goto done;
+ }
+
+ if ( ls->ls_search_entry ) {
+ rc = ls->ls_search_entry( ls, res, &entryUUID, phase );
+ }
+
+done:;
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ if ( ctrls != NULL ) {
+ ldap_controls_free( ctrls );
+ }
+
+ return rc;
+}
+
+/*
+ * handle the LDAP_RES_SEARCH_REFERENCE response
+ * (to be implemented yet)
+ */
+static int
+ldap_sync_search_reference( ldap_sync_t *ls, LDAPMessage *res )
+{
+ int rc = 0;
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\tgot LDAP_RES_SEARCH_REFERENCE\n" );
+#endif /* LDAP_SYNC_TRACE */
+
+ assert( ls != NULL );
+ assert( res != NULL );
+
+ if ( ls->ls_search_reference ) {
+ rc = ls->ls_search_reference( ls, res );
+ }
+
+ return rc;
+}
+
+/*
+ * handle the LDAP_RES_SEARCH_RESULT response
+ */
+static int
+ldap_sync_search_result( ldap_sync_t *ls, LDAPMessage *res )
+{
+ int err;
+ char *matched = NULL,
+ *msg = NULL;
+ LDAPControl **ctrls = NULL;
+ int rc;
+ int refreshDeletes = -1;
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\tgot LDAP_RES_SEARCH_RESULT\n" );
+#endif /* LDAP_SYNC_TRACE */
+
+ assert( ls != NULL );
+ assert( res != NULL );
+
+ /* should not happen in refreshAndPersist... */
+ rc = ldap_parse_result( ls->ls_ld,
+ res, &err, &matched, &msg, NULL, &ctrls, 0 );
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr,
+ "\tldap_parse_result(%d, \"%s\", \"%s\") == %d\n",
+ err,
+ matched ? matched : "",
+ msg ? msg : "",
+ rc );
+#endif /* LDAP_SYNC_TRACE */
+ if ( rc == LDAP_SUCCESS ) {
+ rc = err;
+ }
+
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE;
+
+ switch ( rc ) {
+ case LDAP_SUCCESS: {
+ int i;
+ BerElement *ber = NULL;
+ ber_len_t len;
+ struct berval cookie = { 0 };
+
+ /* deal with control; then fallthru to handler */
+ if ( ctrls == NULL ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ /* lookup the sync state control */
+ for ( i = 0; ctrls[ i ] != NULL; i++ ) {
+ if ( strcmp( ctrls[ i ]->ldctl_oid,
+ LDAP_CONTROL_SYNC_DONE ) == 0 )
+ {
+ break;
+ }
+ }
+
+ /* control must be present; there might be other... */
+ if ( ctrls[ i ] == NULL ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ /* extract data */
+ ber = ber_init( &ctrls[ i ]->ldctl_value );
+
+ ber_scanf( ber, "{" /*"}"*/);
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
+ ber_scanf( ber, "m", &cookie );
+ if ( cookie.bv_val != NULL ) {
+ ber_bvreplace( &ls->ls_cookie, &cookie );
+ }
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot cookie=%s\n",
+ cookie.bv_val ? cookie.bv_val : "(null)" );
+#endif /* LDAP_SYNC_TRACE */
+ }
+
+ refreshDeletes = 0;
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDELETES ) {
+ ber_scanf( ber, "b", &refreshDeletes );
+ if ( refreshDeletes ) {
+ refreshDeletes = 1;
+ }
+ }
+
+ ber_scanf( ber, /*"{"*/ "}" );
+
+ /* NOTE: if any goto/return between ber_init() and here
+ * is introduced, don't forget to ber_free() */
+ ber_free( ber, 1 );
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot refreshDeletes=%s\n",
+ refreshDeletes ? "TRUE" : "FALSE" );
+#endif /* LDAP_SYNC_TRACE */
+
+ /* FIXME: what should we do with the refreshDelete? */
+ switch ( refreshDeletes ) {
+ case 0:
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_PRESENTS;
+ break;
+
+ default:
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_DELETES;
+ break;
+ }
+
+ } /* fallthru */
+
+ case LDAP_SYNC_REFRESH_REQUIRED:
+ /* TODO: check for Sync Done Control */
+ /* FIXME: perhaps the handler should be called
+ * also in case of failure; we'll deal with this
+ * later when implementing refreshOnly */
+ if ( ls->ls_search_result ) {
+ err = ls->ls_search_result( ls, res, refreshDeletes );
+ }
+ break;
+
+ default:
+ break;
+ }
+
+done:;
+ if ( matched != NULL ) {
+ ldap_memfree( matched );
+ }
+
+ if ( msg != NULL ) {
+ ldap_memfree( msg );
+ }
+
+ if ( ctrls != NULL ) {
+ ldap_controls_free( ctrls );
+ }
+
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE;
+
+ return rc;
+}
+
+/*
+ * handle the LDAP_RES_INTERMEDIATE response
+ */
+static int
+ldap_sync_search_intermediate( ldap_sync_t *ls, LDAPMessage *res, int *refreshDone )
+{
+ int rc;
+ char *retoid = NULL;
+ struct berval *retdata = NULL;
+ BerElement *ber = NULL;
+ ber_len_t len;
+ ber_tag_t tag,
+ syncinfo_tag;
+ struct berval cookie;
+ int refreshDeletes = 0;
+ BerVarray syncUUIDs = NULL;
+ ldap_sync_refresh_t phase;
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\tgot LDAP_RES_INTERMEDIATE\n" );
+#endif /* LDAP_SYNC_TRACE */
+
+ assert( ls != NULL );
+ assert( res != NULL );
+ assert( refreshDone != NULL );
+
+ *refreshDone = 0;
+
+ rc = ldap_parse_intermediate( ls->ls_ld, res,
+ &retoid, &retdata, NULL, 0 );
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t%sldap_parse_intermediate(%s) == %d\n",
+ rc != LDAP_SUCCESS ? "!!! " : "",
+ retoid == NULL ? "\"\"" : retoid,
+ rc );
+#endif /* LDAP_SYNC_TRACE */
+ /* parsing must be successful, and yield the OID
+ * of the sync info intermediate response */
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ if ( retoid == NULL || strcmp( retoid, LDAP_SYNC_INFO ) != 0 ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ /* init ber using the value in the response */
+ ber = ber_init( retdata );
+ if ( ber == NULL ) {
+ goto done;
+ }
+
+ syncinfo_tag = ber_peek_tag( ber, &len );
+ switch ( syncinfo_tag ) {
+ case LDAP_TAG_SYNC_NEW_COOKIE:
+ ber_scanf( ber, "tm", &tag, &cookie );
+ if ( cookie.bv_val != NULL ) {
+ ber_bvreplace( &ls->ls_cookie, &cookie );
+ }
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot cookie=%s\n",
+ cookie.bv_val ? cookie.bv_val : "(null)" );
+#endif /* LDAP_SYNC_TRACE */
+ break;
+
+ case LDAP_TAG_SYNC_REFRESH_DELETE:
+ case LDAP_TAG_SYNC_REFRESH_PRESENT:
+ if ( syncinfo_tag == LDAP_TAG_SYNC_REFRESH_DELETE ) {
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot refreshDelete\n" );
+#endif /* LDAP_SYNC_TRACE */
+ switch ( ls->ls_refreshPhase ) {
+ case LDAP_SYNC_CAPI_NONE:
+ case LDAP_SYNC_CAPI_PRESENTS:
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_DELETES;
+ break;
+
+ default:
+ /* TODO: impossible; handle */
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ } else {
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot refreshPresent\n" );
+#endif /* LDAP_SYNC_TRACE */
+ switch ( ls->ls_refreshPhase ) {
+ case LDAP_SYNC_CAPI_NONE:
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_PRESENTS;
+ break;
+
+ default:
+ /* TODO: impossible; handle */
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ }
+
+ ber_scanf( ber, "t{" /*"}"*/, &tag );
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
+ ber_scanf( ber, "m", &cookie );
+ if ( cookie.bv_val != NULL ) {
+ ber_bvreplace( &ls->ls_cookie, &cookie );
+ }
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot cookie=%s\n",
+ cookie.bv_val ? cookie.bv_val : "(null)" );
+#endif /* LDAP_SYNC_TRACE */
+ }
+
+ *refreshDone = 1;
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDONE ) {
+ ber_scanf( ber, "b", refreshDone );
+ }
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot refreshDone=%s\n",
+ *refreshDone ? "TRUE" : "FALSE" );
+#endif /* LDAP_SYNC_TRACE */
+
+ ber_scanf( ber, /*"{"*/ "}" );
+
+ if ( *refreshDone ) {
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE;
+ }
+
+ if ( ls->ls_intermediate ) {
+ ls->ls_intermediate( ls, res, NULL, ls->ls_refreshPhase );
+ }
+
+ break;
+
+ case LDAP_TAG_SYNC_ID_SET:
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot syncIdSet\n" );
+#endif /* LDAP_SYNC_TRACE */
+ ber_scanf( ber, "t{" /*"}"*/, &tag );
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
+ ber_scanf( ber, "m", &cookie );
+ if ( cookie.bv_val != NULL ) {
+ ber_bvreplace( &ls->ls_cookie, &cookie );
+ }
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot cookie=%s\n",
+ cookie.bv_val ? cookie.bv_val : "(null)" );
+#endif /* LDAP_SYNC_TRACE */
+ }
+
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDELETES ) {
+ ber_scanf( ber, "b", &refreshDeletes );
+ }
+
+ ber_scanf( ber, "[W]", &syncUUIDs );
+ ber_scanf( ber, /*"{"*/ "}" );
+ if ( syncUUIDs == NULL ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+#ifdef LDAP_SYNC_TRACE
+ {
+ int i;
+
+ fprintf( stderr, "\t\tgot refreshDeletes=%s\n",
+ refreshDeletes ? "TRUE" : "FALSE" );
+ for ( i = 0; syncUUIDs[ i ].bv_val != NULL; i++ ) {
+ char buf[ BUFSIZ ];
+ fprintf( stderr, "\t\t%s\n",
+ print_UUID( buf, sizeof( buf ),
+ (unsigned char *)syncUUIDs[ i ].bv_val ) );
+ }
+ }
+#endif /* LDAP_SYNC_TRACE */
+
+ if ( refreshDeletes ) {
+ phase = LDAP_SYNC_CAPI_DELETES_IDSET;
+
+ } else {
+ phase = LDAP_SYNC_CAPI_PRESENTS_IDSET;
+ }
+
+ /* FIXME: should touch ls->ls_refreshPhase? */
+ if ( ls->ls_intermediate ) {
+ ls->ls_intermediate( ls, res, syncUUIDs, phase );
+ }
+
+ ber_bvarray_free( syncUUIDs );
+ break;
+
+ default:
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tunknown tag!\n" );
+#endif /* LDAP_SYNC_TRACE */
+ goto done;
+ }
+
+done:;
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ if ( retoid != NULL ) {
+ ldap_memfree( retoid );
+ }
+
+ if ( retdata != NULL ) {
+ ber_bvfree( retdata );
+ }
+
+ return rc;
+}
+
+/*
+ * initialize the sync
+ */
+int
+ldap_sync_init( ldap_sync_t *ls, int mode )
+{
+ LDAPControl ctrl = { 0 },
+ *ctrls[ 2 ];
+ BerElement *ber = NULL;
+ int rc;
+ struct timeval tv = { 0 },
+ *tvp = NULL;
+ LDAPMessage *res = NULL;
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "ldap_sync_init(%s)...\n",
+ mode == LDAP_SYNC_REFRESH_AND_PERSIST ?
+ "LDAP_SYNC_REFRESH_AND_PERSIST" :
+ ( mode == LDAP_SYNC_REFRESH_ONLY ?
+ "LDAP_SYNC_REFRESH_ONLY" : "unknown" ) );
+#endif /* LDAP_SYNC_TRACE */
+
+ assert( ls != NULL );
+ assert( ls->ls_ld != NULL );
+
+ /* support both refreshOnly and refreshAndPersist */
+ switch ( mode ) {
+ case LDAP_SYNC_REFRESH_AND_PERSIST:
+ case LDAP_SYNC_REFRESH_ONLY:
+ break;
+
+ default:
+ fprintf( stderr, "ldap_sync_init: unknown mode=%d\n", mode );
+ return LDAP_PARAM_ERROR;
+ }
+
+ /* check consistency of cookie and reloadHint at initial refresh */
+ if ( ls->ls_cookie.bv_val == NULL && ls->ls_reloadHint != 0 ) {
+ fprintf( stderr, "ldap_sync_init: inconsistent cookie/rhint\n" );
+ return LDAP_PARAM_ERROR;
+ }
+
+ ctrls[ 0 ] = &ctrl;
+ ctrls[ 1 ] = NULL;
+
+ /* prepare the Sync Request control */
+ ber = ber_alloc_t( LBER_USE_DER );
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "%sber_alloc_t() %s= NULL\n",
+ ber == NULL ? "!!! " : "",
+ ber == NULL ? "=" : "!" );
+#endif /* LDAP_SYNC_TRACE */
+ if ( ber == NULL ) {
+ goto done;
+ }
+
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_NONE;
+
+ if ( ls->ls_cookie.bv_val != NULL ) {
+ ber_printf( ber, "{eOb}", mode,
+ &ls->ls_cookie, ls->ls_reloadHint );
+
+ } else {
+ ber_printf( ber, "{eb}", mode, ls->ls_reloadHint );
+ }
+
+ rc = ber_flatten2( ber, &ctrl.ldctl_value, 0 );
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr,
+ "%sber_flatten2() == %d\n",
+ rc ? "!!! " : "",
+ rc );
+#endif /* LDAP_SYNC_TRACE */
+ if ( rc == LBER_ERROR ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ /* make the control critical, as we cannot proceed without */
+ ctrl.ldctl_oid = LDAP_CONTROL_SYNC;
+ ctrl.ldctl_iscritical = 1;
+
+ /* timelimit? */
+ if ( ls->ls_timelimit ) {
+ tv.tv_sec = ls->ls_timelimit;
+ tvp = &tv;
+ }
+
+ /* actually run the search */
+ rc = ldap_search_ext( ls->ls_ld,
+ ls->ls_base, ls->ls_scope, ls->ls_filter,
+ ls->ls_attrs, 0, ctrls, NULL,
+ tvp, ls->ls_sizelimit, &ls->ls_msgid );
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr,
+ "%sldap_search_ext(\"%s\", %d, \"%s\") == %d\n",
+ rc ? "!!! " : "",
+ ls->ls_base, ls->ls_scope, ls->ls_filter, rc );
+#endif /* LDAP_SYNC_TRACE */
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ /* initial content/content update phase */
+ for ( ; ; ) {
+ LDAPMessage *msg = NULL;
+
+ /* NOTE: this very short timeout is just to let
+ * ldap_result() yield long enough to get something */
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+
+ rc = ldap_result( ls->ls_ld, ls->ls_msgid,
+ LDAP_MSG_RECEIVED, &tv, &res );
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr,
+ "\t%sldap_result(%d) == %d\n",
+ rc == -1 ? "!!! " : "",
+ ls->ls_msgid, rc );
+#endif /* LDAP_SYNC_TRACE */
+ switch ( rc ) {
+ case 0:
+ /*
+ * timeout
+ *
+ * TODO: can do something else in the meanwhile)
+ */
+ break;
+
+ case -1:
+ /* smtg bad! */
+ goto done;
+
+ default:
+ for ( msg = ldap_first_message( ls->ls_ld, res );
+ msg != NULL;
+ msg = ldap_next_message( ls->ls_ld, msg ) )
+ {
+ int refreshDone;
+
+ switch ( ldap_msgtype( msg ) ) {
+ case LDAP_RES_SEARCH_ENTRY:
+ rc = ldap_sync_search_entry( ls, res );
+ break;
+
+ case LDAP_RES_SEARCH_REFERENCE:
+ rc = ldap_sync_search_reference( ls, res );
+ break;
+
+ case LDAP_RES_SEARCH_RESULT:
+ rc = ldap_sync_search_result( ls, res );
+ goto done_search;
+
+ case LDAP_RES_INTERMEDIATE:
+ rc = ldap_sync_search_intermediate( ls, res, &refreshDone );
+ if ( rc != LDAP_SUCCESS || refreshDone ) {
+ goto done_search;
+ }
+ break;
+
+ default:
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\tgot something unexpected...\n" );
+#endif /* LDAP_SYNC_TRACE */
+
+ ldap_msgfree( res );
+
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ }
+ ldap_msgfree( res );
+ res = NULL;
+ break;
+ }
+ }
+
+done_search:;
+ ldap_msgfree( res );
+
+done:;
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ return rc;
+}
+
+/*
+ * initialize the refreshOnly sync
+ */
+int
+ldap_sync_init_refresh_only( ldap_sync_t *ls )
+{
+ return ldap_sync_init( ls, LDAP_SYNC_REFRESH_ONLY );
+}
+
+/*
+ * initialize the refreshAndPersist sync
+ */
+int
+ldap_sync_init_refresh_and_persist( ldap_sync_t *ls )
+{
+ return ldap_sync_init( ls, LDAP_SYNC_REFRESH_AND_PERSIST );
+}
+
+/*
+ * poll for new responses
+ */
+int
+ldap_sync_poll( ldap_sync_t *ls )
+{
+ struct timeval tv,
+ *tvp = NULL;
+ LDAPMessage *res = NULL,
+ *msg;
+ int rc = 0;
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "ldap_sync_poll...\n" );
+#endif /* LDAP_SYNC_TRACE */
+
+ assert( ls != NULL );
+ assert( ls->ls_ld != NULL );
+
+ if ( ls->ls_timeout != -1 ) {
+ tv.tv_sec = ls->ls_timeout;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ }
+
+ rc = ldap_result( ls->ls_ld, ls->ls_msgid,
+ LDAP_MSG_RECEIVED, tvp, &res );
+ if ( rc <= 0 ) {
+ return rc;
+ }
+
+ for ( msg = ldap_first_message( ls->ls_ld, res );
+ msg;
+ msg = ldap_next_message( ls->ls_ld, msg ) )
+ {
+ int refreshDone;
+
+ switch ( ldap_msgtype( msg ) ) {
+ case LDAP_RES_SEARCH_ENTRY:
+ rc = ldap_sync_search_entry( ls, res );
+ break;
+
+ case LDAP_RES_SEARCH_REFERENCE:
+ rc = ldap_sync_search_reference( ls, res );
+ break;
+
+ case LDAP_RES_SEARCH_RESULT:
+ rc = ldap_sync_search_result( ls, res );
+ goto done_search;
+
+ case LDAP_RES_INTERMEDIATE:
+ rc = ldap_sync_search_intermediate( ls, res, &refreshDone );
+ if ( rc != LDAP_SUCCESS || refreshDone ) {
+ goto done_search;
+ }
+ break;
+
+ default:
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\tgot something unexpected...\n" );
+#endif /* LDAP_SYNC_TRACE */
+
+ ldap_msgfree( res );
+
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ }
+
+done_search:;
+ ldap_msgfree( res );
+
+done:;
+ return rc;
+}
+