/** BEGIN COPYRIGHT BLOCK
 * Copyright (C) 2005 Red Hat, Inc.
 * All rights reserved.
 *
 * License: GPL (version 3 or any later version).
 * See LICENSE for details.
 * END COPYRIGHT BLOCK **/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif


/******** testdatainterop.c *******************

 This source file provides an example of a plug-in function
 that implements an datainteroprability functionality.
 The plug-in function is called by the server
 when the server is configured to use the null dn.
 meaning dn:
 The server uses the null dn or the root suffix opnly when
 the configuration for the server has the following
 node in the dse.ldif of the server instance
 (in the <server_root>/slapd-<server_id>/config directory).

 dn: cn="",cn=mapping tree,cn=config
 objectClass: top
 objectClass: extensibleObject
 objectClass: nsMappingTree
 cn: ""
 nsslapd-state: container

 The plugin below is a pre-operation plugin which
 provides alternate functionality for the LDAP operations
 of search, modify, add etc. that are targeted at the root-suffix
 or the null-dn to be serviced by an alternate data source or
 alternate access methods allowing datainteroperability.

 The example below creates a berkely db and modifies or adds data
 to the db demonstarting the use of an alternate data source seperate
 from the Directory Server. Also, the results of a search operation
 are completely in the control of the pre-operation plugin. In this
 example a fake entry is returned to express the functionality


 To test this plug-in function, stop the server, edit the dse.ldif file
 (in the <server_root>/slapd-<server_id>/config directory)
 and add the following lines before restarting the server :

 dn: cn="",cn=mapping tree,cn=config
 objectClass: top
 objectClass: extensibleObject
 objectClass: nsMappingTree
 cn: ""
 nsslapd-state: container


 dn: cn=datainterop,cn=plugins,cn=config
 objectClass: top
 objectClass: nsSlapdPlugin
 cn: datainterop
 nsslapd-pluginPath: <server-root>/plugins/slapd/slapi/examples/libtest-plugin.so
 nsslapd-pluginInitfunc: nullsuffix_init
 nsslapd-pluginType: preoperation
 nsslapd-pluginEnabled: on
 nsslapd-pluginId: nullsuffix-preop
 nsslapd-pluginVersion: 7.1
 nsslapd-pluginVendor: My Project
 nsslapd-pluginDescription: sample pre-operation null suffix plugin

 ******************************************/

#include <stdio.h>
#include <string.h>
#include "slapi-plugin.h"

/*
 * Macros.
 */
#define PLUGIN_NAME "nullsuffix-preop"

#define PLUGIN_OPERATION_HANDLED 1
#define PLUGIN_OPERATION_IGNORED 0

#define SEARCH_SCOPE_ANY (-1)


/*
 * Static variables.
 */
static Slapi_PluginDesc plugindesc = {PLUGIN_NAME, VENDOR, DS_PACKAGE_VERSION,
                                      "sample pre-operation null suffix plugin"};

static Slapi_ComponentId *plugin_id = NULL;


/*
 * Function prototypes.
 */
static int nullsuffix_search(Slapi_PBlock *pb);
static int nullsuffix_add(Slapi_PBlock *pb);
static int nullsuffix_close(Slapi_PBlock *pb);
static int nullsuffix_modify(Slapi_PBlock *pb);
static int nullsuffix_delete(Slapi_PBlock *pb);
static int nullsuffix_modrdn(Slapi_PBlock *pb);
static int nullsuffix_bind(Slapi_PBlock *pb);


/*
 * Initialization function.
 */
