Makefile.am | 14
Makefile.in | 103 +++-
ldap/admin/src/scripts/50contentsync.ldif | 22
ldap/ldif/template-dse.ldif.in | 21
ldap/servers/plugins/sync/sync.h | 196 ++++++++
ldap/servers/plugins/sync/sync_init.c | 174 +++++++
ldap/servers/plugins/sync/sync_persist.c | 693 ++++++++++++++++++++++++++++
ldap/servers/plugins/sync/sync_refresh.c | 732 ++++++++++++++++++++++++++++++
ldap/servers/plugins/sync/sync_util.c | 685 ++++++++++++++++++++++++++++
ldap/servers/slapd/connection.c | 7
ldap/servers/slapd/libslapd.def | 4
ldap/servers/slapd/operation.c | 58 ++
ldap/servers/slapd/plugin.c | 9
ldap/servers/slapd/proto-slap.h | 2
ldap/servers/slapd/result.c | 80 +++
ldap/servers/slapd/slapi-plugin.h | 9
16 files changed, 2799 insertions(+), 10 deletions(-)
New commits:
commit 1e7c62deba556af7f6cf30e021dc64be1ad8ccc9
Author: Ludwig Krispenz <lkrispen(a)redhat.com>
Date: Fri Sep 27 13:58:40 2013 +0200
Ticket 47388 - RFE to implement RFC4533 -ver2
Bug Description: Request to implement RFC4533 in ds 389
Fix Description: This fix impements the content synchronization as
a directory server plugin, using the retro changelog
to provide synchronization with cookies.
The design is presented here:
http://port389.org/wiki/Content_synchronization_plugin
https://fedorahosted.org/389/ticket/47388
Reviewed by: nkinder, Thanks
diff --git a/Makefile.am b/Makefile.am
index 12d4641..441fe40 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -225,6 +225,7 @@ serverplugin_LTLIBRARIES = libacl-plugin.la libattr-unique-plugin.la
\
libdes-plugin.la libdistrib-plugin.la libhttp-client-plugin.la \
liblinkedattrs-plugin.la libmanagedentries-plugin.la \
libmemberof-plugin.la libpassthru-plugin.la libpwdstorage-plugin.la \
+ libcontentsync-plugin.la \
libreferint-plugin.la libreplication-plugin.la libretrocl-plugin.la \
libroles-plugin.la libstatechange-plugin.la libsyntax-plugin.la \
libviews-plugin.la libschemareload-plugin.la libusn-plugin.la \
@@ -564,6 +565,7 @@ update_DATA = ldap/admin/src/scripts/exampleupdate.pl \
ldap/admin/src/scripts/50refintprecedence.ldif \
ldap/admin/src/scripts/50retroclprecedence.ldif \
ldap/admin/src/scripts/50rootdnaccesscontrolplugin.ldif \
+ ldap/admin/src/scripts/50contentsync.ldif \
ldap/admin/src/scripts/60upgradeschemafiles.pl \
ldap/admin/src/scripts/70upgradefromldif.pl \
ldap/admin/src/scripts/80upgradednformat.pl \
@@ -1135,6 +1137,18 @@ libpwdstorage_plugin_la_LIBADD = libslapd.la $(NSS_LINK)
$(NSPR_LINK) $(LIBCRYPT
libpwdstorage_plugin_la_LDFLAGS = -avoid-version
#------------------------
+# libcontentsync-plugin
+#------------------------
+libcontentsync_plugin_la_SOURCES = ldap/servers/plugins/sync/sync_init.c \
+ ldap/servers/plugins/sync/sync_util.c \
+ ldap/servers/plugins/sync/sync_refresh.c \
+ ldap/servers/plugins/sync/sync_persist.c
+
+libcontentsync_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
+libcontentsync_plugin_la_LIBADD = libslapd.la $(NSS_LINK) $(NSPR_LINK) $(LIBCRYPT)
+libcontentsync_plugin_la_LDFLAGS = -avoid-version
+
+#------------------------
# libreferint-plugin
#------------------------
libreferint_plugin_la_SOURCES = ldap/servers/plugins/referint/referint.c
diff --git a/Makefile.in b/Makefile.in
index 48444c6..9c9bcd9 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -364,6 +364,18 @@ am_libcollation_plugin_la_OBJECTS =
ldap/servers/plugins/collation/libcollation_
ldap/servers/plugins/collation/libcollation_plugin_la-config.lo \
ldap/servers/plugins/collation/libcollation_plugin_la-orfilter.lo
libcollation_plugin_la_OBJECTS = $(am_libcollation_plugin_la_OBJECTS)
+libcontentsync_plugin_la_DEPENDENCIES = libslapd.la \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+am_libcontentsync_plugin_la_OBJECTS =
ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_init.lo \
+ ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_util.lo \
+ ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_refresh.lo \
+ ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_persist.lo
+libcontentsync_plugin_la_OBJECTS = \
+ $(am_libcontentsync_plugin_la_OBJECTS)
+libcontentsync_plugin_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libcontentsync_plugin_la_LDFLAGS) $(LDFLAGS) -o $@
libcos_plugin_la_DEPENDENCIES = libslapd.la $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1)
am_libcos_plugin_la_OBJECTS = \
@@ -1137,9 +1149,11 @@ SOURCES = $(libavl_a_SOURCES) $(libldaputil_a_SOURCES) \
$(libautomember_plugin_la_SOURCES) $(libback_ldbm_la_SOURCES) \
$(libbitwise_plugin_la_SOURCES) \
$(libchainingdb_plugin_la_SOURCES) \
- $(libcollation_plugin_la_SOURCES) $(libcos_plugin_la_SOURCES) \
- $(libderef_plugin_la_SOURCES) $(libdes_plugin_la_SOURCES) \
- $(libdistrib_plugin_la_SOURCES) $(libdna_plugin_la_SOURCES) \
+ $(libcollation_plugin_la_SOURCES) \
+ $(libcontentsync_plugin_la_SOURCES) \
+ $(libcos_plugin_la_SOURCES) $(libderef_plugin_la_SOURCES) \
+ $(libdes_plugin_la_SOURCES) $(libdistrib_plugin_la_SOURCES) \
+ $(libdna_plugin_la_SOURCES) \
$(libhttp_client_plugin_la_SOURCES) \
$(liblinkedattrs_plugin_la_SOURCES) \
$(libmanagedentries_plugin_la_SOURCES) \
@@ -1171,9 +1185,11 @@ DIST_SOURCES = $(libavl_a_SOURCES) $(libldaputil_a_SOURCES) \
$(libautomember_plugin_la_SOURCES) $(libback_ldbm_la_SOURCES) \
$(libbitwise_plugin_la_SOURCES) \
$(libchainingdb_plugin_la_SOURCES) \
- $(libcollation_plugin_la_SOURCES) $(libcos_plugin_la_SOURCES) \
- $(libderef_plugin_la_SOURCES) $(libdes_plugin_la_SOURCES) \
- $(libdistrib_plugin_la_SOURCES) $(libdna_plugin_la_SOURCES) \
+ $(libcollation_plugin_la_SOURCES) \
+ $(libcontentsync_plugin_la_SOURCES) \
+ $(libcos_plugin_la_SOURCES) $(libderef_plugin_la_SOURCES) \
+ $(libdes_plugin_la_SOURCES) $(libdistrib_plugin_la_SOURCES) \
+ $(libdna_plugin_la_SOURCES) \
$(libhttp_client_plugin_la_SOURCES) \
$(liblinkedattrs_plugin_la_SOURCES) \
$(libmanagedentries_plugin_la_SOURCES) \
@@ -1619,6 +1635,7 @@ serverplugin_LTLIBRARIES = libacl-plugin.la libattr-unique-plugin.la
\
libdes-plugin.la libdistrib-plugin.la libhttp-client-plugin.la \
liblinkedattrs-plugin.la libmanagedentries-plugin.la \
libmemberof-plugin.la libpassthru-plugin.la libpwdstorage-plugin.la \
+ libcontentsync-plugin.la \
libreferint-plugin.la libreplication-plugin.la libretrocl-plugin.la \
libroles-plugin.la libstatechange-plugin.la libsyntax-plugin.la \
libviews-plugin.la libschemareload-plugin.la libusn-plugin.la \
@@ -1954,6 +1971,7 @@ update_DATA = ldap/admin/src/scripts/exampleupdate.pl \
ldap/admin/src/scripts/50refintprecedence.ldif \
ldap/admin/src/scripts/50retroclprecedence.ldif \
ldap/admin/src/scripts/50rootdnaccesscontrolplugin.ldif \
+ ldap/admin/src/scripts/50contentsync.ldif \
ldap/admin/src/scripts/60upgradeschemafiles.pl \
ldap/admin/src/scripts/70upgradefromldif.pl \
ldap/admin/src/scripts/80upgradednformat.pl \
@@ -2461,6 +2479,18 @@ libpwdstorage_plugin_la_LIBADD = libslapd.la $(NSS_LINK)
$(NSPR_LINK) $(LIBCRYPT
libpwdstorage_plugin_la_LDFLAGS = -avoid-version
#------------------------
+# libcontentsync-plugin
+#------------------------
+libcontentsync_plugin_la_SOURCES = ldap/servers/plugins/sync/sync_init.c \
+ ldap/servers/plugins/sync/sync_util.c \
+ ldap/servers/plugins/sync/sync_refresh.c \
+ ldap/servers/plugins/sync/sync_persist.c
+
+libcontentsync_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
+libcontentsync_plugin_la_LIBADD = libslapd.la $(NSS_LINK) $(NSPR_LINK) $(LIBCRYPT)
+libcontentsync_plugin_la_LDFLAGS = -avoid-version
+
+#------------------------
# libreferint-plugin
#------------------------
libreferint_plugin_la_SOURCES = ldap/servers/plugins/referint/referint.c
@@ -3504,6 +3534,26 @@ ldap/servers/plugins/collation/libcollation_plugin_la-orfilter.lo:
\
libcollation-plugin.la: $(libcollation_plugin_la_OBJECTS)
$(libcollation_plugin_la_DEPENDENCIES) $(EXTRA_libcollation_plugin_la_DEPENDENCIES)
$(AM_V_GEN)$(libcollation_plugin_la_LINK) -rpath $(serverplugindir)
$(libcollation_plugin_la_OBJECTS) $(libcollation_plugin_la_LIBADD) $(LIBS)
+ldap/servers/plugins/sync/$(am__dirstamp):
+ @$(MKDIR_P) ldap/servers/plugins/sync
+ @: > ldap/servers/plugins/sync/$(am__dirstamp)
+ldap/servers/plugins/sync/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) ldap/servers/plugins/sync/$(DEPDIR)
+ @: > ldap/servers/plugins/sync/$(DEPDIR)/$(am__dirstamp)
+ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_init.lo: \
+ ldap/servers/plugins/sync/$(am__dirstamp) \
+ ldap/servers/plugins/sync/$(DEPDIR)/$(am__dirstamp)
+ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_util.lo: \
+ ldap/servers/plugins/sync/$(am__dirstamp) \
+ ldap/servers/plugins/sync/$(DEPDIR)/$(am__dirstamp)
+ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_refresh.lo: \
+ ldap/servers/plugins/sync/$(am__dirstamp) \
+ ldap/servers/plugins/sync/$(DEPDIR)/$(am__dirstamp)
+ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_persist.lo: \
+ ldap/servers/plugins/sync/$(am__dirstamp) \
+ ldap/servers/plugins/sync/$(DEPDIR)/$(am__dirstamp)
+libcontentsync-plugin.la: $(libcontentsync_plugin_la_OBJECTS)
$(libcontentsync_plugin_la_DEPENDENCIES) $(EXTRA_libcontentsync_plugin_la_DEPENDENCIES)
+ $(libcontentsync_plugin_la_LINK) -rpath $(serverplugindir)
$(libcontentsync_plugin_la_OBJECTS) $(libcontentsync_plugin_la_LIBADD) $(LIBS)
ldap/servers/plugins/cos/$(am__dirstamp):
@$(MKDIR_P) ldap/servers/plugins/cos
@: > ldap/servers/plugins/cos/$(am__dirstamp)
@@ -5193,6 +5243,8 @@ mostlyclean-compile:
-rm -f ldap/servers/plugins/schema_reload/*.lo
-rm -f ldap/servers/plugins/statechange/*.$(OBJEXT)
-rm -f ldap/servers/plugins/statechange/*.lo
+ -rm -f ldap/servers/plugins/sync/*.$(OBJEXT)
+ -rm -f ldap/servers/plugins/sync/*.lo
-rm -f ldap/servers/plugins/syntaxes/*.$(OBJEXT)
-rm -f ldap/servers/plugins/syntaxes/*.lo
-rm -f ldap/servers/plugins/uiduniq/*.$(OBJEXT)
@@ -5381,6 +5433,10 @@ distclean-compile:
@AMDEP_TRUE@@am__include@
@am__quote@ldap/servers/plugins/rootdn_access/$(DEPDIR)/librootdn_access_plugin_la-rootdn_access.Plo(a)am__quote@
@AMDEP_TRUE@@am__include@
@am__quote@ldap/servers/plugins/schema_reload/$(DEPDIR)/libschemareload_plugin_la-schema_reload.Plo(a)am__quote@
@AMDEP_TRUE@@am__include@
@am__quote@ldap/servers/plugins/statechange/$(DEPDIR)/libstatechange_plugin_la-statechange.Plo(a)am__quote@
+@AMDEP_TRUE@@am__include@
@am__quote@ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_init.Plo(a)am__quote@
+@AMDEP_TRUE@@am__include@
@am__quote@ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_persist.Plo(a)am__quote@
+@AMDEP_TRUE@@am__include@
@am__quote@ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_refresh.Plo(a)am__quote@
+@AMDEP_TRUE@@am__include@
@am__quote@ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_util.Plo(a)am__quote@
@AMDEP_TRUE@@am__include@
@am__quote@ldap/servers/plugins/syntaxes/$(DEPDIR)/libsyntax_plugin_la-bin.Plo(a)am__quote@
@AMDEP_TRUE@@am__include@
@am__quote@ldap/servers/plugins/syntaxes/$(DEPDIR)/libsyntax_plugin_la-bitstring.Plo(a)am__quote@
@AMDEP_TRUE@@am__include@
@am__quote@ldap/servers/plugins/syntaxes/$(DEPDIR)/libsyntax_plugin_la-ces.Plo(a)am__quote@
@@ -6619,6 +6675,34 @@ ldap/servers/plugins/collation/libcollation_plugin_la-orfilter.lo:
ldap/servers/
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp)
@AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC
$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES)
$(INCLUDES) $(libcollation_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o
ldap/servers/plugins/collation/libcollation_plugin_la-orfilter.lo `test -f
'ldap/servers/plugins/collation/orfilter.c' || echo
'$(srcdir)/'`ldap/servers/plugins/collation/orfilter.c
+ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_init.lo:
ldap/servers/plugins/sync/sync_init.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS)
--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES)
$(libcontentsync_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT
ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_init.lo -MD -MP -MF
ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_init.Tpo -c -o
ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_init.lo `test -f
'ldap/servers/plugins/sync/sync_init.c' || echo
'$(srcdir)/'`ldap/servers/plugins/sync/sync_init.c
+@am__fastdepCC_TRUE@ $(am__mv)
ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_init.Tpo
ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_init.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ldap/servers/plugins/sync/sync_init.c'
object='ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_init.lo'
libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp)
@AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS)
--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES)
$(libcontentsync_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o
ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_init.lo `test -f
'ldap/servers/plugins/sync/sync_init.c' || echo
'$(srcdir)/'`ldap/servers/plugins/sync/sync_init.c
+
+ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_util.lo:
ldap/servers/plugins/sync/sync_util.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS)
--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES)
$(libcontentsync_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT
ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_util.lo -MD -MP -MF
ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_util.Tpo -c -o
ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_util.lo `test -f
'ldap/servers/plugins/sync/sync_util.c' || echo
'$(srcdir)/'`ldap/servers/plugins/sync/sync_util.c
+@am__fastdepCC_TRUE@ $(am__mv)
ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_util.Tpo
ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_util.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ldap/servers/plugins/sync/sync_util.c'
object='ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_util.lo'
libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp)
@AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS)
--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES)
$(libcontentsync_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o
ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_util.lo `test -f
'ldap/servers/plugins/sync/sync_util.c' || echo
'$(srcdir)/'`ldap/servers/plugins/sync/sync_util.c
+
+ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_refresh.lo:
ldap/servers/plugins/sync/sync_refresh.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS)
--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES)
$(libcontentsync_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT
ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_refresh.lo -MD -MP -MF
ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_refresh.Tpo -c -o
ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_refresh.lo `test -f
'ldap/servers/plugins/sync/sync_refresh.c' || echo
'$(srcdir)/'`ldap/servers/plugins/sync/sync_refresh.c
+@am__fastdepCC_TRUE@ $(am__mv)
ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_refresh.Tpo
ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_refresh.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ldap/servers/plugins/sync/sync_refresh.c'
object='ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_refresh.lo'
libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp)
@AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS)
--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES)
$(libcontentsync_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o
ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_refresh.lo `test -f
'ldap/servers/plugins/sync/sync_refresh.c' || echo
'$(srcdir)/'`ldap/servers/plugins/sync/sync_refresh.c
+
+ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_persist.lo:
ldap/servers/plugins/sync/sync_persist.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS)
--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES)
$(libcontentsync_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT
ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_persist.lo -MD -MP -MF
ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_persist.Tpo -c -o
ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_persist.lo `test -f
'ldap/servers/plugins/sync/sync_persist.c' || echo
'$(srcdir)/'`ldap/servers/plugins/sync/sync_persist.c
+@am__fastdepCC_TRUE@ $(am__mv)
ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_persist.Tpo
ldap/servers/plugins/sync/$(DEPDIR)/libcontentsync_plugin_la-sync_persist.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ldap/servers/plugins/sync/sync_persist.c'
object='ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_persist.lo'
libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp)
@AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS)
--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES)
$(libcontentsync_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o
ldap/servers/plugins/sync/libcontentsync_plugin_la-sync_persist.lo `test -f
'ldap/servers/plugins/sync/sync_persist.c' || echo
'$(srcdir)/'`ldap/servers/plugins/sync/sync_persist.c
+
ldap/servers/plugins/cos/libcos_plugin_la-cos.lo: ldap/servers/plugins/cos/cos.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS)
$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES)
$(libcos_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT
ldap/servers/plugins/cos/libcos_plugin_la-cos.lo -MD -MP -MF
ldap/servers/plugins/cos/$(DEPDIR)/libcos_plugin_la-cos.Tpo -c -o
ldap/servers/plugins/cos/libcos_plugin_la-cos.lo `test -f
'ldap/servers/plugins/cos/cos.c' || echo
'$(srcdir)/'`ldap/servers/plugins/cos/cos.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv)
ldap/servers/plugins/cos/$(DEPDIR)/libcos_plugin_la-cos.Tpo
ldap/servers/plugins/cos/$(DEPDIR)/libcos_plugin_la-cos.Plo
@@ -9491,6 +9575,7 @@ clean-libtool:
-rm -rf ldap/servers/plugins/rootdn_access/.libs
ldap/servers/plugins/rootdn_access/_libs
-rm -rf ldap/servers/plugins/schema_reload/.libs
ldap/servers/plugins/schema_reload/_libs
-rm -rf ldap/servers/plugins/statechange/.libs ldap/servers/plugins/statechange/_libs
+ -rm -rf ldap/servers/plugins/sync/.libs ldap/servers/plugins/sync/_libs
-rm -rf ldap/servers/plugins/syntaxes/.libs ldap/servers/plugins/syntaxes/_libs
-rm -rf ldap/servers/plugins/uiduniq/.libs ldap/servers/plugins/uiduniq/_libs
-rm -rf ldap/servers/plugins/usn/.libs ldap/servers/plugins/usn/_libs
@@ -10175,6 +10260,8 @@ distclean-generic:
-rm -f ldap/servers/plugins/schema_reload/$(am__dirstamp)
-rm -f ldap/servers/plugins/statechange/$(DEPDIR)/$(am__dirstamp)
-rm -f ldap/servers/plugins/statechange/$(am__dirstamp)
+ -rm -f ldap/servers/plugins/sync/$(DEPDIR)/$(am__dirstamp)
+ -rm -f ldap/servers/plugins/sync/$(am__dirstamp)
-rm -f ldap/servers/plugins/syntaxes/$(DEPDIR)/$(am__dirstamp)
-rm -f ldap/servers/plugins/syntaxes/$(am__dirstamp)
-rm -f ldap/servers/plugins/uiduniq/$(DEPDIR)/$(am__dirstamp)
@@ -10223,7 +10310,7 @@ clean-am: clean-binPROGRAMS clean-generic clean-libtool
clean-local \
distclean: distclean-am
-rm -f $(am__CONFIG_DISTCLEAN_FILES)
- -rm -rf ldap/libraries/libavl/$(DEPDIR) ldap/servers/plugins/acct_usability/$(DEPDIR)
ldap/servers/plugins/acctpolicy/$(DEPDIR) ldap/servers/plugins/acl/$(DEPDIR)
ldap/servers/plugins/automember/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR)
ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR)
ldap/servers/plugins/cos/$(DEPDIR) ldap/servers/plugins/deref/$(DEPDIR)
ldap/servers/plugins/distrib/$(DEPDIR) ldap/servers/plugins/dna/$(DEPDIR)
ldap/servers/plugins/http/$(DEPDIR) ldap/servers/plugins/linkedattrs/$(DEPDIR)
ldap/servers/plugins/memberof/$(DEPDIR) ldap/servers/plugins/mep/$(DEPDIR)
ldap/servers/plugins/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(DEPDIR)
ldap/servers/plugins/posix-winsync/$(DEPDIR) ldap/servers/plugins/presence/$(DEPDIR)
ldap/servers/plugins/pwdstorage/$(DEPDIR) ldap/servers/plugins/referint/$(DEPDIR)
ldap/servers/plugins/replication/$(DEPDIR) ldap/servers/plugins/retrocl/$(DEPDIR)
ldap/servers/plugins/rever/$
(DEPDIR) ldap/servers/plugins/roles/$(DEPDIR)
ldap/servers/plugins/rootdn_access/$(DEPDIR) ldap/servers/plugins/schema_reload/$(DEPDIR)
ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/syntaxes/$(DEPDIR)
ldap/servers/plugins/uiduniq/$(DEPDIR) ldap/servers/plugins/usn/$(DEPDIR)
ldap/servers/plugins/views/$(DEPDIR) ldap/servers/plugins/whoami/$(DEPDIR)
ldap/servers/slapd/$(DEPDIR) ldap/servers/slapd/back-ldbm/$(DEPDIR)
ldap/servers/slapd/tools/$(DEPDIR) ldap/servers/slapd/tools/ldclt/$(DEPDIR)
ldap/servers/slapd/tools/rsearch/$(DEPDIR) ldap/servers/snmp/$(DEPDIR)
ldap/systools/$(DEPDIR) lib/base/$(DEPDIR) lib/ldaputil/$(DEPDIR) lib/libaccess/$(DEPDIR)
lib/libadmin/$(DEPDIR) lib/libsi18n/$(DEPDIR)
+ -rm -rf ldap/libraries/libavl/$(DEPDIR) ldap/servers/plugins/acct_usability/$(DEPDIR)
ldap/servers/plugins/acctpolicy/$(DEPDIR) ldap/servers/plugins/acl/$(DEPDIR)
ldap/servers/plugins/automember/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR)
ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR)
ldap/servers/plugins/cos/$(DEPDIR) ldap/servers/plugins/deref/$(DEPDIR)
ldap/servers/plugins/distrib/$(DEPDIR) ldap/servers/plugins/dna/$(DEPDIR)
ldap/servers/plugins/http/$(DEPDIR) ldap/servers/plugins/linkedattrs/$(DEPDIR)
ldap/servers/plugins/memberof/$(DEPDIR) ldap/servers/plugins/mep/$(DEPDIR)
ldap/servers/plugins/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(DEPDIR)
ldap/servers/plugins/posix-winsync/$(DEPDIR) ldap/servers/plugins/presence/$(DEPDIR)
ldap/servers/plugins/pwdstorage/$(DEPDIR) ldap/servers/plugins/referint/$(DEPDIR)
ldap/servers/plugins/replication/$(DEPDIR) ldap/servers/plugins/retrocl/$(DEPDIR)
ldap/servers/plugins/rever/$
(DEPDIR) ldap/servers/plugins/roles/$(DEPDIR)
ldap/servers/plugins/rootdn_access/$(DEPDIR) ldap/servers/plugins/schema_reload/$(DEPDIR)
ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/sync/$(DEPDIR)
ldap/servers/plugins/syntaxes/$(DEPDIR) ldap/servers/plugins/uiduniq/$(DEPDIR)
ldap/servers/plugins/usn/$(DEPDIR) ldap/servers/plugins/views/$(DEPDIR)
ldap/servers/plugins/whoami/$(DEPDIR) ldap/servers/slapd/$(DEPDIR)
ldap/servers/slapd/back-ldbm/$(DEPDIR) ldap/servers/slapd/tools/$(DEPDIR)
ldap/servers/slapd/tools/ldclt/$(DEPDIR) ldap/servers/slapd/tools/rsearch/$(DEPDIR)
ldap/servers/snmp/$(DEPDIR) ldap/systools/$(DEPDIR) lib/base/$(DEPDIR)
lib/ldaputil/$(DEPDIR) lib/libaccess/$(DEPDIR) lib/libadmin/$(DEPDIR)
lib/libsi18n/$(DEPDIR)
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-hdr distclean-libtool distclean-tags
@@ -10279,7 +10366,7 @@ installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -f $(am__CONFIG_DISTCLEAN_FILES)
-rm -rf $(top_srcdir)/autom4te.cache
- -rm -rf ldap/libraries/libavl/$(DEPDIR) ldap/servers/plugins/acct_usability/$(DEPDIR)
ldap/servers/plugins/acctpolicy/$(DEPDIR) ldap/servers/plugins/acl/$(DEPDIR)
ldap/servers/plugins/automember/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR)
ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR)
ldap/servers/plugins/cos/$(DEPDIR) ldap/servers/plugins/deref/$(DEPDIR)
ldap/servers/plugins/distrib/$(DEPDIR) ldap/servers/plugins/dna/$(DEPDIR)
ldap/servers/plugins/http/$(DEPDIR) ldap/servers/plugins/linkedattrs/$(DEPDIR)
ldap/servers/plugins/memberof/$(DEPDIR) ldap/servers/plugins/mep/$(DEPDIR)
ldap/servers/plugins/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(DEPDIR)
ldap/servers/plugins/posix-winsync/$(DEPDIR) ldap/servers/plugins/presence/$(DEPDIR)
ldap/servers/plugins/pwdstorage/$(DEPDIR) ldap/servers/plugins/referint/$(DEPDIR)
ldap/servers/plugins/replication/$(DEPDIR) ldap/servers/plugins/retrocl/$(DEPDIR)
ldap/servers/plugins/rever/$
(DEPDIR) ldap/servers/plugins/roles/$(DEPDIR)
ldap/servers/plugins/rootdn_access/$(DEPDIR) ldap/servers/plugins/schema_reload/$(DEPDIR)
ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/syntaxes/$(DEPDIR)
ldap/servers/plugins/uiduniq/$(DEPDIR) ldap/servers/plugins/usn/$(DEPDIR)
ldap/servers/plugins/views/$(DEPDIR) ldap/servers/plugins/whoami/$(DEPDIR)
ldap/servers/slapd/$(DEPDIR) ldap/servers/slapd/back-ldbm/$(DEPDIR)
ldap/servers/slapd/tools/$(DEPDIR) ldap/servers/slapd/tools/ldclt/$(DEPDIR)
ldap/servers/slapd/tools/rsearch/$(DEPDIR) ldap/servers/snmp/$(DEPDIR)
ldap/systools/$(DEPDIR) lib/base/$(DEPDIR) lib/ldaputil/$(DEPDIR) lib/libaccess/$(DEPDIR)
lib/libadmin/$(DEPDIR) lib/libsi18n/$(DEPDIR)
+ -rm -rf ldap/libraries/libavl/$(DEPDIR) ldap/servers/plugins/acct_usability/$(DEPDIR)
ldap/servers/plugins/acctpolicy/$(DEPDIR) ldap/servers/plugins/acl/$(DEPDIR)
ldap/servers/plugins/automember/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR)
ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR)
ldap/servers/plugins/cos/$(DEPDIR) ldap/servers/plugins/deref/$(DEPDIR)
ldap/servers/plugins/distrib/$(DEPDIR) ldap/servers/plugins/dna/$(DEPDIR)
ldap/servers/plugins/http/$(DEPDIR) ldap/servers/plugins/linkedattrs/$(DEPDIR)
ldap/servers/plugins/memberof/$(DEPDIR) ldap/servers/plugins/mep/$(DEPDIR)
ldap/servers/plugins/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(DEPDIR)
ldap/servers/plugins/posix-winsync/$(DEPDIR) ldap/servers/plugins/presence/$(DEPDIR)
ldap/servers/plugins/pwdstorage/$(DEPDIR) ldap/servers/plugins/referint/$(DEPDIR)
ldap/servers/plugins/replication/$(DEPDIR) ldap/servers/plugins/retrocl/$(DEPDIR)
ldap/servers/plugins/rever/$
(DEPDIR) ldap/servers/plugins/roles/$(DEPDIR)
ldap/servers/plugins/rootdn_access/$(DEPDIR) ldap/servers/plugins/schema_reload/$(DEPDIR)
ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/sync/$(DEPDIR)
ldap/servers/plugins/syntaxes/$(DEPDIR) ldap/servers/plugins/uiduniq/$(DEPDIR)
ldap/servers/plugins/usn/$(DEPDIR) ldap/servers/plugins/views/$(DEPDIR)
ldap/servers/plugins/whoami/$(DEPDIR) ldap/servers/slapd/$(DEPDIR)
ldap/servers/slapd/back-ldbm/$(DEPDIR) ldap/servers/slapd/tools/$(DEPDIR)
ldap/servers/slapd/tools/ldclt/$(DEPDIR) ldap/servers/slapd/tools/rsearch/$(DEPDIR)
ldap/servers/snmp/$(DEPDIR) ldap/systools/$(DEPDIR) lib/base/$(DEPDIR)
lib/ldaputil/$(DEPDIR) lib/libaccess/$(DEPDIR) lib/libadmin/$(DEPDIR)
lib/libsi18n/$(DEPDIR)
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
diff --git a/ldap/admin/src/scripts/50contentsync.ldif
b/ldap/admin/src/scripts/50contentsync.ldif
new file mode 100644
index 0000000..0f15bdb
--- /dev/null
+++ b/ldap/admin/src/scripts/50contentsync.ldif
@@ -0,0 +1,22 @@
+dn: cn=Content Synchronization,cn=plugins,cn=config
+objectclass: top
+objectclass: nsSlapdPlugin
+cn: Content Synchronization
+nsslapd-pluginpath: libcontentsync-plugin
+nsslapd-plugininitfunc: sync_init
+nsslapd-plugintype: object
+nsslapd-pluginenabled: off
+nsslapd-plugin-depends-on-named: Retro Changelog Plugin
+# these will be replaced when the server loads the plugin
+nsslapd-pluginId: ID
+nsslapd-pluginVersion: PACKAGE_VERSION
+nsslapd-pluginVendor: VENDOR
+nsslapd-pluginDescription: DESC
+
+dn: oid=1.3.6.1.4.1.4203.1.9.1.1,cn=features,cn=config
+objectClass: top
+objectClass: directoryServerFeature
+oid: 1.3.6.1.4.1.4203.1.9.1.1
+cn: Sync Request Control
+aci: (targetattr != "aci")(version 3.0; acl "Sync Request Control";
allow( read
+ , search ) userdn = "ldap:///all";)
diff --git a/ldap/ldif/template-dse.ldif.in b/ldap/ldif/template-dse.ldif.in
index 189c4aa..084aacb 100644
--- a/ldap/ldif/template-dse.ldif.in
+++ b/ldap/ldif/template-dse.ldif.in
@@ -70,6 +70,14 @@ objectClass: directoryServerFeature
oid: 1.3.6.1.4.1.42.2.27.9.5.8
cn: Account Usable Request Control
+dn: oid=1.3.6.1.4.1.4203.1.9.1.1,cn=features,cn=config
+objectClass: top
+objectClass: directoryServerFeature
+oid: 1.3.6.1.4.1.4203.1.9.1.1
+cn: Sync Request Control
+aci: (targetattr != "aci")(version 3.0; acl "Sync Request Control";
allow( read
+ , search ) userdn = "ldap:///all";)
+
dn: cn=plugins,cn=config
objectclass: top
objectclass: nsContainer
@@ -656,6 +664,19 @@ nsslapd-plugintype: betxnpreoperation
nsslapd-pluginenabled: on
nsslapd-plugin-depends-on-type: database
+dn: cn=Content Synchronization,cn=plugins,cn=config
+objectclass: top
+objectclass: nsSlapdPlugin
+objectclass: extensibleObject
+cn: Content Synchronization
+nsslapd-pluginpath: libcontentsync-plugin
+nsslapd-plugininitfunc: sync_init
+nsslapd-plugintype: object
+nsslapd-pluginenabled: off
+nsslapd-pluginbetxn: on
+nsslapd-plugin-depends-on-type: database
+nsslapd-plugin-depends-on-named: Retro Changelog Plugin
+
dn: cn=deref,cn=plugins,cn=config
objectclass: top
objectclass: nsSlapdPlugin
diff --git a/ldap/servers/plugins/sync/sync.h b/ldap/servers/plugins/sync/sync.h
new file mode 100644
index 0000000..cf73ce9
--- /dev/null
+++ b/ldap/servers/plugins/sync/sync.h
@@ -0,0 +1,196 @@
+/** BEGIN COPYRIGHT BLOCK
+ * This Program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; version 2 of the License.
+ *
+ * This Program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * In addition, as a special exception, Red Hat, Inc. gives You the additional
+ * right to link the code of this Program with code not covered under the GNU
+ * General Public License ("Non-GPL Code") and to distribute linked
combinations
+ * including the two, subject to the limitations in this paragraph. Non-GPL Code
+ * permitted under this exception must only link to the code of this Program
+ * through those well defined interfaces identified in the file named EXCEPTION
+ * found in the source code files (the "Approved Interfaces"). The files of
+ * Non-GPL Code may instantiate templates or use macros or inline functions from
+ * the Approved Interfaces without causing the resulting work to be covered by
+ * the GNU General Public License. Only Red Hat, Inc. may make changes or
+ * additions to the list of Approved Interfaces. You must obey the GNU General
+ * Public License in all respects for all of the Program code and other code used
+ * in conjunction with the Program except the Non-GPL Code covered by this
+ * exception. If you modify this file, you may extend this exception to your
+ * version of the file, but you are not obligated to do so. If you do not wish to
+ * provide this exception without modification, you must delete this exception
+ * statement from your version and license this file solely under the GPL without
+ * exception.
+ *
+ *
+ * Copyright (C) 2013 Red Hat, Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/**
+ * LDAP content synchronization plug-in
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "slapi-plugin.h"
+
+#define PLUGIN_NAME "content-sync-plugin"
+
+#define SYNC_PLUGIN_SUBSYSTEM "content-sync-plugin"
+#define SYNC_PREOP_DESC "content-sync-preop-subplugin"
+#define SYNC_POSTOP_DESC "content-sync-postop-subplugin"
+
+#define OP_FLAG_SYNC_PERSIST 0x01
+
+#define E_SYNC_REFRESH_REQUIRED 0x1000
+
+#define CL_ATTR_CHANGENUMBER "changenumber"
+#define CL_ATTR_ENTRYDN "targetDn"
+#define CL_ATTR_UNIQUEID "targetUniqueId"
+#define CL_ATTR_CHGTYPE "changetype"
+#define CL_ATTR_NEWSUPERIOR "newsuperior"
+#define CL_SRCH_BASE "cn=changelog"
+
+typedef struct sync_cookie {
+ char *cookie_client_signature;
+ char *cookie_server_signature;
+ int cookie_change_info;
+} Sync_Cookie;
+
+typedef struct sync_update {
+ char *upd_uuid;
+ int upd_chgtype;
+ Slapi_Entry *upd_e;
+} Sync_UpdateNode;
+
+typedef struct sync_callback {
+ Slapi_PBlock *orig_pb;
+ int changenr;
+ int change_start;
+ int cb_err;
+ Sync_UpdateNode *cb_updates;
+} Sync_CallBackData;
+
+int sync_register_operation_extension(void);
+
+int sync_srch_refresh_pre_search(Slapi_PBlock *pb);
+int sync_srch_refresh_post_search(Slapi_PBlock *pb);
+int sync_srch_refresh_pre_entry(Slapi_PBlock *pb);
+int sync_srch_refresh_pre_result(Slapi_PBlock *pb);
+int sync_del_persist_post_op(Slapi_PBlock *pb);
+int sync_mod_persist_post_op(Slapi_PBlock *pb);
+int sync_modrdn_persist_post_op(Slapi_PBlock *pb);
+int sync_add_persist_post_op(Slapi_PBlock *pb);
+
+int sync_parse_control_value( struct berval *psbvp, ber_int_t *mode, int *reload, char
**cookie );
+int sync_create_state_control( Slapi_Entry *e, LDAPControl **ctrlp, int type, Sync_Cookie
*cookie);
+int sync_create_sync_done_control( LDAPControl **ctrlp, int refresh, char *cookie);
+int sync_intermediate_msg (Slapi_PBlock *pb, int tag, Sync_Cookie *cookie, char
**uuids);
+int sync_result_msg (Slapi_PBlock *pb, Sync_Cookie *cookie);
+int sync_result_err (Slapi_PBlock *pb, int rc, char *msg);
+
+Sync_Cookie *sync_cookie_create (Slapi_PBlock *pb);
+void sync_cookie_update (Sync_Cookie *cookie, Slapi_Entry *ec);
+Sync_Cookie *sync_cookie_parse (char *cookie);
+int sync_cookie_isvalid (Sync_Cookie *testcookie, Sync_Cookie *refcookie);
+void sync_cookie_free (Sync_Cookie **freecookie);
+char * sync_cookie2str(Sync_Cookie *cookie);
+int sync_number2int(char *nrstr);
+char *sync_nsuniqueid2uuid(const char *nsuniqueid);
+
+int sync_is_active (Slapi_Entry *e, Slapi_PBlock *pb);
+int sync_is_active_scope (const Slapi_DN *dn, Slapi_PBlock *pb);
+
+int sync_refresh_update_content(Slapi_PBlock *pb, Sync_Cookie *client_cookie, Sync_Cookie
*session_cookie);
+int sync_refresh_initial_content(Slapi_PBlock *pb, int persist, Sync_Cookie
*session_cookie);
+int sync_read_entry_from_changelog( Slapi_Entry *cl_entry, void *cb_data);
+int sync_send_entry_from_changelog( Slapi_PBlock *pb, int chg_req, char *uniqueid);
+void sync_send_deleted_entries (Slapi_PBlock *pb, Sync_UpdateNode *upd, int chg_count,
Sync_Cookie *session_cookie);
+void sync_send_modified_entries (Slapi_PBlock *pb, Sync_UpdateNode *upd, int chg_count);
+
+int sync_persist_initialize (int argc, char **argv);
+PRThread *sync_persist_add (Slapi_PBlock *pb);
+int sync_persist_startup (PRThread *tid, Sync_Cookie *session_cookie);
+int sync_persist_terminate_all ();
+int sync_persist_terminate (PRThread *tid);
+
+Slapi_PBlock *sync_pblock_copy(Slapi_PBlock *src);
+
+/* prototype for functions not in slapi-plugin.h */
+Slapi_ComponentId *plugin_get_default_component_id();
+
+
+/*
+ * Structures to handle the persitent phase of
+ * Content Synchronization Requests
+ *
+ * A queue of entries being to be sent by a particular persistent
+ * sync thread
+ *
+ * will be created in post op plugins
+ */
+typedef struct sync_queue_node {
+ Slapi_Entry *sync_entry;
+ LDAPControl *pe_ctrls[2]; /* XXX ?? XXX */
+ struct sync_queue_node *sync_next;
+ int sync_chgtype;
+} SyncQueueNode;
+
+/*
+ * Information about a single sync search
+ *
+ * will be created when a content sync control with
+ * mode == 3 is decoded
+ */
+typedef struct sync_request {
+ Slapi_PBlock *req_pblock;
+ PRLock *req_lock;
+ PRThread *req_tid;
+ char *req_orig_base;
+ Slapi_Filter *req_filter;
+ PRInt32 req_complete;
+ Sync_Cookie *req_cookie;
+ SyncQueueNode *ps_eq_head;
+ SyncQueueNode *ps_eq_tail;
+ int req_active;
+ struct sync_request *req_next;
+} SyncRequest;
+
+/*
+ * A list of established persistent synchronization searches.
+ *
+ * will be initialized at plugin initialization
+ */
+#define SYNC_MAX_CONCURRENT 10
+typedef struct sync_request_list {
+ Slapi_RWLock *sync_req_rwlock; /* R/W lock struct to serialize access */
+ SyncRequest *sync_req_head; /* Head of list */
+ PRLock *sync_req_cvarlock; /* Lock for cvar */
+ PRCondVar *sync_req_cvar; /* ps threads sleep on this */
+ int sync_req_max_persist;
+ int sync_req_cur_persist;
+} SyncRequestList;
+
+#define SYNC_FLAG_ADD_STATE_CTRL 0x01
+#define SYNC_FLAG_ADD_DONE_CTRL 0x02
+#define SYNC_FLAG_NO_RESULT 0x04
+#define SYNC_FLAG_SEND_INTERMEDIATE 0x08
+
+typedef struct sync_op_info {
+ int send_flag; /* hint for preop plugins what to send */
+ Sync_Cookie *cookie; /* cookie to add in control */
+} SyncOpInfo;
+
diff --git a/ldap/servers/plugins/sync/sync_init.c
b/ldap/servers/plugins/sync/sync_init.c
new file mode 100644
index 0000000..d648c79
--- /dev/null
+++ b/ldap/servers/plugins/sync/sync_init.c
@@ -0,0 +1,174 @@
+/** BEGIN COPYRIGHT BLOCK
+ * This Program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; version 2 of the License.
+ *
+ * This Program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * In addition, as a special exception, Red Hat, Inc. gives You the additional
+ * right to link the code of this Program with code not covered under the GNU
+ * General Public License ("Non-GPL Code") and to distribute linked
combinations
+ * including the two, subject to the limitations in this paragraph. Non-GPL Code
+ * permitted under this exception must only link to the code of this Program
+ * through those well defined interfaces identified in the file named EXCEPTION
+ * found in the source code files (the "Approved Interfaces"). The files of
+ * Non-GPL Code may instantiate templates or use macros or inline functions from
+ * the Approved Interfaces without causing the resulting work to be covered by
+ * the GNU General Public License. Only Red Hat, Inc. may make changes or
+ * additions to the list of Approved Interfaces. You must obey the GNU General
+ * Public License in all respects for all of the Program code and other code used
+ * in conjunction with the Program except the Non-GPL Code covered by this
+ * exception. If you modify this file, you may extend this exception to your
+ * version of the file, but you are not obligated to do so. If you do not wish to
+ * provide this exception without modification, you must delete this exception
+ * statement from your version and license this file solely under the GPL without
+ * exception.
+ *
+ *
+ * Copyright (C) 2013 Red Hat, Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "sync.h"
+
+static Slapi_PluginDesc pdesc = { PLUGIN_NAME, VENDOR, DS_PACKAGE_VERSION, "Context
Synchronization (RFC4533) plugin" };
+
+static int sync_start(Slapi_PBlock * pb);
+static int sync_close(Slapi_PBlock * pb);
+static int sync_preop_init( Slapi_PBlock *pb );
+static int sync_postop_init( Slapi_PBlock *pb );
+
+int sync_init( Slapi_PBlock *pb )
+{
+ char *plugin_identity = NULL;
+ int rc = 0;
+
+ slapi_log_error(SLAPI_LOG_TRACE, SYNC_PLUGIN_SUBSYSTEM,
+ "--> sync_init\n");
+
+ /**
+ * Store the plugin identity for later use.
+ * Used for internal operations
+ */
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &plugin_identity);
+ PR_ASSERT(plugin_identity);
+
+ if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
+ (void *) sync_start) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
+ (void *) sync_close) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *) &pdesc) != 0 ) {
+ slapi_log_error(SLAPI_LOG_FATAL, SYNC_PLUGIN_SUBSYSTEM,
+ "sync_init: failed to register plugin\n");
+ rc = 1;
+ }
+
+ if (rc == 0) {
+ char *plugin_type = "preoperation";
+ /* the config change checking post op */
+ if (slapi_register_plugin(
+ plugin_type,
+ 1, /* Enabled */
+ "sync_init", /* this function desc */
+ sync_preop_init,/* init func for post op */
+ SYNC_PREOP_DESC,/* plugin desc */
+ NULL,
+ plugin_identity)) {
+ slapi_log_error(SLAPI_LOG_FATAL, SYNC_PLUGIN_SUBSYSTEM,
+ "sync_init: failed to register preop
plugin\n");
+ rc = 1;
+ }
+ }
+
+ if (rc == 0) {
+ char *plugin_type = "postoperation";
+ /* the config change checking post op */
+ if (slapi_register_plugin(plugin_type,
+ 1, /* Enabled */
+ "sync_init", /* this function desc */
+ sync_postop_init, /* init func for post op */
+ SYNC_POSTOP_DESC, /* plugin desc */
+ NULL,
+ plugin_identity )) {
+ slapi_log_error(SLAPI_LOG_FATAL, SYNC_PLUGIN_SUBSYSTEM,
+ "sync_init: failed to register postop
plugin\n");
+ rc = 1;
+ }
+ }
+
+ return( rc );
+}
+
+static int
+sync_preop_init( Slapi_PBlock *pb )
+{
+ int rc;
+ rc = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_SEARCH_FN, (void *)
sync_srch_refresh_pre_search);
+ rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ENTRY_FN, (void *)
sync_srch_refresh_pre_entry);
+ rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_RESULT_FN, (void *)
sync_srch_refresh_pre_result);
+ rc |= sync_register_operation_extension();
+ return(rc);
+
+}
+
+static int
+sync_postop_init( Slapi_PBlock *pb )
+{
+ int rc;
+ rc = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN, (void *)
sync_add_persist_post_op);
+ rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN, (void *)
sync_del_persist_post_op);
+ rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN, (void *)
sync_mod_persist_post_op);
+ rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN, (void *)
sync_modrdn_persist_post_op);
+ rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_POST_SEARCH_FN, (void *)
sync_srch_refresh_post_search);
+ return(rc);
+}
+
+/*
+ sync_start
+ --------------
+ Register the Content Synchronization Control.
+ Initialize locks and queues for the persitent phase.
+*/
+static int
+sync_start(Slapi_PBlock * pb)
+{
+ int argc;
+ char **argv;
+
+ slapi_register_supported_control( LDAP_CONTROL_SYNC,
+ SLAPI_OPERATION_SEARCH );
+ slapi_log_error(SLAPI_LOG_TRACE, SYNC_PLUGIN_SUBSYSTEM,
+ "--> sync_start\n");
+
+ if ( slapi_pblock_get( pb, SLAPI_PLUGIN_ARGC, &argc ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_PLUGIN_ARGV, &argv ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, SYNC_PLUGIN_SUBSYSTEM,
+ "unable to get arguments\n" );
+ return( -1 );
+ }
+ sync_persist_initialize(argc, argv);
+
+ return (0);
+}
+
+/*
+ sync_close
+ --------------
+ Free locks and queues allocated.
+*/
+static int
+sync_close(Slapi_PBlock * pb)
+{
+ sync_persist_terminate_all();
+ return (0);
+}
diff --git a/ldap/servers/plugins/sync/sync_persist.c
b/ldap/servers/plugins/sync/sync_persist.c
new file mode 100644
index 0000000..d35dde9
--- /dev/null
+++ b/ldap/servers/plugins/sync/sync_persist.c
@@ -0,0 +1,693 @@
+/** BEGIN COPYRIGHT BLOCK
+ent sync_srch_refresh_pre_op(Slapi_PBlock *pb);
+ * This Program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; version 2 of the License.
+ *
+ * This Program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * In addition, as a special exception, Red Hat, Inc. gives You the additional
+ * right to link the code of this Program with code not covered under the GNU
+ * General Public License ("Non-GPL Code") and to distribute linked
combinations
+ * including the two, subject to the limitations in this paragraph. Non-GPL Code
+ * permitted under this exception must only link to the code of this Program
+ * through those well defined interfaces identified in the file named EXCEPTION
+ * found in the source code files (the "Approved Interfaces"). The files of
+ * Non-GPL Code may instantiate templates or use macros or inline functions from
+ * the Approved Interfaces without causing the resulting work to be covered by
+ * the GNU General Public License. Only Red Hat, Inc. may make changes or
+ * additions to the list of Approved Interfaces. You must obey the GNU General
+ * Public License in all respects for all of the Program code and other code used
+ * in conjunction with the Program except the Non-GPL Code covered by this
+ * exception. If you modify this file, you may extend this exception to your
+ * version of the file, but you are not obligated to do so. If you do not wish to
+ * provide this exception without modification, you must delete this exception
+ * statement from your version and license this file solely under the GPL without
+ * exception.
+ *
+ *
+ * Copyright (C) 2013 Red Hat, Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "sync.h"
+
+/* Main list of established persistent synchronizaton searches */
+static SyncRequestList *sync_request_list = NULL;
+/*
+ * Convenience macros for locking the list of persistent searches
+ */
+#define SYNC_LOCK_READ() slapi_rwlock_rdlock(sync_request_list->sync_req_rwlock)
+#define SYNC_UNLOCK_READ() slapi_rwlock_unlock(sync_request_list->sync_req_rwlock)
+#define SYNC_LOCK_WRITE() slapi_rwlock_wrlock(sync_request_list->sync_req_rwlock)
+#define SYNC_UNLOCK_WRITE() slapi_rwlock_unlock(sync_request_list->sync_req_rwlock)
+
+/*
+ * Convenience macro for checking if the Content Synchronization subsystem has
+ * been initialized.
+ */
+#define SYNC_IS_INITIALIZED() (sync_request_list != NULL)
+
+
+static int sync_add_request( SyncRequest *req );
+static void sync_remove_request( SyncRequest *req );
+static SyncRequest *sync_request_alloc();
+void sync_queue_change( Slapi_Entry *e, Slapi_Entry *eprev, ber_int_t chgtype );
+static void sync_send_results( void *arg );
+static void sync_request_wakeup_all();
+static void sync_node_free( SyncQueueNode **node );
+
+static int sync_acquire_connection (Slapi_Connection *conn);
+static int sync_release_connection (Slapi_PBlock *pb, Slapi_Connection *conn,
Slapi_Operation *op, int release);
+
+int sync_add_persist_post_op(Slapi_PBlock *pb)
+{
+ Slapi_Entry *e;
+
+ if ( !SYNC_IS_INITIALIZED()) {
+ return(0);
+ }
+ slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &e);
+ sync_queue_change(e, NULL, LDAP_REQ_ADD);
+ return( 0 );
+}
+
+int sync_del_persist_post_op(Slapi_PBlock *pb)
+{
+ Slapi_Entry *e;
+
+ if ( !SYNC_IS_INITIALIZED()) {
+ return(0);
+ }
+ slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &e);
+ sync_queue_change(e, NULL, LDAP_REQ_DELETE);
+ return( 0 );
+}
+
+int sync_mod_persist_post_op(Slapi_PBlock *pb)
+{
+ Slapi_Entry *e;
+
+ if ( !SYNC_IS_INITIALIZED()) {
+ return(0);
+ }
+ slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &e);
+ sync_queue_change(e, NULL, LDAP_REQ_MODIFY);
+ return( 0 );
+}
+
+int sync_modrdn_persist_post_op(Slapi_PBlock *pb)
+{
+ Slapi_Entry *e, *e_prev;
+
+ if ( !SYNC_IS_INITIALIZED()) {
+ return(0);
+ }
+ slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &e);
+ slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &e_prev);
+ sync_queue_change(e, e_prev, LDAP_REQ_MODRDN);
+ return( 0 );
+}
+
+void
+sync_queue_change( Slapi_Entry *e, Slapi_Entry *eprev, ber_int_t chgtype )
+{
+ SyncRequest *req = NULL;
+ SyncQueueNode *node = NULL;
+ int matched = 0;
+ int prev_match = 0;
+ int cur_match = 0;
+
+ if ( !SYNC_IS_INITIALIZED()) {
+ return;
+ }
+
+ if ( NULL == e ) {
+ /* For now, some backends such as the chaining backend do not provide a post-op entry
*/
+ return;
+ }
+
+ SYNC_LOCK_READ();
+
+ for ( req = sync_request_list->sync_req_head; NULL != req; req = req->req_next )
{
+ Slapi_DN *base = NULL;
+ int scope;
+ Slapi_Operation *op;
+
+ /* Skip the nodes that have no more active operation
+ */
+ slapi_pblock_get( req->req_pblock, SLAPI_OPERATION, &op );
+ if ( op == NULL || slapi_op_abandoned( req->req_pblock ) ) {
+ continue;
+ }
+
+ slapi_pblock_get( req->req_pblock, SLAPI_SEARCH_TARGET_SDN, &base );
+ slapi_pblock_get( req->req_pblock, SLAPI_SEARCH_SCOPE, &scope );
+ if (NULL == base) {
+ base = slapi_sdn_new_dn_byref(req->req_orig_base);
+ slapi_pblock_set(req->req_pblock, SLAPI_SEARCH_TARGET_SDN, base);
+ }
+
+ /*
+ * See if the entry meets the scope and filter criteria.
+ * We cannot do the acl check here as this thread
+ * would then potentially clash with the ps_send_results()
+ * thread on the aclpb in ps->req_pblock.
+ * By avoiding the acl check in this thread, and leaving all the acl
+ * checking to the ps_send_results() thread we avoid
+ * the req_pblock contention problem.
+ * The lesson here is "Do not give multiple threads arbitary access
+ * to the same pblock" this kind of muti-threaded access
+ * to the same pblock must be done carefully--there is currently no
+ * generic satisfactory way to do this.
+ */
+
+ /* if the change is a modrdn then we need to check if the entry was
+ * moved into scope, out of scope, or stays in scope
+ */
+ if (chgtype == LDAP_REQ_MODRDN)
+ prev_match = slapi_sdn_scope_test( slapi_entry_get_sdn_const(eprev), base, scope )
&&
+ ( 0 == slapi_vattr_filter_test( req->req_pblock, eprev, req->req_filter, 0 /*
verify_access */ ));
+
+ cur_match = slapi_sdn_scope_test( slapi_entry_get_sdn_const(e), base, scope )
&&
+ ( 0 == slapi_vattr_filter_test( req->req_pblock, e, req->req_filter, 0 /*
verify_access */ ));
+
+ if (prev_match || cur_match) {
+ SyncQueueNode *pOldtail;
+
+ /* The scope and the filter match - enqueue it */
+
+ matched++;
+ node = (SyncQueueNode *)slapi_ch_calloc( 1, sizeof( SyncQueueNode ));
+ node->sync_entry = slapi_entry_dup( e );
+
+ if ( chgtype == LDAP_REQ_MODRDN) {
+ if (prev_match && cur_match)
+ node->sync_chgtype = LDAP_REQ_MODIFY;
+ else if (prev_match)
+ node->sync_chgtype = LDAP_REQ_DELETE;
+ else
+ node->sync_chgtype = LDAP_REQ_ADD;
+ } else {
+ node->sync_chgtype = chgtype;
+ }
+ /* Put it on the end of the list for this sync search */
+ PR_Lock( req->req_lock );
+ pOldtail = req->ps_eq_tail;
+ req->ps_eq_tail = node;
+ if ( NULL == req->ps_eq_head ) {
+ req->ps_eq_head = req->ps_eq_tail;
+ }
+ else {
+ pOldtail->sync_next = req->ps_eq_tail;
+ }
+ PR_Unlock( req->req_lock );
+ }
+ }
+
+ SYNC_UNLOCK_READ();
+
+ /* Were there any matches? */
+ if ( matched ) {
+ /* Notify update threads */
+ sync_request_wakeup_all();
+ slapi_log_error (SLAPI_LOG_TRACE, SYNC_PLUGIN_SUBSYSTEM, "sync search: enqueued
entry "
+ "\"%s\" on %d request listeners\n", slapi_entry_get_dn_const(e),
matched );
+ } else {
+ slapi_log_error (SLAPI_LOG_TRACE, SYNC_PLUGIN_SUBSYSTEM, "sync search: entry
"
+ "\"%s\" not enqueued on any request search listeners\n",
slapi_entry_get_dn_const(e) );
+ }
+
+}
+/*
+ * Initialize the list structure which contains the list
+ * of established content sync persistent requests
+ */
+int
+sync_persist_initialize (int argc, char **argv)
+{
+ if ( !SYNC_IS_INITIALIZED()) {
+ sync_request_list = (SyncRequestList *) slapi_ch_calloc( 1, sizeof( SyncRequestList
));
+ if (( sync_request_list->sync_req_rwlock = slapi_new_rwlock()) == NULL ) {
+ slapi_log_error (SLAPI_LOG_FATAL, SYNC_PLUGIN_SUBSYSTEM,
"sync_persist_initialize: cannot initialize lock structure(1). ");
+ return( -1 );
+ }
+ if (( sync_request_list->sync_req_cvarlock = PR_NewLock()) == NULL ) {
+ slapi_log_error (SLAPI_LOG_FATAL, SYNC_PLUGIN_SUBSYSTEM,
"sync_persist_initialize: cannot initialize lock structure(2). ");
+ return( -1 );
+ }
+ if (( sync_request_list->sync_req_cvar = PR_NewCondVar(
sync_request_list->sync_req_cvarlock )) == NULL ) {
+ slapi_log_error (SLAPI_LOG_FATAL, SYNC_PLUGIN_SUBSYSTEM,
"sync_persist_initialize: cannot initialize condition variable. ");
+ return( -1 );
+ }
+ sync_request_list->sync_req_head = NULL;
+ sync_request_list->sync_req_cur_persist = 0;
+ sync_request_list->sync_req_max_persist = SYNC_MAX_CONCURRENT;
+ if (argc > 0) {
+ /* for now the only plugin arg is the max concurrent
+ * persistent sync searches
+ */
+ sync_request_list->sync_req_max_persist = sync_number2int(argv[0]);
+ if (sync_request_list->sync_req_max_persist == -1) {
+ sync_request_list->sync_req_max_persist = SYNC_MAX_CONCURRENT;
+ }
+ }
+ }
+ return (0);
+}
+/*
+ * Add the given pblock to the list of established sync searches.
+ * Then, start a thread to send the results to the client as they
+ * are dispatched by add, modify, and modrdn operations.
+ */
+PRThread *
+sync_persist_add (Slapi_PBlock *pb)
+{
+ SyncRequest *req = NULL;
+ char *base;
+ Slapi_Filter *filter;
+
+ if ( SYNC_IS_INITIALIZED() && NULL != pb ) {
+ /* Create the new node */
+ req = sync_request_alloc();
+ req->req_pblock = sync_pblock_copy(pb);
+ slapi_pblock_get(pb, SLAPI_ORIGINAL_TARGET_DN, &base);
+ req->req_orig_base = slapi_ch_strdup(base);
+ slapi_pblock_get( pb, SLAPI_SEARCH_FILTER, &filter );
+ req->req_filter = slapi_filter_dup(filter);
+
+ /* Add it to the head of the list of persistent searches */
+ if ( 0 == sync_add_request( req )) {
+
+ /* Start a thread to send the results */
+ req->req_tid = PR_CreateThread( PR_USER_THREAD, sync_send_results,
+ (void *) req, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE );
+
+ /* Checking if the thread is succesfully created and
+ * if the thread is not created succesfully.... we send
+ * error messages to the Log file
+ */
+ if(NULL == (req->req_tid)){
+ int prerr;
+ prerr = PR_GetError();
+ slapi_log_error(SLAPI_LOG_FATAL, "Content Synchronization Search",
+ "sync_persist_add function: failed to create persitent thread, error %d
(%s)\n",
+ prerr, slapi_pr_strerror(prerr));
+ /* Now remove the ps from the list so call the function ps_remove */
+ sync_remove_request(req);
+ PR_DestroyLock ( req->req_lock );
+ req->req_lock = NULL;
+ slapi_ch_free((void **) &req->req_pblock );
+ slapi_ch_free((void **) &req );
+ } else {
+ return( req->req_tid);
+ }
+ }
+ }
+ return( NULL);
+}
+
+int
+sync_persist_startup (PRThread *tid, Sync_Cookie *cookie)
+{
+ SyncRequest *cur;
+ int rc = 1;
+
+ if ( SYNC_IS_INITIALIZED() && NULL != tid ) {
+ SYNC_LOCK_READ();
+ /* Find and change */
+ cur = sync_request_list->sync_req_head;
+ while ( NULL != cur ) {
+ if ( cur->req_tid == tid ) {
+ cur->req_active = PR_TRUE;
+ cur->req_cookie = cookie;
+ rc = 0;
+ break;
+ }
+ cur = cur->req_next;
+ }
+ SYNC_UNLOCK_READ();
+ }
+ return (rc);
+}
+
+
+int
+sync_persist_terminate (PRThread *tid)
+{
+ SyncRequest *cur;
+ int rc = 1;
+
+ if ( SYNC_IS_INITIALIZED() && NULL != tid ) {
+ SYNC_LOCK_READ();
+ /* Find and change */
+ cur = sync_request_list->sync_req_head;
+ while ( NULL != cur ) {
+ if ( cur->req_tid == tid ) {
+ cur->req_active = PR_TRUE;
+ rc = 0;
+ break;
+ }
+ cur = cur->req_next;
+ }
+ SYNC_UNLOCK_READ();
+ }
+ if (rc == 0) {
+ sync_remove_request(cur);
+ }
+ return(rc);
+}
+int
+sync_persist_terminate_all ()
+{
+ SyncRequest *cur;
+
+ if ( SYNC_IS_INITIALIZED() ) {
+ SYNC_LOCK_READ();
+ cur = sync_request_list->sync_req_head;
+ while ( NULL != cur ) {
+ cur->req_complete = PR_TRUE;
+ cur = cur->req_next;
+ }
+ SYNC_UNLOCK_READ();
+ }
+
+ return (0);
+}
+
+/*
+ * Allocate and initialize an empty Sync node.
+ */
+static SyncRequest *
+sync_request_alloc()
+{
+ SyncRequest *req;
+
+ req = (SyncRequest *) slapi_ch_calloc( 1, sizeof( SyncRequest ));
+
+ req->req_pblock = NULL;
+ if (( req->req_lock = PR_NewLock()) == NULL ) {
+ slapi_log_error (SLAPI_LOG_FATAL, SYNC_PLUGIN_SUBSYSTEM, "sync_request_alloc:
cannot initialize lock structure. ");
+ slapi_ch_free((void **)&req);
+ return( NULL );
+ }
+ req->req_tid = (PRThread *) NULL;
+ req->req_complete = 0;
+ req->req_cookie = NULL;
+ req->ps_eq_head = req->ps_eq_tail = (SyncQueueNode *) NULL;
+ req->req_next = NULL;
+ req->req_active = PR_FALSE;
+ return req;
+}
+
+
+
+/*
+ * Add the given persistent search to the
+ * head of the list of persistent searches.
+ */
+static int
+sync_add_request( SyncRequest *req )
+{
+ int rc = 0;
+ if ( SYNC_IS_INITIALIZED() && NULL != req ) {
+ SYNC_LOCK_WRITE();
+ if (sync_request_list->sync_req_cur_persist <
sync_request_list->sync_req_max_persist) {
+ sync_request_list->sync_req_cur_persist++;
+ req->req_next = sync_request_list->sync_req_head;
+ sync_request_list->sync_req_head = req;
+ } else {
+ rc = 1;
+ }
+ SYNC_UNLOCK_WRITE();
+ }
+ return(rc);
+}
+
+static void
+sync_remove_request( SyncRequest *req )
+{
+ SyncRequest *cur;
+ int removed = 0;
+
+ if ( SYNC_IS_INITIALIZED() && NULL != req ) {
+ SYNC_LOCK_WRITE();
+ if ( NULL == sync_request_list->sync_req_head ) {
+ /* should not happen, attempt to remove a request never added */
+ } else if ( req == sync_request_list->sync_req_head ) {
+ /* Remove from head */
+ sync_request_list->sync_req_head =
sync_request_list->sync_req_head->req_next;
+ removed = 1;
+ } else {
+ /* Find and remove from list */
+ cur = sync_request_list->sync_req_head;
+ while ( NULL != cur->req_next ) {
+ if ( cur->req_next == req ) {
+ cur->req_next = cur->req_next->req_next;
+ removed = 1;
+ break;
+ } else {
+ cur = cur->req_next;
+ }
+ }
+ }
+ if (removed) {
+ sync_request_list->sync_req_cur_persist--;
+ }
+ SYNC_UNLOCK_WRITE();
+ if (!removed) {
+ slapi_log_error (SLAPI_LOG_PLUGIN, SYNC_PLUGIN_SUBSYSTEM, "attempt to remove
nonexistent req");
+ }
+ }
+}
+
+static void
+sync_request_wakeup_all()
+{
+ if ( SYNC_IS_INITIALIZED()) {
+ PR_Lock( sync_request_list->sync_req_cvarlock );
+ PR_NotifyAllCondVar( sync_request_list->sync_req_cvar );
+ PR_Unlock( sync_request_list->sync_req_cvarlock );
+ }
+}
+static int
+sync_acquire_connection (Slapi_Connection *conn)
+{
+ int rc;
+ /* need to acquire a reference to this connection so that it will not
+ be released or cleaned up out from under us
+
+ in psearch.c it is implemented as:
+ PR_Lock( ps->req_pblock->pb_conn->c_mutex );
+ conn_acq_flag = connection_acquire_nolock(ps->req_pblock->pb_conn);
+ PR_Unlock( ps->req_pblock->pb_conn->c_mutex );
+
+
+ HOW TO DO FROM A PLUGIN
+ - either expose the functions from the connection code in the private api
+ and allow to link them in
+ - or fake a connection structure
+ struct fake_conn {
+ void *needed1
+ void *needed2
+ void *pad1
+ void *pad2
+ void *needed3;
+ }
+ struct fake_conn *c = (struct fake_conn *) conn;
+ c->needed3 ++;
+ this would require knowledge or analysis of the connection structure,
+ could probably be done for servers with a common history
+ */
+ /* use exposed slapi_connection functions */
+ rc = slapi_connection_acquire(conn);
+ return (rc);
+}
+
+static int
+sync_release_connection (Slapi_PBlock *pb, Slapi_Connection *conn, Slapi_Operation *op,
int release)
+{
+ /* see comments in sync_acquire_connection */
+
+ /* using exposed connection handling functions */
+
+ slapi_connection_remove_operation(pb, conn, op, release);
+
+ return(0);
+}
+/*
+ * Thread routine for sending search results to a client
+ * which is persistently waiting for them.
+ *
+ * This routine will terminate when either (a) the ps_complete
+ * flag is set, or (b) the associated operation is abandoned.
+ * In any case, the thread won't notice until it wakes from
+ * sleeping on the ps_list condition variable, so it needs
+ * to be awakened.
+ */
+static void
+sync_send_results( void *arg )
+{
+ SyncRequest *req = (SyncRequest *)arg;
+ SyncQueueNode *qnode, *qnodenext;
+ int conn_acq_flag = 0;
+ Slapi_Connection *conn = NULL;
+ Slapi_Operation *op = NULL;
+ int rc;
+ PRUint64 connid;
+ int opid;
+
+ slapi_pblock_get(req->req_pblock, SLAPI_CONN_ID, &connid);
+ slapi_pblock_get(req->req_pblock, SLAPI_OPERATION_ID, &opid);
+ slapi_pblock_get(req->req_pblock, SLAPI_CONNECTION, &conn);
+ slapi_pblock_get(req->req_pblock, SLAPI_OPERATION, &op);
+
+ conn_acq_flag = sync_acquire_connection (conn);
+ if (conn_acq_flag) {
+ slapi_log_error(SLAPI_LOG_FATAL, "Content Synchronization Search",
+ "conn=%" NSPRIu64 " op=%d Could not acquire the connection -
aborted\n",
+ (long long unsigned int)connid, opid);
+ }
+
+ PR_Lock( sync_request_list->sync_req_cvarlock );
+
+ while ( (conn_acq_flag == 0) && !req->req_complete ) {
+ /* Check for an abandoned operation */
+ Slapi_Operation *op;
+ slapi_pblock_get(req->req_pblock, SLAPI_OPERATION, &op);
+ if ( op == NULL || slapi_op_abandoned( req->req_pblock ) ) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, "Content Synchronization Search",
+ "conn=%" NSPRIu64 " op=%d Operation no longer active -
terminating\n",
+ (long long unsigned int)connid, opid);
+ break;
+ }
+ if ( NULL == req->ps_eq_head || !req->req_active) {
+ /* Nothing to do yet, or the refresh phase is not yet completed */
+ /* If an operation is abandoned, we do not get notified by the
+ * connection code. Wake up every second to check if thread
+ * should terminate.
+ */
+ PR_WaitCondVar( sync_request_list->sync_req_cvar, PR_SecondsToInterval(1) );
+ } else {
+ /* dequeue the item */
+ int attrsonly;
+ char **attrs;
+ char **noattrs = NULL;
+ LDAPControl **ectrls = NULL;
+ Slapi_Entry *ec;
+ int chg_type;
+
+ /* deque one element */
+ PR_Lock( req->req_lock );
+ qnode = req->ps_eq_head;
+ req->ps_eq_head = qnode->sync_next;
+ if ( NULL == req->ps_eq_head ) {
+ req->ps_eq_tail = NULL;
+ }
+ PR_Unlock( req->req_lock );
+
+ /* Get all the information we need to send the result */
+ ec = qnode->sync_entry;
+ slapi_pblock_get( req->req_pblock, SLAPI_SEARCH_ATTRS, &attrs );
+ slapi_pblock_get( req->req_pblock, SLAPI_SEARCH_ATTRSONLY, &attrsonly );
+
+ /*
+ * Send the result. Since send_ldap_search_entry can block for
+ * up to 30 minutes, we relinquish all locks before calling it.
+ */
+ PR_Unlock(sync_request_list->sync_req_cvarlock);
+
+ /*
+ * The entry is in the right scope and matches the filter
+ * but we need to redo the filter test here to check access
+ * controls. See the comments at the slapi_filter_test()
+ * call in sync_persist_add().
+ */
+
+ if ( slapi_vattr_filter_test( req->req_pblock, ec, req->req_filter,
+ 1 /* verify_access */ ) == 0 ) {
+ slapi_pblock_set( req->req_pblock, SLAPI_SEARCH_RESULT_ENTRY, ec );
+
+ /* NEED TO BUILD THE CONTROL */
+ switch (qnode->sync_chgtype){
+ case LDAP_REQ_ADD:
+ chg_type = LDAP_SYNC_ADD;
+ break;
+ case LDAP_REQ_MODIFY:
+ chg_type = LDAP_SYNC_MODIFY;
+ break;
+ case LDAP_REQ_MODRDN:
+ chg_type = LDAP_SYNC_MODIFY;
+ break;
+ case LDAP_REQ_DELETE:
+ chg_type = LDAP_SYNC_DELETE;
+ noattrs = (char **)slapi_ch_calloc(2, sizeof (char *));
+ noattrs[0] = slapi_ch_strdup("1.1");
+ noattrs[1] = NULL;
+ break;
+ }
+ ectrls = (LDAPControl **)slapi_ch_calloc(2, sizeof (LDAPControl *));
+ if (req->req_cookie)
+ sync_cookie_update(req->req_cookie, ec);
+ sync_create_state_control(ec, &ectrls[0], chg_type, req->req_cookie);
+ rc = slapi_send_ldap_search_entry( req->req_pblock,
+ ec, ectrls,
+ noattrs?noattrs:attrs, attrsonly );
+ if (rc) {
+ slapi_log_error(SLAPI_LOG_CONNS, SYNC_PLUGIN_SUBSYSTEM,
+ "Error %d sending entry %s\n",
+ rc, slapi_entry_get_dn_const(ec));
+ }
+ ldap_controls_free(ectrls);
+ slapi_ch_array_free(noattrs);
+
+ }
+ PR_Lock(sync_request_list->sync_req_cvarlock);
+
+ /* Deallocate our wrapper for this entry */
+ sync_node_free( &qnode );
+ }
+ }
+ PR_Unlock( sync_request_list->sync_req_cvarlock );
+ sync_remove_request( req );
+
+ /* indicate the end of search */
+
+ sync_release_connection(req->req_pblock, conn, op, conn_acq_flag == 0);
+
+ PR_DestroyLock ( req->req_lock );
+ req->req_lock = NULL;
+
+ slapi_ch_free((void **) &req->req_pblock );
+ slapi_ch_free((void **) &req->req_orig_base );
+ slapi_filter_free(req->req_filter, 1);
+ sync_cookie_free(&req->req_cookie);
+ for ( qnode = req->ps_eq_head; qnode; qnode = qnodenext) {
+ qnodenext = qnode->sync_next;
+ sync_node_free( &qnode );
+ }
+ slapi_ch_free((void **) &req );
+}
+
+
+/*
+ * Free a sync update node (and everything it holds).
+ */
+static void
+sync_node_free( SyncQueueNode **node )
+{
+ if ( node != NULL && *node != NULL ) {
+ if ( (*node)->sync_entry != NULL ) {
+ slapi_entry_free( (*node)->sync_entry );
+ (*node)->sync_entry = NULL;
+ }
+ slapi_ch_free( (void **)node );
+ }
+}
diff --git a/ldap/servers/plugins/sync/sync_refresh.c
b/ldap/servers/plugins/sync/sync_refresh.c
new file mode 100644
index 0000000..1487289
--- /dev/null
+++ b/ldap/servers/plugins/sync/sync_refresh.c
@@ -0,0 +1,732 @@
+/** BEGIN COPYRIGHT BLOCK
+ * This Program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; version 2 of the License.
+ *
+ * This Program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * In addition, as a special exception, Red Hat, Inc. gives You the additional
+ * right to link the code of this Program with code not covered under the GNU
+ * General Public License ("Non-GPL Code") and to distribute linked
combinations
+ * including the two, subject to the limitations in this paragraph. Non-GPL Code
+ * permitted under this exception must only link to the code of this Program
+ * through those well defined interfaces identified in the file named EXCEPTION
+ * found in the source code files (the "Approved Interfaces"). The files of
+ * Non-GPL Code may instantiate templates or use macros or inline functions from
+ * the Approved Interfaces without causing the resulting work to be covered by
+ * the GNU General Public License. Only Red Hat, Inc. may make changes or
+ * additions to the list of Approved Interfaces. You must obey the GNU General
+ * Public License in all respects for all of the Program code and other code used
+ * in conjunction with the Program except the Non-GPL Code covered by this
+ * exception. If you modify this file, you may extend this exception to your
+ * version of the file, but you are not obligated to do so. If you do not wish to
+ * provide this exception without modification, you must delete this exception
+ * statement from your version and license this file solely under the GPL without
+ * exception.
+ *
+ *
+ * Copyright (C) 2013 Red Hat, Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "sync.h"
+
+static SyncOpInfo *new_SyncOpInfo(int flag, Sync_Cookie *cookie);
+
+static int sync_extension_type;
+static int sync_extension_handle;
+
+static SyncOpInfo *sync_get_operation_extension(Slapi_PBlock *pb);
+static void sync_set_operation_extension(Slapi_PBlock *pb, SyncOpInfo *spec);
+static int sync_find_ref_by_uuid(Sync_UpdateNode *updates, int stop, char *uniqueid);
+static void sync_free_update_nodes (Sync_UpdateNode **updates, int count);
+Slapi_Entry *sync_deleted_entry_from_changelog(Slapi_Entry *cl_entry);
+static int sync_feature_allowed (Slapi_PBlock *pb);
+
+static int sync_feature_allowed (Slapi_PBlock *pb)
+{
+ int isroot = 0;
+ int ldapcode = LDAP_SUCCESS;
+
+ slapi_pblock_get(pb, SLAPI_REQUESTOR_ISROOT, &isroot);
+ if ( !isroot) {
+ char *dn;
+ Slapi_Entry *feature = NULL;
+
+ /* Fetch the feature entry and see if the requestor is allowed access. */
+ dn = slapi_ch_smprintf("dn: oid=%s,cn=features,cn=config",
LDAP_CONTROL_SYNC);
+ if ((feature = slapi_str2entry(dn,0)) != NULL) {
+ char *dummy_attr = "1.1";
+
+ ldapcode = slapi_access_allowed(pb, feature, dummy_attr, NULL, SLAPI_ACL_READ);
+ }
+
+ /* If the feature entry does not exist, deny use of the control. Only
+ * the root DN will be allowed to use the control in this case. */
+ if ((feature == NULL) || (ldapcode != LDAP_SUCCESS)) {
+ ldapcode = LDAP_INSUFFICIENT_ACCESS;
+ }
+ slapi_ch_free((void **)&dn);
+ slapi_entry_free(feature);
+ }
+ return(ldapcode);
+}
+
+int sync_srch_refresh_pre_search(Slapi_PBlock *pb)
+{
+
+ LDAPControl **requestcontrols;
+ struct berval *psbvp;
+ Sync_Cookie *client_cookie = NULL;
+ Sync_Cookie *session_cookie = NULL;
+ int rc = 0;
+ int sync_persist = 0;
+ PRThread *tid = NULL;
+ int entries_sent = 0;
+
+ slapi_pblock_get (pb, SLAPI_REQCONTROLS, &requestcontrols);
+ if ( slapi_control_present( requestcontrols, LDAP_CONTROL_SYNC, &psbvp, NULL )){
+ char *cookie = NULL;
+ int mode = 1;
+ int refresh = 0;
+
+ if ( sync_parse_control_value( psbvp, &mode,
+ &refresh, &cookie ) != LDAP_SUCCESS )
+ {
+ rc = 1;
+ goto error_return;
+ } else {
+ /* control is valid, check if usere is allowed to perform sync searches */
+ rc = sync_feature_allowed(pb);
+ if (rc) {
+ sync_result_err(pb,rc,NULL);
+ goto error_return;
+ }
+ }
+
+ if ( mode == 1 || mode == 3 )
+ {
+
+ /* we need to return a cookie in the result message
+ * indicating a state to be used in future sessions
+ * as starting point - create it now
+ */
+ session_cookie = sync_cookie_create(pb);
+ /*
+ * if mode is persist we need to setup the persit handler
+ * to catch the mods while the refresh is done
+ */
+ if ( mode == 3 )
+ {
+ tid = sync_persist_add(pb);
+ if ( tid )
+ sync_persist = 1;
+ else {
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ sync_result_err(pb,rc,"Too many active synchronization sessions");
+ goto error_return;
+ }
+ }
+ /*
+ * now handl the refresh request
+ * there are two scenarios
+ * 1. no cookie is provided this means send all entries matching the search request
+ * 2. a cookie is provided: send all entries changed since the cookie was issued
+ * -- return an error if the cookie is invalid
+ * -- return e-syncRefreshRequired if the data referenced in the cookie are no
+ * longer in the history
+ */
+ if (cookie &&
+ ( client_cookie = sync_cookie_parse (cookie))) {
+ if (sync_cookie_isvalid(client_cookie, session_cookie)) {
+ rc = sync_refresh_update_content(pb, client_cookie, session_cookie);
+ if (rc == 0)
+ entries_sent = 1;
+ if (sync_persist)
+ rc = sync_intermediate_msg(pb, LDAP_TAG_SYNC_REFRESH_DELETE, session_cookie,
NULL);
+ else
+ rc = sync_result_msg(pb, session_cookie);
+ } else {
+ rc = E_SYNC_REFRESH_REQUIRED;
+ sync_result_err(pb,rc, "Invalid session cookie");
+ }
+ } else {
+ rc = sync_refresh_initial_content (pb, sync_persist, session_cookie);
+ if (rc == 0 && !sync_persist)
+ /* maintained in postop code */
+ session_cookie = NULL;
+ /* if persis it will be handed over to persist code */
+ }
+
+ if ( rc ) {
+ if (sync_persist)
+ sync_persist_terminate (tid);
+ goto error_return;
+ } else if (sync_persist){
+ Slapi_Operation *operation;
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &operation);
+ rc = sync_persist_startup(tid, session_cookie);
+
+ if (rc == 0) {
+ session_cookie = NULL; /* maintained in persist code */
+ slapi_operation_set_flag(operation, OP_FLAG_SYNC_PERSIST);
+ }
+ }
+
+
+ } else {
+ /* unknown mode, return an error */
+ rc = 1;
+ }
+error_return:
+ sync_cookie_free(&client_cookie);
+ sync_cookie_free(&session_cookie);
+ slapi_ch_free((void **)&cookie);
+ }
+
+ /* if we sent the entries
+ * return "error" to abort normal search
+ */
+ if ( entries_sent > 0 ) {
+ return(1);
+ } else {
+ return(rc);
+ }
+}
+
+int sync_srch_refresh_post_search(Slapi_PBlock *pb)
+{
+ int rc = 0;
+ SyncOpInfo *info = sync_get_operation_extension(pb);
+
+ if (!info) {
+ return (0); /* nothing to do */
+ }
+ if (info->send_flag & SYNC_FLAG_SEND_INTERMEDIATE) {
+ rc = sync_intermediate_msg(pb, LDAP_TAG_SYNC_REFRESH_DELETE, info->cookie, NULL);
+ /* the refresh phase is over, now the post op
+ * plugins will create the state control
+ * depending on the operation type, reset flag
+ */
+ info->send_flag &= ~SYNC_FLAG_ADD_STATE_CTRL;
+ }
+ if (info->send_flag & SYNC_FLAG_ADD_DONE_CTRL) {
+ LDAPControl **ctrl = (LDAPControl **)slapi_ch_calloc(2, sizeof (LDAPControl *));
+ char *cookiestr = sync_cookie2str(info->cookie);
+ sync_create_sync_done_control( &ctrl[0], 0, cookiestr);
+ slapi_pblock_set(pb, SLAPI_RESCONTROLS, ctrl);
+ slapi_ch_free((void **)&cookiestr);
+ }
+ return(rc);
+}
+
+int sync_srch_refresh_pre_entry(Slapi_PBlock *pb)
+{
+ int rc = 0;
+ SyncOpInfo *info = sync_get_operation_extension(pb);
+
+ if (!info) {
+ rc = 0; /* nothing to do */
+ } else if (info->send_flag & SYNC_FLAG_ADD_STATE_CTRL) {
+ Slapi_Entry *e;
+ slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_ENTRY, &e);
+ LDAPControl **ctrl = (LDAPControl **)slapi_ch_calloc(2, sizeof (LDAPControl *));
+ sync_create_state_control(e, &ctrl[0], LDAP_SYNC_ADD, NULL);
+ slapi_pblock_set(pb, SLAPI_SEARCH_CTRLS, ctrl);
+ }
+ return(rc);
+}
+
+int sync_srch_refresh_pre_result(Slapi_PBlock *pb)
+{
+ SyncOpInfo *info = sync_get_operation_extension(pb);
+
+ if (!info) {
+ return 0; /* nothing to do */
+ }
+ if (info->send_flag & SYNC_FLAG_NO_RESULT ) {
+ return(1);
+ } else {
+ return(0);
+ }
+}
+
+static void
+sync_free_update_nodes (Sync_UpdateNode **updates, int count)
+{
+ int i;
+
+ for (i=0; i<count;i++) {
+ if ((*updates)[i].upd_uuid)
+ slapi_ch_free((void **)&((*updates)[i].upd_uuid));
+ if ((*updates)[i].upd_e)
+ slapi_entry_free((*updates)[i].upd_e);
+ }
+ slapi_ch_free((void **)updates);
+}
+
+int
+sync_refresh_update_content(Slapi_PBlock *pb, Sync_Cookie *client_cookie, Sync_Cookie
*server_cookie)
+{
+ Slapi_PBlock *seq_pb;
+ char *filter;
+ Sync_CallBackData cb_data;
+ int rc;
+ int chg_count = server_cookie->cookie_change_info -
+ client_cookie->cookie_change_info + 1;
+
+ cb_data.cb_updates = (Sync_UpdateNode *)slapi_ch_calloc(chg_count,
sizeof(Sync_UpdateNode));
+
+ seq_pb = slapi_pblock_new();
+ slapi_pblock_init(seq_pb);
+
+ cb_data.orig_pb = pb;
+ cb_data.change_start = client_cookie->cookie_change_info;
+
+ filter =
slapi_ch_smprintf("(&(changenumber>=%d)(changenumber<=%d))",
+ client_cookie->cookie_change_info,
+ server_cookie->cookie_change_info);
+ slapi_search_internal_set_pb(
+ seq_pb,
+ CL_SRCH_BASE,
+ LDAP_SCOPE_ONE,
+ filter,
+ NULL,
+ 0,
+ NULL, NULL,
+ plugin_get_default_component_id(),
+ 0);
+
+ rc = slapi_search_internal_callback_pb (
+ seq_pb, &cb_data, NULL, sync_read_entry_from_changelog, NULL);
+ slapi_pblock_destroy(seq_pb);
+
+ /* Now send the deleted entries in a sync info message
+ * and the modified entries as single entries
+ */
+ sync_send_deleted_entries(pb, cb_data.cb_updates, chg_count, server_cookie);
+ sync_send_modified_entries(pb, cb_data.cb_updates, chg_count);
+
+ sync_free_update_nodes(&cb_data.cb_updates, chg_count);
+ slapi_ch_free((void **)&filter);
+ return (rc);
+}
+
+int
+sync_refresh_initial_content(Slapi_PBlock *pb, int sync_persist, Sync_Cookie *sc)
+{
+ /* the entries will be sent in the normal search process, but
+ * - a control has to be sent with each entry
+ * if sync persist:
+ * - an intermediate response has to be sent
+ * - no result message must be sent
+ *
+ * else
+ * - a result message with a sync done control has to be sent
+ *
+ * setup on operation extension to take care of in
+ * pre_entry, pre_result and post_search plugins
+ */
+ SyncOpInfo *info;
+
+ if (sync_persist) {
+ info = new_SyncOpInfo
+ (SYNC_FLAG_ADD_STATE_CTRL |
+ SYNC_FLAG_SEND_INTERMEDIATE |
+ SYNC_FLAG_NO_RESULT,
+ sc);
+ } else {
+ info = new_SyncOpInfo
+ (SYNC_FLAG_ADD_STATE_CTRL |
+ SYNC_FLAG_ADD_DONE_CTRL,
+ sc);
+ }
+ sync_set_operation_extension(pb, info);
+
+ return(0);
+}
+
+static int
+sync_str2chgreq(char *chgtype)
+{
+ if (chgtype == NULL) {
+ return(-1);
+ }
+ if (strcasecmp(chgtype,"add") == 0) {
+ return(LDAP_REQ_ADD);
+ } else if (strcasecmp(chgtype,"modify") == 0) {
+ return(LDAP_REQ_MODIFY);
+ } else if (strcasecmp(chgtype,"modrdn") == 0) {
+ return(LDAP_REQ_MODRDN);
+ } else if (strcasecmp(chgtype,"delete") == 0) {
+ return(LDAP_REQ_DELETE);
+ } else {
+ return(-1);
+ }
+}
+
+static char*
+sync_get_attr_value_from_entry( Slapi_Entry *cl_entry, char *attrtype)
+{
+ Slapi_Value *sval=NULL;
+ const struct berval *value;
+ char *strvalue = NULL;
+ if ( NULL != cl_entry ) {
+ Slapi_Attr *chattr = NULL;
+ sval = NULL;
+ value = NULL;
+ if ( slapi_entry_attr_find( cl_entry, attrtype, &chattr ) == 0 ) {
+ slapi_attr_first_value( chattr,&sval );
+ if ( NULL != sval ) {
+ value = slapi_value_get_berval ( sval );
+ if( NULL != value && NULL != value->bv_val &&
+ '\0' != value->bv_val[0]) {
+ strvalue = slapi_ch_strdup( value->bv_val);
+ }
+ }
+ }
+ }
+ return (strvalue);
+}
+
+static int
+sync_find_ref_by_uuid(Sync_UpdateNode *updates, int stop, char *uniqueid)
+{
+ int rc = -1;
+ int i;
+ for (i=0; i<stop; i++) {
+ if ( updates[i].upd_uuid && (0 == strcmp(uniqueid, updates[i].upd_uuid))) {
+ rc = i;
+ break;
+ }
+ }
+ return (rc);
+}
+
+static int
+sync_is_entry_in_scope(Slapi_PBlock *pb, Slapi_Entry *db_entry)
+{
+ Slapi_Filter *origfilter;
+ slapi_pblock_get( pb, SLAPI_SEARCH_FILTER, &origfilter );
+ if (db_entry &&
+ sync_is_active(db_entry, pb) &&
+ (slapi_vattr_filter_test( pb, db_entry, origfilter, 1) == 0)) {
+ return(1);
+ } else {
+ return(0);
+ }
+}
+
+Slapi_Entry *
+sync_deleted_entry_from_changelog(Slapi_Entry *cl_entry)
+{
+ Slapi_Entry *db_entry = NULL;
+ char *entrydn = NULL;
+ char *uniqueid = NULL;
+
+ entrydn = sync_get_attr_value_from_entry (cl_entry, CL_ATTR_ENTRYDN);
+ uniqueid = sync_get_attr_value_from_entry (cl_entry, CL_ATTR_UNIQUEID);
+
+ /* when the Retro CL can provide the deleted entry
+ * the entry will be taken from th RCL.
+ * For now. just create an entry to holde the nsuniqueid
+ */
+ db_entry = slapi_entry_alloc();
+ slapi_entry_init(db_entry, entrydn, NULL);
+ slapi_entry_add_string(db_entry, "nsuniqueid", uniqueid);
+ slapi_ch_free((void**)&uniqueid);
+
+ return(db_entry);
+}
+
+int
+sync_read_entry_from_changelog( Slapi_Entry *cl_entry, void *cb_data)
+{
+ char *uniqueid = NULL;
+ char *chgtype = NULL;
+ char *chgnr = NULL;
+ int chg_req;
+ int prev = 0;
+ int index = 0;
+ Sync_CallBackData *cb = (Sync_CallBackData *) cb_data;
+
+ uniqueid = sync_get_attr_value_from_entry (cl_entry, CL_ATTR_UNIQUEID);
+ chgtype = sync_get_attr_value_from_entry (cl_entry, CL_ATTR_CHGTYPE);
+ chgnr = sync_get_attr_value_from_entry (cl_entry, CL_ATTR_CHANGENUMBER);
+
+ if (uniqueid == NULL) {
+ slapi_log_error (SLAPI_LOG_FATAL, SYNC_PLUGIN_SUBSYSTEM,
+ "Retro Changelog does not provied nsuniquedid."
+ "Check RCL plugin configuration." );
+ }
+
+
+ index = sync_number2int(chgnr) - cb->change_start;
+ chg_req = sync_str2chgreq(chgtype);
+ switch (chg_req){
+ case LDAP_REQ_ADD:
+ /* nsuniqueid cannot exist, just add reference */
+ cb->cb_updates[index].upd_chgtype = LDAP_REQ_ADD;
+ cb->cb_updates[index].upd_uuid = uniqueid;
+ break;
+ case LDAP_REQ_MODIFY:
+ /* check if we have seen this uuid already */
+ prev = sync_find_ref_by_uuid(cb->cb_updates, index, uniqueid);
+ if (prev == -1) {
+ cb->cb_updates[index].upd_chgtype = LDAP_REQ_MODIFY;
+ cb->cb_updates[index].upd_uuid = uniqueid;
+ } else {
+ /* was add or mod, keep it */
+ cb->cb_updates[index].upd_uuid = 0;
+ cb->cb_updates[index].upd_chgtype = 0;
+ slapi_ch_free((void **)&uniqueid);
+ }
+ break;
+ case LDAP_REQ_MODRDN:
+ {
+ /* if it is a modrdn, we finally need to decide if this will
+ * trigger a present or delete state, keep the info that
+ * the entry was subject to a modrdn
+ */
+ int new_scope = 0;
+ int old_scope = 0;
+ Slapi_DN *original_dn;
+ char *newsuperior = sync_get_attr_value_from_entry (cl_entry, CL_ATTR_NEWSUPERIOR);
+ char *entrydn = sync_get_attr_value_from_entry (cl_entry, CL_ATTR_ENTRYDN);
+ /* if newsuperior is set we need to checkif the entry has been moved into
+ * or moved out of the scope of the synchronization request
+ */
+ original_dn = slapi_sdn_new_dn_byref(entrydn);
+ old_scope = sync_is_active_scope(original_dn,cb->orig_pb);
+ slapi_sdn_free(&original_dn);
+ slapi_ch_free((void **)&entrydn);
+ if (newsuperior) {
+ Slapi_DN *newbase;
+ newbase = slapi_sdn_new_dn_byref(newsuperior);
+ new_scope = sync_is_active_scope(newbase, cb->orig_pb);
+ slapi_ch_free((void **)&newsuperior);
+ slapi_sdn_free(&newbase);
+ } else {
+ /* scope didn't change */
+ new_scope = old_scope;
+ }
+ prev = sync_find_ref_by_uuid(cb->cb_updates, index, uniqueid);
+ if ( old_scope && new_scope ) {
+ /* nothing changed, it's just a MOD */
+ if (prev == -1) {
+ cb->cb_updates[index].upd_chgtype = LDAP_REQ_MODIFY;
+ cb->cb_updates[index].upd_uuid = uniqueid;
+ } else {
+ cb->cb_updates[index].upd_uuid = 0;
+ cb->cb_updates[index].upd_chgtype = 0;
+ slapi_ch_free((void **)&uniqueid);
+ }
+ } else if ( old_scope ) {
+ /* it was moved out of scope, handle as DEL */
+ if (prev == -1) {
+ cb->cb_updates[index].upd_chgtype = LDAP_REQ_DELETE;
+ cb->cb_updates[index].upd_uuid = uniqueid;
+ cb->cb_updates[index].upd_e = sync_deleted_entry_from_changelog(cl_entry);
+ } else {
+ cb->cb_updates[prev].upd_chgtype = LDAP_REQ_DELETE;
+ cb->cb_updates[prev].upd_e = sync_deleted_entry_from_changelog(cl_entry);
+ slapi_ch_free((void **)&uniqueid);
+ }
+ } else if ( new_scope ) {
+ /* moved into scope, handle as ADD */
+ cb->cb_updates[index].upd_chgtype = LDAP_REQ_ADD;
+ cb->cb_updates[index].upd_uuid = uniqueid;
+ } else {
+ /* nothing to do */
+ }
+ slapi_sdn_free(&original_dn);
+ break;
+ }
+ case LDAP_REQ_DELETE:
+ /* check if we have seen this uuid already */
+ prev = sync_find_ref_by_uuid(cb->cb_updates, index, uniqueid);
+ if (prev == -1) {
+ cb->cb_updates[index].upd_chgtype = LDAP_REQ_DELETE;
+ cb->cb_updates[index].upd_uuid = uniqueid;
+ cb->cb_updates[index].upd_e = sync_deleted_entry_from_changelog(cl_entry);
+ } else {
+ /* if it was added since last cookie state, we
+ * can ignoere it */
+ if (cb->cb_updates[prev].upd_chgtype == LDAP_REQ_ADD) {
+ slapi_ch_free((void **)&(cb->cb_updates[prev].upd_uuid));
+ slapi_ch_free((void **)&uniqueid);
+ cb->cb_updates[prev].upd_uuid = NULL;
+ cb->cb_updates[index].upd_uuid = NULL;
+ } else {
+ /* ignore previous mod */
+ cb->cb_updates[index].upd_uuid = NULL;
+ cb->cb_updates[prev].upd_chgtype = LDAP_REQ_DELETE;
+ cb->cb_updates[prev].upd_e = sync_deleted_entry_from_changelog(cl_entry);
+ }
+ }
+ break;
+ }
+ slapi_ch_free((void **)&chgtype);
+ slapi_ch_free((void **)&chgnr);
+
+ return (0);
+}
+#define SYNC_MAX_DELETED_UUID_BATCH 50
+
+void
+sync_send_deleted_entries(Slapi_PBlock *pb, Sync_UpdateNode *upd, int chg_count,
Sync_Cookie *cookie)
+{
+ char *syncUUIDs[SYNC_MAX_DELETED_UUID_BATCH + 1];
+ int uuid_index = 0;
+ int index, i;
+
+ syncUUIDs[0] = NULL;
+ for (index=0; index < chg_count; index++) {
+ if (upd[index].upd_chgtype == LDAP_REQ_DELETE &&
+ upd[index].upd_uuid ) {
+ if (uuid_index < SYNC_MAX_DELETED_UUID_BATCH) {
+ syncUUIDs[uuid_index++] = sync_nsuniqueid2uuid(upd[index].upd_uuid);
+ } else {
+ /* max number of uuids to be sent in one sync info message */
+ syncUUIDs[uuid_index] = NULL;
+ sync_intermediate_msg (pb, LDAP_TAG_SYNC_ID_SET, cookie, &syncUUIDs[0]);
+ for (i=0; i<uuid_index;i++) {
+ slapi_ch_free((void **)&syncUUIDs[i]);
+ syncUUIDs[i] = NULL;
+ }
+ uuid_index = 0;
+ }
+ }
+ }
+
+ if (uuid_index > 0 && syncUUIDs[uuid_index-1]) {
+ /* more entries to send */
+ syncUUIDs[uuid_index] = NULL;
+ sync_intermediate_msg (pb, LDAP_TAG_SYNC_ID_SET, cookie, &syncUUIDs[0]);
+ for (i=0; i<uuid_index;i++) {
+ slapi_ch_free((void **)&syncUUIDs[i]);
+ syncUUIDs[i] = NULL;
+ }
+ }
+}
+
+void
+sync_send_modified_entries(Slapi_PBlock *pb, Sync_UpdateNode *upd, int chg_count)
+{
+ int index;
+
+ for (index=0; index < chg_count; index++) {
+ if (upd[index].upd_chgtype != LDAP_REQ_DELETE &&
+ upd[index].upd_uuid )
+
+ sync_send_entry_from_changelog(pb, upd[index].upd_chgtype, upd[index].upd_uuid);
+ }
+}
+
+int
+sync_send_entry_from_changelog(Slapi_PBlock *pb,int chg_req, char *uniqueid)
+{
+ Slapi_Entry *db_entry = NULL;
+ int chg_type = LDAP_SYNC_ADD;
+ int rv;
+ Slapi_PBlock *search_pb = NULL;
+ Slapi_Entry **entries = NULL;
+ char *origbase;
+ char *filter = slapi_ch_smprintf("(nsuniqueid=%s)",uniqueid);
+
+ slapi_pblock_get( pb, SLAPI_ORIGINAL_TARGET_DN, &origbase );
+ search_pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(search_pb, origbase,
+ LDAP_SCOPE_SUBTREE, filter,
+ NULL, 0, NULL, NULL, plugin_get_default_component_id(), 0);
+ slapi_search_internal_pb(search_pb);
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &rv);
+ if ( rv == LDAP_SUCCESS) {
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if (entries)
+ db_entry = *entries; /* there can only be one */
+ }
+
+ if (db_entry && sync_is_entry_in_scope(pb, db_entry)) {
+ LDAPControl **ctrl = (LDAPControl **)slapi_ch_calloc(2, sizeof (LDAPControl *));
+ sync_create_state_control(db_entry, &ctrl[0], chg_type, NULL);
+ slapi_send_ldap_search_entry (pb, db_entry, ctrl, NULL, 0);
+ ldap_controls_free(ctrl);
+ }
+ slapi_free_search_results_internal(search_pb);
+ slapi_pblock_destroy(search_pb);
+ slapi_ch_free((void **)&filter);
+ return (0);
+}
+
+static SyncOpInfo*
+new_SyncOpInfo(int flag, Sync_Cookie *cookie) {
+ SyncOpInfo *spec = (SyncOpInfo *)slapi_ch_calloc(1, sizeof(SyncOpInfo));
+ spec->send_flag = flag;
+ spec->cookie = cookie;
+
+ return spec;
+}
+/* consumer operation extension constructor */
+static void *
+sync_operation_extension_ctor(void *object, void *parent)
+{
+ /* we only set the extension value explicitly if the
+ client requested the control - see deref_pre_search */
+ return NULL; /* we don't set anything in the ctor */
+}
+
+/* consumer operation extension destructor */
+static void
+sync_delete_SyncOpInfo(SyncOpInfo **info)
+{
+ if (info && *info) {
+ sync_cookie_free(&((*info)->cookie));
+ slapi_ch_free((void **)info);
+ }
+}
+
+static void
+sync_operation_extension_dtor(void *ext, void *object, void *parent)
+{
+ SyncOpInfo *spec = (SyncOpInfo *)ext;
+ sync_delete_SyncOpInfo(&spec);
+}
+
+static SyncOpInfo *
+sync_get_operation_extension(Slapi_PBlock *pb)
+{
+ Slapi_Operation *op;
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ return (SyncOpInfo *)slapi_get_object_extension(sync_extension_type,
+ op, sync_extension_handle);
+}
+
+static void
+sync_set_operation_extension(Slapi_PBlock *pb, SyncOpInfo *spec)
+{
+ Slapi_Operation *op;
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ slapi_set_object_extension(sync_extension_type, op,
+ sync_extension_handle, (void *)spec);
+}
+
+int
+sync_register_operation_extension(void)
+{
+ return slapi_register_object_extension(SYNC_PLUGIN_SUBSYSTEM,
+ SLAPI_EXT_OPERATION,
+ sync_operation_extension_ctor,
+ sync_operation_extension_dtor,
+ &sync_extension_type,
+ &sync_extension_handle);
+}
diff --git a/ldap/servers/plugins/sync/sync_util.c
b/ldap/servers/plugins/sync/sync_util.c
new file mode 100644
index 0000000..9e98561
--- /dev/null
+++ b/ldap/servers/plugins/sync/sync_util.c
@@ -0,0 +1,685 @@
+/** BEGIN COPYRIGHT BLOCK
+ * This Program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; version 2 of the License.
+ *
+ * This Program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * In addition, as a special exception, Red Hat, Inc. gives You the additional
+ * right to link the code of this Program with code not covered under the GNU
+ * General Public License ("Non-GPL Code") and to distribute linked
combinations
+ * including the two, subject to the limitations in this paragraph. Non-GPL Code
+ * permitted under this exception must only link to the code of this Program
+ * through those well defined interfaces identified in the file named EXCEPTION
+ * found in the source code files (the "Approved Interfaces"). The files of
+ * Non-GPL Code may instantiate templates or use macros or inline functions from
+ * the Approved Interfaces without causing the resulting work to be covered by
+ * the GNU General Public License. Only Red Hat, Inc. may make changes or
+ * additions to the list of Approved Interfaces. You must obey the GNU General
+ * Public License in all respects for all of the Program code and other code used
+ * in conjunction with the Program except the Non-GPL Code covered by this
+ * exception. If you modify this file, you may extend this exception to your
+ * version of the file, but you are not obligated to do so. If you do not wish to
+ * provide this exception without modification, you must delete this exception
+ * statement from your version and license this file solely under the GPL without
+ * exception.
+ *
+ *
+ * Copyright (C) 2013 Red Hat, Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "sync.h"
+
+static struct berval * create_syncinfo_value( int type, const char *cookie, const char
**uuids);
+static char * sync_cookie_get_server_info(Slapi_PBlock *pb);
+static char * sync_cookie_get_client_info(Slapi_PBlock *pb);
+
+/*
+ * Parse the value from an LDAPv3 sync request control. They look
+ * like this:
+ *
+ * syncRequestValue ::= SEQUENCE {
+ * mode ENUMERATED {
+ * -- 0 unused
+ * refreshOnly (1),
+ * -- 2 reserved
+ * refreshAndPersist (3)
+ * },
+ * cookie syncCookie OPTIONAL,
+ * reloadHint BOOLEAN DEFAULT FALSE
+ * }
+ *
+ * Return an LDAP error code (LDAP_SUCCESS if all goes well).
+ *
+ */
+int
+sync_parse_control_value( struct berval *psbvp, ber_int_t *mode, int *reload, char
**cookie )
+{
+ int rc= LDAP_SUCCESS;
+
+ if ( psbvp->bv_len == 0 || psbvp->bv_val == NULL )
+ {
+ rc= LDAP_PROTOCOL_ERROR;
+ }
+ else
+ {
+ BerElement *ber= ber_init( psbvp );
+ if ( ber == NULL )
+ {
+ rc= LDAP_OPERATIONS_ERROR;
+ }
+ else
+ {
+ if ( ber_scanf( ber, "{e", mode ) == LBER_ERROR )
+ {
+ rc= LDAP_PROTOCOL_ERROR;
+ } else if ( ber_scanf( ber, "a", cookie ) != LBER_ERROR )
+ ber_scanf( ber, "b}", reload );
+
+ /* the ber encoding is no longer needed */
+ ber_free(ber,1);
+ }
+ }
+
+ return( rc );
+}
+
+char *
+sync_nsuniqueid2uuid(const char *nsuniqueid)
+{
+ char *uuid;
+ char u[17];
+
+ u[0] = slapi_str_to_u8(nsuniqueid);
+ u[1] = slapi_str_to_u8(nsuniqueid+2);
+ u[2] = slapi_str_to_u8(nsuniqueid+4);
+ u[3] = slapi_str_to_u8(nsuniqueid+6);
+
+ u[4] = slapi_str_to_u8(nsuniqueid+9);
+ u[5] = slapi_str_to_u8(nsuniqueid+11);
+ u[6] = slapi_str_to_u8(nsuniqueid+13);
+ u[7] = slapi_str_to_u8(nsuniqueid+15);
+
+ u[8] = slapi_str_to_u8(nsuniqueid+18);
+ u[9] = slapi_str_to_u8(nsuniqueid+20);
+ u[10] = slapi_str_to_u8(nsuniqueid+22);
+ u[11] = slapi_str_to_u8(nsuniqueid+24);
+
+ u[12] = slapi_str_to_u8(nsuniqueid+27);
+ u[13] = slapi_str_to_u8(nsuniqueid+29);
+ u[14] = slapi_str_to_u8(nsuniqueid+31);
+ u[15] = slapi_str_to_u8(nsuniqueid+33);
+
+ u[16] = '\0';
+
+ uuid = slapi_ch_smprintf("%s",(char *)u);
+
+ return(uuid);
+}
+
+/*
+ * syncStateValue ::= SEQUENCE {
+ * state ENUMERATED {
+ * present (0),
+ * add (1),
+ * modify (2),
+ * delete (3)
+ * },
+ * entryUUID syncUUID,
+ * cookie syncCookie OPTIONAL
+ * }
+ *
+ */
+int
+sync_create_state_control( Slapi_Entry *e, LDAPControl **ctrlp, int type, Sync_Cookie
*cookie)
+{
+ int rc;
+ BerElement *ber;
+ struct berval *bvp;
+ char *uuid;
+ Slapi_Attr *attr;
+ Slapi_Value *val;
+
+ if ( ctrlp == NULL || ( ber = der_alloc()) == NULL ) {
+ return( LDAP_OPERATIONS_ERROR );
+ }
+
+ *ctrlp = NULL;
+
+ slapi_entry_attr_find(e, SLAPI_ATTR_UNIQUEID, &attr);
+ slapi_attr_first_value(attr, &val);
+ uuid = sync_nsuniqueid2uuid(slapi_value_get_string(val));
+ if (( rc = ber_printf( ber, "{eo", type , uuid, 16 )) != -1 ) {
+ if (cookie) {
+ char *cookiestr = sync_cookie2str(cookie);
+ rc = ber_printf( ber, "s}", cookiestr);
+ slapi_ch_free((void **)&cookiestr);
+ } else {
+ rc = ber_printf( ber, "}");
+
+ }
+ }
+ if (rc != -1) {
+ rc = ber_flatten( ber, &bvp );
+ }
+ ber_free( ber, 1 );
+ slapi_ch_free((void **)&uuid);
+
+ if ( rc == -1 ) {
+ return( LDAP_OPERATIONS_ERROR );
+ }
+
+ *ctrlp = (LDAPControl *)slapi_ch_malloc( sizeof( LDAPControl ));
+ (*ctrlp)->ldctl_iscritical = 0;
+ (*ctrlp)->ldctl_oid = slapi_ch_strdup( LDAP_CONTROL_SYNC_STATE );
+ (*ctrlp)->ldctl_value = *bvp; /* struct copy */
+
+ bvp->bv_val = NULL;
+ ber_bvfree( bvp );
+
+ return (LDAP_SUCCESS);
+}
+
+/*
+ * syncDoneValue ::= SEQUENCE {
+ * cookie syncCookie OPTIONAL
+ * refreshDeletes BOOLEAN DEFAULT FALSE
+ * }
+ *
+ */
+int
+sync_create_sync_done_control( LDAPControl **ctrlp, int refresh, char *cookie)
+{
+ int rc;
+ BerElement *ber;
+ struct berval *bvp;
+
+ if ( ctrlp == NULL || ( ber = der_alloc()) == NULL ) {
+ return( LDAP_OPERATIONS_ERROR );
+ }
+
+ *ctrlp = NULL;
+
+ if (cookie) {
+ if (( rc = ber_printf( ber, "{s", cookie)) != -1 ) {
+ if (refresh) {
+ rc = ber_printf( ber, "e}", refresh);
+ } else {
+ rc = ber_printf( ber, "}");
+ }
+ }
+ } else {
+ if (refresh) {
+ rc = ber_printf( ber, "{e}", refresh);
+ } else {
+ rc = ber_printf( ber, "{}");
+ }
+ }
+ if (rc != -1) {
+ rc = ber_flatten( ber, &bvp );
+ }
+ ber_free( ber, 1 );
+
+ if ( rc == -1 ) {
+ return( LDAP_OPERATIONS_ERROR );
+ }
+
+ *ctrlp = (LDAPControl *)slapi_ch_malloc( sizeof( LDAPControl ));
+ (*ctrlp)->ldctl_iscritical = 0;
+ (*ctrlp)->ldctl_oid = slapi_ch_strdup( LDAP_CONTROL_SYNC_DONE );
+ (*ctrlp)->ldctl_value = *bvp; /* struct copy */
+
+ bvp->bv_val = NULL;
+ ber_bvfree( bvp );
+
+ return (LDAP_SUCCESS);
+
+}
+
+char *
+sync_cookie2str(Sync_Cookie *cookie)
+{
+ char *cookiestr = NULL;
+
+ if (cookie) {
+ cookiestr = slapi_ch_smprintf("%s#%s#%d",
+ cookie->cookie_server_signature,
+ cookie->cookie_client_signature,
+ cookie->cookie_change_info);
+ }
+ return(cookiestr);
+}
+
+int
+sync_intermediate_msg (Slapi_PBlock *pb, int tag, Sync_Cookie *cookie, char **uuids)
+{
+ int rc;
+ struct berval *syncInfo;
+ LDAPControl *ctrlp = NULL;
+ char *cookiestr = sync_cookie2str(cookie);
+
+ syncInfo = create_syncinfo_value(tag, cookiestr, (const char **)uuids);
+
+ rc = slapi_send_ldap_intermediate( pb, &ctrlp, LDAP_SYNC_INFO, syncInfo);
+ slapi_ch_free((void **)&cookiestr);
+ ber_bvfree(syncInfo);
+ return (rc);
+}
+
+int sync_result_msg (Slapi_PBlock *pb, Sync_Cookie *cookie)
+{
+ int rc = 0;
+ char *cookiestr = sync_cookie2str(cookie);
+
+ LDAPControl **ctrl = (LDAPControl **)slapi_ch_calloc(2, sizeof (LDAPControl *));
+ sync_create_sync_done_control( &ctrl[0], 0, cookiestr);
+ slapi_pblock_set(pb, SLAPI_RESCONTROLS, ctrl);
+ slapi_send_ldap_result(pb, 0, NULL, NULL, 0, NULL );
+
+ slapi_ch_free((void **)&cookiestr);
+ return (rc);
+}
+
+int sync_result_err (Slapi_PBlock *pb, int err, char *msg)
+{
+ int rc = 0;
+
+ slapi_send_ldap_result(pb, err, NULL, msg, 0, NULL );
+
+ return (rc);
+}
+
+static struct berval *
+create_syncinfo_value( int type, const char *cookie, const char **uuids)
+{
+ BerElement *ber;
+ struct berval *bvp = NULL;
+
+ if ( ( ber = der_alloc() ) == NULL ) {
+ return( NULL );
+ }
+
+ switch (type) {
+ case LDAP_TAG_SYNC_NEW_COOKIE:
+ ber_printf(ber, "to", type, cookie);
+ break;
+ case LDAP_TAG_SYNC_REFRESH_DELETE:
+ case LDAP_TAG_SYNC_REFRESH_PRESENT:
+ ber_printf(ber, "t{", type);
+ if (cookie)
+ ber_printf(ber, "s", cookie);
+ /* ber_printf(ber, "b",1); */
+ ber_printf(ber, "}");
+ break;
+ case LDAP_TAG_SYNC_ID_SET:
+ ber_printf(ber, "t{", type);
+ if (cookie)
+ ber_printf(ber, "s", cookie);
+ if (uuids)
+ ber_printf(ber, "b[v]", 1, uuids);
+ ber_printf(ber, "}");
+ break;
+ default:
+ break;
+ }
+ ber_flatten( ber, &bvp );
+ ber_free( ber, 1 );
+
+ return( bvp );
+}
+
+static int
+sync_handle_cnum_entry(Slapi_Entry *e, void *cb_data)
+{
+ int rc = 0;
+ Sync_CallBackData *cb = (Sync_CallBackData *)cb_data;
+ Slapi_Value *sval=NULL;
+ const struct berval *value;
+
+ cb->changenr = 0;
+
+ if ( NULL != e ) {
+ Slapi_Attr *chattr = NULL;
+ sval = NULL;
+ value = NULL;
+ if ( slapi_entry_attr_find( e, CL_ATTR_CHANGENUMBER, &chattr ) == 0 ) {
+ slapi_attr_first_value( chattr,&sval );
+ if ( NULL != sval ) {
+ value = slapi_value_get_berval ( sval );
+ if( NULL != value && NULL != value->bv_val &&
+ '\0' != value->bv_val[0]) {
+ cb->changenr = sync_number2int(value->bv_val);
+ }
+ }
+ }
+ }
+ return (rc);
+}
+
+/*
+ * a cookie is used to synchronize client server sessions,
+ * it consist of three parts
+ * -- server id, client should only sync with one server
+ * -- client id, client should use same bind dn, and srch params
+ * -- change info, kind of state info like csn, ruv,
+ * in the first implementation use changenumber from retro cl
+ *
+ * syntax: <server-id>#<client-id>#change
+ *
+ */
+static char *
+sync_cookie_get_server_info(Slapi_PBlock *pb)
+{
+ char *info_enc;
+ int rc = 0;
+ Slapi_Entry **entries;
+ Slapi_PBlock *srch_pb = NULL;
+ char *host = NULL;
+ char *port = NULL;
+ char *server_attrs[] = {"nsslapd-localhost", "nsslapd-port", NULL};
+
+ srch_pb = slapi_pblock_new ();
+ slapi_search_internal_set_pb (srch_pb, "cn=config", LDAP_SCOPE_BASE,
+ "objectclass=*", server_attrs, 0, NULL, NULL,
+ plugin_get_default_component_id(), 0);
+ slapi_search_internal_pb (srch_pb);
+ slapi_pblock_get(srch_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, SYNC_PLUGIN_SUBSYSTEM,
"sync_cookie_get_server_info: "
+ "unable to read server configuration: error %d\n", rc);
+ }
+ else
+ {
+ slapi_pblock_get(srch_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if (NULL == entries || NULL == entries[0])
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, SYNC_PLUGIN_SUBSYSTEM,
"sync_cookie_get_server_info: "
+ "server configuration missing\n");
+ rc = -1;
+ }
+ else
+ {
+ host = slapi_entry_attr_get_charptr(entries[0], "nsslapd-localhost");
+ port = slapi_entry_attr_get_charptr(entries[0], "nsslapd-port");
+ }
+ }
+ info_enc =
slapi_ch_smprintf("%s:%s",host?host:"nohost",port?port:"noport");
+
+ slapi_ch_free ((void**)&host);
+ slapi_ch_free ((void**)&port);
+ slapi_free_search_results_internal(srch_pb);
+ slapi_pblock_destroy (srch_pb);
+ return(info_enc);
+}
+
+static char *
+sync_cookie_get_client_info(Slapi_PBlock *pb)
+{
+ char *targetdn;
+ char *strfilter;
+ char *clientdn;
+ char *clientinfo;
+
+ slapi_pblock_get(pb, SLAPI_TARGET_DN, &targetdn);
+ slapi_pblock_get(pb, SLAPI_SEARCH_STRFILTER, &strfilter);
+ slapi_pblock_get(pb, SLAPI_REQUESTOR_DN, &clientdn);
+ clientinfo = slapi_ch_smprintf("%s:%s:%s",clientdn,targetdn,strfilter);
+ return (clientinfo);
+}
+static int
+sync_cookie_get_change_number(int lastnr, const char *uniqueid)
+{
+ Slapi_PBlock *srch_pb;
+ Slapi_Entry **entries;
+ Slapi_Entry *cl_entry;
+ int rv;
+ int newnr = -1;
+ char *filter =
slapi_ch_smprintf("(&(changenumber>=%d)(targetuniqueid=%s))",lastnr,uniqueid);
+
+ srch_pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(srch_pb, CL_SRCH_BASE,
+ LDAP_SCOPE_SUBTREE, filter,
+ NULL, 0, NULL, NULL, plugin_get_default_component_id(), 0);
+ slapi_search_internal_pb(srch_pb);
+ slapi_pblock_get(srch_pb, SLAPI_PLUGIN_INTOP_RESULT, &rv);
+ if ( rv == LDAP_SUCCESS) {
+ slapi_pblock_get(srch_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if (entries && *entries) {
+ Slapi_Attr *attr;
+ Slapi_Value *val;
+ cl_entry = *entries; /* only use teh first one */
+ slapi_entry_attr_find(cl_entry, CL_ATTR_CHANGENUMBER, &attr);
+ slapi_attr_first_value(attr, &val);
+ newnr = sync_number2int((char *)slapi_value_get_string(val));
+ }
+ }
+
+ slapi_free_search_results_internal(srch_pb);
+ slapi_pblock_destroy(srch_pb);
+ slapi_ch_free((void **)&filter);
+ return (newnr);
+}
+
+static int
+sync_cookie_get_change_info(Sync_CallBackData *scbd)
+{
+ Slapi_PBlock *seq_pb;
+ char *base;
+ char *attrname;
+ int rc;
+
+ base = slapi_ch_strdup("cn=changelog");
+ attrname = slapi_ch_strdup("changenumber");
+
+ seq_pb = slapi_pblock_new();
+ slapi_pblock_init(seq_pb);
+
+ slapi_seq_internal_set_pb(seq_pb, base, SLAPI_SEQ_LAST, attrname, NULL, NULL, 0, 0,
+ plugin_get_default_component_id(), 0);
+
+ rc = slapi_seq_internal_callback_pb (seq_pb, scbd, NULL, sync_handle_cnum_entry, NULL);
+ slapi_pblock_destroy(seq_pb);
+
+ slapi_ch_free((void **)&attrname);
+ slapi_ch_free((void **)&base);
+
+ return (rc);
+
+}
+
+Sync_Cookie *
+sync_cookie_create (Slapi_PBlock *pb)
+{
+
+ Sync_CallBackData scbd;
+ int rc;
+ Sync_Cookie *sc = (Sync_Cookie *)slapi_ch_malloc(sizeof(Sync_Cookie));
+
+
+ rc = sync_cookie_get_change_info (&scbd);
+
+ if (rc == 0) {
+ sc->cookie_server_signature = sync_cookie_get_server_info(pb);
+ sc->cookie_client_signature = sync_cookie_get_client_info(pb);
+ sc->cookie_change_info = scbd.changenr;
+ } else {
+ slapi_ch_free ((void **)&sc);
+ sc = NULL;
+ }
+
+ return (sc);
+}
+
+void sync_cookie_update (Sync_Cookie *sc, Slapi_Entry *ec)
+{
+ const char *uniqueid = NULL;
+ Slapi_Attr *attr;
+ Slapi_Value *val;
+
+ slapi_entry_attr_find(ec, SLAPI_ATTR_UNIQUEID, &attr);
+ slapi_attr_first_value(attr, &val);
+ uniqueid = slapi_value_get_string(val);
+
+ sc->cookie_change_info = sync_cookie_get_change_number (sc->cookie_change_info,
uniqueid);
+}
+
+Sync_Cookie *
+sync_cookie_parse (char *cookie)
+{
+ char *p, *q;
+ Sync_Cookie *sc;
+
+ if (cookie == NULL || *cookie == '\0' ) {
+ return NULL;
+ }
+
+ p = q = cookie;
+ sc = (Sync_Cookie *)slapi_ch_malloc(sizeof(Sync_Cookie));
+
+ sc->cookie_client_signature = NULL;
+ sc->cookie_server_signature = NULL;
+ sc->cookie_change_info = -1;
+ p = strchr(q, '#');
+ if (p) {
+ *p = '\0';
+ sc->cookie_server_signature = slapi_ch_strdup(q);
+ q = p + 1;
+ p = strchr(q, '#');
+ if (p) {
+ *p = '\0';
+ sc->cookie_client_signature = slapi_ch_strdup(q);
+ sc->cookie_change_info = sync_number2int(p+1);
+ }
+ }
+
+ return (sc);
+}
+
+int
+sync_cookie_isvalid (Sync_Cookie *testcookie, Sync_Cookie *refcookie)
+{
+ /* client and server info must match */
+ if (strcmp(testcookie->cookie_client_signature,refcookie->cookie_client_signature)
||
+ strcmp(testcookie->cookie_server_signature,refcookie->cookie_server_signature) ||
+ testcookie->cookie_change_info == -1 ||
+ testcookie->cookie_change_info > refcookie->cookie_change_info )
+ return (0);
+ /* could add an additional check if the requested state in client cookie is still
+ * available. Accept any state request for now.
+ */
+ return (1);
+}
+
+void
+sync_cookie_free (Sync_Cookie **freecookie)
+{
+ if (*freecookie) {
+ slapi_ch_free((void **)&((*freecookie)->cookie_client_signature));
+ slapi_ch_free((void **)&((*freecookie)->cookie_server_signature));
+ slapi_ch_free((void **)freecookie);
+ }
+}
+
+int sync_is_active_scope (const Slapi_DN *dn, Slapi_PBlock *pb)
+{
+ int rc;
+ char *origbase = NULL;
+ Slapi_DN *base = NULL;
+ int scope;
+
+ slapi_pblock_get( pb, SLAPI_ORIGINAL_TARGET_DN, &origbase );
+ slapi_pblock_get( pb, SLAPI_SEARCH_TARGET_SDN, &base );
+ slapi_pblock_get( pb, SLAPI_SEARCH_SCOPE, &scope );
+ if (NULL == base) {
+ base = slapi_sdn_new_dn_byref(origbase);
+ slapi_pblock_set(pb, SLAPI_SEARCH_TARGET_SDN, base);
+ }
+ if ( slapi_sdn_scope_test(dn, base, scope )) {
+ rc = 1;
+ } else {
+ rc = 0;
+ }
+
+ return (rc);
+}
+
+int sync_is_active (Slapi_Entry *e, Slapi_PBlock *pb)
+{
+ if ( pb == NULL ) {
+ /* not yet initialized */
+ return(0);
+ } else {
+ /* check id entry is in scope of sync request */
+ return(sync_is_active_scope( slapi_entry_get_sdn_const(e),pb));
+ }
+}
+
+
+Slapi_PBlock *
+sync_pblock_copy(Slapi_PBlock *src)
+{
+ Slapi_Operation *operation;
+ Slapi_Connection *connection;
+ int *scope;
+ int *deref;
+ int *filter_normalized;
+ char *fstr;
+ char **attrs, **attrs_dup;
+ int *attrsonly;
+ int *isroot;
+ int *sizelimit;
+ int *timelimit;
+ struct slapdplugin *pi;
+
+
+ slapi_pblock_get( src, SLAPI_OPERATION, &operation );
+ slapi_pblock_get( src, SLAPI_CONNECTION, &connection );
+ slapi_pblock_get( src, SLAPI_SEARCH_SCOPE, &scope );
+ slapi_pblock_get( src, SLAPI_SEARCH_DEREF, &deref );
+ slapi_pblock_get( src, SLAPI_PLUGIN_SYNTAX_FILTER_NORMALIZED, &filter_normalized );
+ slapi_pblock_get( src, SLAPI_SEARCH_STRFILTER, &fstr );
+ slapi_pblock_get( src, SLAPI_SEARCH_ATTRS, &attrs );
+ slapi_pblock_get( src, SLAPI_SEARCH_ATTRSONLY, &attrsonly );
+ slapi_pblock_get( src, SLAPI_REQUESTOR_ISROOT, &isroot );
+ slapi_pblock_get( src, SLAPI_SEARCH_SIZELIMIT, &sizelimit );
+ slapi_pblock_get( src, SLAPI_SEARCH_TIMELIMIT, &timelimit );
+ slapi_pblock_get( src, SLAPI_PLUGIN, &pi);
+
+ Slapi_PBlock *dest = slapi_pblock_new();
+
+ slapi_pblock_set( dest, SLAPI_OPERATION, operation );
+ slapi_pblock_set( dest, SLAPI_CONNECTION, connection );
+ slapi_pblock_set( dest, SLAPI_SEARCH_SCOPE, &scope );
+ slapi_pblock_set( dest, SLAPI_SEARCH_DEREF, &deref );
+ slapi_pblock_set( dest, SLAPI_PLUGIN_SYNTAX_FILTER_NORMALIZED, &filter_normalized
);
+ slapi_pblock_set( dest, SLAPI_SEARCH_STRFILTER, slapi_ch_strdup(fstr) );
+ attrs_dup = slapi_ch_array_dup(attrs);
+ slapi_pblock_set( dest, SLAPI_SEARCH_ATTRS, attrs_dup );
+ slapi_pblock_set( dest, SLAPI_SEARCH_ATTRSONLY, &attrsonly );
+ slapi_pblock_set( dest, SLAPI_REQUESTOR_ISROOT, &isroot );
+ slapi_pblock_set( dest, SLAPI_SEARCH_SIZELIMIT, &sizelimit );
+ slapi_pblock_set( dest, SLAPI_SEARCH_TIMELIMIT, &timelimit );
+ slapi_pblock_set( dest, SLAPI_PLUGIN, pi);
+
+ return dest;
+}
+
+int sync_number2int(char *chgnrstr)
+{
+ char *end;
+ int nr;
+ nr = strtoul(chgnrstr, &end, 10);
+ if ( *end == '\0') {
+ return (nr);
+ } else {
+ return (-1);
+ }
+}
diff --git a/ldap/servers/slapd/connection.c b/ldap/servers/slapd/connection.c
index f5fa51b..92352e0 100644
--- a/ldap/servers/slapd/connection.c
+++ b/ldap/servers/slapd/connection.c
@@ -3015,7 +3015,12 @@ connection_abandon_operations( Connection *c )
/* abandon the operation only if it is not yet
completed (i.e., no result has been sent yet to
the client */
- if ( op->o_status != SLAPI_OP_STATUS_RESULT_SENT ) {
+ /* sync repl uses the persist mode, and it cannot prevent
+ * setting o_status, but has to be abandonned
+ * handle it here until a better solution is found
+ */
+ if ( op->o_status != SLAPI_OP_STATUS_RESULT_SENT ||
+ op->o_flags & OP_FLAG_PS ) {
op->o_status = SLAPI_OP_STATUS_ABANDONED;
}
}
diff --git a/ldap/servers/slapd/libslapd.def b/ldap/servers/slapd/libslapd.def
index 4b7cd7b..150147b 100644
--- a/ldap/servers/slapd/libslapd.def
+++ b/ldap/servers/slapd/libslapd.def
@@ -1200,3 +1200,7 @@ EXPORTS
config_get_pw_mintokenlength @1207
slapi_check_account_lock @1208
slapi_is_ldapi_conn @1209
+; slapi connection functions
+ slapi_connection_acquire @1211
+ slapi_connection_remove_operation @1212
+
diff --git a/ldap/servers/slapd/operation.c b/ldap/servers/slapd/operation.c
index 8ac413f..1c5c9b5 100644
--- a/ldap/servers/slapd/operation.c
+++ b/ldap/servers/slapd/operation.c
@@ -49,6 +49,7 @@
#include <sys/socket.h>
#endif
#include "slap.h"
+#include "fe.h"
int
slapi_op_abandoned( Slapi_PBlock *pb )
@@ -550,3 +551,60 @@ void operation_parameters_free(struct slapi_operation_parameters
**sop)
slapi_ch_free ((void**)sop);
}
}
+
+int slapi_connection_acquire(Slapi_Connection *conn)
+{
+ int rc;
+
+ PR_Lock(conn->c_mutex);
+ /* rc = connection_acquire_nolock(conn); */
+ /* connection in the closing state can't be acquired */
+ if (conn->c_flags & CONN_FLAG_CLOSING)
+ {
+ /* This may happen while other threads are still working on this connection */
+ slapi_log_error(SLAPI_LOG_FATAL, "connection",
+ "conn=%" NSPRIu64 " fd=%d Attempt to acquire connection
in the closing state\n",
+ (long long unsigned int)conn->c_connid, conn->c_sd);
+ rc = -1;
+ }
+ else
+ {
+ conn->c_refcnt++;
+ rc = 0;
+ }
+ PR_Unlock(conn->c_mutex);
+ return(rc);
+}
+
+int
+slapi_connection_remove_operation( Slapi_PBlock *pb, Slapi_Connection *conn,
Slapi_Operation *op, int release)
+{
+ int rc = 0;
+ Slapi_Operation **olist= &conn->c_ops;
+ Slapi_Operation **tmp;
+ PR_Lock( conn->c_mutex );
+ /* connection_remove_operation_ext(pb, conn,op); */
+ for ( tmp = olist; *tmp != NULL && *tmp != op; tmp = &(*tmp)->o_next )
+ ; /* NULL */
+ if ( *tmp == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "connection_remove_operation: can't find op %d for
conn %" NSPRIu64 "\n",
+ (int)op->o_msgid, conn->c_connid, 0 );
+ } else {
+ *tmp = (*tmp)->o_next;
+ }
+
+ if (release) {
+ /* connection_release_nolock(conn); */
+ if (conn->c_refcnt <= 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, "connection",
+ "conn=%" NSPRIu64 " fd=%d Attempt to release connection
that is not acquired\n",
+ (long long unsigned int)conn->c_connid, conn->c_sd);
+ rc = -1;
+ } else {
+ conn->c_refcnt--;
+ rc = 0;
+ }
+ }
+ PR_Unlock( conn->c_mutex );
+ return (rc);
+}
diff --git a/ldap/servers/slapd/plugin.c b/ldap/servers/slapd/plugin.c
index aa33b02..66b011b 100644
--- a/ldap/servers/slapd/plugin.c
+++ b/ldap/servers/slapd/plugin.c
@@ -586,6 +586,15 @@ plugin_get_pwd_storage_scheme_list(int index)
return( names_list );
}
+int slapi_send_ldap_intermediate( Slapi_PBlock *pb, LDAPControl **ectrls,
+ char *responseName, struct berval *responseValue)
+{
+ /* no SLAPI_PLUGIN_DB_INTERMEDIATE_FN defined
+ * always directly call slapd_ function
+ */
+ return send_ldap_intermediate(pb, ectrls, responseName, responseValue);
+}
+
int
slapi_send_ldap_search_entry( Slapi_PBlock *pb, Slapi_Entry *e, LDAPControl **ectrls,
char **attrs, int attrsonly )
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
index 95bfafd..e256728 100644
--- a/ldap/servers/slapd/proto-slap.h
+++ b/ldap/servers/slapd/proto-slap.h
@@ -958,6 +958,8 @@ int send_ldap_search_entry_ext( Slapi_PBlock *pb, Slapi_Entry *e,
LDAPControl **
char **attrs, int attrsonly, int send_result, int nentries, struct berval **urls );
void send_ldap_result_ext( Slapi_PBlock *pb, int err, char *matched, char *text,
int nentries, struct berval **urls, BerElement *ber );
+int send_ldap_intermediate( Slapi_PBlock *pb, LDAPControl **ectrls,
+ char *responseName, struct berval *responseValue);
void send_nobackend_ldap_result( Slapi_PBlock *pb );
int send_ldap_referral( Slapi_PBlock *pb, Slapi_Entry *e, struct berval **refs,
struct berval ***urls );
diff --git a/ldap/servers/slapd/result.c b/ldap/servers/slapd/result.c
index f520e6b..655717f 100644
--- a/ldap/servers/slapd/result.c
+++ b/ldap/servers/slapd/result.c
@@ -82,6 +82,7 @@ static char * op_to_string(int tag);
#define _LDAP_SEND_RESULT 0
#define _LDAP_SEND_REFERRAL 1
#define _LDAP_SEND_ENTRY 2
+#define _LDAP_SEND_INTERMED 4
#define SLAPI_SEND_VATTR_FLAG_REALONLY 0x01
#define SLAPI_SEND_VATTR_FLAG_VIRTUALONLY 0x02
@@ -223,6 +224,79 @@ send_ldap_result(
send_ldap_result_ext(pb, err, matched, text, nentries, urls, NULL);
}
+int send_ldap_intermediate( Slapi_PBlock *pb, LDAPControl **ectrls,
+ char *responseName, struct berval *responseValue)
+{
+ ber_tag_t tag;
+ BerElement *ber;
+ Slapi_Connection *connection;
+ Slapi_Operation *operation;
+ int rc = 0;
+ int logit = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> send_ldap_intermediate\n", 0, 0, 0 );
+ slapi_pblock_get (pb, SLAPI_OPERATION, &operation);
+ slapi_pblock_get (pb, SLAPI_CONNECTION, &connection);
+
+ if (operation->o_status == SLAPI_OP_STATUS_RESULT_SENT) {
+ return(rc); /* result already sent */
+ }
+ tag = LDAP_RES_INTERMEDIATE;
+ if ( (ber = der_alloc()) == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ber_alloc failed\n", 0, 0, 0 );
+ goto log_and_return;
+ }
+ /* add the intermediate message */
+ rc = ber_printf( ber, "{it{", operation->o_msgid, tag);
+ /* print responsename */
+ rc = ber_printf ( ber, "ts", LDAP_TAG_IM_RES_OID, responseName);
+ /* print responsevalue */
+ rc = ber_printf ( ber, "tO", LDAP_TAG_IM_RES_VALUE, responseValue);
+
+
+
+ if ( rc != LBER_ERROR ) {
+ rc = ber_printf( ber, "}" ); /* one more } to come */
+ }
+
+ if ( ectrls != NULL
+ && connection->c_ldapversion >= LDAP_VERSION3
+ && write_controls( ber, ectrls ) != 0 ) {
+ rc = (int)LBER_ERROR;
+ }
+
+ if ( rc != LBER_ERROR ) { /* end the LDAPMessage sequence */
+ rc = ber_put_seq( ber );
+ }
+ if ( rc == LBER_ERROR ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ber_printf failed\n", 0, 0, 0 );
+ ber_free( ber, 1 /* freebuf */ );
+ goto log_and_return;
+ }
+
+ /* write only one pdu at a time - wait til it's our turn */
+ if ( flush_ber( pb, connection, operation, ber, _LDAP_SEND_INTERMED ) == 0 ) {
+ logit = 1;
+ }
+log_and_return:
+ /* operation->o_status = SLAPI_OP_STATUS_RESULT_SENT;
+ * there could be multiple intermediate messages on
+ * the same connection, unlike in send_result do not
+ * set o_status
+ */
+
+ if ( logit && operation_is_flag_set( operation,
+ OP_FLAG_ACTION_LOG_ACCESS )) {
+ log_result( pb, operation, rc, tag, 0 );
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= send_ldap_intermediate\n", 0, 0, 0 );
+ if (rc == LBER_ERROR) {
+ return(1);
+ } else {
+ return(0);
+ }
+}
static int
check_and_send_extended_result(Slapi_PBlock *pb, ber_tag_t tag, BerElement *ber)
@@ -1555,7 +1629,7 @@ send_ldap_search_entry_ext(
}
/* if the client explicitly specified a list of attributes look through each attribute
requested */
- if( (rc == 0) && (attrs!=NULL)) {
+ if( (rc == 0) && (attrs!=NULL) && !noattrs) {
rc =
send_specific_attrs(e,attrs,op,pb,ber,attrsonly,conn->c_ldapversion,dontsendattr,real_attrs_only);
}
@@ -1695,6 +1769,8 @@ flush_ber(
case _LDAP_SEND_REFERRAL:
rc = plugin_call_plugins( pb, SLAPI_PLUGIN_PRE_REFERRAL_FN );
break;
+ case _LDAP_SEND_INTERMED:
+ break; /* not a plugin entry point */
}
if ( rc != 0 ) {
@@ -1764,6 +1840,8 @@ flush_ber(
slapi_counter_increment(g_get_global_snmp_vars()->ops_tbl.dsEntriesReturned);
plugin_call_plugins( pb, SLAPI_PLUGIN_POST_ENTRY_FN );
break;
+ case _LDAP_SEND_INTERMED:
+ break; /* not a plugin entry point */
}
return( rc );
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
index e02a125..f904267 100644
--- a/ldap/servers/slapd/slapi-plugin.h
+++ b/ldap/servers/slapd/slapi-plugin.h
@@ -4966,6 +4966,13 @@ char *slapi_op_type_to_string(unsigned long type);
int slapi_op_internal( Slapi_PBlock *pb );
/*
+ * connection routines
+ */
+
+int slapi_connection_acquire(Slapi_Connection *conn);
+int slapi_connection_remove_operation( Slapi_PBlock *pb, Slapi_Connection *conn,
Slapi_Operation *op, int release);
+
+/*
* LDAPMod manipulation routines
*/
Slapi_Mods* slapi_mods_new( void );
@@ -5127,6 +5134,8 @@ void slapi_send_ldap_result( Slapi_PBlock *pb, int err, char
*matched,
char *text, int nentries, struct berval **urls );
int slapi_send_ldap_referral( Slapi_PBlock *pb, Slapi_Entry *e,
struct berval **refs, struct berval ***urls );
+int slapi_send_ldap_intermediate( Slapi_PBlock *pb, LDAPControl **ectrls,
+ char *responseName, struct berval *responseValue);
typedef int (*send_ldap_search_entry_fn_ptr_t)( Slapi_PBlock *pb,
Slapi_Entry *e, LDAPControl **ectrls, char **attrs, int attrsonly );
typedef void (*send_ldap_result_fn_ptr_t)( Slapi_PBlock *pb, int err,