/* 
 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved 
 * Use is subject to license terms. 
 * Some preexisting portions Copyright 1997-2001 Netscape Communications Corp.
 * All rights reserved.
 */
/************************************************************

 testextendedop.c

 This source file provides an example of a plug-in function 
 that implements an extended operation.  The plug-in function
 is called by the server if an LDAP client request contains
 the OID "1.2.3.4" (which identifies this operation).

 To test this plug-in function, you need to write an LDAP
 v3 client that can send requests for extended operations.
 You can use the Sun(tm) Directory SDK for C or the 
 Sun Directory SDK for Java to build these clients.  

 The LDAP client should send an extended operation request
 with the OID 1.2.3.4. To verify that the operation completed
 successfully, your client should check the OID and value
 returned in the LDAP response.

 ACTIVATING THIS PLUG-IN
 -----------------------
 1. Build the library containing the plug-in.
      
 2. Configure the server to log plug-in messages.

    $ dsconf set-log-prop error level:err-plugins

 3. Configure the server to load the plug-in.
     
    $ dsconf create-plugin -H <lib-path> -F testexop_init -Y extendedop "Test ExtendedOp"
    $ dsconf set-plugin-prop "Test ExtendedOp" depends-on-type:database feature:test-extendedop version:7.0 desc:"Sample extended operation plug-in" argument:1.2.3.4
    $ dsconf enable-plugin "Test ExtendedOp"
    Directory Server needs to be restarted for changes to take effect
    $ dsadm restart <inst-path>

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

Slapi_PluginDesc expdesc = {
    "test-extendedop",                 /* plug-in identifier      */
    "Oracle Corporation (test)",       /* vendor name             */
    "11.1.1.3.0",                      /* plug-in revision number */
    "Sample extended operation plug-in"/* plug-in description     */
};

/* Handle a simple extended operation request.                     */
int
test_extendedop(Slapi_PBlock * pb)
{
    char          * oid;               /* Client request OID       */
    struct berval * client_bval;       /* Value from client        */
    char          * result;            /* Result to send to client */
    char          * tmp_msg;
    struct berval * result_bval;       /* Encoded result           */
    int             connId, opId, rc = 0;
    long            msgId;

    /* Identify the request for logging.                           */
    rc |= slapi_pblock_get(pb, SLAPI_OPERATION_MSGID, &msgId);
    rc |= slapi_pblock_get(pb, SLAPI_CONN_ID,         &connId);
    rc |= slapi_pblock_get(pb, SLAPI_OPERATION_ID,    &opId);
    if (rc != 0) {
        slapi_log_info_ex(
            SLAPI_LOG_INFO_AREA_PLUGIN,
            SLAPI_LOG_INFO_LEVEL_DEFAULT,
            SLAPI_LOG_NO_MSGID,
            SLAPI_LOG_NO_CONNID,
            SLAPI_LOG_NO_OPID,
            "test_extendedop in test-extendedop plug-in",
            "Could not identify message, connection or operation.\n"
        );
    }

    /* Get the OID and the value included in the request.          */
    rc |= slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_OID,   &oid );
    rc |= slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_VALUE, &client_bval);
    if (rc != 0) {
        tmp_msg = "Could not get OID and value from request";
        slapi_log_info_ex(
            SLAPI_LOG_INFO_AREA_PLUGIN,
            SLAPI_LOG_INFO_LEVEL_DEFAULT,
            msgId,
            connId,
            opId,
            "test_extendedop in test-extendedop plug-in",
            "%s\n", tmp_msg
        );
        slapi_send_ldap_result(
            pb,                        /* PBlock for request       */
            LDAP_OPERATIONS_ERROR,     /* LDAP result code         */
            NULL,                      /* For LDAP_NO_SUCH_OBJECT  */
            tmp_msg,                   /* Text message for client  */
            0,                         /* Number of entries sent   */
            NULL                       /* URL for referral         */
        );
        return (SLAPI_PLUGIN_EXTENDED_SENT_RESULT);
    } else {
        slapi_log_info_ex(
            SLAPI_LOG_INFO_AREA_PLUGIN,
            SLAPI_LOG_INFO_LEVEL_DEFAULT,
            msgId,
            connId,
            opId,
            "test_extendedop in test-extendedop plug-in",
            "Request with OID: %s  Value from client: %s\n",
            oid, client_bval->bv_val
        );
    }

    /* 
     * Set the value to return to the client, depending on what your
     * plug-in function does. Here, we return the value sent by the
     * client, prefixed with the string "Value from client: ".     */
    tmp_msg             = "Value from client: ";
    result              = (char *)slapi_ch_malloc(
        client_bval->bv_len + strlen(tmp_msg) + 1);
    sprintf(result, "%s%s", tmp_msg, client_bval->bv_val);
    result_bval         = (struct berval *)slapi_ch_malloc(
        sizeof(struct berval));
    result_bval->bv_val = result;
    result_bval->bv_len = strlen(result_bval->bv_val);

    /* 
     * Prepare the PBlock to return and OID and value to the client.
     * Here, we demonstrate that the plug-in may return a different
     * OID than the one sent by the client. You may, for example,
     * use the different OID to indicate something to the client.  */
    rc |= slapi_pblock_set(pb, SLAPI_EXT_OP_RET_OID,   "5.6.7.8");
    rc |= slapi_pblock_set(pb, SLAPI_EXT_OP_RET_VALUE, result_bval);
    if (rc != 0) {
        slapi_ch_free((void **)&result);
        tmp_msg = "Could not set results to return to client.";
        slapi_log_info_ex(
            SLAPI_LOG_INFO_AREA_PLUGIN,
            SLAPI_LOG_INFO_LEVEL_DEFAULT,
            msgId,
            connId,
            opId,
            "test_extendedop in test-extendedop plug-in",
            "%s\n", tmp_msg
        );
        slapi_send_ldap_result(
            pb,
            LDAP_OPERATIONS_ERROR,
            NULL,
            tmp_msg,
            0,
            NULL
        );
        return (SLAPI_PLUGIN_EXTENDED_SENT_RESULT);
    }

    /* Send the result to the client.                              */
    slapi_send_ldap_result(
        pb,
        LDAP_SUCCESS,
        NULL,
        "Extended operation successful!",
        0,
        NULL
    );
    slapi_log_info_ex(
        SLAPI_LOG_INFO_AREA_PLUGIN,
        SLAPI_LOG_INFO_LEVEL_DEFAULT,
        msgId,
        connId,
        opId,
        "test_extendedop in test-extendedop plug-in",
        "OID sent to client: %s  Value sent to client: %s\n",
        "5.6.7.8", result
    );
    slapi_ch_free((void **)&result);

    /* Tell the server we sent the result.                         */
    return (SLAPI_PLUGIN_EXTENDED_SENT_RESULT);
}