int
nullsuffix_init(Slapi_PBlock *pb)
{
    int i;

    slapi_log_err(SLAPI_LOG_PLUGIN, PLUGIN_NAME, "nullsuffix_init\n");

    /* retrieve plugin identity to later pass to internal operations */
    if (slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &plugin_id) != 0) {
        slapi_log_err(SLAPI_LOG_PLUGIN, PLUGIN_NAME,
                      "unable to get SLAPI_PLUGIN_IDENTITY\n");
        return -1;
    }

    /* register the pre-operation search function, etc. */
    if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01) != 0 || slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&plugindesc) != 0 || slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_SEARCH_FN, (void *)nullsuffix_search) != 0 || slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN, (void *)nullsuffix_close) != 0 || slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN, (void *)nullsuffix_add) != 0 || slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODIFY_FN, (void *)nullsuffix_modify) != 0 || slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_DELETE_FN, (void *)nullsuffix_delete) != 0 || slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_BIND_FN, (void *)nullsuffix_bind) != 0 || slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODRDN_FN, (void *)nullsuffix_modrdn) != 0) {
        slapi_log_err(SLAPI_LOG_PLUGIN, PLUGIN_NAME,
                      "failed to set version and function\n");
        return -1;
    }


    return 0;
}

static int
nullsuffix_bind(Slapi_PBlock *pb)
{
    if (slapi_op_reserved(pb)) {
        return PLUGIN_OPERATION_IGNORED;
    }
    slapi_log_err(SLAPI_LOG_PLUGIN, PLUGIN_NAME, "nullsuffix_bind\n");
    send_ldap_result(pb, LDAP_SUCCESS, NULL, NULL, 0, NULL);
    return PLUGIN_OPERATION_HANDLED;
}

static int
nullsuffix_add(Slapi_PBlock *pb)
{
    char *dn;
    if (slapi_op_reserved(pb)) {
        return PLUGIN_OPERATION_IGNORED;
    }
    slapi_log_err(SLAPI_LOG_PLUGIN, PLUGIN_NAME, "nullsuffix_add\n");
    slapi_pblock_get(pb, SLAPI_ADD_TARGET, &dn);
    db_put_dn(dn);
    send_ldap_result(pb, LDAP_SUCCESS, NULL, NULL, 0, NULL);
    return PLUGIN_OPERATION_HANDLED;
}

static int
nullsuffix_modify(Slapi_PBlock *pb)
{
    Slapi_Entry *entry;
    int i;
    int j;
    char *dn;
    if (slapi_op_reserved(pb)) {
        return PLUGIN_OPERATION_IGNORED;
    }
    slapi_pblock_get(pb, SLAPI_MODIFY_TARGET, &dn);
    slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &entry);
    db_put_dn(dn);
    send_ldap_result(pb, LDAP_SUCCESS, NULL, NULL, 0, NULL);
    slapi_log_err(SLAPI_LOG_PLUGIN, PLUGIN_NAME, "nullsuffix_modify\n");
    return PLUGIN_OPERATION_HANDLED;
}

static int
nullsuffix_delete(Slapi_PBlock *pb)
{
    if (slapi_op_reserved(pb)) {
        return PLUGIN_OPERATION_IGNORED;
    }
    slapi_log_err(SLAPI_LOG_PLUGIN, PLUGIN_NAME, "nullsuffix_delete\n");
    send_ldap_result(pb, LDAP_SUCCESS, NULL, NULL, 0, NULL);
    return PLUGIN_OPERATION_HANDLED;
}

static int
nullsuffix_modrdn(Slapi_PBlock *pb)
{
    if (slapi_op_reserved(pb)) {
        return PLUGIN_OPERATION_IGNORED;
    }
    slapi_log_err(SLAPI_LOG_PLUGIN, PLUGIN_NAME, "nullsuffix_modrdn\n");
    send_ldap_result(pb, LDAP_SUCCESS, NULL, NULL, 0, NULL);
    return PLUGIN_OPERATION_HANDLED;
}

