From 1c92d0f09fbb278ec9ff6479027e7d0b9dbec5f9 Mon Sep 17 00:00:00 2001
From: Miroslav Vadkerti <mvadkert@redhat.com>
Date: Tue, 4 Dec 2012 13:36:14 +0100
Subject: [PATCH] Added D-Bus plugin

Signed-off-by: Miroslav Vadkerti <mvadkert@redhat.com>
---
 Makefile.am                                        |   14 +-
 ldap/admin/src/scripts/50dbusplugin.ldif           |   14 +
 ldap/servers/plugins/dbusplugin/README             |  153 ++
 .../plugins/dbusplugin/config/dbusplugin.conf      |   16 +
 .../servers/plugins/dbusplugin/config/example.ldif |   31 +
 ldap/servers/plugins/dbusplugin/dbus-plugin.c      | 1552 ++++++++++++++++++++
 ldap/servers/plugins/dbusplugin/dbus-plugin.h      |  143 ++
 7 files changed, 1921 insertions(+), 2 deletions(-)
 create mode 100644 ldap/admin/src/scripts/50dbusplugin.ldif
 create mode 100644 ldap/servers/plugins/dbusplugin/README
 create mode 100644 ldap/servers/plugins/dbusplugin/config/dbusplugin.conf
 create mode 100644 ldap/servers/plugins/dbusplugin/config/example.ldif
 create mode 100644 ldap/servers/plugins/dbusplugin/dbus-plugin.c
 create mode 100644 ldap/servers/plugins/dbusplugin/dbus-plugin.h

diff --git a/Makefile.am b/Makefile.am
index 094d13c..b1161ae 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -211,8 +211,8 @@ serverplugin_LTLIBRARIES = libacl-plugin.la libattr-unique-plugin.la \
 	libreferint-plugin.la libreplication-plugin.la libretrocl-plugin.la \
 	libroles-plugin.la libstatechange-plugin.la libsyntax-plugin.la \
 	libviews-plugin.la libschemareload-plugin.la libusn-plugin.la \
-	libacctusability-plugin.la librootdn-access-plugin.la $(LIBACCTPOLICY_PLUGIN) \
-	$(LIBPAM_PASSTHRU_PLUGIN) $(LIBDNA_PLUGIN) \
+	libacctusability-plugin.la librootdn-access-plugin.la libdbus-plugin.la \
+	$(LIBACCTPOLICY_PLUGIN) $(LIBPAM_PASSTHRU_PLUGIN) $(LIBDNA_PLUGIN) \
 	$(LIBBITWISE_PLUGIN) $(LIBPRESENCE_PLUGIN) $(LIBPOSIX_WINSYNC_PLUGIN)
 
 nodist_property_DATA = ns-slapd.properties
@@ -499,6 +499,7 @@ update_DATA = ldap/admin/src/scripts/exampleupdate.pl \
 	ldap/admin/src/scripts/50refintprecedence.ldif \
 	ldap/admin/src/scripts/50retroclprecedence.ldif \
 	ldap/admin/src/scripts/50rootdnaccesscontrolplugin.ldif \
+	ldap/admin/src/scripts/50dbusplugin.ldif \
 	ldap/admin/src/scripts/60upgradeschemafiles.pl \
 	ldap/admin/src/scripts/70upgradefromldif.pl \
 	ldap/admin/src/scripts/80upgradednformat.pl \
@@ -852,6 +853,15 @@ libautomember_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS)
 libautomember_plugin_la_LIBADD = libslapd.la $(NSPR_LINK)
 libautomember_plugin_la_LDFLAGS = -avoid-version
 
+# libdbus-plugin
+#------------------------
+libdbus_plugin_la_SOURCES = ldap/servers/plugins/dbusplugin/dbus-plugin.c
+
+libdbus_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) `pkg-config --cflags dbus-1`
+libdbus_plugin_la_LIBADD = libslapd.la $(NSPR_LINK)
+libdbus_plugin_la_LDFLAGS = -avoid-version `pkg-config --cflags --libs dbus-1`
+
+
 #------------------------
 # libattr-unique-plugin
 #------------------------
