summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am83
-rw-r--r--Makefile.in128
-rw-r--r--ldap/admin/src/scripts/template-fixup-linkedattrs.pl.in152
-rw-r--r--ldap/ldif/template-dse.ldif.in12
-rw-r--r--ldap/servers/plugins/linkedattrs/fixup_task.c401
-rw-r--r--ldap/servers/plugins/linkedattrs/linked_attrs.c2024
-rw-r--r--ldap/servers/plugins/linkedattrs/linked_attrs.h141
-rw-r--r--ldap/servers/slapd/add.c4
-rw-r--r--ldap/servers/slapd/attr.c2
-rw-r--r--ldap/servers/slapd/dse.c13
10 files changed, 2886 insertions, 74 deletions
diff --git a/Makefile.am b/Makefile.am
index 27869a92..4dd8973e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -148,12 +148,13 @@ enable_presence = off
endif
serverplugin_LTLIBRARIES = libacl-plugin.la libattr-unique-plugin.la \
- libback-ldbm.la libchainingdb-plugin.la libcos-plugin.la libdes-plugin.la \
- libdistrib-plugin.la libhttp-client-plugin.la libcollation-plugin.la \
+ libback-ldbm.la libchainingdb-plugin.la libcollation-plugin.la \
+ libcos-plugin.la libdes-plugin.la libdistrib-plugin.la \
+ libhttp-client-plugin.la liblinkedattrs-plugin.la \
libmemberof-plugin.la libpassthru-plugin.la libpwdstorage-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 $(LIBPAM_PASSTHRU_PLUGIN) \
+ libroles-plugin.la libschemareload-plugin.la libstatechange-plugin.la \
+ libsyntax-plugin.la libviews-plugin.la $(LIBPAM_PASSTHRU_PLUGIN) \
$(LIBDNA_PLUGIN) $(LIBBITWISE_PLUGIN) $(LIBPRESENCE_PLUGIN)
nodist_property_DATA = ns-slapd.properties
@@ -299,6 +300,7 @@ task_SCRIPTS = ldap/admin/src/scripts/template-bak2db \
ldap/admin/src/scripts/template-db2bak.pl \
ldap/admin/src/scripts/template-db2index.pl \
ldap/admin/src/scripts/template-db2ldif.pl \
+ ldap/admin/src/scripts/template-fixup-linkedattrs.pl \
ldap/admin/src/scripts/template-fixup-memberof.pl \
ldap/admin/src/scripts/template-ldif2db.pl \
ldap/admin/src/scripts/template-ns-accountstatus.pl \
@@ -664,6 +666,14 @@ libattr_unique_plugin_la_CPPFLAGS = -I$(srcdir)/ldap/servers/plugins/shared $(PL
libattr_unique_plugin_la_LDFLAGS = -avoid-version
#------------------------
+# libbitwise-plugin
+#------------------------
+libbitwise_plugin_la_SOURCES = ldap/servers/plugins/bitwise/bitwise.c
+
+libbitwise_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
+libbitwise_plugin_la_LDFLAGS = -avoid-version
+
+#------------------------
# libchainingdb-plugin
#------------------------
libchainingdb_plugin_la_SOURCES = ldap/servers/plugins/chainingdb/cb_abandon.c \
@@ -696,6 +706,18 @@ libchainingdb_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
libchainingdb_plugin_la_LDFLAGS = -avoid-version
#------------------------
+# libcollation-plugin
+#------------------------
+libcollation_plugin_la_SOURCES = ldap/servers/plugins/collation/collate.c \
+ ldap/servers/plugins/collation/config.c \
+ ldap/servers/plugins/collation/orfilter.c
+
+libcollation_plugin_la_CPPFLAGS = @icu_inc@ $(PLUGIN_CPPFLAGS)
+libcollation_plugin_la_LIBADD = $(ICU_LINK) $(LIBCSTD) $(LIBCRUN)
+libcollation_plugin_la_LDFLAGS = -avoid-version
+libcollation_plugin_la_LINK = $(CXXLINK) -avoid-version
+
+#------------------------
# libcos-plugin
#------------------------
libcos_plugin_la_SOURCES = ldap/servers/plugins/cos/cos.c \
@@ -722,6 +744,14 @@ libdistrib_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
libdistrib_plugin_la_LDFLAGS = -avoid-version
#------------------------
+# libdna-plugin
+#------------------------
+libdna_plugin_la_SOURCES = ldap/servers/plugins/dna/dna.c
+
+libdna_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
+libdna_plugin_la_LDFLAGS = -avoid-version
+
+#------------------------
# libhttp-client-plugin
#------------------------
libhttp_client_plugin_la_SOURCES = ldap/servers/plugins/http/http_client.c \
@@ -731,16 +761,13 @@ libhttp_client_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
libhttp_client_plugin_la_LDFLAGS = -avoid-version
#------------------------
-# libcollation-plugin
+# liblinkedattrs-plugin
#------------------------
-libcollation_plugin_la_SOURCES = ldap/servers/plugins/collation/collate.c \
- ldap/servers/plugins/collation/config.c \
- ldap/servers/plugins/collation/orfilter.c
+liblinkedattrs_plugin_la_SOURCES = ldap/servers/plugins/linkedattrs/fixup_task.c \
+ ldap/servers/plugins/linkedattrs/linked_attrs.c
-libcollation_plugin_la_CPPFLAGS = @icu_inc@ $(PLUGIN_CPPFLAGS)
-libcollation_plugin_la_LIBADD = $(ICU_LINK) $(LIBCSTD) $(LIBCRUN)
-libcollation_plugin_la_LDFLAGS = -avoid-version
-libcollation_plugin_la_LINK = $(CXXLINK) -avoid-version
+liblinkedattrs_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
+liblinkedattrs_plugin_la_LDFLAGS = -avoid-version
#------------------------
# libmemberof-plugin
@@ -892,6 +919,14 @@ libroles_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
libroles_plugin_la_LDFLAGS = -avoid-version
#------------------------
+# libschemareload-plugin
+#------------------------
+libschemareload_plugin_la_SOURCES = ldap/servers/plugins/schema_reload/schema_reload.c
+
+libschemareload_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
+libschemareload_plugin_la_LDFLAGS = -avoid-version
+
+#------------------------
# libstatechange-plugin
#------------------------
libstatechange_plugin_la_SOURCES = ldap/servers/plugins/statechange/statechange.c
@@ -928,30 +963,6 @@ libviews_plugin_la_SOURCES = ldap/servers/plugins/views/views.c
libviews_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
libviews_plugin_la_LDFLAGS = -avoid-version
-#------------------------
-# libschemareload-plugin
-#------------------------
-libschemareload_plugin_la_SOURCES = ldap/servers/plugins/schema_reload/schema_reload.c
-
-libschemareload_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
-libschemareload_plugin_la_LDFLAGS = -avoid-version
-
-#------------------------
-# libdna-plugin
-#------------------------
-libdna_plugin_la_SOURCES = ldap/servers/plugins/dna/dna.c
-
-libdna_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
-libdna_plugin_la_LDFLAGS = -avoid-version
-
-#------------------------
-# libbitwise-plugin
-#------------------------
-libbitwise_plugin_la_SOURCES = ldap/servers/plugins/bitwise/bitwise.c
-
-libbitwise_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
-libbitwise_plugin_la_LDFLAGS = -avoid-version
-
#////////////////////////////////////////////////////////////////
#
diff --git a/Makefile.in b/Makefile.in
index 1e423694..e82a1e64 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -279,6 +279,14 @@ libhttp_client_plugin_la_OBJECTS = \
libhttp_client_plugin_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(libhttp_client_plugin_la_LDFLAGS) $(LDFLAGS) -o $@
+liblinkedattrs_plugin_la_LIBADD =
+am_liblinkedattrs_plugin_la_OBJECTS = ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-fixup_task.lo \
+ ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-linked_attrs.lo
+liblinkedattrs_plugin_la_OBJECTS = \
+ $(am_liblinkedattrs_plugin_la_OBJECTS)
+liblinkedattrs_plugin_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(liblinkedattrs_plugin_la_LDFLAGS) $(LDFLAGS) -o $@
libmemberof_plugin_la_LIBADD =
am_libmemberof_plugin_la_OBJECTS = ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof.lo \
ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof_config.lo
@@ -872,6 +880,7 @@ SOURCES = $(libavl_a_SOURCES) $(libldaputil_a_SOURCES) \
$(libdes_plugin_la_SOURCES) $(libdistrib_plugin_la_SOURCES) \
$(libdna_plugin_la_SOURCES) \
$(libhttp_client_plugin_la_SOURCES) \
+ $(liblinkedattrs_plugin_la_SOURCES) \
$(libmemberof_plugin_la_SOURCES) $(libns_dshttpd_la_SOURCES) \
$(libpam_passthru_plugin_la_SOURCES) \
$(libpassthru_plugin_la_SOURCES) \
@@ -898,6 +907,7 @@ DIST_SOURCES = $(libavl_a_SOURCES) $(libldaputil_a_SOURCES) \
$(libdes_plugin_la_SOURCES) $(libdistrib_plugin_la_SOURCES) \
$(libdna_plugin_la_SOURCES) \
$(libhttp_client_plugin_la_SOURCES) \
+ $(liblinkedattrs_plugin_la_SOURCES) \
$(libmemberof_plugin_la_SOURCES) $(libns_dshttpd_la_SOURCES) \
$(libpam_passthru_plugin_la_SOURCES) \
$(libpassthru_plugin_la_SOURCES) \
@@ -1222,12 +1232,13 @@ server_LTLIBRARIES = libslapd.la libns-dshttpd.la
@enable_presence_FALSE@enable_presence = off
@enable_presence_TRUE@enable_presence = on
serverplugin_LTLIBRARIES = libacl-plugin.la libattr-unique-plugin.la \
- libback-ldbm.la libchainingdb-plugin.la libcos-plugin.la libdes-plugin.la \
- libdistrib-plugin.la libhttp-client-plugin.la libcollation-plugin.la \
+ libback-ldbm.la libchainingdb-plugin.la libcollation-plugin.la \
+ libcos-plugin.la libdes-plugin.la libdistrib-plugin.la \
+ libhttp-client-plugin.la liblinkedattrs-plugin.la \
libmemberof-plugin.la libpassthru-plugin.la libpwdstorage-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 $(LIBPAM_PASSTHRU_PLUGIN) \
+ libroles-plugin.la libschemareload-plugin.la libstatechange-plugin.la \
+ libsyntax-plugin.la libviews-plugin.la $(LIBPAM_PASSTHRU_PLUGIN) \
$(LIBDNA_PLUGIN) $(LIBBITWISE_PLUGIN) $(LIBPRESENCE_PLUGIN)
nodist_property_DATA = ns-slapd.properties
@@ -1372,6 +1383,7 @@ task_SCRIPTS = ldap/admin/src/scripts/template-bak2db \
ldap/admin/src/scripts/template-db2bak.pl \
ldap/admin/src/scripts/template-db2index.pl \
ldap/admin/src/scripts/template-db2ldif.pl \
+ ldap/admin/src/scripts/template-fixup-linkedattrs.pl \
ldap/admin/src/scripts/template-fixup-memberof.pl \
ldap/admin/src/scripts/template-ldif2db.pl \
ldap/admin/src/scripts/template-ns-accountstatus.pl \
@@ -1684,6 +1696,13 @@ libattr_unique_plugin_la_CPPFLAGS = -I$(srcdir)/ldap/servers/plugins/shared $(PL
libattr_unique_plugin_la_LDFLAGS = -avoid-version
#------------------------
+# libbitwise-plugin
+#------------------------
+libbitwise_plugin_la_SOURCES = ldap/servers/plugins/bitwise/bitwise.c
+libbitwise_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
+libbitwise_plugin_la_LDFLAGS = -avoid-version
+
+#------------------------
# libchainingdb-plugin
#------------------------
libchainingdb_plugin_la_SOURCES = ldap/servers/plugins/chainingdb/cb_abandon.c \
@@ -1716,6 +1735,18 @@ libchainingdb_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
libchainingdb_plugin_la_LDFLAGS = -avoid-version
#------------------------
+# libcollation-plugin
+#------------------------
+libcollation_plugin_la_SOURCES = ldap/servers/plugins/collation/collate.c \
+ ldap/servers/plugins/collation/config.c \
+ ldap/servers/plugins/collation/orfilter.c
+
+libcollation_plugin_la_CPPFLAGS = @icu_inc@ $(PLUGIN_CPPFLAGS)
+libcollation_plugin_la_LIBADD = $(ICU_LINK) $(LIBCSTD) $(LIBCRUN)
+libcollation_plugin_la_LDFLAGS = -avoid-version
+libcollation_plugin_la_LINK = $(CXXLINK) -avoid-version
+
+#------------------------
# libcos-plugin
#------------------------
libcos_plugin_la_SOURCES = ldap/servers/plugins/cos/cos.c \
@@ -1741,6 +1772,13 @@ libdistrib_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
libdistrib_plugin_la_LDFLAGS = -avoid-version
#------------------------
+# libdna-plugin
+#------------------------
+libdna_plugin_la_SOURCES = ldap/servers/plugins/dna/dna.c
+libdna_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
+libdna_plugin_la_LDFLAGS = -avoid-version
+
+#------------------------
# libhttp-client-plugin
#------------------------
libhttp_client_plugin_la_SOURCES = ldap/servers/plugins/http/http_client.c \
@@ -1750,16 +1788,13 @@ libhttp_client_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
libhttp_client_plugin_la_LDFLAGS = -avoid-version
#------------------------
-# libcollation-plugin
+# liblinkedattrs-plugin
#------------------------
-libcollation_plugin_la_SOURCES = ldap/servers/plugins/collation/collate.c \
- ldap/servers/plugins/collation/config.c \
- ldap/servers/plugins/collation/orfilter.c
+liblinkedattrs_plugin_la_SOURCES = ldap/servers/plugins/linkedattrs/fixup_task.c \
+ ldap/servers/plugins/linkedattrs/linked_attrs.c
-libcollation_plugin_la_CPPFLAGS = @icu_inc@ $(PLUGIN_CPPFLAGS)
-libcollation_plugin_la_LIBADD = $(ICU_LINK) $(LIBCSTD) $(LIBCRUN)
-libcollation_plugin_la_LDFLAGS = -avoid-version
-libcollation_plugin_la_LINK = $(CXXLINK) -avoid-version
+liblinkedattrs_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
+liblinkedattrs_plugin_la_LDFLAGS = -avoid-version
#------------------------
# libmemberof-plugin
@@ -1909,6 +1944,13 @@ libroles_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
libroles_plugin_la_LDFLAGS = -avoid-version
#------------------------
+# libschemareload-plugin
+#------------------------
+libschemareload_plugin_la_SOURCES = ldap/servers/plugins/schema_reload/schema_reload.c
+libschemareload_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
+libschemareload_plugin_la_LDFLAGS = -avoid-version
+
+#------------------------
# libstatechange-plugin
#------------------------
libstatechange_plugin_la_SOURCES = ldap/servers/plugins/statechange/statechange.c
@@ -1943,27 +1985,6 @@ libviews_plugin_la_SOURCES = ldap/servers/plugins/views/views.c
libviews_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
libviews_plugin_la_LDFLAGS = -avoid-version
-#------------------------
-# libschemareload-plugin
-#------------------------
-libschemareload_plugin_la_SOURCES = ldap/servers/plugins/schema_reload/schema_reload.c
-libschemareload_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
-libschemareload_plugin_la_LDFLAGS = -avoid-version
-
-#------------------------
-# libdna-plugin
-#------------------------
-libdna_plugin_la_SOURCES = ldap/servers/plugins/dna/dna.c
-libdna_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
-libdna_plugin_la_LDFLAGS = -avoid-version
-
-#------------------------
-# libbitwise-plugin
-#------------------------
-libbitwise_plugin_la_SOURCES = ldap/servers/plugins/bitwise/bitwise.c
-libbitwise_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
-libbitwise_plugin_la_LDFLAGS = -avoid-version
-
#////////////////////////////////////////////////////////////////
#
# Programs
@@ -2814,6 +2835,20 @@ ldap/servers/plugins/http/libhttp_client_plugin_la-http_impl.lo: \
ldap/servers/plugins/http/$(DEPDIR)/$(am__dirstamp)
libhttp-client-plugin.la: $(libhttp_client_plugin_la_OBJECTS) $(libhttp_client_plugin_la_DEPENDENCIES)
$(libhttp_client_plugin_la_LINK) -rpath $(serverplugindir) $(libhttp_client_plugin_la_OBJECTS) $(libhttp_client_plugin_la_LIBADD) $(LIBS)
+ldap/servers/plugins/linkedattrs/$(am__dirstamp):
+ @$(MKDIR_P) ldap/servers/plugins/linkedattrs
+ @: > ldap/servers/plugins/linkedattrs/$(am__dirstamp)
+ldap/servers/plugins/linkedattrs/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) ldap/servers/plugins/linkedattrs/$(DEPDIR)
+ @: > ldap/servers/plugins/linkedattrs/$(DEPDIR)/$(am__dirstamp)
+ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-fixup_task.lo: \
+ ldap/servers/plugins/linkedattrs/$(am__dirstamp) \
+ ldap/servers/plugins/linkedattrs/$(DEPDIR)/$(am__dirstamp)
+ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-linked_attrs.lo: \
+ ldap/servers/plugins/linkedattrs/$(am__dirstamp) \
+ ldap/servers/plugins/linkedattrs/$(DEPDIR)/$(am__dirstamp)
+liblinkedattrs-plugin.la: $(liblinkedattrs_plugin_la_OBJECTS) $(liblinkedattrs_plugin_la_DEPENDENCIES)
+ $(liblinkedattrs_plugin_la_LINK) -rpath $(serverplugindir) $(liblinkedattrs_plugin_la_OBJECTS) $(liblinkedattrs_plugin_la_LIBADD) $(LIBS)
ldap/servers/plugins/memberof/$(am__dirstamp):
@$(MKDIR_P) ldap/servers/plugins/memberof
@: > ldap/servers/plugins/memberof/$(am__dirstamp)
@@ -4180,6 +4215,10 @@ mostlyclean-compile:
-rm -f ldap/servers/plugins/http/libhttp_client_plugin_la-http_client.lo
-rm -f ldap/servers/plugins/http/libhttp_client_plugin_la-http_impl.$(OBJEXT)
-rm -f ldap/servers/plugins/http/libhttp_client_plugin_la-http_impl.lo
+ -rm -f ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-fixup_task.$(OBJEXT)
+ -rm -f ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-fixup_task.lo
+ -rm -f ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-linked_attrs.$(OBJEXT)
+ -rm -f ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-linked_attrs.lo
-rm -f ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof.$(OBJEXT)
-rm -f ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof.lo
-rm -f ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof_config.$(OBJEXT)
@@ -4937,6 +4976,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/dna/$(DEPDIR)/libdna_plugin_la-dna.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/http/$(DEPDIR)/libhttp_client_plugin_la-http_client.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/http/$(DEPDIR)/libhttp_client_plugin_la-http_impl.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/linkedattrs/$(DEPDIR)/liblinkedattrs_plugin_la-fixup_task.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/linkedattrs/$(DEPDIR)/liblinkedattrs_plugin_la-linked_attrs.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/memberof/$(DEPDIR)/libmemberof_plugin_la-memberof.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/memberof/$(DEPDIR)/libmemberof_plugin_la-memberof_config.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/pam_passthru/$(DEPDIR)/libpam_passthru_plugin_la-pam_ptconfig.Plo@am__quote@
@@ -6285,6 +6326,20 @@ ldap/servers/plugins/http/libhttp_client_plugin_la-http_impl.lo: ldap/servers/pl
@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) $(libhttp_client_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ldap/servers/plugins/http/libhttp_client_plugin_la-http_impl.lo `test -f 'ldap/servers/plugins/http/http_impl.c' || echo '$(srcdir)/'`ldap/servers/plugins/http/http_impl.c
+ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-fixup_task.lo: ldap/servers/plugins/linkedattrs/fixup_task.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblinkedattrs_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-fixup_task.lo -MD -MP -MF ldap/servers/plugins/linkedattrs/$(DEPDIR)/liblinkedattrs_plugin_la-fixup_task.Tpo -c -o ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-fixup_task.lo `test -f 'ldap/servers/plugins/linkedattrs/fixup_task.c' || echo '$(srcdir)/'`ldap/servers/plugins/linkedattrs/fixup_task.c
+@am__fastdepCC_TRUE@ mv -f ldap/servers/plugins/linkedattrs/$(DEPDIR)/liblinkedattrs_plugin_la-fixup_task.Tpo ldap/servers/plugins/linkedattrs/$(DEPDIR)/liblinkedattrs_plugin_la-fixup_task.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ldap/servers/plugins/linkedattrs/fixup_task.c' object='ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-fixup_task.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) $(liblinkedattrs_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-fixup_task.lo `test -f 'ldap/servers/plugins/linkedattrs/fixup_task.c' || echo '$(srcdir)/'`ldap/servers/plugins/linkedattrs/fixup_task.c
+
+ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-linked_attrs.lo: ldap/servers/plugins/linkedattrs/linked_attrs.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblinkedattrs_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-linked_attrs.lo -MD -MP -MF ldap/servers/plugins/linkedattrs/$(DEPDIR)/liblinkedattrs_plugin_la-linked_attrs.Tpo -c -o ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-linked_attrs.lo `test -f 'ldap/servers/plugins/linkedattrs/linked_attrs.c' || echo '$(srcdir)/'`ldap/servers/plugins/linkedattrs/linked_attrs.c
+@am__fastdepCC_TRUE@ mv -f ldap/servers/plugins/linkedattrs/$(DEPDIR)/liblinkedattrs_plugin_la-linked_attrs.Tpo ldap/servers/plugins/linkedattrs/$(DEPDIR)/liblinkedattrs_plugin_la-linked_attrs.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ldap/servers/plugins/linkedattrs/linked_attrs.c' object='ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-linked_attrs.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) $(liblinkedattrs_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-linked_attrs.lo `test -f 'ldap/servers/plugins/linkedattrs/linked_attrs.c' || echo '$(srcdir)/'`ldap/servers/plugins/linkedattrs/linked_attrs.c
+
ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof.lo: ldap/servers/plugins/memberof/memberof.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmemberof_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof.lo -MD -MP -MF ldap/servers/plugins/memberof/$(DEPDIR)/libmemberof_plugin_la-memberof.Tpo -c -o ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof.lo `test -f 'ldap/servers/plugins/memberof/memberof.c' || echo '$(srcdir)/'`ldap/servers/plugins/memberof/memberof.c
@am__fastdepCC_TRUE@ mv -f ldap/servers/plugins/memberof/$(DEPDIR)/libmemberof_plugin_la-memberof.Tpo ldap/servers/plugins/memberof/$(DEPDIR)/libmemberof_plugin_la-memberof.Plo
@@ -8962,6 +9017,7 @@ clean-libtool:
-rm -rf ldap/servers/plugins/distrib/.libs ldap/servers/plugins/distrib/_libs
-rm -rf ldap/servers/plugins/dna/.libs ldap/servers/plugins/dna/_libs
-rm -rf ldap/servers/plugins/http/.libs ldap/servers/plugins/http/_libs
+ -rm -rf ldap/servers/plugins/linkedattrs/.libs ldap/servers/plugins/linkedattrs/_libs
-rm -rf ldap/servers/plugins/memberof/.libs ldap/servers/plugins/memberof/_libs
-rm -rf ldap/servers/plugins/pam_passthru/.libs ldap/servers/plugins/pam_passthru/_libs
-rm -rf ldap/servers/plugins/passthru/.libs ldap/servers/plugins/passthru/_libs
@@ -9459,6 +9515,8 @@ distclean-generic:
-rm -f ldap/servers/plugins/dna/$(am__dirstamp)
-rm -f ldap/servers/plugins/http/$(DEPDIR)/$(am__dirstamp)
-rm -f ldap/servers/plugins/http/$(am__dirstamp)
+ -rm -f ldap/servers/plugins/linkedattrs/$(DEPDIR)/$(am__dirstamp)
+ -rm -f ldap/servers/plugins/linkedattrs/$(am__dirstamp)
-rm -f ldap/servers/plugins/memberof/$(DEPDIR)/$(am__dirstamp)
-rm -f ldap/servers/plugins/memberof/$(am__dirstamp)
-rm -f ldap/servers/plugins/pam_passthru/$(DEPDIR)/$(am__dirstamp)
@@ -9529,7 +9587,7 @@ clean-am: clean-binPROGRAMS clean-generic clean-libtool \
distclean: distclean-am
-rm -f $(am__CONFIG_DISTCLEAN_FILES)
- -rm -rf ldap/libraries/libavl/$(DEPDIR) ldap/servers/plugins/acl/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR) ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR) ldap/servers/plugins/cos/$(DEPDIR) ldap/servers/plugins/distrib/$(DEPDIR) ldap/servers/plugins/dna/$(DEPDIR) ldap/servers/plugins/http/$(DEPDIR) ldap/servers/plugins/memberof/$(DEPDIR) ldap/servers/plugins/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(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/schema_reload/$(DEPDIR) ldap/servers/plugins/shared/$(DEPDIR) ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/syntaxes/$(DEPDIR) ldap/servers/plugins/uiduniq/$(DEPDIR) ldap/servers/plugins/views/$(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/acl/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR) ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR) ldap/servers/plugins/cos/$(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/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(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/schema_reload/$(DEPDIR) ldap/servers/plugins/shared/$(DEPDIR) ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/syntaxes/$(DEPDIR) ldap/servers/plugins/uiduniq/$(DEPDIR) ldap/servers/plugins/views/$(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
@@ -9571,7 +9629,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/acl/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR) ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR) ldap/servers/plugins/cos/$(DEPDIR) ldap/servers/plugins/distrib/$(DEPDIR) ldap/servers/plugins/dna/$(DEPDIR) ldap/servers/plugins/http/$(DEPDIR) ldap/servers/plugins/memberof/$(DEPDIR) ldap/servers/plugins/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(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/schema_reload/$(DEPDIR) ldap/servers/plugins/shared/$(DEPDIR) ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/syntaxes/$(DEPDIR) ldap/servers/plugins/uiduniq/$(DEPDIR) ldap/servers/plugins/views/$(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/acl/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR) ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR) ldap/servers/plugins/cos/$(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/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(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/schema_reload/$(DEPDIR) ldap/servers/plugins/shared/$(DEPDIR) ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/syntaxes/$(DEPDIR) ldap/servers/plugins/uiduniq/$(DEPDIR) ldap/servers/plugins/views/$(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/template-fixup-linkedattrs.pl.in b/ldap/admin/src/scripts/template-fixup-linkedattrs.pl.in
new file mode 100644
index 00000000..48a89266
--- /dev/null
+++ b/ldap/admin/src/scripts/template-fixup-linkedattrs.pl.in
@@ -0,0 +1,152 @@
+#{{PERL-EXEC}}
+#
+# 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) 2001 Sun Microsystems, Inc. Used by permission.
+# Copyright (C) 2009 Red Hat, Inc.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+
+sub usage {
+ print(STDERR "Usage: $0 [-v] -D rootdn { -w password | -w - | -j filename } \n");
+ print(STDERR " [-l linkDN]\n");
+ print(STDERR " Opts: -D rootdn - Directory Manager\n");
+ print(STDERR " : -w password - Directory Manager's password\n");
+ print(STDERR " : -w - - Prompt for Directory Manager's password\n");
+ print(STDERR " : -j filename - Read Directory Manager's password from file\n");
+ print(STDERR " : -l linkDN - DN of link config entry that you want to fix\n");
+ print(STDERR " up the links for. If omitted, all configured\n");
+ print(STDERR " linked attributes will be fixed up.\n");
+ print(STDERR " : -v - verbose\n");
+}
+
+$rootdn = "";
+$passwd = "";
+$passwdfile = "";
+$linkdn_arg = "";
+$verbose = 0;
+
+$prefix = "{{DS-ROOT}}";
+
+$ENV{'PATH'} = "$prefix@ldapsdk_bindir@:$prefix/usr/bin:@ldapsdk_bindir@:/usr/bin";
+$ENV{'LD_LIBRARY_PATH'} = "$prefix@nss_libdir@:$prefix/usr/lib:@nss_libdir@:/usr/lib";
+$ENV{'SHLIB_PATH'} = "$prefix@nss_libdir@:$prefix/usr/lib:@nss_libdir@:/usr/lib";
+
+$i = 0;
+while ($i <= $#ARGV)
+{
+ if ("$ARGV[$i]" eq "-l")
+ {
+ # link DN
+ $i++; $linkdn_arg = $ARGV[$i];
+ }
+ elsif ("$ARGV[$i]" eq "-D")
+ {
+ # Directory Manager
+ $i++; $rootdn = $ARGV[$i];
+ }
+ elsif ("$ARGV[$i]" eq "-w")
+ {
+ # Directory Manager's password
+ $i++; $passwd = $ARGV[$i];
+ }
+ elsif ("$ARGV[$i]" eq "-j")
+ {
+ # Read Directory Manager's password from a file
+ $i++; $passwdfile = $ARGV[$i];
+ }
+ elsif ("$ARGV[$i]" eq "-v")
+ {
+ # verbose
+ $verbose = 1;
+ }
+ else
+ {
+ &usage; exit(1);
+ }
+ $i++;
+}
+
+if ($passwdfile ne ""){
+# Open file and get the password
+ unless (open (RPASS, $passwdfile)) {
+ die "Error, cannot open password file $passwdfile\n";
+ }
+ $passwd = <RPASS>;
+ chomp($passwd);
+ close(RPASS);
+} elsif ($passwd eq "-"){
+# Read the password from terminal
+ print "Bind Password: ";
+ # Disable console echo
+ system("stty -echo");
+ # read the answer
+ $passwd = <STDIN>;
+ # Enable console echo
+ system("stty echo");
+ print "\n";
+ chop($passwd); # trim trailing newline
+}
+
+if ( $rootdn eq "" || $passwd eq "" )
+{
+ &usage;
+ exit(1);
+}
+
+$vstr = "";
+if ($verbose != 0)
+{
+ $vstr = "-v";
+}
+
+# Use a timestamp as part of the task entry name
+($s, $m, $h, $dy, $mn, $yr, $wdy, $ydy, $r) = localtime(time);
+$mn++; $yr += 1900;
+$taskname = "linked_attrs_fixup_${yr}_${mn}_${dy}_${h}_${m}_${s}";
+
+# Build the task entry to add
+$dn = "dn: cn=$taskname, cn=fixup linked attributes, cn=tasks, cn=config\n";
+$misc = "changetype: add\nobjectclass: top\nobjectclass: extensibleObject\n";
+$cn = "cn: $taskname\n";
+if ($linkdn_arg ne "")
+{
+ $linkdn = "linkdn: $linkdn_arg\n";
+}
+
+$entry = "${dn}${misc}${cn}${basedn}${linkdn}";
+open(FOO, "| ldapmodify $vstr -h {{SERVER-NAME}} -p {{SERVER-PORT}} -D \"$rootdn\" -w \"$passwd\" -a" );
+print(FOO "$entry");
+close(FOO);
diff --git a/ldap/ldif/template-dse.ldif.in b/ldap/ldif/template-dse.ldif.in
index 82326d55..2694f0f1 100644
--- a/ldap/ldif/template-dse.ldif.in
+++ b/ldap/ldif/template-dse.ldif.in
@@ -554,6 +554,18 @@ nsslapd-plugintype: preoperation
nsslapd-pluginenabled: on
nsslapd-plugin-depends-on-type: database
+dn: cn=Linked Attributes,cn=plugins,cn=config
+objectclass: top
+objectclass: nsSlapdPlugin
+objectclass: extensibleObject
+objectclass: nsContainer
+cn: Linked Attributes
+nsslapd-pluginpath: liblinkedattrs-plugin
+nsslapd-plugininitfunc: linked_attrs_init
+nsslapd-plugintype: preoperation
+nsslapd-pluginenabled: on
+nsslapd-plugin-depends-on-type: database
+
dn: cn=Pass Through Authentication,cn=plugins,cn=config
objectclass: top
objectclass: nsSlapdPlugin
diff --git a/ldap/servers/plugins/linkedattrs/fixup_task.c b/ldap/servers/plugins/linkedattrs/fixup_task.c
new file mode 100644
index 00000000..50d35d0e
--- /dev/null
+++ b/ldap/servers/plugins/linkedattrs/fixup_task.c
@@ -0,0 +1,401 @@
+/** 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) 2009 Red Hat, Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* fixup_task.c - linked attributes fixup task */
+
+#include "linked_attrs.h"
+
+/*
+ * Function Prototypes
+ */
+static void linked_attrs_fixup_task_destructor(Slapi_Task *task);
+static void linked_attrs_fixup_task_thread(void *arg);
+static void linked_attrs_fixup_links(struct configEntry *config);
+static int linked_attrs_remove_backlinks_callback(Slapi_Entry *e, void *callback_data);
+static int linked_attrs_add_backlinks_callback(Slapi_Entry *e, void *callback_data);
+static const char *fetch_attr(Slapi_Entry *e, const char *attrname,
+ const char *default_val);
+
+/*
+ * Function Implementations
+ */
+int
+linked_attrs_fixup_task_add(Slapi_PBlock *pb, Slapi_Entry *e,
+ Slapi_Entry *eAfter, int *returncode,
+ char *returntext, void *arg)
+{
+ PRThread *thread = NULL;
+ int rv = SLAPI_DSE_CALLBACK_OK;
+ task_data *mytaskdata = NULL;
+ Slapi_Task *task = NULL;
+ const char *linkdn = NULL;
+
+ *returncode = LDAP_SUCCESS;
+ /* get arg(s) */
+ linkdn = fetch_attr(e, "linkdn", 0);
+
+ /* setup our task data */
+ mytaskdata = (task_data*)slapi_ch_calloc(1, sizeof(task_data));
+ if (mytaskdata == NULL) {
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+
+ if (linkdn) {
+ mytaskdata->linkdn = slapi_dn_normalize(slapi_ch_strdup(linkdn));
+ }
+
+ /* allocate new task now */
+ task = slapi_new_task(slapi_entry_get_ndn(e));
+
+ /* register our destructor for cleaning up our private data */
+ slapi_task_set_destructor_fn(task, linked_attrs_fixup_task_destructor);
+
+ /* Stash a pointer to our data in the task */
+ slapi_task_set_data(task, mytaskdata);
+
+ /* start the sample task as a separate thread */
+ thread = PR_CreateThread(PR_USER_THREAD, linked_attrs_fixup_task_thread,
+ (void *)task, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE);
+ if (thread == NULL) {
+ slapi_log_error( SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "unable to create task thread!\n");
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ slapi_task_finish(task, *returncode);
+ } else {
+ rv = SLAPI_DSE_CALLBACK_OK;
+ }
+
+out:
+ return rv;
+}
+
+static void
+linked_attrs_fixup_task_destructor(Slapi_Task *task)
+{
+ if (task) {
+ task_data *mydata = (task_data *)slapi_task_get_data(task);
+ if (mydata) {
+ slapi_ch_free_string(&mydata->linkdn);
+ /* Need to cast to avoid a compiler warning */
+ slapi_ch_free((void **)&mydata);
+ }
+ }
+}
+
+static void
+linked_attrs_fixup_task_thread(void *arg)
+{
+ int rc = 0;
+ Slapi_Task *task = (Slapi_Task *)arg;
+ task_data *td = NULL;
+ PRCList *main_config = NULL;
+ int found_config = 0;
+
+ /* Fetch our task data from the task */
+ td = (task_data *)slapi_task_get_data(task);
+
+ /* Log started message. */
+ slapi_task_begin(task, 1);
+ slapi_task_log_notice(task, "Linked attributes fixup task starting (link dn: \"%s\") ...\n",
+ td->linkdn ? td->linkdn : "");
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "Syntax validate task starting (link dn: \"%s\") ...\n",
+ td->linkdn ? td->linkdn : "");
+
+ linked_attrs_read_lock();
+ main_config = linked_attrs_get_config();
+ if (!PR_CLIST_IS_EMPTY(main_config)) {
+ struct configEntry *config_entry = NULL;
+ PRCList *list = PR_LIST_HEAD(main_config);
+
+ while (list != main_config) {
+ config_entry = (struct configEntry *) list;
+
+ /* See if this is the requested config and fix up if so. */
+ if (td->linkdn) {
+ if (strcasecmp(td->linkdn, config_entry->dn) == 0) {
+ found_config = 1;
+ slapi_task_log_notice(task, "Fixing up linked attribute pair (%s)\n",
+ config_entry->dn);
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "Fixing up linked attribute pair (%s)\n",
+ config_entry->dn);
+
+ linked_attrs_fixup_links(config_entry);
+ break;
+ }
+ } else {
+ /* No config DN was supplied, so fix up all configured links. */
+ slapi_task_log_notice(task, "Fixing up linked attribute pair (%s)\n",
+ config_entry->dn);
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "Fixing up linked attribute pair (%s)\n", config_entry->dn);
+
+ linked_attrs_fixup_links(config_entry);
+ }
+
+ list = PR_NEXT_LINK(list);
+ }
+ }
+
+ /* Log a message if we didn't find the requested attribute pair. */
+ if (td->linkdn && !found_config) {
+ slapi_task_log_notice(task, "Requested link config DN not found (%s)\n",
+ td->linkdn);
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "Requested link config DN not found (%s)\n", td->linkdn);
+ }
+
+ linked_attrs_unlock();
+
+ /* Log finished message. */
+ slapi_task_log_notice(task, "Linked attributes fixup task complete.\n");
+ slapi_task_log_status(task, "Linked attributes fixup task complete.\n");
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM, "Linked attributes fixup task complete.\n");
+ slapi_task_inc_progress(task);
+
+ /* this will queue the destruction of the task */
+ slapi_task_finish(task, rc);
+}
+
+static void
+linked_attrs_fixup_links(struct configEntry *config)
+{
+ Slapi_PBlock *pb = slapi_pblock_new();
+ char *del_filter = NULL;
+ char *add_filter = NULL;
+
+ del_filter = slapi_ch_smprintf("%s=*", config->managedtype);
+ add_filter = slapi_ch_smprintf("%s=*", config->linktype);
+
+ /* Lock the attribute pair. */
+ slapi_lock_mutex(config->lock);
+
+ if (config->scope) {
+ /* Find all entries with the managed type present
+ * within the scope and remove the managed type. */
+ slapi_search_internal_set_pb(pb, config->scope, LDAP_SCOPE_SUBTREE,
+ del_filter, 0, 0, 0, 0, linked_attrs_get_plugin_id(), 0);
+
+ slapi_search_internal_callback_pb(pb, config->managedtype, 0,
+ linked_attrs_remove_backlinks_callback, 0);
+
+ /* Clean out pblock for reuse. */
+ slapi_pblock_init(pb);
+
+ /* Find all entries with the link type present within the
+ * scope and add backlinks to the entries they point to. */
+ slapi_search_internal_set_pb(pb, config->scope, LDAP_SCOPE_SUBTREE,
+ add_filter, 0, 0, 0, 0, linked_attrs_get_plugin_id(), 0);
+
+ slapi_search_internal_callback_pb(pb, config, 0,
+ linked_attrs_add_backlinks_callback, 0);
+ } else {
+ /* Loop through all non-private backend suffixes and
+ * remove the managed type from any entry that has it.
+ * We then find any entry with the linktype present and
+ * generate the proper backlinks. */
+ void *node = NULL;
+ Slapi_DN * suffix = slapi_get_first_suffix (&node, 0);
+
+ while (suffix) {
+ slapi_search_internal_set_pb(pb, slapi_sdn_get_dn(suffix),
+ LDAP_SCOPE_SUBTREE, del_filter,
+ 0, 0, 0, 0,
+ linked_attrs_get_plugin_id(), 0);
+
+ slapi_search_internal_callback_pb(pb, config->managedtype, 0,
+ linked_attrs_remove_backlinks_callback, 0);
+
+ /* Clean out pblock for reuse. */
+ slapi_pblock_init(pb);
+
+ slapi_search_internal_set_pb(pb, slapi_sdn_get_dn(suffix),
+ LDAP_SCOPE_SUBTREE, add_filter,
+ 0, 0, 0, 0,
+ linked_attrs_get_plugin_id(), 0);
+
+ slapi_search_internal_callback_pb(pb, config, 0,
+ linked_attrs_add_backlinks_callback, 0);
+
+ /* Clean out pblock for reuse. */
+ slapi_pblock_init(pb);
+
+ suffix = slapi_get_next_suffix (&node, 0);
+ }
+ }
+
+ /* Unlock the attribute pair. */
+ slapi_unlock_mutex(config->lock);
+
+ slapi_ch_free_string(&del_filter);
+ slapi_ch_free_string(&add_filter);
+ slapi_pblock_destroy(pb);
+}
+
+static int
+linked_attrs_remove_backlinks_callback(Slapi_Entry *e, void *callback_data)
+{
+ int rc = 0;
+ char *dn = slapi_entry_get_dn(e);
+ char *type = (char *)callback_data;
+ Slapi_PBlock *pb = slapi_pblock_new();
+ char *val[1];
+ LDAPMod mod;
+ LDAPMod *mods[2];
+
+ /* Remove all values of the passed in type. */
+ val[0] = 0;
+
+ mod.mod_op = LDAP_MOD_DELETE;
+ mod.mod_type = type;
+ mod.mod_values = val;
+
+ mods[0] = &mod;
+ mods[1] = 0;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM,
+ "Removing backpointer attribute (%s) from entry (%s)\n",
+ type, dn);
+
+ /* Perform the operation. */
+ slapi_modify_internal_set_pb(pb, dn, mods, 0, 0,
+ linked_attrs_get_plugin_id(), 0);
+ slapi_modify_internal_pb(pb);
+
+ slapi_pblock_destroy(pb);
+
+ return rc;
+}
+
+static int
+linked_attrs_add_backlinks_callback(Slapi_Entry *e, void *callback_data)
+{
+ int rc = 0;
+ char *linkdn = slapi_entry_get_dn(e);
+ struct configEntry *config = (struct configEntry *)callback_data;
+ Slapi_PBlock *pb = slapi_pblock_new();
+ int i = 0;
+ char **targets = NULL;
+ char *val[2];
+ LDAPMod mod;
+ LDAPMod *mods[2];
+
+ /* Setup the modify operation. Only the target will
+ * change, so we only need to do this once. */
+ val[0] = linkdn;
+ val[1] = 0;
+
+ mod.mod_op = LDAP_MOD_ADD;
+ mod.mod_type = config->managedtype;
+ mod.mod_values = val;
+
+ mods[0] = &mod;
+ mods[1] = 0;
+
+ targets = slapi_entry_attr_get_charray(e, config->linktype);
+ for (i = 0; targets && targets[i]; ++i) {
+ char *targetdn = (char *)targets[i];
+ int perform_update = 0;
+
+ if (config->scope) {
+ /* Check if the target is within the scope. */
+ perform_update = slapi_dn_issuffix(targetdn, config->scope);
+ } else {
+ /* Find out the root suffix that the linkdn is in
+ * and see if the target is in the same backend. */
+ Slapi_Backend *be = NULL;
+ Slapi_DN *linksdn = slapi_sdn_new_dn_byref(linkdn);
+ Slapi_DN *targetsdn = slapi_sdn_new_dn_byref(targetdn);
+
+ if ((be = slapi_be_select(linksdn))) {
+ perform_update = slapi_sdn_issuffix(targetsdn, slapi_be_getsuffix(be, 0));
+ }
+
+ slapi_sdn_free(&linksdn);
+ slapi_sdn_free(&targetsdn);
+ }
+
+ if (perform_update) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM,
+ "Adding backpointer (%s) in entry (%s)\n",
+ linkdn, targetdn);
+
+ /* Perform the modify operation. */
+ slapi_modify_internal_set_pb(pb, targetdn, mods, 0, 0,
+ linked_attrs_get_plugin_id(), 0);
+ slapi_modify_internal_pb(pb);
+
+ /* Initialize the pblock so we can reuse it. */
+ slapi_pblock_init(pb);
+ }
+ }
+
+ slapi_ch_array_free(targets);
+ slapi_pblock_destroy(pb);
+
+ return rc;
+}
+
+/* extract a single value from the entry (as a string) -- if it's not in the
+ * entry, the default will be returned (which can be NULL).
+ * you do not need to free anything returned by this.
+ */
+static const char *
+fetch_attr(Slapi_Entry *e, const char *attrname,
+ const char *default_val)
+{
+Slapi_Attr *attr;
+Slapi_Value *val = NULL;
+
+ if (slapi_entry_attr_find(e, attrname, &attr) != 0) {
+ return default_val;
+ }
+
+ slapi_attr_first_value(attr, &val);
+
+ return slapi_value_get_string(val);
+}
+
diff --git a/ldap/servers/plugins/linkedattrs/linked_attrs.c b/ldap/servers/plugins/linkedattrs/linked_attrs.c
new file mode 100644
index 00000000..5126e35a
--- /dev/null
+++ b/ldap/servers/plugins/linkedattrs/linked_attrs.c
@@ -0,0 +1,2024 @@
+/** 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) 2009 Red Hat, Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/*
+ * Linked attributes plug-in
+ */
+#include "linked_attrs.h"
+
+
+/*
+ * Plug-in globals
+ */
+static PRCList *g_link_config = NULL;
+static PRCList *g_managed_config_index = NULL;
+static PRRWLock *g_config_lock;
+
+static void *_PluginID = NULL;
+static char *_PluginDN = NULL;
+static int g_plugin_started = 0;
+
+static Slapi_PluginDesc pdesc = { LINK_FEATURE_DESC,
+ PLUGIN_MAGIC_VENDOR_STR,
+ PRODUCTTEXT,
+ LINK_PLUGIN_DESC };
+
+/*
+ * Plug-in management functions
+ */
+int linked_attrs_init(Slapi_PBlock * pb);
+static int linked_attrs_start(Slapi_PBlock * pb);
+static int linked_attrs_close(Slapi_PBlock * pb);
+static int linked_attrs_postop_init(Slapi_PBlock * pb);
+static int linked_attrs_internal_postop_init(Slapi_PBlock *pb);
+
+/*
+ * Operation callbacks (where the real work is done)
+ */
+static int linked_attrs_mod_post_op(Slapi_PBlock *pb);
+static int linked_attrs_add_post_op(Slapi_PBlock *pb);
+static int linked_attrs_del_post_op(Slapi_PBlock *pb);
+static int linked_attrs_modrdn_post_op(Slapi_PBlock *pb);
+static int linked_attrs_pre_op(Slapi_PBlock *pb, int modop);
+static int linked_attrs_mod_pre_op(Slapi_PBlock *pb);
+static int linked_attrs_add_pre_op(Slapi_PBlock *pb);
+
+/*
+ * Config cache management functions
+ */
+static int linked_attrs_load_config();
+static void linked_attrs_delete_config();
+static int linked_attrs_parse_config_entry(Slapi_Entry * e, int apply);
+static void linked_attrs_insert_config_index(struct configEntry *entry);
+static void linked_attrs_free_config_entry(struct configEntry ** entry);
+
+/*
+ * helpers
+ */
+static char *linked_attrs_get_dn(Slapi_PBlock * pb);
+static int linked_attrs_dn_is_config(char *dn);
+static void linked_attrs_find_config(const char *dn, const char *type,
+ struct configEntry **config);
+static void linked_attrs_find_config_reverse(const char *dn,
+ const char *type, struct configEntry **config);
+static int linked_attrs_config_index_has_type(char *type);
+static int linked_attrs_config_exists(struct configEntry *entry);
+static int linked_attrs_config_exists_reverse(struct configEntry *entry);
+static int linked_attrs_oktodo(Slapi_PBlock *pb);
+void linked_attrs_load_array(Slapi_Value **array, Slapi_Attr *attr);
+int linked_attrs_compare(const void *a, const void *b);
+static void linked_attrs_add_backpointers(char *linkdn, struct configEntry *config,
+ Slapi_Mod *smod);
+static void linked_attrs_del_backpointers(Slapi_PBlock *pb, char *linkdn,
+ struct configEntry *config, Slapi_Mod *smod);
+static void linked_attrs_replace_backpointers(Slapi_PBlock *pb, char *linkdn,
+ struct configEntry *config, Slapi_Mod *smod);
+static void linked_attrs_mod_backpointers(char *linkdn, char *type, char *scope,
+ int modop, Slapi_ValueSet *targetvals);
+
+/*
+ * Config cache locking functions
+ */
+void
+linked_attrs_read_lock()
+{
+ PR_RWLock_Rlock(g_config_lock);
+}
+
+void
+linked_attrs_write_lock()
+{
+ PR_RWLock_Wlock(g_config_lock);
+}
+
+void
+linked_attrs_unlock()
+{
+ PR_RWLock_Unlock(g_config_lock);
+}
+
+
+/*
+ * Plugin identity functions
+ */
+void
+linked_attrs_set_plugin_id(void *pluginID)
+{
+ _PluginID = pluginID;
+}
+
+void *
+linked_attrs_get_plugin_id()
+{
+ return _PluginID;
+}
+
+void
+linked_attrs_set_plugin_dn(char *pluginDN)
+{
+ _PluginDN = pluginDN;
+}
+
+char *
+linked_attrs_get_plugin_dn()
+{
+ return _PluginDN;
+}
+
+
+/*
+ * Plug-in initialization functions
+ */
+int
+linked_attrs_init(Slapi_PBlock *pb)
+{
+ int status = 0;
+ char *plugin_identity = NULL;
+
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "--> linked_attrs_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);
+ linked_attrs_set_plugin_id(plugin_identity);
+
+ /* Register callbacks */
+ if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
+ (void *) linked_attrs_start) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
+ (void *) linked_attrs_close) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *) &pdesc) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODIFY_FN,
+ (void *) linked_attrs_mod_pre_op) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN,
+ (void *) linked_attrs_add_pre_op) != 0 ||
+ slapi_register_plugin("internalpostoperation", /* op type */
+ 1, /* Enabled */
+ "linked_attrs_init", /* this function desc */
+ linked_attrs_internal_postop_init, /* init func */
+ LINK_INT_POSTOP_DESC, /* plugin desc */
+ NULL, /* ? */
+ plugin_identity /* access control */
+ ) ||
+ slapi_register_plugin("postoperation", /* op type */
+ 1, /* Enabled */
+ "linked_attrs_init", /* this function desc */
+ linked_attrs_postop_init, /* init func for post op */
+ LINK_POSTOP_DESC, /* plugin desc */
+ NULL, /* ? */
+ plugin_identity /* access control */
+ )
+ ) {
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_init: failed to register plugin\n");
+ status = -1;
+ }
+
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "<-- linked_attrs_init\n");
+ return status;
+}
+
+static int
+linked_attrs_internal_postop_init(Slapi_PBlock *pb)
+{
+ int status = 0;
+
+ if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *) &pdesc) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_ADD_FN,
+ (void *) linked_attrs_add_post_op) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_DELETE_FN,
+ (void *) linked_attrs_del_post_op) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_MODIFY_FN,
+ (void *) linked_attrs_mod_post_op) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_MODRDN_FN,
+ (void *) linked_attrs_modrdn_post_op) != 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_internal_postop_init: failed to register plugin\n");
+ status = -1;
+ }
+
+ return status;
+}
+
+static int
+linked_attrs_postop_init(Slapi_PBlock *pb)
+{
+ int status = 0;
+
+ if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *) &pdesc) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN,
+ (void *) linked_attrs_add_post_op) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN,
+ (void *) linked_attrs_del_post_op) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN,
+ (void *) linked_attrs_mod_post_op) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN,
+ (void *) linked_attrs_modrdn_post_op) != 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_postop_init: failed to register plugin\n");
+ status = -1;
+ }
+
+ return status;
+}
+
+
+/*
+ * linked_attrs_start()
+ *
+ * Creates config lock and loads config cache.
+ */
+static int
+linked_attrs_start(Slapi_PBlock * pb)
+{
+ char *plugindn = NULL;
+
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "--> linked_attrs_start\n");
+
+ /* Check if we're already started */
+ if (g_plugin_started) {
+ goto done;
+ }
+
+ g_config_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "linkedattrs");
+
+ if (!g_config_lock) {
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_start: lock creation failed\n");
+
+ return -1;
+ }
+
+ /*
+ * Get the plug-in target dn from the system
+ * and store it for future use. */
+ slapi_pblock_get(pb, SLAPI_TARGET_DN, &plugindn);
+ if (NULL == plugindn || 0 == strlen(plugindn)) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_start: unable to retrieve plugin dn\n");
+ return -1;
+ }
+
+ linked_attrs_set_plugin_dn(plugindn);
+
+ /*
+ * Load the config cache
+ */
+ g_link_config = (PRCList *)slapi_ch_calloc(1, sizeof(struct configEntry));
+ PR_INIT_CLIST(g_link_config);
+ g_managed_config_index = (PRCList *)slapi_ch_calloc(1, sizeof(struct configIndex));
+ PR_INIT_CLIST(g_managed_config_index);
+
+ if (linked_attrs_load_config() != 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_start: unable to load plug-in configuration\n");
+ return -1;
+ }
+
+ /*
+ * Register our task callback
+ */
+ slapi_task_register_handler("fixup linked attributes", linked_attrs_fixup_task_add);
+
+ g_plugin_started = 1;
+ slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM,
+ "linked attributes plug-in: ready for service\n");
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "<-- linked_attrs_start\n");
+
+done:
+ return 0;
+}
+
+/*
+ * linked_attrs_close()
+ *
+ * Cleans up the config cache.
+ */
+static int
+linked_attrs_close(Slapi_PBlock * pb)
+{
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "--> linked_attrs_close\n");
+
+ linked_attrs_delete_config();
+
+ slapi_ch_free((void **)&g_link_config);
+ slapi_ch_free((void **)&g_managed_config_index);
+
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "<-- linked_attrs_close\n");
+
+ return 0;
+}
+
+PRCList *
+linked_attrs_get_config()
+{
+ return g_link_config;
+}
+
+/*
+ * config looks like this
+ * - cn=myplugin
+ * --- cn=manager link
+ * --- cn=owner link
+ * --- cn=etc
+ */
+static int
+linked_attrs_load_config()
+{
+ int status = 0;
+ int result;
+ int i;
+ Slapi_PBlock *search_pb;
+ Slapi_Entry **entries = NULL;
+
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "--> linked_attrs_load_config\n");
+
+ /* Clear out any old config. */
+ linked_attrs_write_lock();
+ linked_attrs_delete_config();
+
+ /* Find the config entries beneath our plugin entry. */
+ search_pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(search_pb, linked_attrs_get_plugin_dn(),
+ LDAP_SCOPE_SUBTREE, "objectclass=*",
+ NULL, 0, NULL, NULL, linked_attrs_get_plugin_id(), 0);
+ slapi_search_internal_pb(search_pb);
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
+
+ if (LDAP_SUCCESS != result) {
+ status = -1;
+ goto cleanup;
+ }
+
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
+ &entries);
+ if (NULL == entries || NULL == entries[0]) {
+ /* If there are no config entries, we're done. */
+ goto cleanup;
+ }
+
+ /* Loop through all of the entries we found and parse them. */
+ for (i = 0; (entries[i] != NULL); i++) {
+ /* We don't care about the status here because we may have
+ * some invalid config entries, but we just want to continue
+ * looking for valid ones. */
+ linked_attrs_parse_config_entry(entries[i], 1);
+ }
+
+ cleanup:
+ slapi_free_search_results_internal(search_pb);
+ slapi_pblock_destroy(search_pb);
+ linked_attrs_unlock();
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "<-- linked_attrs_load_config\n");
+
+ return status;
+}
+
+/*
+ * linked_attrs_parse_config_entry()
+ *
+ * Parses a single config entry. If apply is non-zero, then
+ * we will load and start using the new config. You can simply
+ * validate config without making any changes by setting apply
+ * to 0.
+ *
+ * Returns 0 if the entry is valid and -1 if it is invalid.
+ */
+static int
+linked_attrs_parse_config_entry(Slapi_Entry * e, int apply)
+{
+ char *value;
+ struct configEntry *entry = NULL;
+ struct configEntry *config_entry;
+ PRCList *list;
+ int entry_added = 0;
+ int ret = 0;
+
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "--> linked_attrs_parse_config_entry\n");
+
+ /* If this is the main plug-in
+ * config entry, just bail. */
+ if (strcasecmp(linked_attrs_get_plugin_dn(), slapi_entry_get_ndn(e)) == 0) {
+ ret = -1;
+ goto bail;
+ }
+
+ entry = (struct configEntry *)slapi_ch_calloc(1, sizeof(struct configEntry));
+ if (NULL == entry) {
+ ret = -1;
+ goto bail;
+ }
+
+ value = slapi_entry_get_ndn(e);
+ if (value) {
+ entry->dn = slapi_ch_strdup(value);
+ } else {
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_parse_config_entry: Error "
+ "reading dn from config entry\n");
+ ret = -1;
+ goto bail;
+ }
+
+ slapi_log_error(SLAPI_LOG_CONFIG, LINK_PLUGIN_SUBSYSTEM,
+ "----------> dn [%s]\n", entry->dn);
+
+ value = slapi_entry_attr_get_charptr(e, LINK_LINK_TYPE);
+ if (value) {
+ int not_dn_syntax = 0;
+ char *syntaxoid = NULL;
+ Slapi_Attr *attr = slapi_attr_new();
+
+ /* Set this first so we free it if we encounter an error */
+ entry->linktype = value;
+
+ /* Gather some information about this attribute. */
+ slapi_attr_init(attr, value);
+ slapi_attr_get_syntax_oid_copy(attr, &syntaxoid );
+ not_dn_syntax = strcmp(syntaxoid, DN_SYNTAX_OID);
+ slapi_ch_free_string(&syntaxoid);
+ slapi_attr_free(&attr);
+
+ /* Check if the link type's syntax is Distinguished Name.
+ * We only treat this as a warning. */
+ if (not_dn_syntax) {
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_parse_config_entry: The %s config "
+ "setting must be set to an attribute with the "
+ "Distinguished Name syntax for linked attribute "
+ "pair \"%s\".\n", LINK_LINK_TYPE, entry->dn);
+ }
+ } else {
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_parse_config_entry: The %s config "
+ "setting is required for linked attribute pair \"%s\".\n",
+ LINK_LINK_TYPE, entry->dn);
+ ret = -1;
+ goto bail;
+ }
+
+ slapi_log_error(SLAPI_LOG_CONFIG, LINK_PLUGIN_SUBSYSTEM,
+ "----------> %s [%s]\n", LINK_LINK_TYPE, entry->linktype);
+
+ value = slapi_entry_attr_get_charptr(e, LINK_MANAGED_TYPE);
+ if (value) {
+ int single_valued = 0;
+ int not_dn_syntax = 0;
+ char *syntaxoid = NULL;
+ Slapi_Attr *attr = slapi_attr_new();
+
+ /* Set this first so we free it if we encounter an error */
+ entry->managedtype = value;
+
+ /* Gather some information about this attribute. */
+ slapi_attr_init(attr, value);
+ slapi_attr_get_syntax_oid_copy(attr, &syntaxoid );
+ not_dn_syntax = strcmp(syntaxoid, DN_SYNTAX_OID);
+ single_valued = slapi_attr_flag_is_set(attr, SLAPI_ATTR_FLAG_SINGLE);
+ slapi_ch_free_string(&syntaxoid);
+ slapi_attr_free(&attr);
+
+ /* Ensure that the managed type is a multi-valued attribute. */
+ if (single_valued) {
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_parse_config_entry: The %s config "
+ "setting must be set to a multi-valued attribute "
+ "for linked attribute pair \"%s\".\n",
+ LINK_MANAGED_TYPE, entry->dn);
+ ret = -1;
+ goto bail;
+ /* Check if the link type's syntax is Distinguished Name.
+ * We only treat this as a warning. */
+ } else if (not_dn_syntax) {
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_parse_config_entry: The %s config "
+ "setting must be set to an attribute with the "
+ "Distinguished Name syntax for linked attribute "
+ "pair \"%s\".\n", LINK_MANAGED_TYPE, entry->dn);
+ }
+ } else {
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_parse_config_entry: The %s config "
+ "setting is required for linked attribute pair \"%s\".\n",
+ LINK_MANAGED_TYPE, entry->dn);
+ ret = -1;
+ goto bail;
+ }
+
+ slapi_log_error(SLAPI_LOG_CONFIG, LINK_PLUGIN_SUBSYSTEM,
+ "----------> %s [%s]\n", LINK_MANAGED_TYPE,
+ entry->managedtype);
+
+ /* A scope is not required. No scope means it
+ * applies to any part of the DIT. */
+ value = slapi_entry_attr_get_charptr(e, LINK_SCOPE);
+ if (value) {
+ entry->scope = value;
+ }
+
+ slapi_log_error(SLAPI_LOG_CONFIG, LINK_PLUGIN_SUBSYSTEM,
+ "----------> %s [%s]\n", LINK_SCOPE,
+ entry->scope ? entry->scope : "NULL");
+
+ /* Check if config already exists for
+ * the link type at the same scope. */
+ if (linked_attrs_config_exists(entry)) {
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_parse_config_entry: A config "
+ "entry for the link attribute %s already "
+ "exists at a scope of \"%s\".\n", entry->linktype,
+ entry->scope);
+ ret = -1;
+ goto bail;
+ }
+
+ /* Check if config already exists for
+ * the managed type at the same scope. */
+ if (linked_attrs_config_exists_reverse(entry)) {
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_parse_config_entry: A config "
+ "entry for the managed attribute %s already "
+ "exists at a scope of \"%s\".\n", entry->managedtype,
+ entry->scope);
+ ret = -1;
+ goto bail;
+ }
+
+ /* If we were only called to validate config, we can
+ * just bail out before applying the config changes */
+ if (apply == 0) {
+ goto bail;
+ }
+
+ /* Create a lock for this attribute pair. */
+ entry->lock = slapi_new_mutex();
+ if (!entry->lock) {
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_parse_config_entry: Unable to create "
+ "lock for linked attribute pair \"%s\".\n", entry->dn);
+ ret = -1;
+ goto bail;
+ }
+
+ /* Add the entry to the list. We group by link type. We
+ * also maintain a reverse list grouped by managed type. */
+ if (!PR_CLIST_IS_EMPTY(g_link_config)) {
+ list = PR_LIST_HEAD(g_link_config);
+ while (list != g_link_config) {
+ config_entry = (struct configEntry *) list;
+
+ /* See if the types match. We want to group
+ * entries for the same link type together. */
+ if (slapi_attr_type_cmp(config_entry->linktype, entry->linktype, 1) == 0) {
+ PR_INSERT_BEFORE(&(entry->list), list);
+ slapi_log_error(SLAPI_LOG_CONFIG, LINK_PLUGIN_SUBSYSTEM,
+ "store [%s] before [%s] \n", entry->dn,
+ config_entry->dn);
+
+ /* add to managed type index */
+ linked_attrs_insert_config_index(entry);
+
+ entry_added = 1;
+ break;
+ }
+
+ list = PR_NEXT_LINK(list);
+
+ if (g_link_config == list) {
+ /* add to tail */
+ PR_INSERT_BEFORE(&(entry->list), list);
+ slapi_log_error(SLAPI_LOG_CONFIG, LINK_PLUGIN_SUBSYSTEM,
+ "store [%s] at tail\n", entry->dn);
+
+ /* add to managed type index */
+ linked_attrs_insert_config_index(entry);
+
+ entry_added = 1;
+ break;
+ }
+ }
+ } else {
+ /* first entry */
+ PR_INSERT_LINK(&(entry->list), g_link_config);
+ slapi_log_error(SLAPI_LOG_CONFIG, LINK_PLUGIN_SUBSYSTEM,
+ "store [%s] at head \n", entry->dn);
+
+ /* add to managed type index */
+ linked_attrs_insert_config_index(entry);
+
+ entry_added = 1;
+ }
+
+ bail:
+ if (0 == entry_added) {
+ /* Don't log error if we weren't asked to apply config */
+ if ((apply != 0) && (entry != NULL)) {
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_parse_config_entry: Invalid config entry "
+ "[%s] skipped\n", entry->dn);
+ }
+ linked_attrs_free_config_entry(&entry);
+ } else {
+ ret = 0;
+ }
+
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "<-- linked_attrs_parse_config_entry\n");
+
+ return ret;
+}
+
+/*
+ * linked_attrs_insert_config_index()
+ *
+ * Adds an entry to the ordered config index. We maintain
+ * an list of pointers to the cached config entries that is
+ * grouped by managed type. We use this index to find the
+ * appropriate config entry when given a backpointer. This
+ * is useful for the case where an entry with backpointers
+ * is renamed and we need to updated the forward link.
+ */
+static void
+linked_attrs_insert_config_index(struct configEntry *entry)
+{
+ struct configEntry *config_entry = NULL;
+ struct configIndex *index_entry = NULL;
+ PRCList *list = PR_LIST_HEAD(g_managed_config_index);
+
+ index_entry = (struct configIndex *)slapi_ch_calloc(1, sizeof(struct configIndex));
+ index_entry->config = entry;
+
+ if (!PR_CLIST_IS_EMPTY(g_managed_config_index)) {
+ while (list != g_managed_config_index) {
+ config_entry = ((struct configIndex *)list)->config;
+
+ /* See if the types match. */
+ if (slapi_attr_type_cmp(config_entry->managedtype, entry->managedtype, 1) == 0) {
+ PR_INSERT_BEFORE(&(index_entry->list), list);
+ slapi_log_error(SLAPI_LOG_CONFIG, LINK_PLUGIN_SUBSYSTEM,
+ "store [%s] before [%s] \n", entry->dn,
+ config_entry->dn);
+ break;
+ }
+
+ list = PR_NEXT_LINK(list);
+
+ if (g_managed_config_index == list) {
+ /* add to tail */
+ PR_INSERT_BEFORE(&(index_entry->list), list);
+ slapi_log_error(SLAPI_LOG_CONFIG, LINK_PLUGIN_SUBSYSTEM,
+ "store [%s] at tail\n", entry->dn);
+ break;
+ }
+ }
+ } else {
+ /* first entry */
+ slapi_log_error(SLAPI_LOG_CONFIG, LINK_PLUGIN_SUBSYSTEM,
+ "store [%s] at head \n", entry->dn);
+ PR_INSERT_LINK(&(index_entry->list), g_managed_config_index);
+ }
+}
+
+static void
+linked_attrs_free_config_entry(struct configEntry ** entry)
+{
+ struct configEntry *e = *entry;
+
+ if (e == NULL)
+ return;
+
+ if (e->dn) {
+ slapi_log_error(SLAPI_LOG_CONFIG, LINK_PLUGIN_SUBSYSTEM,
+ "freeing config entry [%s]\n", e->dn);
+ slapi_ch_free_string(&e->dn);
+ }
+
+ if (e->linktype)
+ slapi_ch_free_string(&e->linktype);
+
+ if (e->managedtype)
+ slapi_ch_free_string(&e->managedtype);
+
+ if (e->scope)
+ slapi_ch_free_string(&e->scope);
+
+ if (e->lock)
+ slapi_destroy_mutex(e->lock);
+
+ slapi_ch_free((void **) entry);
+}
+
+static void
+linked_attrs_delete_configEntry(PRCList *entry)
+{
+ PR_REMOVE_LINK(entry);
+ linked_attrs_free_config_entry((struct configEntry **) &entry);
+}
+
+static void
+linked_attrs_delete_config()
+{
+ PRCList *list;
+
+ /* Delete the config cache. */
+ while (!PR_CLIST_IS_EMPTY(g_link_config)) {
+ list = PR_LIST_HEAD(g_link_config);
+ linked_attrs_delete_configEntry(list);
+ }
+
+ /* Delete the reverse index. */
+ while (!PR_CLIST_IS_EMPTY(g_managed_config_index)) {
+ list = PR_LIST_HEAD(g_managed_config_index);
+ PR_REMOVE_LINK(list);
+ slapi_ch_free((void **)&list);
+ }
+
+ return;
+}
+
+
+/*
+ * Helper functions
+ */
+static char *
+linked_attrs_get_dn(Slapi_PBlock * pb)
+{
+ char *dn = 0;
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "--> linked_attrs_get_dn\n");
+
+ if (slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn)) {
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_get_dn: failed to get dn of changed entry");
+ goto bail;
+ }
+
+ bail:
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "<-- linked_attrs_get_dn\n");
+
+ return dn;
+}
+
+/*
+ * linked_attrs_dn_is_config()
+ *
+ * Checks if dn is a linked attribute config entry.
+ */
+static int
+linked_attrs_dn_is_config(char *dn)
+{
+ int ret = 0;
+
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "--> linked_attrs_dn_is_config\n");
+
+ /* Return 1 if the passed in dn is a child of the main
+ * plugin config entry. */
+ if (slapi_dn_issuffix(dn, linked_attrs_get_plugin_dn()) &&
+ strcasecmp(dn, linked_attrs_get_plugin_dn())) {
+ ret = 1;
+ }
+
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "<-- linked_attrs_dn_is_config\n");
+
+ return ret;
+}
+
+/*
+ * linked_attrs_find_config()
+ *
+ * Finds the appropriate config entry for a given dn and
+ * link type. A read lock must be held on the config
+ * before calling this function. The configEntry that is
+ * returned is a pointer to the actual config entry in
+ * the config cache. It should not be modified in any
+ * way. The read lock should not be released until you
+ * are finished with the config entry that is returned.
+
+ * Returns NULL if no applicable config entry is found.
+ */
+static void
+linked_attrs_find_config(const char *dn,
+ const char *type, struct configEntry **config)
+{
+ int found_type = 0;
+ PRCList *list = NULL;
+
+ *config = NULL;
+
+ if (!PR_CLIST_IS_EMPTY(g_link_config)) {
+ list = PR_LIST_HEAD(g_link_config);
+ while (list != g_link_config) {
+ if (slapi_attr_type_cmp(((struct configEntry *)list)->linktype,
+ type, 1) == 0) {
+ /* Set a flag indicating that we found a config entry
+ * for this type. We use this flag so we can stop
+ * processing early if we don't find a matching scope. */
+ found_type = 1;
+
+ /* Check if the dn is in the scope of this config
+ * entry. If the config entry doesn't have a scope
+ * (global), consider it a match. If we have a match,
+ * we can stop processing the config. */
+ if ((((struct configEntry *)list)->scope == NULL) ||
+ (slapi_dn_issuffix(dn, ((struct configEntry *)list)->scope))) {
+ *config = (struct configEntry *)list;
+ break;
+ }
+ } else {
+ /* If flag is set, we're done. We have configured links
+ * for this type, but none of the scopes match. */
+ if (found_type) {
+ break;
+ }
+ }
+
+ list = PR_NEXT_LINK(list);
+ }
+ }
+}
+
+/*
+ * linked_attrs_find_config_reverse()
+ *
+ * Finds the appropriate config entry for a given dn and
+ * managed type. A read lock must be held on the config
+ * before calling this function. The configEntry that is
+ * returned is a pointer to the actual config entry in
+ * the config cache. It should not be modified in any
+ * way. The read lock should not be released until you
+ * are finished with the config entry that is returned.
+
+ * Returns NULL if no applicable config entry is found.
+ */
+static void
+linked_attrs_find_config_reverse(const char *dn,
+ const char *type, struct configEntry **config)
+{
+ int found_type = 0;
+ PRCList *list = NULL;
+
+ *config = NULL;
+
+ if (!PR_CLIST_IS_EMPTY(g_managed_config_index)) {
+ list = PR_LIST_HEAD(g_managed_config_index);
+ while (list != g_managed_config_index) {
+ if (slapi_attr_type_cmp(((struct configIndex *)list)->config->managedtype,
+ type, 1) == 0) {
+ /* Set a flag indicating that we found a config entry
+ * for this type. We use this flag so we can stop
+ * processing early if we don't find a matching scope. */
+ found_type = 1;
+
+ /* Check if the dn is in the scope of this config
+ * entry. If the config entry doesn't have a scope
+ * (global), consider it a match. If we have a match,
+ * we can stop processing the config. */
+ if ((((struct configIndex *)list)->config->scope == NULL) ||
+ (slapi_dn_issuffix(dn, ((struct configIndex *)list)->config->scope))) {
+ *config = ((struct configIndex *)list)->config;
+ break;
+ }
+ } else {
+ /* If flag is set, we're done. We have configured links
+ * for this type, but none of the scopes match. */
+ if (found_type) {
+ break;
+ }
+ }
+
+ list = PR_NEXT_LINK(list);
+ }
+ }
+}
+
+/*
+ * linked_attrs_config_index_has_type()
+ *
+ * Returns 1 if a config entry exists with the passed
+ * in managed type.
+ *
+ * A read lock on the config must be held before calling
+ * this function.
+ */
+static int
+linked_attrs_config_index_has_type(char *type)
+{
+ int rc = 0;
+ PRCList *list = NULL;
+
+ if (!PR_CLIST_IS_EMPTY(g_managed_config_index)) {
+ list = PR_LIST_HEAD(g_managed_config_index);
+ while (list != g_managed_config_index) {
+ if (slapi_attr_type_cmp(((struct configIndex *)list)->config->managedtype,
+ type, 1) == 0) {
+ rc = 1;
+ break;
+ }
+
+ list = PR_NEXT_LINK(list);
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * linked_attrs_config_exists()
+ *
+ * Returns 1 if a config entry exists in the cache
+ * already for the given link type at the given scope.
+ * This will detect if the cached config entry is really
+ * the same one as the passed in entry by comparing the
+ * dn of the config entry. We will still return 0 in
+ * this case as it's one and the same config entry. We
+ * really want to use this to prevent multiple config
+ * entries for the same link type at the same scope.
+ *
+ * A read lock on the config must be held before calling
+ * this function.
+ */
+static int
+linked_attrs_config_exists(struct configEntry *entry)
+{
+ int rc = 0;
+ int found_type = 0;
+ PRCList *list = NULL;
+
+ if (!PR_CLIST_IS_EMPTY(g_link_config)) {
+ list = PR_LIST_HEAD(g_link_config);
+ while (list != g_link_config) {
+ if (slapi_attr_type_cmp(((struct configEntry *)list)->linktype,
+ entry->linktype, 1) == 0) {
+ found_type = 1;
+ /* We don't allow nested config for the same type. We
+ * need to check for nesting in both directions here.
+ * If no scope is set, we consider the entry global. */
+ if ((((struct configEntry *)list)->scope == NULL) ||
+ slapi_dn_issuffix(entry->scope, ((struct configEntry *)list)->scope) ||
+ slapi_dn_issuffix(((struct configEntry *)list)->scope, entry->scope)) {
+ /* Make sure that this isn't the same exact entry
+ * in the list already. This can happen if a config
+ * entry is being modified. Both of these were already
+ * normalized when the config struct was filled in. */
+ if (strcasecmp(entry->dn, ((struct configEntry *)list)->dn) != 0) {
+ rc = 1;
+ break;
+ }
+ }
+ } else {
+ if (found_type) {
+ /* Since the list is sorted by link type, we
+ * are finished if we get here since we found
+ * the type but didn't match the scope. */
+ break;
+ }
+ }
+
+ list = PR_NEXT_LINK(list);
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * linked_attrs_config_exists_reverse()
+ *
+ * Returns 1 if a config entry exists in the cache
+ * already for the given managed type at the given scope.
+ * This will detect if the cached config entry is really
+ * the same one as the passed in entry by comparing the
+ * dn of the config entry. We will still return 0 in
+ * this case as it's one and the same config entry. We
+ * really want to use this to prevent multiple config
+ * entries for the same managed type at the same scope.
+ *
+ * A read lock on the config must be held before calling
+ * this function.
+ */
+static int
+linked_attrs_config_exists_reverse(struct configEntry *entry)
+{
+ int rc = 0;
+ int found_type = 0;
+ PRCList *list = NULL;
+
+ if (!PR_CLIST_IS_EMPTY(g_managed_config_index)) {
+ list = PR_LIST_HEAD(g_managed_config_index);
+ while (list != g_managed_config_index) {
+ if (slapi_attr_type_cmp(((struct configIndex *)list)->config->managedtype,
+ entry->managedtype, 1) == 0) {
+ found_type = 1;
+ /* We don't allow nested config for the same type. We
+ * need to check for nesting in both directions here. */
+ if ((((struct configIndex *)list)->config->scope == NULL) ||
+ slapi_dn_issuffix(entry->scope,
+ ((struct configIndex *)list)->config->scope) ||
+ slapi_dn_issuffix(((struct configIndex *)list)->config->scope,
+ entry->scope)) {
+ /* Make sure that this isn't the same exact entry
+ * in the list already. This can happen if a config
+ * entry is being modified. Both of these were already
+ * normalized when the config struct was filled in. */
+ if (strcasecmp(entry->dn, ((struct configIndex *)list)->config->dn) != 0) {
+ rc = 1;
+ break;
+ }
+ }
+ } else {
+ if (found_type) {
+ /* Since the list is sorted by link type, we
+ * are finished if we get here since we found
+ * the type but didn't match the scope. */
+ break;
+ }
+ }
+
+ list = PR_NEXT_LINK(list);
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * linked_attrs_oktodo()
+ *
+ * Check if we want to process this operation. We need to be
+ * sure that the operation succeeded. We also respond to replicated
+ * ops so we don't test for that. This does require that the managed
+ * attributes not be replicated.
+ */
+static int
+linked_attrs_oktodo(Slapi_PBlock *pb)
+{
+ int ret = 1;
+ int oprc = 0;
+
+ slapi_log_error( SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "--> linked_attrs_oktodo\n" );
+
+ if(slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &oprc) != 0)
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_oktodo: could not get parameters\n" );
+ ret = -1;
+ }
+
+ /* This plugin should only execute if the operation succeeded. */
+ if(oprc != 0)
+ {
+ ret = 0;
+ }
+
+ slapi_log_error( SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "<-- linked_attrs_oktodo\n" );
+
+ return ret;
+}
+
+/* linked_attrs_load_array()
+ *
+ * put attribute values in array structure
+ */
+void
+linked_attrs_load_array(Slapi_Value **array, Slapi_Attr *attr)
+{
+ Slapi_Value *val = 0;
+ int hint = slapi_attr_first_value(attr, &val);
+
+ while(val)
+ {
+ *array = val;
+ array++;
+ hint = slapi_attr_next_value(attr, hint, &val);
+ }
+}
+
+/* linked_attrs_compare()
+ *
+ * Compare two attr values using the DN syntax.
+ */
+int
+linked_attrs_compare(const void *a, const void *b)
+{
+ int rc = 0;
+ Slapi_Value *val1 = *((Slapi_Value **)a);
+ Slapi_Value *val2 = *((Slapi_Value **)b);
+ Slapi_Attr *linkattr = slapi_attr_new();
+
+ slapi_attr_init(linkattr, "distinguishedName");
+
+ rc = slapi_attr_value_cmp(linkattr,
+ slapi_value_get_berval(val1),
+ slapi_value_get_berval(val2));
+
+ slapi_attr_free(&linkattr);
+
+ return rc;
+}
+
+/*
+ * linked_attrs_add_backpointers()
+ *
+ * Adds backpointers pointing to dn to the entries referred to
+ * by the values in smod.
+ */
+static void
+linked_attrs_add_backpointers(char *linkdn, struct configEntry *config,
+ Slapi_Mod *smod)
+{
+ Slapi_ValueSet *vals = slapi_valueset_new();
+
+ slapi_valueset_set_from_smod(vals, smod);
+ linked_attrs_mod_backpointers(linkdn, config->managedtype, config->scope,
+ LDAP_MOD_ADD, vals);
+
+ slapi_valueset_free(vals);
+}
+
+/*
+ * linked_attrs_del_backpointers()
+ *
+ * Remove backpointers pointing to linkdn in the entries referred
+ * to by the values in smod.
+ */
+static void
+linked_attrs_del_backpointers(Slapi_PBlock *pb, char *linkdn,
+ struct configEntry *config, Slapi_Mod *smod)
+{
+ Slapi_ValueSet *vals = NULL;
+
+ /* If no values are listed in the smod, we need to get
+ * a list of all of the values that were deleted by
+ * looking at the pre-op copy of the entry. */
+ if (slapi_mod_get_num_values(smod) == 0) {
+ Slapi_Entry *pre_e = NULL;
+ Slapi_Attr *pre_attr = NULL;
+
+ slapi_pblock_get( pb, SLAPI_ENTRY_PRE_OP, &pre_e );
+ slapi_entry_attr_find( pre_e, config->linktype, &pre_attr );
+ slapi_attr_get_valueset(pre_attr, &vals);
+ } else {
+ vals = slapi_valueset_new();
+ slapi_valueset_set_from_smod(vals, smod);
+ }
+
+ linked_attrs_mod_backpointers(linkdn, config->managedtype, config->scope,
+ LDAP_MOD_DELETE, vals);
+
+ slapi_valueset_free(vals);
+}
+
+/*
+ * linked_attrs_replace_backpointers()
+ *
+ * Remove backpointers pointing to linkdn from the entries
+ * whose values were deleted in smod and add backpointers
+ * for any new values that were added as a part of the
+ * replace operation.
+ */
+static void
+linked_attrs_replace_backpointers(Slapi_PBlock *pb, char *linkdn,
+ struct configEntry *config, Slapi_Mod *smod)
+{
+ Slapi_Entry *pre_e = NULL;
+ Slapi_Entry *post_e = NULL;
+ Slapi_Attr *pre_attr = 0;
+ Slapi_Attr *post_attr = 0;
+
+ /* Get the pre and post copy of the entry to see
+ * what values have been added and removed. */
+ slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &pre_e);
+ slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &post_e);
+
+ if(pre_e && post_e) {
+ slapi_entry_attr_find(pre_e, config->linktype, &pre_attr);
+ slapi_entry_attr_find(post_e, config->linktype, &post_attr);
+ }
+
+ if(pre_attr || post_attr) {
+ int pre_total = 0;
+ int post_total = 0;
+ Slapi_Value **pre_array = 0;
+ Slapi_Value **post_array = 0;
+ int pre_index = 0;
+ int post_index = 0;
+ Slapi_ValueSet *addvals = NULL;
+ Slapi_ValueSet *delvals = NULL;
+
+ /* create arrays of values */
+ if(pre_attr) {
+ slapi_attr_get_numvalues(pre_attr, &pre_total);
+ }
+
+ if(post_attr) {
+ slapi_attr_get_numvalues(post_attr, &post_total);
+ }
+
+ if(pre_total) {
+ pre_array = (Slapi_Value**) slapi_ch_malloc(sizeof(Slapi_Value*)*pre_total);
+ linked_attrs_load_array(pre_array, pre_attr);
+ qsort(pre_array, pre_total, sizeof(Slapi_Value*), linked_attrs_compare);
+ }
+
+ if(post_total) {
+ post_array = (Slapi_Value**) slapi_ch_malloc(sizeof(Slapi_Value*)*post_total);
+ linked_attrs_load_array(post_array, post_attr);
+ qsort(post_array, post_total, sizeof(Slapi_Value*), linked_attrs_compare);
+ }
+
+ /* Work through arrays, following these rules:
+ * - in pre, in post, do nothing
+ * - in pre, not in post, delete from entry
+ * - not in pre, in post, add to entry
+ */
+ while(pre_index < pre_total || post_index < post_total) {
+ if(pre_index == pre_total) {
+ /* add the rest of post */
+ if (addvals == NULL) {
+ addvals = slapi_valueset_new();
+ }
+
+ slapi_valueset_add_value(addvals, post_array[post_index]);
+ post_index++;
+ } else if(post_index == post_total) {
+ /* delete the rest of pre */
+ if (delvals == NULL) {
+ delvals = slapi_valueset_new();
+ }
+
+ slapi_valueset_add_value(delvals, pre_array[pre_index]);
+ pre_index++;
+ } else {
+ /* decide what to do */
+ int cmp = linked_attrs_compare(&(pre_array[pre_index]),
+ &(post_array[post_index]));
+
+ if(cmp < 0) {
+ /* delete pre array */
+ if (delvals == NULL) {
+ delvals = slapi_valueset_new();
+ }
+
+ slapi_valueset_add_value(delvals, pre_array[pre_index]);
+ pre_index++;
+ } else if(cmp > 0) {
+ /* add post array */
+ if (addvals == NULL) {
+ addvals = slapi_valueset_new();
+ }
+
+ slapi_valueset_add_value(addvals, post_array[post_index]);
+ post_index++;
+ } else {
+ /* do nothing, advance */
+ pre_index++;
+ post_index++;
+ }
+ }
+ }
+
+ /* Perform the actual updates to the target entries. */
+ if (delvals) {
+ linked_attrs_mod_backpointers(linkdn, config->managedtype,
+ config->scope, LDAP_MOD_DELETE, delvals);
+ slapi_valueset_free(delvals);
+ }
+
+ if (addvals) {
+ linked_attrs_mod_backpointers(linkdn, config->managedtype,
+ config->scope, LDAP_MOD_ADD, addvals);
+ slapi_valueset_free(addvals);
+ }
+
+ slapi_ch_free((void **)&pre_array);
+ slapi_ch_free((void **)&post_array);
+ }
+}
+
+/*
+ * linked_attrs_mod_backpointers()
+ *
+ * Performs backpointer management.
+ */
+static void
+linked_attrs_mod_backpointers(char *linkdn, char *type,
+ char *scope, int modop, Slapi_ValueSet *targetvals)
+{
+ char *val[2];
+ int i = 0;
+ Slapi_PBlock *mod_pb = slapi_pblock_new();
+ LDAPMod mod;
+ LDAPMod *mods[2];
+ Slapi_Value *targetval = NULL;
+
+ /* Setup the modify operation. Only the target will
+ * change, so we only need to do this once. */
+ val[0] = linkdn;
+ val[1] = 0;
+
+ mod.mod_op = modop;
+ mod.mod_type = type;
+ mod.mod_values = val;
+
+ mods[0] = &mod;
+ mods[1] = 0;
+
+ i = slapi_valueset_first_value(targetvals, &targetval);
+ while(targetval)
+ {
+ int perform_update = 0;
+ const char *targetdn = slapi_value_get_string(targetval);
+
+ /* If we have a scope, only update the target if it is within
+ * the scope. If we don't have a scope, only update the target
+ * if it is in the same backend as the linkdn. */
+ if (scope) {
+ perform_update = slapi_dn_issuffix(targetdn, scope);
+ } else {
+ Slapi_Backend *be = NULL;
+ Slapi_DN *linksdn = slapi_sdn_new_dn_byref(linkdn);
+ Slapi_DN *targetsdn = slapi_sdn_new_dn_byref(targetdn);
+
+ if ((be = slapi_be_select(linksdn))) {
+ perform_update = slapi_sdn_issuffix(targetsdn, slapi_be_getsuffix(be, 0));
+ }
+
+ slapi_sdn_free(&linksdn);
+ slapi_sdn_free(&targetsdn);
+ }
+
+ if (perform_update) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM,
+ "%s backpointer (%s) in entry (%s)\n",
+ (modop == LDAP_MOD_ADD) ? "Adding" : "Removing",
+ linkdn, targetdn);
+
+ /* Perform the modify operation. */
+ slapi_modify_internal_set_pb(mod_pb, targetdn, mods, 0, 0,
+ linked_attrs_get_plugin_id(), 0);
+ slapi_modify_internal_pb(mod_pb);
+
+ /* Initialize the pblock so we can reuse it. */
+ slapi_pblock_init(mod_pb);
+ }
+
+ i = slapi_valueset_next_value(targetvals, i, &targetval);
+ }
+
+ slapi_pblock_destroy(mod_pb);
+}
+
+
+/*
+ * Operation callback functions
+ */
+
+/*
+ * linked_attrs_pre_op()
+ *
+ * Checks if an operation is modifying the linked
+ * attribute config and validates the config changes.
+ */
+static int
+linked_attrs_pre_op(Slapi_PBlock * pb, int modop)
+{
+ char *dn = 0;
+ Slapi_Entry *e = 0;
+ Slapi_Mods *smods = 0;
+ LDAPMod **mods;
+ int free_entry = 0;
+ char *errstr = NULL;
+ int ret = 0;
+
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "--> linked_attrs_pre_op\n");
+
+ /* Just bail if we aren't ready to service requests yet. */
+ if (!g_plugin_started)
+ goto bail;
+
+ if (0 == (dn = linked_attrs_get_dn(pb)))
+ goto bail;
+
+ if (linked_attrs_dn_is_config(dn)) {
+ /* Validate config changes, but don't apply them.
+ * This allows us to reject invalid config changes
+ * here at the pre-op stage. Applying the config
+ * needs to be done at the post-op stage. */
+
+ if (LDAP_CHANGETYPE_ADD == modop) {
+ slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &e);
+ } else {
+ /* Fetch the entry being modified so we can
+ * create the resulting entry for validation. */
+ Slapi_DN *tmp_dn = slapi_sdn_new_dn_byref(dn);
+ if (tmp_dn) {
+ slapi_search_internal_get_entry(tmp_dn, 0, &e, linked_attrs_get_plugin_id());
+ slapi_sdn_free(&tmp_dn);
+ free_entry = 1;
+ }
+
+ /* If the entry doesn't exist, just bail and
+ * let the server handle it. */
+ if (e == NULL) {
+ goto bail;
+ }
+
+ /* Grab the mods. */
+ slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
+ smods = slapi_mods_new();
+ slapi_mods_init_passin(smods, mods);
+
+ /* Apply the mods to create the resulting entry. */
+ if (mods && (slapi_entry_apply_mods(e, mods) != LDAP_SUCCESS)) {
+ /* The mods don't apply cleanly, so we just let this op go
+ * to let the main server handle it. */
+ goto bailmod;
+ }
+ }
+
+ if (linked_attrs_parse_config_entry(e, 0) != 0) {
+ /* Refuse the operation if config parsing failed. */
+ ret = LDAP_UNWILLING_TO_PERFORM;
+ if (LDAP_CHANGETYPE_ADD == modop) {
+ errstr = slapi_ch_smprintf("Not a valid linked attribute configuration entry.");
+ } else {
+ errstr = slapi_ch_smprintf("Changes result in an invalid "
+ "linked attribute configuration.");
+ }
+ }
+
+ bailmod:
+ /* Clean up smods. */
+ if (LDAP_CHANGETYPE_MODIFY == modop) {
+ mods = slapi_mods_get_ldapmods_passout(smods);
+ slapi_pblock_set(pb, SLAPI_MODIFY_MODS, mods);
+ slapi_mods_free(&smods);
+ }
+ }
+
+ bail:
+ if (free_entry && e)
+ slapi_entry_free(e);
+
+ if (ret) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_pre_op: operation failure [%d]\n", ret);
+ slapi_send_ldap_result(pb, ret, NULL, errstr, 0, NULL);
+ slapi_ch_free((void **)&errstr);
+ ret = -1;
+ }
+
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "<-- linked_attrs_pre_op\n");
+
+ return ret;
+}
+
+static int
+linked_attrs_add_pre_op(Slapi_PBlock * pb)
+{
+ return linked_attrs_pre_op(pb, LDAP_CHANGETYPE_ADD);
+}
+
+static int
+linked_attrs_mod_pre_op(Slapi_PBlock * pb)
+{
+ return linked_attrs_pre_op(pb, LDAP_CHANGETYPE_MODIFY);
+}
+
+static int
+linked_attrs_mod_post_op(Slapi_PBlock *pb)
+{
+ Slapi_Mods *smods = NULL;
+ Slapi_Mod *smod = NULL;
+ LDAPMod **mods;
+ Slapi_Mod *next_mod = NULL;
+ char *dn = NULL;
+ struct configEntry *config = NULL;
+ void *caller_id = NULL;
+
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "--> linked_attrs_mod_post_op\n");
+
+ /* Just bail if we aren't ready to service requests yet. */
+ if (!g_plugin_started)
+ return 0;
+
+ /* We don't want to process internal modify
+ * operations that originate from this plugin.
+ * Doing so could cause a deadlock. */
+ slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &caller_id);
+
+ if (caller_id == linked_attrs_get_plugin_id()) {
+ /* Just return without processing */
+ return 0;
+ }
+
+ if (linked_attrs_oktodo(pb) &&
+ (dn = linked_attrs_get_dn(pb))) {
+ /* First check if the config is being modified. */
+ if (linked_attrs_dn_is_config(dn)) {
+ linked_attrs_load_config();
+ } else {
+ slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_mod_post_op: Error "
+ "retrieving dn\n");
+ }
+
+ /* get the mod set */
+ slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
+ smods = slapi_mods_new();
+ slapi_mods_init_byref(smods, mods);
+
+ next_mod = slapi_mod_new();
+ smod = slapi_mods_get_first_smod(smods, next_mod);
+ while(smod) {
+ char *type = (char *)slapi_mod_get_type(smod);
+
+ /* See if there is an applicable link configured. */
+ linked_attrs_read_lock();
+ linked_attrs_find_config(dn, type, &config);
+
+ /* If we have a matching config entry, we have
+ * work to do. If not, we can go to the next smod. */
+ if (config) {
+ int op = slapi_mod_get_operation(smod);
+
+ /* Prevent other threads from managing
+ * this specific link at the same time. */
+ slapi_lock_mutex(config->lock);
+
+ switch(op & ~LDAP_MOD_BVALUES) {
+ case LDAP_MOD_ADD:
+ /* Find the entries pointed to by the new
+ * values and add the backpointers. */
+ linked_attrs_add_backpointers(dn, config, smod);
+ break;
+ case LDAP_MOD_DELETE:
+ /* Find the entries pointed to by the deleted
+ * values and remove the backpointers. */
+ linked_attrs_del_backpointers(pb, dn, config, smod);
+ break;
+ case LDAP_MOD_REPLACE:
+ /* Find the entries pointed to by the deleted
+ * values and remove the backpointers. If
+ * any new values are being added, find those
+ * entries and add the backpointers. */
+ linked_attrs_replace_backpointers(pb, dn, config, smod);
+ break;
+ default:
+ slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_mod_post_op: unknown mod type\n" );
+ break;
+ }
+
+ slapi_unlock_mutex(config->lock);
+ }
+
+ config = NULL;
+ linked_attrs_unlock();
+ slapi_mod_done(next_mod);
+ smod = slapi_mods_get_next_smod(smods, next_mod);
+ }
+
+ slapi_mod_free(&next_mod);
+ slapi_mods_free(&smods);
+ }
+
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "<-- linked_attrs_mod_post_op\n");
+
+ return 0;
+}
+
+static int
+linked_attrs_add_post_op(Slapi_PBlock *pb)
+{
+ Slapi_Entry *e = NULL;
+ char *dn = NULL;
+
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "--> linked_attrs_add_post_op\n");
+
+ /* Just bail if we aren't ready to service requests yet. */
+ if (!g_plugin_started || !linked_attrs_oktodo(pb))
+ return 0;
+
+ /* Reload config if a config entry was added. */
+ if ((dn = linked_attrs_get_dn(pb))) {
+ if (linked_attrs_dn_is_config(dn))
+ linked_attrs_load_config();
+ } else {
+ slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_add_post_op: Error "
+ "retrieving dn\n");
+ }
+
+ /* Get the newly added entry. */
+ slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &e);
+
+ if (e) {
+ Slapi_Attr *attr = NULL;
+ char *type = NULL;
+ struct configEntry *config = NULL;
+
+ slapi_entry_first_attr(e, &attr);
+ while (attr) {
+ slapi_attr_get_type(attr, &type);
+
+ /* See if there is an applicable link configured. */
+ linked_attrs_read_lock();
+ linked_attrs_find_config(dn, type, &config);
+
+ /* If config was found, add the backpointers to this entry. */
+ if (config) {
+ Slapi_ValueSet *vals = NULL;
+
+ slapi_attr_get_valueset(attr, &vals);
+
+ slapi_lock_mutex(config->lock);
+
+ linked_attrs_mod_backpointers(dn, config->managedtype,
+ config->scope, LDAP_MOD_ADD, vals);
+
+ slapi_unlock_mutex(config->lock);
+
+ slapi_valueset_free(vals);
+ }
+
+ config = NULL;
+ linked_attrs_unlock();
+
+ slapi_entry_next_attr(e, attr, &attr);
+ }
+ } else {
+ slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_add_post_op: Error "
+ "retrieving post-op entry %s\n", dn);
+ }
+
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "<-- linked_attrs_add_post_op\n");
+
+ return 0;
+}
+
+static int
+linked_attrs_del_post_op(Slapi_PBlock *pb)
+{
+ char *dn = NULL;
+ Slapi_Entry *e = NULL;
+
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "--> linked_attrs_del_post_op\n");
+
+ /* Just bail if we aren't ready to service requests yet. */
+ if (!g_plugin_started || !linked_attrs_oktodo(pb))
+ return 0;
+
+ /* Reload config if a config entry was deleted. */
+ if ((dn = linked_attrs_get_dn(pb))) {
+ if (linked_attrs_dn_is_config(dn))
+ linked_attrs_load_config();
+ } else {
+ slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_del_post_op: Error "
+ "retrieving dn\n");
+ }
+
+ /* Get deleted entry, then go through types to find config. */
+ slapi_pblock_get( pb, SLAPI_ENTRY_PRE_OP, &e );
+
+ if (e) {
+ Slapi_Attr *attr = NULL;
+ char *type = NULL;
+ struct configEntry *config = NULL;
+
+ slapi_entry_first_attr(e, &attr);
+ while (attr) {
+ slapi_attr_get_type(attr, &type);
+
+ /* See if there is an applicable link configured. */
+ linked_attrs_read_lock();
+ linked_attrs_find_config(dn, type, &config);
+
+ /* If config was found, delete the backpointers to this entry. */
+ if (config) {
+ Slapi_ValueSet *vals = NULL;
+
+ slapi_attr_get_valueset(attr, &vals);
+
+ slapi_lock_mutex(config->lock);
+
+ linked_attrs_mod_backpointers(dn, config->managedtype,
+ config->scope, LDAP_MOD_DELETE, vals);
+
+ slapi_unlock_mutex(config->lock);
+
+ slapi_valueset_free(vals);
+ }
+
+ config = NULL;
+
+ /* See if any of the values for this attribute are managed
+ * backpointers. We need to remove the forward link if so. */
+ if (linked_attrs_config_index_has_type(type)) {
+ int hint = 0;
+ Slapi_Value *val = NULL;
+
+ /* Loop through values and see if we have matching config */
+ hint = slapi_attr_first_value(attr, &val);
+ while (val) {
+ linked_attrs_find_config_reverse(slapi_value_get_string(val),
+ type, &config);
+
+ if (config) {
+ Slapi_ValueSet *vals = slapi_valueset_new();
+ slapi_valueset_add_value(vals, val);
+
+ slapi_lock_mutex(config->lock);
+
+ /* Delete forward link value. */
+ linked_attrs_mod_backpointers(dn, config->linktype,
+ config->scope, LDAP_MOD_DELETE, vals);
+
+ slapi_unlock_mutex(config->lock);
+
+ slapi_valueset_free(vals);
+ config = NULL;
+ }
+
+ hint = slapi_attr_next_value(attr, hint, &val);
+ }
+ }
+
+ linked_attrs_unlock();
+
+ slapi_entry_next_attr(e, attr, &attr);
+ }
+ } else {
+ slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_del_post_op: Error "
+ "retrieving pre-op entry %s\n", dn);
+ }
+
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "<-- linked_attrs_del_post_op\n");
+
+ return 0;
+}
+
+static int
+linked_attrs_modrdn_post_op(Slapi_PBlock *pb)
+{
+ char *old_dn = NULL;
+ char *new_dn = NULL;
+ Slapi_Entry *post_e = NULL;
+ Slapi_Attr *attr = NULL;
+ char *type = NULL;
+ struct configEntry *config = NULL;
+
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "--> linked_attrs_modrdn_post_op\n");
+
+ /* Just bail if we aren't ready to service requests yet. */
+ if (!g_plugin_started || !linked_attrs_oktodo(pb))
+ return 0;;
+
+ /* Reload config if an existing config entry was renamed,
+ * or if the new dn brings an entry into the scope of the
+ * config entries. */
+ slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &post_e);
+ if (post_e) {
+ new_dn = slapi_entry_get_ndn(post_e);
+ } else {
+ slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_modrdn_post_op: Error "
+ "retrieving post-op entry\n");
+ }
+
+ if ((old_dn = linked_attrs_get_dn(pb))) {
+ if (linked_attrs_dn_is_config(old_dn) || linked_attrs_dn_is_config(new_dn))
+ linked_attrs_load_config();
+ } else {
+ slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM,
+ "linked_attrs_modrdn_post_op: Error "
+ "retrieving dn\n");
+ }
+
+ /* Check if this operation requires any updates to links. */
+ slapi_entry_first_attr(post_e, &attr);
+ while (attr) {
+ slapi_attr_get_type(attr, &type);
+
+ /* See if there is an applicable link configured. */
+ linked_attrs_read_lock();
+ linked_attrs_find_config(old_dn, type, &config);
+
+ /* If config was found for the old dn, delete the backpointers
+ * to this entry. */
+ if (config) {
+ Slapi_ValueSet *vals = NULL;
+
+ slapi_attr_get_valueset(attr, &vals);
+
+ slapi_lock_mutex(config->lock);
+
+ /* Delete old dn value. */
+ linked_attrs_mod_backpointers(old_dn, config->managedtype,
+ config->scope, LDAP_MOD_DELETE, vals);
+
+ slapi_unlock_mutex(config->lock);
+
+ slapi_valueset_free(vals);
+ config = NULL;
+ }
+
+ linked_attrs_find_config(new_dn, type, &config);
+
+ /* If config was found for the new dn, add the backpointers
+ * to this entry. We do this separate check for both dn's
+ * to catch an entry that comes into or goes out of scope
+ * from the MODRDN operation. */
+ if (config) {
+ Slapi_ValueSet *vals = NULL;
+
+ slapi_attr_get_valueset(attr, &vals);
+
+ slapi_lock_mutex(config->lock);
+
+ /* Add new dn value. */
+ linked_attrs_mod_backpointers(new_dn, config->managedtype,
+ config->scope, LDAP_MOD_ADD, vals);
+
+ slapi_unlock_mutex(config->lock);
+
+ slapi_valueset_free(vals);
+ config = NULL;
+ }
+
+ /* See if any of the values for this attribute are managed
+ * backpointers. We need to update the forward link if so. */
+ if (linked_attrs_config_index_has_type(type)) {
+ int hint = 0;
+ Slapi_Value *val = NULL;
+
+ /* Loop through values and see if we have matching config */
+ hint = slapi_attr_first_value(attr, &val);
+ while (val) {
+ linked_attrs_find_config_reverse(slapi_value_get_string(val),
+ type, &config);
+
+ if (config) {
+ Slapi_ValueSet *vals = slapi_valueset_new();
+ slapi_valueset_add_value(vals, val);
+
+ slapi_lock_mutex(config->lock);
+
+ /* Delete old dn value. */
+ linked_attrs_mod_backpointers(old_dn, config->linktype,
+ config->scope, LDAP_MOD_DELETE, vals);
+
+ /* Add new dn value. */
+ linked_attrs_mod_backpointers(new_dn, config->linktype,
+ config->scope, LDAP_MOD_ADD, vals);
+
+ slapi_unlock_mutex(config->lock);
+
+ slapi_valueset_free(vals);
+ config = NULL;
+ }
+
+ hint = slapi_attr_next_value(attr, hint, &val);
+ }
+ }
+
+ linked_attrs_unlock();
+
+ slapi_entry_next_attr(post_e, attr, &attr);
+ }
+
+ slapi_log_error(SLAPI_LOG_TRACE, LINK_PLUGIN_SUBSYSTEM,
+ "<-- linked_attrs_modrdn_post_op\n");
+
+ return 0;
+}
+
+
+/*
+ * Debug functions to print config
+ */
+void
+linked_attrs_dump_config()
+{
+ PRCList *list;
+
+ linked_attrs_read_lock();
+
+ if (!PR_CLIST_IS_EMPTY(g_link_config)) {
+ list = PR_LIST_HEAD(g_link_config);
+ while (list != g_link_config) {
+ linked_attrs_dump_config_entry((struct configEntry *)list);
+ list = PR_NEXT_LINK(list);
+ }
+ }
+
+ linked_attrs_unlock();
+}
+
+void
+linked_attrs_dump_config_index()
+{
+ PRCList *list;
+
+ linked_attrs_read_lock();
+
+ if (!PR_CLIST_IS_EMPTY(g_managed_config_index)) {
+ list = PR_LIST_HEAD(g_managed_config_index);
+ while (list != g_managed_config_index) {
+ linked_attrs_dump_config_entry(((struct configIndex *)list)->config);
+ list = PR_NEXT_LINK(list);
+ }
+ }
+
+ linked_attrs_unlock();
+}
+
+
+void
+linked_attrs_dump_config_entry(struct configEntry * entry)
+{
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "<==== Linked Attribute Pair =====>\n");
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "<---- config entry dn -----> %s\n", entry->dn);
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "<---- link type -----------> %s\n", entry->linktype);
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "<---- managed type --------> %s\n", entry->managedtype);
+ slapi_log_error(SLAPI_LOG_FATAL, LINK_PLUGIN_SUBSYSTEM,
+ "<---- scope ---------------> %s\n", entry->scope);
+}
diff --git a/ldap/servers/plugins/linkedattrs/linked_attrs.h b/ldap/servers/plugins/linkedattrs/linked_attrs.h
new file mode 100644
index 00000000..8a3b2621
--- /dev/null
+++ b/ldap/servers/plugins/linkedattrs/linked_attrs.h
@@ -0,0 +1,141 @@
+/** 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) 2009 Red Hat, Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/*
+ * Linked attributes plug-in header file
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include "portable.h"
+#include "nspr.h"
+#include "slapi-plugin.h"
+#include "dirlite_strings.h"
+#include "dirver.h"
+#include "prclist.h"
+#include "ldif.h"
+
+/*
+ * Plug-in defines
+ */
+#define LINK_PLUGIN_SUBSYSTEM "linkedattrs-plugin"
+#define LINK_FEATURE_DESC "Linked Attributes"
+#define LINK_PLUGIN_DESC "Linked Attributes plugin"
+#define LINK_INT_POSTOP_DESC "Linked Attributes internal postop plugin"
+#define LINK_POSTOP_DESC "Linked Attributes postop plugin"
+
+/*
+ * Config type defines
+ */
+#define LINK_LINK_TYPE "linkType"
+#define LINK_MANAGED_TYPE "managedType"
+#define LINK_SCOPE "linkScope"
+
+/*
+ * Other defines
+ */
+#define DN_SYNTAX_OID "1.3.6.1.4.1.1466.115.121.1.12"
+
+/*
+ * Linked list of config entries.
+ */
+struct configEntry {
+ PRCList list;
+ char *dn;
+ char *linktype;
+ char *managedtype;
+ char *scope;
+ Slapi_Mutex *lock;
+};
+
+/*
+ * Linked list used for indexing config entries
+ * by managed type.
+ */
+struct configIndex {
+ PRCList list;
+ struct configEntry *config;
+};
+
+/*
+ * Fixup task private data.
+ */
+typedef struct _task_data
+{
+ char *linkdn;
+} task_data;
+
+
+/*
+ * Debug functions - global, for the debugger
+ */
+void linked_attrs_dump_config();
+void linked_attrs_dump_config_index();
+void linked_attrs_dump_config_entry(struct configEntry *);
+
+/*
+ * Config fetch function
+ */
+PRCList *linked_attrs_get_config();
+
+/*
+ * Config cache locking functions
+ */
+void linked_attrs_read_lock();
+void linked_attrs_write_lock();
+void linked_attrs_unlock();
+
+/*
+ * Plugin identity functions
+ */
+void linked_attrs_set_plugin_id(void *pluginID);
+void *linked_attrs_get_plugin_id();
+void linked_attrs_set_plugin_dn(char *pluginDN);
+char *linked_attrs_get_plugin_dn();
+
+/*
+ * Fixup task callback
+ */
+int linked_attrs_fixup_task_add(Slapi_PBlock *pb, Slapi_Entry *e,
+ Slapi_Entry *eAfter, int *returncode,
+ char *returntext, void *arg);
+
diff --git a/ldap/servers/slapd/add.c b/ldap/servers/slapd/add.c
index 6607eff9..1bd2d2f2 100644
--- a/ldap/servers/slapd/add.c
+++ b/ldap/servers/slapd/add.c
@@ -641,7 +641,9 @@ static void op_shared_add (Slapi_PBlock *pb)
slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &pse);
do_ps_service(pse, NULL, LDAP_CHANGETYPE_ADD, 0);
- e = NULL; /* if be_add succeeded, then e is consumed. Must prevent e from being free'd. */
+ /* If be_add succeeded, then e is consumed. We
+ * set e to NULL to prevent freeing it ourselves. */
+ e = NULL;
}
else
{
diff --git a/ldap/servers/slapd/attr.c b/ldap/servers/slapd/attr.c
index 6c3a6a0f..d78342ee 100644
--- a/ldap/servers/slapd/attr.c
+++ b/ldap/servers/slapd/attr.c
@@ -835,7 +835,7 @@ attr_add_valuearray(Slapi_Attr *a, Slapi_Value **vals, const char *dn)
}
}
- slapi_log_error( SLAPI_LOG_FATAL, NULL, "add value \"%s\" to "
+ slapi_log_error( SLAPI_LOG_TRACE, NULL, "add value \"%s\" to "
"attribute type \"%s\" in entry \"%s\" failed: %s\n",
duplicate_string,
a->a_type,
diff --git a/ldap/servers/slapd/dse.c b/ldap/servers/slapd/dse.c
index c48d1f20..04a1b356 100644
--- a/ldap/servers/slapd/dse.c
+++ b/ldap/servers/slapd/dse.c
@@ -1065,6 +1065,11 @@ dse_write_entry( caddr_t data, caddr_t arg )
return 0;
}
+/*
+ * Adds an entry to the dse backend. The passed in entry will be
+ * free'd upon success. If we don't return 0, the caller is responsible
+ * for freeing the entry.
+ */
static int
dse_add_entry_pb(struct dse* pdse, Slapi_Entry *e, Slapi_PBlock *pb)
{
@@ -1139,7 +1144,13 @@ dse_add_entry_pb(struct dse* pdse, Slapi_Entry *e, Slapi_PBlock *pb)
slapi_entry_free(schemacheckentry);
}
- return rc;
+ /* Callers expect e (SLAPI_ADD_ENTRY) to be freed or otherwise
+ * consumed if the add was successful. */
+ if (rc == 0) {
+ slapi_entry_free(e);
+ }
+
+ return rc;
}
/*