/* Register the plug-in with the server.                           */
#ifdef _WIN32
__declspec(dllexport)
#endif
int
testexop_init(Slapi_PBlock * pb)
{
    char ** argv;                      /* Args from configuration  */
    int     argc;                      /* entry for plug-in.       */
    char ** oid_list;                  /* OIDs supported           */
    int     rc = 0;                    /* 0 means success          */
    int     i;

    /* Get the arguments from the configuration entry.             */
    rc |= slapi_pblock_get(pb, SLAPI_PLUGIN_ARGV, &argv);
    rc |= slapi_pblock_get(pb, SLAPI_PLUGIN_ARGC, &argc);
    if (rc != 0) {
        slapi_log_info_ex(
            SLAPI_LOG_INFO_AREA_PLUGIN,
            SLAPI_LOG_INFO_LEVEL_DEFAULT,
            SLAPI_LOG_NO_MSGID,
            SLAPI_LOG_NO_CONNID,
            SLAPI_LOG_NO_OPID,
            "testexop_init in test-extendedop plug-in",
            "Could not get plug-in arguments.\n"
        );
        return (rc);
    }

    /* Extended operation plug-ins may handle a range of OIDs.     */
    oid_list = (char **)slapi_ch_malloc((argc + 1) * sizeof(char *));
    for (i = 0; i < argc; ++i) {
        oid_list[i] = slapi_ch_strdup(argv[i]);
        slapi_log_info_ex(
            SLAPI_LOG_INFO_AREA_PLUGIN,
            SLAPI_LOG_INFO_LEVEL_DEFAULT,
            SLAPI_LOG_NO_MSGID,
            SLAPI_LOG_NO_CONNID,
            SLAPI_LOG_NO_OPID,
            "testexop_init in test-extendedop plug-in",
            "Registering plug-in for extended operation %s.\n",
            oid_list[i]
        );
    }
    oid_list[argc] = NULL;
        
    rc |= slapi_pblock_set(            /* Plug-in API version      */
        pb,
        SLAPI_PLUGIN_VERSION,
        SLAPI_PLUGIN_CURRENT_VERSION
    );
    rc |= slapi_pblock_set(            /* Plug-in description      */
        pb,
        SLAPI_PLUGIN_DESCRIPTION,
        (void *) &expdesc
    );
    rc |= slapi_pblock_set(            /* Extended op. handler     */
        pb,
        SLAPI_PLUGIN_EXT_OP_FN,
        (void *) test_extendedop
    );
    rc |= slapi_pblock_set(            /* List of OIDs handled     */
        pb,
        SLAPI_PLUGIN_EXT_OP_OIDLIST,
        oid_list
    );
    return (rc);
}