static int
nullsuffix_search(Slapi_PBlock *pb)
{
    char *dn_base, **attrs, *newStr;
    int scope, sizelimit, timelimit, deref, attrsonly;
    Slapi_Filter *filter;
    Slapi_DN *sdn_base;
    int ldaperr = LDAP_SUCCESS; /* optimistic */
    int nentries = 0;           /* entry count */
    int i;
    Slapi_Operation *op;
    Slapi_Entry *e;

    const char *entrystr =
        "dn:cn=Joe Smith,o=Example\n"
        "objectClass: top\n"
        "objectClass: person\n"
        "objectClass: organizationalPerson\n"
        "objectClass: inetOrgPerson\n"
        "cn:Joe Smith\n"
        "sn:Smith\n"
        "uid:jsmith\n"
        "mail:jsmith@example.com\n";

    slapi_log_err(SLAPI_LOG_PLUGIN, PLUGIN_NAME, "nullsuffix_search\n");
    if (slapi_op_reserved(pb)) {
        return PLUGIN_OPERATION_IGNORED;
    }

    /* get essential search parameters */
    if (slapi_pblock_get(pb, SLAPI_SEARCH_TARGET, &dn_base) != 0 ||
        slapi_pblock_get(pb, SLAPI_SEARCH_SCOPE, &scope) != 0) {
        slapi_log_err(SLAPI_LOG_PLUGIN, PLUGIN_NAME,
                      "could not get base DN and scope search parameters\n");
    }
    if (dn_base == NULL) {
        dn_base = "";
    }
    sdn_base = slapi_sdn_new_dn_byval(dn_base);
    slapi_pblock_get(pb, SLAPI_OPERATION, &op);

    /* get remaining search parameters */
    if (slapi_pblock_get(pb, SLAPI_SEARCH_DEREF, &deref) != 0 ||
        slapi_pblock_get(pb, SLAPI_SEARCH_SIZELIMIT, &sizelimit) != 0 ||
        slapi_pblock_get(pb, SLAPI_SEARCH_TIMELIMIT, &timelimit) != 0 ||
        slapi_pblock_get(pb, SLAPI_SEARCH_FILTER, &filter) != 0 ||
        slapi_pblock_get(pb, SLAPI_SEARCH_ATTRS, &attrs) != 0 ||
        slapi_pblock_get(pb, SLAPI_SEARCH_ATTRSONLY, &attrsonly) != 0) {
        slapi_log_err(SLAPI_LOG_PLUGIN, PLUGIN_NAME,
                      "could not get remaining search parameters\n");
    }

    if (slapi_pblock_get(pb, SLAPI_OPERATION, &op) != 0) {
        slapi_log_err(SLAPI_LOG_PLUGIN, PLUGIN_NAME,
                      "could not get operation\n");
    } else {
        slapi_operation_set_flag(op, SLAPI_OP_FLAG_NO_ACCESS_CHECK);
    }

    /* create a fake entry and send it along */
    newStr = slapi_ch_strdup(entrystr);
    if (NULL == (e = slapi_str2entry(newStr,
                                     SLAPI_STR2ENTRY_ADDRDNVALS | SLAPI_STR2ENTRY_EXPAND_OBJECTCLASSES))) {
        slapi_log_err(SLAPI_LOG_PLUGIN, PLUGIN_NAME,
                      "nullsuffix_search: slapi_str2entry() failed\n");
    } else {
        slapi_send_ldap_search_entry(pb, e, NULL /* controls */,
                                     attrs, attrsonly);
        ++nentries;
        slapi_entry_free(e);
    }

    slapi_send_ldap_result(pb, ldaperr, NULL, "kilroy was here",
                           nentries, NULL);
    slapi_log_err(SLAPI_LOG_PLUGIN, PLUGIN_NAME, "nullsuffix_search:"
                                                 " handled search based at %s with scope %d; ldaperr=%d\n",
                  dn_base, scope, ldaperr);

    slapi_ch_free_string(&newStr);
    slapi_sdn_free(&sdn_base);

    return PLUGIN_OPERATION_HANDLED;
}


/*
 * Shutdown function.
 */
static int
nullsuffix_close(Slapi_PBlock *pb)
{
    slapi_log_err(SLAPI_LOG_PLUGIN, PLUGIN_NAME, "nullsuffix_close\n");
    return 0;
}