diff --git a/ldap/admin/src/scripts/50dbusplugin.ldif b/ldap/admin/src/scripts/50dbusplugin.ldif
new file mode 100644
index 0000000..e8eca87
--- /dev/null
+++ b/ldap/admin/src/scripts/50dbusplugin.ldif
@@ -0,0 +1,14 @@
+dn: cn=D-Bus Plugin,cn=plugins,cn=config
+objectclass: top
+objectclass: nsSlapdPlugin
+objectclass: extensibleObject
+cn: D-Bus Plugin
+nsslapd-pluginpath: libdbus-plugin
+nsslapd-plugininitfunc: dp_postop_init
+nsslapd-plugintype: postoperation
+nsslapd-pluginenabled: on
+# these will be replaced when the server loads the plugin
+nsslapd-pluginId: ID
+nsslapd-pluginVersion: PACKAGE_VERSION
+nsslapd-pluginVendor: VENDOR
+nsslapd-pluginDescription: DESC
diff --git a/ldap/servers/plugins/dbusplugin/README b/ldap/servers/plugins/dbusplugin/README
new file mode 100644
index 0000000..e15ba77
--- /dev/null
+++ b/ldap/servers/plugins/dbusplugin/README
@@ -0,0 +1,153 @@
+D-Bus plugin for watching changes on nodes
+------------------------------------------
+
+This postoperation plugin can watch changes on specified nodes with a given
+scope. Only successful operations are processed. Matched operations are
+exported via D-Bus signals to the system bus. The plugin currently supports
+watching add, remove and modify operations.
+
+The exported messages on the D-Bus contain the DN of the matched item.
+Moreover, list of all (or selected) attributes is contained in the exported
+messages for add operations. This behavior can be changed using the
+dbus-plugin-attr and dbus-plugin-attr-all attributes. The watched node needs
+dbus-plugin-enabled set to yes to work. By default the watching is disabled.
+
+The configuration of the plugin is stored in LDAP under
+cn=D-Bus Plugin,cn=plugins,cn=config
+
+The configurations of the watched nodes are stored in subtree nodes of the plugin
+configuration. For example to watch all add operations on whole subtree of
+ou=People,dc=example,dc=com use the configuration below. Attributes mail and
+roomNumber will be exported as strings in addtion to matched item DN.
+
+dn: cn=add,cn=D-Bus Plugin,cn=plugins,cn=config
+objectclass: top
+objectclass: extensibleObject
+cn: add
+dbus-plugin-type: add
+dbus-plugin-target: ou=People,dc=example,dc=com
+dbus-plugin-scope: subtree
+dbus-plugin-attr: mail, roomNumber
+dbus-plugin-attr-all: no
+dbus-plugin-signal: DeletePeopleAdd
+dbus-plugin-enabled: yes
+
+The plugin will reload its configuration automatically if it detects
+changes in its configuration. No sever restart is needed.
+
+The examples in this readme use tools from these packages:
+qt (qdbus)
+openldap-clients (ldapadd, etc.)
+dbus (dbus-monitor)
+
+
+Installation
+------------
+1. apply provided patch on the master branch of ds.git
+
+# git apply --whitespace=fix 0001-Added-D-Bus-plugin.patch
+
+2. copy plugin D-Bus configuration file
+Please note that the configuration expects nobody user as the owner of this
+service. If you use other user please modify the configuration file. By default
+all users in wheel group are permitted to access the plugin's methods and signals.
+
+# cd ldap/servers/plugins/dbusplugin/config/
+# cp dbusplugin.conf /etc/dbus-1/system.d/dbusplugin.conf
+
+3. build, install and setup server
+Important note:
+ - run autogen.sh before building and install dbus-devel package
+
+# ./autogen.sh
+# ./configure --with-openldap
+# make && make install
+# /opt/dirsrv/sbin/setup-ds.pl
+# /opt/dirsrv/sbin/stop-dirsrv
+
+4. apply updated to the server
+This will bring in D-Bus plugin configuration. Please use Offline method
+for updating.
+# /opt/dirsrv/sbin/setup-ds.pl --update
+
+4. check if plugin works
+# qdbus --system org.fedoraproject.ds.dbusplugin / List
+No nodes
+
+
+Usage
+-----
+1. Add examples for watching nodes
+You may need to update the config/example.ldif with your desired configuration and use
+correct LDAP manager instead of cn=manager.
+
+# cd ldap/servers/plugins/dbusplugin/config/
+# cat config/example.ldif | ldapadd -D  -W
+
+2. List all available watched nodes
+# qdbus --system org.fedoraproject.ds.dbusplugin / List
+
+Plugin:     cn=add,cn=D-Bus Plugin,cn=plugins,cn=config
+Enabled:    yes
+Operation:  add
+Target:     ou=People,dc=example,dc=com
+Scope:      subtree
+Interface:  org.fedoraproject.ds.dbusplugin
+Signal:     DeletePeopleAdd
+Attributes: mail roomNumber
+All attr:   false
+
+Plugin:     cn=modify,cn=D-Bus Plugin,cn=plugins,cn=config
+Enabled:    yes
+Operation:  modify
+Target:     ou=People,dc=example,dc=com
+Scope:      base
+Interface:  org.fedoraproject.ds.dbusplugin
+Signal:     ModifyPeopleBase
+Attributes: none
+All attr:   false
+
+Plugin:     cn=delete,cn=D-Bus Plugin,cn=plugins,cn=config
+Enabled:    yes
+Operation:  delete
+Target:     ou=People,dc=example,dc=com
+Scope:      onelevel
+Interface:  org.fedoraproject.ds.dbusplugin
+Signal:     DeletePeopleOnelevel
+Attributes: none
+
+3. List all available methods/signals
+# qdbus --system org.fedoraproject.ds.dbusplugin /
+method QString org.freedesktop.DBus.Introspectable.Introspect()
+signal void org.fedoraproject.ds.dbusplugin.DeletePeopleAdd(QString operation, QString dn)
+signal void org.fedoraproject.ds.dbusplugin.DeletePeopleOnelevel(QString operation, QString dn)
+method void org.fedoraproject.ds.dbusplugin.List()
+signal void org.fedoraproject.ds.dbusplugin.ModifyPeopleBase(QString operation, QString dn)
+
+4. Try to monitor D-Bus and add/remove/modify some items under ou=People,dc=example,dc=com
+Example below shows D-Bus message sent to signal interface
+org.fedoraproject.ds.dbusplugin.AddPeopleSubtree of the plugin after adding
+record uid=jvedder,ou=People,dc=example,dc=com.
+
+signal sender=:1.451 -> dest=(null destination) serial=756 path=/org/fedoraproject/ds/dbusplugin/dpobject; interface=org.fedoraproject.ds.dbusplugin; member=AddPeopleSubtree
+   string "uid=jvedder,ou=People,dc=example,dc=net"
+   string "mail: jvedder@example.com"
+   string "roomNumber: 3445"
+
+
+Future features
+---------------
+1. D-Bus methods for adding/removing watched nodes
+
+2. More details exported to D-Bus for delete, modify, modifyrdn operations
+
+3. Search operation watch
+
+
+Known issues
+------------
+1. The signals introspect message with one exported parameter does not correspond to the
+real count of exported parameters (for the ADD operation only).
+
+--
+version 0.1
diff --git a/ldap/servers/plugins/dbusplugin/config/dbusplugin.conf b/ldap/servers/plugins/dbusplugin/config/dbusplugin.conf
new file mode 100644
index 0000000..32b8e71
--- /dev/null
+++ b/ldap/servers/plugins/dbusplugin/config/dbusplugin.conf
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE busconfig PUBLIC
+    "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+        "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+  <!-- The owner of the service is the user that runs 389 -->
+  <policy user="nobody">
+      <allow own="org.fedoraproject.ds.dbusplugin"/>
+  </policy>
+
+  <!-- Allow wheel users to invoke methods -->
+  <policy group="wheel">
+      <allow send_destination="org.fedoraproject.ds.dbusplugin"/>
+      <allow receive_sender="org.fedoraproject.ds.dbusplugin"/>
+  </policy>
+</busconfig>
diff --git a/ldap/servers/plugins/dbusplugin/config/example.ldif b/ldap/servers/plugins/dbusplugin/config/example.ldif
new file mode 100644
index 0000000..6a223ba
--- /dev/null
+++ b/ldap/servers/plugins/dbusplugin/config/example.ldif
@@ -0,0 +1,31 @@
+dn: cn=add,cn=D-Bus Plugin,cn=plugins,cn=config
+objectclass: top
+objectclass: extensibleObject
+cn: add
+dbus-plugin-type: add
+dbus-plugin-target: ou=People,dc=example,dc=com
+dbus-plugin-scope: subtree
+dbus-plugin-attr: mail, roomNumber
+dbus-plugin-attr-all: no
+dbus-plugin-signal: AddPeopleSubtree
+dbus-plugin-enabled: yes
+
+dn: cn=delete,cn=D-Bus Plugin,cn=plugins,cn=config
+objectclass: top
+objectclass: extensibleObject
+cn: delete
+dbus-plugin-type: delete
+dbus-plugin-target: ou=People,dc=example,dc=com
+dbus-plugin-scope: onelevel
+dbus-plugin-enabled: yes
+dbus-plugin-signal: DeletePeopleOnelevel
+
+dn: cn=modify,cn=D-Bus Plugin,cn=plugins,cn=config
+objectclass: top
+objectclass: extensibleObject
+cn: modify
+dbus-plugin-type: modify
+dbus-plugin-target: ou=People,dc=example,dc=com
+dbus-plugin-scope: base
+dbus-plugin-enabled: yes
+dbus-plugin-signal: ModifyPeopleBase
diff --git a/ldap/servers/plugins/dbusplugin/dbus-plugin.c b/ldap/servers/plugins/dbusplugin/dbus-plugin.c
new file mode 100644
index 0000000..0230ca3
--- /dev/null
+++ b/ldap/servers/plugins/dbusplugin/dbus-plugin.c
@@ -0,0 +1,1552 @@
+/** 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) 2011 Red Hat, Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* system includes */
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <dbus/dbus.h>
+#include <pthread.h>
+
+/* local includes */
+#include "slapi-plugin.h"
+#include "dbus-plugin.h"
+
+/* D-Bus plugin description */
+Slapi_PluginDesc dp_desc = { "dbus-plugin", "redhat.com", DBUS_PLUGIN_VERSION,
+    "DBus plug-in" };
+
+/* D-Bus plugin identity */
+static Slapi_ComponentId *dp_identity;
+
+/* All add opeartions */
+dp_list_t dp_operation[DP_OP_COUNT];
+
+/* D-Bus shared data */
+DBusError dbus_err;
+DBusConnection *dbus_conn;
+dbus_uint32_t serial = 0;
+
+/* D-Bus listener thread */
+pthread_t dbus_thread;
+bool dbus_listen;
+
+/* POSIX RWlock */
+Slapi_RWLock *dp_lock;
+
+/*
+ * Log debug function
+ */
+void dp_debug(const char* format, ...)
+{
+    char    *out = slapi_ch_calloc(1024, sizeof(char));
+    va_list args;
+    va_start(args, format);
+    vsnprintf(out, 1024, format, args);
+    va_end(args);
+    slapi_log_error(SLAPI_LOG_PLUGIN, "dbus-plugin", "[:: debug ::] %s", out);
+    slapi_ch_free_string(&out);
+}
+
+/*
+ * Log info function
+ */
+void dp_info(const char* format, ...)
+{
+    char    *out = slapi_ch_calloc(1024, sizeof(char));
+    va_list args;
+    va_start(args, format);
+    vsnprintf(out, 1024, format, args);
+    va_end(args);
+    slapi_log_error(SLAPI_LOG_PLUGIN, "dbus-plugin", "[:: info ::] %s", out);
+    slapi_ch_free_string(&out);
+}
+
+/*
+ * Log error function
+ */
+void dp_error(const char* format, ...)
+{
+    char    *out = slapi_ch_calloc(1024, sizeof(char));
+    va_list args;
+    va_start(args, format);
+    vsnprintf(out, 1024, format, args);
+    va_end(args);
+    slapi_log_error(SLAPI_LOG_PLUGIN, "dbus-plugin", "[:: error ::] %s", out);
+    slapi_ch_free_string(&out);
+}
+
+
+/*
+ * Try out if D-Bus signal can be created
+ */
+bool dp_dbus_signal_check(p_dp_node_t node)
+{
+    DBusMessage      *msg;
+
+    /* create a signal and check for errors */
+    msg = dbus_message_new_signal(
+        DBUS_OBJECT_PATH,
+        DBUS_PLUGIN_INTERFACE,
+        node->signal);
+
+    if (msg == NULL) {
+        dp_error("D-Bus signal test failed\n");
+        return false;
+    }
+
+    dbus_message_unref(msg);
+    return true;
+}
+
+/*
+ * Send message via D-Bus signal containing only DN of the matched node
+ */
+int dp_dbus_signal_send(p_dp_node_t node, char *matched)
+{
+   DBusMessage* msg;
+   DBusMessageIter args;
+
+   dp_debug("Sending signal with value %s\n", matched);
+
+   /*  create a signal & check for errors  */
+   msg = dbus_message_new_signal(DBUS_OBJECT_PATH,  /*  object name of the signal */
+                                 DBUS_PLUGIN_INTERFACE,  /*  interface name of the signal */
+                                 node->signal);  /*  name of the signal */
+   if (NULL == msg)
+   {
+      dp_error("Message Null\n");
+      return 1;
+   }
+
+   /*  append arguments onto signal */
+   dbus_message_iter_init_append(msg, &args);
+   if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &matched)) {
+      dp_error("Out Of Memory!\n");
+      return 1;
+   }
+
+   /*  send the message */
+   if (!dbus_connection_send(dbus_conn, msg, &serial)) {
+      dp_error("Out Of Memory!\n");
+      return 1;
+   }
+   dp_debug("Signal Sent\n");
+
+   /*  free the message */
+   dbus_message_unref(msg);
+   serial++;
+
+   return 0;
+}
+
+/*
+ * Send D-Bus signal for add node
+ */
+int dp_dbus_signal_send_add(p_dp_node_t node, char *matched, Slapi_Entry *entry)
+{
+    DBusMessage*    msg;
+    DBusMessageIter args;
+    int             rc;
+    Slapi_Attr      *attr;
+    Slapi_Attr      *prevattr;
+    char            *type;
+    char            *string;
+    char            *value;
+    p_dp_node_attr_t node_attr;
+
+    dp_debug("Sending signal with value %s (attr-all: %s)\n",
+        matched, (node->attr_all) ? "true" : "false" );
+
+    /* create a signal & check for errors */
+    msg = dbus_message_new_signal(DBUS_OBJECT_PATH,
+                                  DBUS_PLUGIN_INTERFACE,
+                                  node->signal);
+
+    if (msg == NULL)
+    {
+       dp_error("Message Null\n");
+       return 1;
+    }
+
+    /* append arguments onto signal */
+    dbus_message_iter_init_append(msg, &args);
+    if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &matched)) {
+       dp_error("Out Of Memory!\n");
+       return 1;
+    }
+
+    /* append all arguments if requested */
+    if(node->attr_all) {
+        /* append all attributes to the dbus message */
+        rc = slapi_entry_first_attr(entry, &attr);
+        while(rc == 0) {
+            slapi_attr_get_type(attr, &type);
+            value = slapi_entry_attr_get_charptr(entry, type);
+            /* dp_debug("%s=%s\n", type, value); */
+            string = slapi_ch_calloc(strlen(type)+strlen(value)+3, sizeof(char));
+            strcat(string, type);
+            strcat(string, ": ");
+            strcat(string, value);
+            dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &string);
+            slapi_ch_free_string(&string);
+            prevattr = attr;
+            rc = slapi_entry_next_attr(entry, prevattr, &attr);
+        }
+    } else {
+        /* print all attributes */
+        node_attr = node->first;
+        while(node_attr != NULL) {
+            dp_debug("Checking attr: %s\n", node_attr->attribute);
+            type = node_attr->attribute;
+            if(type == NULL) {
+                dp_debug("No attribute to print");
+                break;
+            }
+            value = slapi_entry_attr_get_charptr(entry, node_attr->attribute);
+            if(value == NULL) {
+                dp_debug("Attribute %s not found in entry\n", type);
+                node_attr = node_attr->next;
+                continue;
+            }
+            string = slapi_ch_calloc(strlen(type)+strlen(value)+3, sizeof(char));
+            strcat(string, type);
+            strcat(string, ": ");
+            strcat(string, value);
+            dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &string);
+            slapi_ch_free_string(&string);
+            node_attr = node_attr->next;
+        }
+    }
+
+    /* send the message */
+    if (!dbus_connection_send(dbus_conn, msg, &serial)) {
+       dp_error("Out Of Memory!\n");
+       return 1;
+    }
+    dp_debug("Signal Sent\n");
+
+    /* free the message */
+    dbus_message_unref(msg);
+
+    /* incerement message serial */
+    serial++;
+
+    return 0;
+}
+
+/*
+ * converts scope types to int
+ */
+int dp_get_scope_id(char *scope)
+{
+    if(!strcasecmp(scope, "subtree")) {
+        dp_debug("subtree scope\n");
+        return LDAP_SCOPE_SUB;
+    } else if(!strcasecmp(scope, "onelevel")) {
+        dp_debug("onelevel scope\n");
+        return LDAP_SCOPE_ONE;
+    } else if(!strcasecmp(scope, "base")) {
+        dp_debug("base scope\n");
+        return LDAP_SCOPE_BASE;
+    }
+
+    /* Note that -2 is unused value
+     * in ldap.h for LDAP_SCOPE* defines */
+    return DP_SCOPE_INVALID;
+}
+
+
+/*
+ * search basedn in dn according to given LDAP scope
+ */
+bool dp_search_dn_in_base(const char *dn, const char *basedn, int scope)
+{
+    int basedn_len, dn_len;
+    int len_diff;
+    int j;
+    bool comma_found = false;
+    bool backslash_found = false;
+
+    if (dn == NULL) {
+        dp_debug("No dn specified\n");
+        return 1;
+    }
+
+    if (basedn == NULL) {
+        dp_debug("No base specified\n");
+        return 1;
+    }
+
+    dn_len = strlen(dn);
+    basedn_len = strlen(basedn);
+
+    /* search dn is larger */
+    if (basedn_len > dn_len) {
+        /* dp_debug("BASE DN is larger then DN\n"); */
+        return false;
+    }
+
+    /* check if basedn found in dn */
+    len_diff = dn_len - basedn_len;
+    if(strncasecmp(&dn[len_diff], basedn, basedn_len) != 0) {
+        return false;
+    }
+
+    switch (scope) {
+        case LDAP_SCOPE_BASE:
+            dp_debug("Using base scope\n");
+            /* dn > base? */
+            if (len_diff != 0) {
+                return false;
+            }
+            break;
+
+        case LDAP_SCOPE_ONE:
+            dp_debug("Using one-level scope\n");
+            if (len_diff == 0) {
+                /*
+                 * Base object doesn't belong to scope=one
+                 * search
+                 */
+                return false;
+            }
+
+            comma_found = false;
+            for (j = 0; j < len_diff - 1; j++) { /* ignore comma before base */
+                if (dn[j] == '\\') {
+                    backslash_found = true;
+                } else if (dn[j] == ',' && !backslash_found) {
+                    comma_found = true;
+                    break;
+                } else {
+                    backslash_found = false;
+                }
+            }
+
+            /* it has at least one more level */
+            if (comma_found) {
+                return false;
+            }
+
+            break;
+
+        case LDAP_SCOPE_SUB:
+            dp_debug("Using subtree scope\n");
+            /* dn length >= base dn length && base_confirmed == true */
+            break;
+
+        default:
+            dp_debug("Unsupported scope\n");
+            return false;
+    }
+
+    return true;
+}
+
+/*
+ * create list of watched attributes
+ */
+int
+dp_node_add_attr(p_dp_node_t node, char *attr)
+{
+    int len;
+    char *pch;
+    p_dp_node_attr_t item;
+    p_dp_node_attr_t prev = NULL;
+    p_dp_node_attr_t first = NULL;
+
+    if(attr == NULL) {
+        node->first  = NULL;
+        return 0;
+    }
+
+    pch = strtok(attr, " \t,");
+    while (pch != NULL)
+    {
+        len = strlen(pch) + 1;
+
+        /* add new list item */
+        dp_debug("Added attr attribute: %s\n", pch);
+        item = (p_dp_node_attr_t)slapi_ch_malloc(sizeof(dp_node_attr_t));
+        if(first == NULL) first = item;
+        item->next = NULL;
+        if(prev != NULL) {
+            prev->next = item;
+        }
+        prev = item;
+
+        /* allocate attribute string */
+        item->attribute = slapi_ch_calloc(len, sizeof(char));
+        strcpy(item->attribute, pch);
+
+        /* read next string */
+        pch = strtok (NULL, " ,.-");
+    }
+
+    /* set first item */
+    node->first = first;
+
+    return 0;
+}
+
+
+/*
+ * add new operation
+ */
+int
+dp_node_add(int optype, char *cn, char *dn, char *scope, char* signal,
+    char *target, char *attr, bool attr_all, bool enabled)
+{
+    p_dp_node_t node;
+    int len;
+    int sid;
+
+    /* debug */
+    dp_debug("Adding node: %d, cn: %s, dn: %s, scope: %s, signal: %s, target: %s,"
+        "attr: %s, attr-all: %s, enabled: %s\n", optype, cn, dn, scope, signal,
+        target, (attr == NULL) ? "none" : attr, (attr_all == true) ? "true" : "false",
+        (enabled == true) ? "true" : "false");
+
+    /* sanity checks */
+    if(optype >= DP_OP_COUNT || dn == NULL || target == NULL
+        || cn == NULL) {
+        dp_debug("Invalid paramters\n");
+        return DP_PARAMS_INVALID;
+    }
+
+    /* allocate new operation node */
+    node = (p_dp_node_t) slapi_ch_malloc(sizeof(dp_node_t));
+    if (node == NULL) {
+        dp_debug("Cannot slapi_ch_malloc new operation node\n");
+        return DP_ALLOC_ERROR;
+    }
+
+    /* copy target */
+    len = strlen(target) + 1;
+    node->target = slapi_ch_calloc(len, sizeof(char));
+    if(node->target == NULL) {
+        dp_debug("Cannot slapi_ch_calloc target string\n");
+        slapi_ch_free((void**)&node);
+        return DP_ALLOC_ERROR;
+    }
+    strncpy(node->target, target, len);
+
+    /* copy dn */
+    len = strlen(dn) + 1;
+    node->dn = (char*) slapi_ch_calloc(len, sizeof(char));
+    if(node->dn == NULL) {
+        dp_debug("Cannot slapi_ch_calloc dn string\n");
+        slapi_ch_free_string(&node->target);
+        slapi_ch_free((void**)&node);
+        return DP_ALLOC_ERROR;
+    }
+    strncpy(node->dn, dn, len);
+
+    /* copy cn */
+    len = strlen(cn) + 1;
+    node->name = (char*) slapi_ch_calloc(len, sizeof(char));
+    if(node->name == NULL) {
+        dp_debug("Cannot slapi_ch_calloc cn string\n");
+        slapi_ch_free_string(&node->dn);
+        slapi_ch_free_string(&node->target);
+        slapi_ch_free((void**)&node);
+        return DP_ALLOC_ERROR;
+    }
+    strncpy(node->name, cn, len);
+
+    /* sanitize scope and convert
+     * it to numeric representation */
+    sid = dp_get_scope_id(scope);
+    if(sid == DP_SCOPE_INVALID) {
+        slapi_ch_free_string(&node->name);
+        slapi_ch_free_string(&node->dn);
+        slapi_ch_free_string(&node->target);
+        slapi_ch_free((void**)&node);
+        dp_debug("Invalid scope\n");
+        return DP_SCOPE_INVALID;
+    }
+    node->scope = sid;
+
+    /* copy signal */
+    len = strlen(signal) + 1;
+    node->signal = (char*) slapi_ch_calloc(len, sizeof(char));
+    if(node->signal == NULL) {
+        dp_debug("Cannot slapi_ch_calloc signal string\n");
+        slapi_ch_free_string(&node->name);
+        slapi_ch_free_string(&node->dn);
+        slapi_ch_free_string(&node->target);
+        slapi_ch_free((void**)&node);
+        return DP_ALLOC_ERROR;
+    }
+    strncpy(node->signal, signal, len);
+
+    /* try to initialize D-Bus signal for node (test only) */
+    if(!dp_dbus_signal_check(node)) {
+        dp_debug("Failed to init D-Bus signal for %s\n", node->dn);
+        slapi_ch_free_string(&node->name);
+        slapi_ch_free_string(&node->dn);
+        slapi_ch_free_string(&node->target);
+        slapi_ch_free((void**)&node);
+        return DP_SIGNAL_ERROR;
+    }
+
+    /* create list of watched attributes */
+    dp_node_add_attr(node, attr);
+
+    /* set type */
+    node->type = optype;
+
+    /* set watch all attributes boolean */
+    node->attr_all = attr_all;
+
+    /* set enabled status */
+    node->enabled = enabled;
+
+    /* add the new node to the beginning of the list */
+    node->next = dp_operation[optype].first;
+    dp_operation[optype].first = node;
+
+    /* increase the count of nodes */
+    dp_operation[optype].count++;
+
+    return 0;
+}
+
+/*
+ * Free all plugin configuration
+ */
+void dp_free_all()
+{
+    int k;
+    p_dp_node_t node, prevnode;
+    p_dp_node_attr_t node_attr, prevnode_attr;
+
+    /* free all structures with operations */
+    dp_debug("Freeing plugin configuration\n");
+    for(k=0; k<DP_OP_COUNT; k++) {
+        node = dp_operation[k].first;
+        while(node != NULL) {
+            slapi_ch_free_string(&node->signal);
+            slapi_ch_free_string(&node->dn);
+            slapi_ch_free_string(&node->target);
+            slapi_ch_free_string(&node->name);
+            node_attr = node->first;
+            while(node_attr != NULL) {
+                slapi_ch_free_string(&node_attr->attribute);
+                prevnode_attr = node_attr;
+                node_attr = node_attr->next;
+                slapi_ch_free((void**)&prevnode_attr);
+            }
+            prevnode = node;
+            node = node->next;
+            slapi_ch_free((void**)&prevnode);
+        }
+        dp_operation[k].first = NULL;
+        dp_operation[k].count = 0;
+    }
+}
+
+/*
+ * Load plugin configuration
+ */
+int dp_config_load(Slapi_PBlock *pb)
+{
+    char            *dn = DBUS_PLUGIN_DN;
+    Slapi_PBlock    *search_pb;
+    int             res;
+    Slapi_Entry     **entries = NULL;
+    int             ecount;
+    char            *itype;
+    char            *idn;
+    char            *itarget;
+    char            *scope;
+    char            *signal;
+    char            *cn;
+    char            *attr;
+    int             k;
+    bool            attr_all;
+    char            *string;
+    bool            enabled;
+
+   /* search for all plugin configuration entries */
+    search_pb = slapi_pblock_new();
+    slapi_search_internal_set_pb(search_pb, dn,
+        LDAP_SCOPE_SUB, "objectclass=*", NULL, 0, NULL, NULL,
+        dp_identity, 0);
+    slapi_search_internal_pb(search_pb);
+
+    /* check for search result */
+    slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+    if (res != LDAP_SUCCESS) {
+        dp_debug("failed to search onelevel on DN: %s\n", dn);
+        return res;
+    }
+
+    /* get count of the entries found */
+    slapi_pblock_get(search_pb, SLAPI_NENTRIES, &ecount);
+    dp_debug("found %d configuration entries\n", ecount);
+
+    /* get all found entries */
+    slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+    if(entries == NULL) {
+        dp_debug("failed to get search entries\n");
+        return LDAP_OPERATIONS_ERROR;
+    }
+
+    /* print all found entries */
+    /* if(dp_is_debug()) */
+    dp_print_all_entries(entries, ecount);
+
+    /* parse all atributes for found entries */
+    for(k = 0; k < ecount; k++)
+    {
+        /* get DN */
+        idn = slapi_entry_get_dn(entries[k]);
+        if(!idn) {
+            dp_debug("Error finding dn in entry %d (skipping)\n", k);
+            continue;
+        }
+
+        /* get dbus-plugin-type */
+        itype = slapi_entry_attr_get_charptr(entries[k], "dbus-plugin-type");
+        if(!itype) {
+            dp_debug("Error finding dbus-plugin-type in entry %s (skipping)\n", idn);
+            continue;
+        }
+
+        /* get dbus-plugin-target */
+        itarget = slapi_entry_attr_get_charptr(entries[k], "dbus-plugin-target");
+        if(!itarget) {
+            dp_debug("Error finding dbus-plugin-target in entry %s (skipping)\n", idn);
+            continue;
+        }
+
+        /* get dbus-plugin-scope */
+        scope = slapi_entry_attr_get_charptr(entries[k], "dbus-plugin-scope");
+        if(!scope) {
+            dp_debug("Error finding dbus-plugin-scope in entry %s (skipping node)\n", idn);
+            continue;
+        }
+
+        /* get dbus-plugin-signal */
+        signal = slapi_entry_attr_get_charptr(entries[k], "dbus-plugin-signal");
+        if(!signal) {
+            dp_debug("Error finding dbus-plugin-signal in entry %s (skipping node)\n", idn);
+            continue;
+        }
+
+        /* get cn */
+        cn = slapi_entry_attr_get_charptr(entries[k], "cn");
+        if(!cn) {
+            dp_debug("Error finding cn in entry %s (skipping node)\n", idn);
+            continue;
+        }
+
+        /* get dbus-plugin-attr */
+        attr = slapi_entry_attr_get_charptr(entries[k], "dbus-plugin-attr");
+        if(!attr) {
+            dp_debug("Cannot find dbus-plugin-attr in entry %s (skipping)\n", idn);
+            attr = NULL;
+        }
+
+        /* get dbus-plugin-attr-all */
+        attr_all = false;
+        string = slapi_entry_attr_get_charptr(entries[k], "dbus-plugin-attr-all");
+        if(!string) {
+            dp_debug("Cannot finding dbus-plugin-attr-all in entry %s (using NULL)\n", idn);
+        } else {
+            if(!strcmp(string, "true") || !strcmp(string, "yes")) {
+                dp_debug("Show all attributes for entry\n");
+                attr_all = true;
+            }
+        }
+
+        /* get dbus-plugin-enabled */
+        enabled = false;
+        string = slapi_entry_attr_get_charptr(entries[k], "dbus-plugin-enabled");
+        if(!string) {
+            dp_debug("Cannot finding dbus-plugin-enabled in entry (plugin disabled) %s\n", idn);
+        } else {
+            if(!strcmp(string, "true") || !strcmp(string, "yes")) {
+                dp_debug("Plugin enabled\n");
+                enabled = true;
+            }
+        }
+
+        /* add node to list */
+        if(!strcmp(itype,"add")) {
+            res = dp_node_add(DP_OP_ADD, cn, idn, scope, signal, itarget, attr, attr_all, enabled);
+        } else if(!strcmp(itype,"modify")) {
+            res = dp_node_add(DP_OP_MODIFY, cn, idn, scope, signal, itarget, attr, attr_all, enabled);
+        } else if(!strcmp(itype,"delete")) {
+            res = dp_node_add(DP_OP_DELETE, cn, idn, scope, signal, itarget, attr, attr_all, enabled);
+        } else if(!strcmp(itype,"rename")) {
+            res = dp_node_add(DP_OP_MODRDN, cn, idn, scope, signal, itarget, attr, attr_all, enabled);
+        } else {
+            dp_debug("Invalid dbus-plugin-type %s\n", itype);
+            return LDAP_OPERATIONS_ERROR;
+        }
+
+        if(res) {
+            dp_debug("Error adding operation (code %d)\n", res);
+            return LDAP_OPERATIONS_ERROR;
+        }
+
+        /* node correctly added */
+        dp_info("Added %s %s node dn:%s target:%s scope:%s attr_all:%s\n",
+            (enabled==true) ? "enabled" : "disabled", itype, idn, itarget,
+            scope, (attr_all == true) ? "yes" : "no");
+    }
+
+    /* destroy search pb */
+    slapi_pblock_destroy(search_pb);
+
+    return 0;
+}
+
+int dp_plugin_reload(Slapi_PBlock *pb)
+{
+    /* write lock */
+    slapi_rwlock_wrlock(dp_lock);
+
+    /* free all structures with operations */
+    dp_debug("Freeing plugin configuration\n");
+    dp_free_all();
+
+    dp_debug("Reloading plugin configuration\n");
+    dp_config_load(pb);
+
+    /* unlock write lock */
+    slapi_rwlock_unlock(dp_lock);
+
+    return 0;
+}
+
+
+/*
+ * Print all entries
+ */
+int dp_print_all_entries(Slapi_Entry **entries, int ecount)
+{
+    char *ldif;
+    int k;
+
+    for(k = 0; k < ecount; k++) {
+        ldif = slapi_entry2str_with_options(entries[k], NULL, 0);
+        dp_debug("entry %d is \"\n%s\"\n", k, ldif);
+        slapi_ch_free_string(&ldif);
+    }
+
+    return 0;
+}
+
+/*
+ * Process plugin node
+ */
+int dp_node_process(int optype, Slapi_PBlock *pb)
+{
+    p_dp_node_t  node;
+    char         *adn;
+    bool         found;
+    Slapi_Entry  *entry = NULL;
+    Slapi_Mods   **mods = NULL;
+    int          rc;
+
+    /* bail out if operation failed */
+    slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &rc);
+    if (rc != LDAP_SUCCESS)
+    {
+        dp_debug("Skipping unsuccessful operation\n");
+        return 0;
+    }
+
+    /* bail out if no nodes */
+    if(dp_operation[optype].count == 0) {
+        return 0;
+    }
+
+    switch(optype) {
+        case DP_OP_ADD:
+            dp_debug("Processing add operation\n");
+            slapi_pblock_get(pb, SLAPI_ADD_TARGET, &adn);
+            slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &entry);
+            break;
+        case DP_OP_MODIFY:
+            slapi_pblock_get(pb, SLAPI_MODIFY_TARGET, &adn);
+            break;
+        case DP_OP_DELETE:
+            slapi_pblock_get(pb, SLAPI_DELETE_TARGET, &adn);
+            break;
+        case DP_OP_MODRDN:
+            slapi_pblock_get(pb, SLAPI_MODRDN_TARGET, &adn);
+            break;
+    }
+
+    if(adn == NULL) {
+        dp_debug("Failed to get entry DN\n");
+        return 1;
+    }
+
+    /* check if plugin config changed */
+    found = dp_search_dn_in_base(adn, DBUS_PLUGIN_DN, LDAP_SCOPE_SUB);
+    if(found) {
+        /* reload plugin configuration */
+        dp_plugin_reload(pb);
+    }
+
+    /* read lock */
+    slapi_rwlock_rdlock(dp_lock);
+
+    /* check all nodes */
+    node = dp_operation[optype].first;
+    while(node != NULL) {
+        /* skip disabled nodes */
+        if(node->enabled == false) {
+            dp_debug("Skipping disabled node %s\n", node->name);
+            node = node->next;
+            continue;
+        }
+
+        /* search for node's target in adn */
+        found = dp_search_dn_in_base(adn, node->target, node->scope);
+
+        if(found) {
+            dp_debug("Matched operation %s (node: %s scope: %s)\n", dp_text[optype], node->target,
+                     dp_scope_to_string(node->scope));
+            switch(optype) {
+                case DP_OP_ADD:
+                    /* get added entry and  */
+                    slapi_pblock_get(pb, SLAPI_ADD_TARGET, &adn);
+                    slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &entry);
+                    if(dp_dbus_signal_send_add(node, adn, entry)) {
+                        dp_debug("Failed to send D-Bus signal to %s\n", node->signal);
+                        return 1;
+                    }
+                    break;
+                case DP_OP_MODIFY:
+                    slapi_pblock_get(pb, SLAPI_MODIFY_TARGET, &adn);
+                    if(dp_dbus_signal_send(node, adn)) {
+                        dp_debug("Failed to send D-Bus signal to %s\n", node->signal);
+                        return 1;
+                    }
+                    break;
+                case DP_OP_DELETE:
+                    slapi_pblock_get(pb, SLAPI_DELETE_TARGET, &adn);
+                    if(dp_dbus_signal_send(node, adn)) {
+                        dp_debug("Failed to send D-Bus signal to %s\n", node->signal);
+                        return 1;
+                    }
+                    break;
+                case DP_OP_MODRDN:
+                    slapi_pblock_get(pb, SLAPI_MODRDN_TARGET, &adn);
+                    if(!dp_dbus_signal_send(node, adn)) {
+                        dp_debug("Failed to send D-Bus signal to %s\n", node->signal);
+                        return 1;
+                    }
+                    break;
+            }
+
+         }
+
+        /* move to next node */
+        node = node->next;
+    }
+
+    /* read lock */
+    slapi_rwlock_unlock(dp_lock);
+
+    if(!found) {
+        dp_debug("No operation matched\n");
+    }
+
+    return 0;
+}
+
+
+/*
+ * Post-operation initialization function
+ */
+int dp_postop_init(Slapi_PBlock *pb)
+{
+    dp_debug("Registering DBus plug-in\n");
+
+    /* get our plug-in identity - we will need this to perform
+       any internal operations (search, modify, etc.) */
+    slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &dp_identity);
+
+    /* specify the version of the plug-in - requires 389 DS version > 4.x */
+    if(slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_03) != 0)
+    {
+        dp_debug("Error setting DBus plug-in version.\n");
+        return 1;
+    }
+
+    /* specify the description of the plug-in */
+    if(slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void*)&dp_desc) != 0)
+    {
+        dp_debug("Error setting DBus plug-in description.\n");
+        return 1;
+    }
+
+    /* register start function */
+    if(slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
+        (void*)dp_start) != 0)
+    {
+        dp_debug("Error setting DBus plugin start function.\n");
+        return 1;
+    }
+
+    /* register stop function */
+    if(slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
+        (void*)dp_close) != 0)
+    {
+        dp_debug("Error setting DBus plugin close function.\n");
+        return 1;
+    }
+
+    /* register post add function */
+    if(slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN,
+        (void*)dp_postop_add) != 0)
+    {
+        dp_debug("Error setting DBus plugin post add function.\n");
+        return 1;
+    }
+
+    /* register post modify function */
+    if(slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN,
+        (void*)dp_postop_modify) != 0)
+    {
+        dp_debug("Error setting DBus plugin post modify function.\n");
+        return 1;
+    }
+
+    /* register post delete function */
+    if(slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN,
+        (void*)dp_postop_delete) != 0)
+    {
+        dp_debug("Error setting DBus plugin post delete function.\n");
+        return 1;
+    }
+
+    /* register post rename function */
+    if(slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN,
+        (void*)dp_postop_modrdn) != 0)
+    {
+        dp_debug("Error setting DBus plugin post rename function.\n");
+        return 1;
+    }
+
+    /* initialize POSIX RWlock */
+    dp_lock = slapi_new_rwlock();
+
+    return 0;
+}
+
+/*
+ * Initialize D-Bus
+ */
+int dp_dbus_init() {
+
+    dp_debug("D-Bus initialization.\n");
+
+    /* initialise the errors */
+	dbus_error_init(&dbus_err);
+
+	/* connect to the bus */
+	dbus_conn = dbus_bus_get(DBUS_BUS_SYSTEM, &dbus_err);
+	if (dbus_error_is_set(&dbus_err)) {
+		dp_debug("D-Bus connection Error (%s)\n", dbus_err.message);
+		dbus_error_free(&dbus_err);
+        return 1;
+	}
+	if (dbus_conn == NULL) {
+		dp_debug("D-Bus connection failed.\n");
+        return 1;
+	}
+
+    /*
+     * Initialize new unique dbus name
+     */
+    char *signal = "org.fedoraproject.ds.dbusplugin";
+    dp_debug("Requesting name %s\n", signal);
+    int ret = dbus_bus_request_name(dbus_conn, signal,
+        DBUS_NAME_FLAG_DO_NOT_QUEUE, &dbus_err);
+    if (dbus_error_is_set(&dbus_err)) {
+        dp_debug("Error requesting bus name: %s\n", dbus_err.message);
+        dbus_error_free(&dbus_err);
+        return 1;
+    }
+    if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret) {
+        dp_error("Unable to became primary owner of the %s bus name\n", DBUS_PLUGIN_INTERFACE);
+        return 1;
+    }
+
+    return 0;
+}
+
+/*
+ * dp_dbus_list_all
+ *
+ * Reply to default interface Introspect from org.freedesktop.DBus
+ */
+char*
+dp_dbus_list_all(char *start, char *end)
+{
+     /*  char            tmp[512]; */
+    p_dp_node_t  node;
+    int             total;
+    char            **list;
+    char            *list_xml;
+    int             list_len;
+    int             k;
+    int             l;
+
+    /* read lock */
+    slapi_rwlock_rdlock(dp_lock);
+
+    /* pointer on array of node descriptions */
+    total = dp_node_count();
+    list = (char**)slapi_ch_calloc(total, sizeof(char*));
+
+    /* create list of operations */
+    for(k = 0, l = 0; k < DP_OP_COUNT; k++) {
+        /* check all nodes */
+        node = dp_operation[k].first;
+        while(node != NULL) {
+            if(l == total) {
+                dp_error("Error creating list\n");
+                break;
+            }
+
+            /* add node signal to the list */
+            list[l++] = node->signal;
+
+            /* move to next node */
+            node = node->next;
+        }
+    }
+
+    /* create reply text */
+    for(list_len = 0, k = 0; k < total; k++) {
+        list_len += strlen(list[k]);
+    }
+    list_xml = dp_dbus_list_signals(list, total, start, end);
+
+    /* free allocatated  */
+    slapi_ch_free((void**)&list);
+
+    /* read lock */
+    slapi_rwlock_unlock(dp_lock);
+
+    return list_xml;
+}
+
+/*
+ * dp_dbus_reply_introspect
+ *
+ * Reply to default interface Introspect from org.freedesktop.DBus
+ */
+int
+dp_dbus_reply_introspect(DBusMessage* msg, DBusConnection* conn)
+{
+    DBusMessage         *reply;
+    DBusMessageIter     args;
+    char                *reply_text;
+    /* introspect XML start */
+    char                *reply_text_start =
+"<!DOCTYPE node PUBLIC \"- /* freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
+"   \"http: /* www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
+"<node name=\"/org/fedoraproject/ds/dbusplugin\">\n"
+"   <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
+"       <method name=\"Introspect\">\n"
+"           <arg type=\"s\" name=\"xml_data\" direction=\"out\"/>\n"
+"       </method>\n"
+"   </interface>\n"
+"   <interface name=\"org.fedoraproject.ds.dbusplugin\">\n"
+"       <method name=\"List\"/>\n";
+    /* introspect XML end */
+    char            *reply_text_end = "   </interface>\n</node>";
+
+    /* generate list of available nodes */
+    reply_text = dp_dbus_list_all(reply_text_start,reply_text_end);
+
+    /* unique number to associate replies with requests */
+    dbus_uint32_t    serial = 0;
+
+    /* read the arguments */
+    dbus_message_iter_init(msg, &args);
+
+    /* create a reply from the message */
+    reply = dbus_message_new_method_return(msg);
+
+    /* add the arguments to the reply */
+    dbus_message_iter_init_append(reply, &args);
+    if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &reply_text)) {
+        /* free reply text */
+        slapi_ch_free_string(&reply_text);
+        return DP_DBUS_ERROR;
+    }
+
+    /* send the reply & flush the connection */
+    if (!dbus_connection_send(dbus_conn, reply, &serial)) {
+        /* free reply text */
+        slapi_ch_free_string(&reply_text);
+        return DP_DBUS_ERROR;
+    }
+    dbus_connection_flush(dbus_conn);
+
+    /* free the reply */
+    dbus_message_unref(reply);
+
+    return 0;
+}
+
+/*
+ * Total count of observing nodes
+ */
+int dp_node_count()
+{
+    int k;
+    int total = 0;
+
+    for(k = 0; k < DP_OP_COUNT; k++) {
+        total += dp_operation[k].count;
+    }
+
+    return total;
+}
+
+/*
+ * Convert scope to string
+ */
+const char* dp_scope_to_string(int scope)
+{
+    switch(scope) {
+        case LDAP_SCOPE_BASE:
+            return dp_scope_text[0];
+        break;
+
+        case LDAP_SCOPE_SUB:
+            return dp_scope_text[1];
+        break;
+
+        case LDAP_SCOPE_ONE:
+            return dp_scope_text[2];
+        break;
+
+        default:
+            return dp_scope_text[3];
+        break;
+    }
+}
+
+/*
+ * Translate watched attributes to string
+ */
+char*
+dp_node_attr_get_all(p_dp_node_attr_t attr)
+{
+    p_dp_node_attr_t item;
+    char *none = "none";
+    char *str;
+    int total = 0;
+
+    /* if no attributes */
+    if(attr  == NULL) {
+        str = slapi_ch_calloc(strlen(none)+1, sizeof(char));
+        if(str == NULL) {
+           dp_error("Error allocating space for list of watched attributes!\n");
+           return NULL;
+        }
+        strcpy(str, none);
+        return str;
+    }
+
+    /* count lenght of the attribute list */
+    item = attr;
+    while(item != NULL) {
+        total += strlen(item->attribute) + 1; /* include ending separator */
+        item = item->next;
+    }
+    total++; /* ending \0 */
+
+    /* allocate and convert */
+    str = slapi_ch_calloc(total, sizeof(char));
+    if(str == NULL) {
+        dp_error("Error allocating space for list of watched attributes!\n");
+        return NULL;
+    }
+    item = attr;
+    while(item != NULL) {
+        strcat(str, item->attribute);
+        strcat(str, " ");
+        item = item->next;
+    }
+
+    return str;
+}
+
+/*
+ * Translates one node to string
+ */
+char*
+dp_node_to_string(p_dp_node_t node, int type)
+{
+    char *nodetext;
+    const char *scope;
+    int total;
+    int other_text = 1024;
+    char* attrs;
+
+    /*
+     *  Additional 1024 bytes for the helper text
+     *  in the output + true/false
+     */
+    scope = dp_scope_to_string(node->scope);
+    attrs = dp_node_attr_get_all(node->first);
+    total = strlen(node->target) + strlen(node->dn)
+        + strlen(node->signal) + strlen(scope)
+        + strlen(attrs) + other_text;
+    nodetext = slapi_ch_calloc(total, sizeof(char));
+
+    snprintf(nodetext, total,
+            "\nPlugin:     %s"
+            "\nEnabled:    %s"
+            "\nOperation:  %s"
+            "\nTarget:     %s"
+            "\nScope:      %s"
+            "\nInterface:  %s"
+            "\nSignal:     %s"
+            "\nAttributes: %s"
+            "\nAll attr:   %s\n",
+        node->dn, (node->enabled) ? "yes" : "no", dp_text[type],
+        node->target, scope, DBUS_PLUGIN_INTERFACE,
+        node->signal, attrs, (node->attr_all) ? "true" : "false");
+
+    /* free attr  string */
+    slapi_ch_free_string(&attrs);
+
+    return nodetext;
+}
+
+/*
+ * Creates one string from array of strings
+ */
+char* dp_dbus_reply_from_strings(char **list, int total)
+{
+    int     k;
+    int     tlen = 0;
+    char    *reply;
+
+    /* count the length of reply */
+    for(k = 0; k < total; k++) {
+        tlen += strlen(list[k]);
+    }
+
+    reply = slapi_ch_calloc(tlen + 1, sizeof(char));
+
+    for(k = 0; k < total; k++) {
+        strcat(reply,list[k]);
+    }
+
+    return reply;
+}
+
+/*
+ * Returns allocated strings with signals for introspect XML
+ */
+char* dp_dbus_list_signals(char **list, int total, char *start, char *end)
+{
+    int     k;
+    int     tlen = 0;
+    char    *reply;
+    char    *signal_start = "   <signal name=\"";
+    char    *signal_end = "\">\n       <arg name=\"operation\" type=\"s\"/>\n"
+            "      <arg name=\"dn\" type=\"s\"/>\n"
+            "   </signal>\n";
+
+    /* count the length of reply */
+    for(k = 0; k < total; k++) {
+        tlen += strlen(list[k]);
+    }
+    tlen += total*strlen(signal_start) + total*strlen(signal_end) +
+        strlen(start) + strlen(end) + 1;
+
+    /* create signal list */
+    reply = slapi_ch_calloc(tlen, sizeof(char));
+    strcat(reply, start);
+    for(k = 0; k < total; k++) {
+        strcat(reply, signal_start);
+        strcat(reply, list[k]);
+        strcat(reply, signal_end);
+    }
+    strcat(reply, end);
+
+    return reply;
+}
+
+/*
+ * Reply to List method
+ */
+int
+dp_dbus_reply_list(DBusMessage* msg, DBusConnection* conn)
+{
+    DBusMessage     *reply;
+    DBusMessageIter args;
+    p_dp_node_t     node;
+    char            *reply_text;
+    char            **list;
+    int             k, l;
+    int             total;
+    int             ret = 0;
+    dbus_uint32_t   serial = 0;
+
+    /* get reply text for all nodes */
+    total = dp_node_count();
+    if(total != 0) {
+        list = (char**)slapi_ch_calloc(total, sizeof(char*));
+
+        /* create reply with all details of all observed nodes */
+        reply_text = slapi_ch_calloc(total, sizeof(char));
+        for(k = 0, l = 0; k < DP_OP_COUNT; k++) {
+            node = dp_operation[k].first;
+            while(node != NULL) {
+                if(l == total) {
+                    dp_debug("Error creating list\n");
+                    break;
+                }
+
+                /* convert node to string */
+                list[l++] = dp_node_to_string(node, k);
+
+                /* move to next node */
+                node = node->next;
+            }
+        }
+
+        /* create reply text */
+        reply_text = dp_dbus_reply_from_strings(list, total);
+    }
+    /* if no nodes */
+    else {
+        reply_text = "No nodes";
+    }
+
+    /* initiate reply message */
+    dbus_message_iter_init(msg, &args);
+    reply = dbus_message_new_method_return(msg);
+
+    /* add the created reply text to the message */
+    dbus_message_iter_init_append(reply, &args);
+    if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &reply_text)) {
+        ret = DP_DBUS_ERROR;
+        goto replyerror;
+    }
+
+    /* send the reply & flush the connection */
+    if (!dbus_connection_send(dbus_conn, reply, &serial)) {
+        ret = DP_DBUS_ERROR;
+        goto replyerror;
+    }
+    dbus_connection_flush(dbus_conn);
+
+    /* free the reply */
+    dbus_message_unref(reply);
+
+replyerror:
+
+    for(k = 0; k < total; k++) {
+         slapi_ch_free_string(&list[k]);
+    }
+
+    if(total != 0) {
+        slapi_ch_free_string(&reply_text);
+    }
+
+    return ret;
+}
+
+/*
+ * Create thread that implements the introspect method
+ */
+void* dp_dbus_listener()
+{
+    DBusMessage     *msg;
+
+    /* loop, testing for new messages */
+    while (dbus_listen) {
+        /* non blocking read of the next available message */
+        dbus_connection_read_write(dbus_conn, 1000);
+        msg = dbus_connection_pop_message(dbus_conn);
+
+        /* loop again if we haven't got a message */
+        if (msg == NULL) {
+            continue;
+        }
+
+        dp_debug("Got message: to=%s from=%s member=%s\n",
+            dbus_message_get_destination(msg), dbus_message_get_sender (msg),
+            dbus_message_get_member(msg));
+
+
+        /* read lock */
+        slapi_rwlock_rdlock(dp_lock);
+
+        /* default interface Introspect  */
+        if (dbus_message_is_method_call(msg,
+            "org.freedesktop.DBus.Introspectable", "Introspect"))
+        {
+            dp_debug("Returning introspect xml\n");
+            dp_dbus_reply_introspect(msg, dbus_conn);
+        }
+        /* List all observed operations */
+        else if (dbus_message_is_method_call(msg,
+            "org.fedoraproject.ds.dbusplugin", "List")) {
+            dp_debug("List all observed nodes\n");
+            dp_dbus_reply_list(msg, dbus_conn);
+        }
+
+        /* read lock */
+        slapi_rwlock_unlock(dp_lock);
+
+        /* free the message */
+        dbus_message_unref(msg);
+    }
+
+    return 0;
+}
+
+/*
+ * Start function - initialize plugin operations
+ */
+int dp_start(Slapi_PBlock *pb)
+{
+    int             res;
+
+    /* Log a message to indicate when the plug-in function starts */
+    dp_debug("Initializing DBus plugin\n");
+
+    /* initialize dbus */
+    if(dp_dbus_init()) {
+        dp_debug("Could not initialize DBus\n");
+        return LDAP_OPERATIONS_ERROR;
+    }
+
+    /* write lock */
+    slapi_rwlock_wrlock(dp_lock);
+
+    /* load plugin configuration */
+    dp_config_load(pb);
+
+    /* unlock lock */
+    slapi_rwlock_unlock(dp_lock);
+
+    /* start dbus_listener in other thread */
+    dbus_listen = true;
+    res = pthread_create(&dbus_thread, NULL, &dp_dbus_listener, (void*) NULL);
+    if(res) {
+            dp_debug("Could not start dbus method listener thread");
+            return LDAP_OPERATIONS_ERROR;
+    }
+
+    return 0;
+}
+
+/*
+ * Start function - initialize plugin operations
+ */
+int dp_close(Slapi_PBlock *pb)
+{
+    void *ret = NULL;
+
+    /* finish the dbus listener */
+    dp_debug("Waiting for D-Bus listener to finish\n");
+    dbus_listen = false;
+    pthread_join(dbus_thread, &ret);
+
+    /* POSIX RWlock */
+    slapi_rwlock_wrlock(dp_lock);
+
+    /* free all nodes allocated */
+    dp_debug("Freeing up all nodes\n");
+    dp_free_all();
+
+    /* free POSIX RWlock */
+    slapi_rwlock_unlock(dp_lock);
+    slapi_destroy_rwlock(dp_lock);
+
+    return 0;
+}
+
+/*
+ * Post-operation add plug-in function
+ */
+int dp_postop_add(Slapi_PBlock *pb)
+{
+    dp_node_process(DP_OP_ADD, pb);
+    return 0;
+}
+
+/*
+ * Post-operation modify plug-in function
+ */
+int dp_postop_modify(Slapi_PBlock *pb)
+{
+    dp_node_process(DP_OP_MODIFY, pb);
+    return 0;
+}
+
+/*
+ * Pre-operation delete plug-in function
+ */
+int dp_postop_delete(Slapi_PBlock *pb)
+{
+    dp_node_process(DP_OP_DELETE, pb);
+    return 0;
+}
+
+/*
+ * Post-operation modrdn plug-in function
+ */
+int dp_postop_modrdn(Slapi_PBlock *pb)
+{
+    dp_node_process(DP_OP_MODRDN, pb);
+    return 0;
+}
diff --git a/ldap/servers/plugins/dbusplugin/dbus-plugin.h b/ldap/servers/plugins/dbusplugin/dbus-plugin.h
new file mode 100644
index 0000000..2efedd6
--- /dev/null
+++ b/ldap/servers/plugins/dbusplugin/dbus-plugin.h
@@ -0,0 +1,143 @@
+/** 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) 2012 Red Hat, Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifndef DBUS_PLUGIN_H
+#define DBUS_PLUGIN_H
+
+/* define operation types */
+#define DP_OP_ADD 0
+#define DP_OP_MODIFY 1
+#define DP_OP_DELETE 2
+#define DP_OP_MODRDN 3
+
+/* defines */
+#define DBUS_PLUGIN_DN "cn=D-Bus Plugin,cn=plugins,cn=config"
+#define DBUS_PLUGIN_INTERFACE "org.fedoraproject.ds.dbusplugin"
+#define DBUS_OBJECT_PATH "/org/fedoraproject/ds/dbusplugin/dpobject"
+#define DBUS_PLUGIN_VERSION "0.1"
+
+/*
+ * operation type in text
+ * must match DP_OP_* defines order!
+ */
+const char *dp_text[] = {
+    "add", "modify", "delete", "modrdn"
+};
+
+/* operation type in text */
+const char *dp_scope_text[] = {
+    "base", "subtree", "onelevel", "unknown - error!"
+};
+
+/* count of opearation types */
+#define DP_OP_COUNT 4
+#define DP_DBUS_MAX_REPLY 10240
+
+/* invalid scope */
+#define DP_SCOPE_INVALID -2
+#define DP_ALLOC_ERROR -3
+#define DP_SIGNAL_ERROR -4
+#define DP_PARAMS_INVALID -5
+#define DP_DBUS_ERROR -6
+
+/*
+ * Linked list of observed attributes
+ */
+typedef struct dp_node_attr
+{
+    char *attribute;
+    struct dp_node_attr *next;
+} dp_node_attr_t, *p_dp_node_attr_t;
+
+/*
+ * DBus plugin operation on node
+ *
+ * i.e. iternal representation of the observed
+ *      operation on node
+ */
+typedef struct dp_node
+{
+    int    type; /* DP_OP_* MACROS */
+    char   *target;
+    char   *dn;
+    char   *name;
+    int    scope;
+    char   *signal;
+    bool   attr_all;
+    bool   enabled;
+    struct dp_node *next;
+    p_dp_node_attr_t first;
+} dp_node_t, *p_dp_node_t;
+
+/*
+ * DBus plugin operation list
+ */
+typedef struct
+{
+    int count;
+    p_dp_node_t first;
+} dp_list_t, *p_dp_list_t;
+
+/* operations structure functions */
+int dp_node_add(int optype, char *cn, char *dn, char *scope,
+                char *signal, char *target, char *attr, bool attr_all, bool);
+int dp_remove(int optype, char *dn, char *target);
+int dp_node_process(int optype, Slapi_PBlock *pb);
+void dp_free_all();
+
+/* helper functions */
+int dp_node_count();
+const char* dp_scope_to_string(int);
+int dp_print_all_entries(Slapi_Entry **entries, int ecount);
+
+
+/* start/close functions */
+int dp_start(Slapi_PBlock *pb);
+int dp_close(Slapi_PBlock *pb);
+
+/* D-Bus related functions */
+char* dp_dbus_reply_from_strings(char**, int);
+char* dp_dbus_list_signals(char**, int, char*, char*);
+
+/* post operation function prototypes */
+int dp_postop_init(Slapi_PBlock *pb);
+int dp_postop_add(Slapi_PBlock *pb);
+int dp_postop_modify(Slapi_PBlock *pb);
+int dp_postop_delete(Slapi_PBlock *pb);
+int dp_postop_modrdn(Slapi_PBlock *pb);
+
+#endif
-- 
1.7.1

