]> git.sur5r.net Git - openldap/commitdiff
import of LDAP Sync client API
authorPierangelo Masarati <ando@openldap.org>
Sat, 6 Jan 2007 18:28:32 +0000 (18:28 +0000)
committerPierangelo Masarati <ando@openldap.org>
Sat, 6 Jan 2007 18:28:32 +0000 (18:28 +0000)
include/ldap.h
libraries/libldap/Makefile.in
libraries/libldap/ldap_sync.c [new file with mode: 0644]
libraries/libldap_r/Makefile.in

index d3321be909d87f0b380d52c8af4de8b43b22f522..35187aeb6d3c8f2fefbf5608e7592a32b2e9cdda 100644 (file)
@@ -735,6 +735,138 @@ typedef struct ldap_url_desc {
 #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.
@@ -2120,5 +2252,53 @@ ldap_txn_end_s LDAP_P(( LDAP *ld,
        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 */
index 04cc2853d2990005c5b118a93c5b0795652fa8ac..76a63b899bacdfa0dabafc5860554c0900b91117 100644 (file)
@@ -26,7 +26,7 @@ SRCS  = bind.c open.c result.c error.c compare.c search.c \
        request.c os-ip.c url.c pagectrl.c sortctrl.c vlvctrl.c \
        init.c options.c print.c string.c util-int.c schema.c \
        charray.c tls.c os-local.c dnssrv.c utf-8.c utf-8-conv.c \
-       turn.c ppolicy.c dds.c txn.c
+       turn.c ppolicy.c dds.c txn.c ldap_sync.c
 
 OBJS   = bind.lo open.lo result.lo error.lo compare.lo search.lo \
        controls.lo messages.lo references.lo extended.lo cyrus.lo \
@@ -37,7 +37,7 @@ OBJS  = bind.lo open.lo result.lo error.lo compare.lo search.lo \
        request.lo os-ip.lo url.lo pagectrl.lo sortctrl.lo vlvctrl.lo \
        init.lo options.lo print.lo string.lo util-int.lo schema.lo \
        charray.lo tls.lo os-local.lo dnssrv.lo utf-8.lo utf-8-conv.lo \
-       turn.lo ppolicy.lo dds.lo txn.lo
+       turn.lo ppolicy.lo dds.lo txn.lo ldap_sync.lo
 
 LDAP_INCDIR= ../../include       
 LDAP_LIBDIR= ../../libraries
diff --git a/libraries/libldap/ldap_sync.c b/libraries/libldap/ldap_sync.c
new file mode 100644 (file)
index 0000000..0957baa
--- /dev/null
@@ -0,0 +1,928 @@
+/* $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;
+}
+
index d5e47114e948280a51a1b13abac1960bb635d780..8d4adfccb1604fb1b5e4d7dbb7ceb637e6be2de3 100644 (file)
@@ -28,7 +28,7 @@ XXSRCS    = apitest.c test.c \
        request.c os-ip.c url.c pagectrl.c sortctrl.c vlvctrl.c \
        init.c options.c print.c string.c util-int.c schema.c \
        charray.c tls.c os-local.c dnssrv.c utf-8.c utf-8-conv.c \
-       turn.c ppolicy.c dds.c txn.c
+       turn.c ppolicy.c dds.c txn.c ldap_sync.c
 SRCS   = threads.c rdwr.c rmutex.c tpool.c rq.c \
        thr_posix.c thr_cthreads.c thr_thr.c thr_lwp.c thr_nt.c \
        thr_pth.c thr_stub.c thr_debug.c
@@ -44,7 +44,7 @@ OBJS  = threads.lo rdwr.lo rmutex.lo tpool.lo  rq.lo \
        request.lo os-ip.lo url.lo pagectrl.lo sortctrl.lo vlvctrl.lo \
        init.lo options.lo print.lo string.lo util-int.lo schema.lo \
        charray.lo tls.lo os-local.lo dnssrv.lo utf-8.lo utf-8-conv.lo \
-       turn.lo ppolicy.lo dds.lo txn.lo
+       turn.lo ppolicy.lo dds.lo txn.lo ldap_sync.lo
 
 LDAP_INCDIR= ../../include       
 LDAP_LIBDIR= ../../libraries