/** BEGIN COPYRIGHT BLOCK
 * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
 * 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

#include <stdio.h>
#include <string.h>
#include "portable.h"
#include "nspr.h"
#include "slapi-plugin.h"
#include "slapi-private.h"
#include "cos_cache.h"
#include "vattr_spi.h"
#include <sys/stat.h>


/*** secret slapd stuff ***/

/*
	these are required here because they are not available
	in any public header.  They must exactly match their
	counterparts in the server or they will fail to work
	correctly.
*/

/*** from proto-slap.h ***/

int slapd_log_error_proc( char *subsystem, char *fmt, ... );

/*** end secrets ***/

#define COS_PLUGIN_SUBSYSTEM   "cos-plugin"   /* used for logging */

/* subrelease in the following version info is for odd-ball cos releases
 * which do not fit into a general release, this can be used for beta releases
 * and other (this version stuff is really to help outside applications which
 * may wish to update cos decide whether the cos version they want to update to
 * is a higher release than the installed plugin)
 *
 * note: release origin is 00 for directory server
 *       sub-release should be:
 *         50 for initial RTM products
 *         from 0 increasing for alpha/beta releases
 *         from 51 increasing for patch releases
 */
#define COS_VERSION	0x00050050		/* version format: 0x release origin 00 major 05 minor 00 sub-release 00 */

/* other function prototypes */
int cos_init( Slapi_PBlock *pb ); 
int cos_compute(computed_attr_context *c,char* type,Slapi_Entry *e,slapi_compute_output_t outputfn);
int cos_start( Slapi_PBlock *pb );
int cos_close( Slapi_PBlock *pb );
int cos_post_op( Slapi_PBlock *pb );

static Slapi_PluginDesc pdesc = { "cos", VENDOR, DS_PACKAGE_VERSION,
	"class of service plugin" };

static void * cos_plugin_identity = NULL;


/*
** Plugin identity mgmt
*/

void cos_set_plugin_identity(void * identity) 
{
	cos_plugin_identity=identity;
}

void * cos_get_plugin_identity()
{
	return cos_plugin_identity;
}

int cos_version()
{
	return COS_VERSION;
}

/* 
 * cos_postop_init: registering cos_post_op
 * cos_post_op just calls cos_cache_change_notify, which does not have any
 * backend operations.  Thus, no need to be in transaction.  Rather, it is
 * harmful if putting in the transaction since tring to hold change_lock
 * inside of transaction would cause a deadlock.
 */
int
cos_postop_init ( Slapi_PBlock *pb )
{
	int rc = 0;

	if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
		 slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN, (void *)cos_post_op ) != 0 ||
		 slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN, (void *)cos_post_op ) != 0 ||
		 slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN, (void *)cos_post_op ) != 0 ||
		 slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN, (void *)cos_post_op ) != 0 )
	{
		slapi_log_error( SLAPI_LOG_FATAL, COS_PLUGIN_SUBSYSTEM,
						 "cos_postop_init: failed to register plugin\n" );
		rc = -1;
	}
	return rc;
}

int
cos_internalpostop_init ( Slapi_PBlock *pb )
{
	int rc = 0;

	if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, 
							SLAPI_PLUGIN_VERSION_01 ) != 0 ||
		 slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_MODIFY_FN,
							(void *)cos_post_op ) != 0 ||
		 slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_MODRDN_FN,
							(void *)cos_post_op ) != 0 ||
		 slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_ADD_FN,
							(void *) cos_post_op ) != 0 ||
		 slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_DELETE_FN,
							(void *) cos_post_op ) != 0 )
	{
		slapi_log_error( SLAPI_LOG_FATAL, COS_PLUGIN_SUBSYSTEM,
					"cos_internalpostop_init: failed to register plugin\n" );
		rc = -1;
	}
	return rc;
}

/* 
	cos_init
	--------
	adds our callbacks to the list
*/
int cos_init( Slapi_PBlock *pb )
{
	int ret = 0;
	void * plugin_identity=NULL;

	LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_init\n",0,0,0);

	/*
	** Store the plugin identity for later use.
	** Used for internal operations
	*/
	
	slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &plugin_identity);
	PR_ASSERT (plugin_identity);
	cos_set_plugin_identity(plugin_identity);
	
	if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
		 slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, (void *) cos_start ) != 0 ||
		 slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN, (void *) cos_close ) != 0 ||
		 slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&pdesc ) != 0 )
	{
		slapi_log_error( SLAPI_LOG_FATAL, COS_PLUGIN_SUBSYSTEM,
						 "cos_init: failed to register plugin\n" );
		ret = -1;
		goto bailout;
	}
	ret = slapi_register_plugin("postoperation", 1 /* Enabled */,
					"cos_postop_init", cos_postop_init,
					"Class of Service postoperation plugin", NULL,
					plugin_identity);
	if ( ret < 0 ) {
		goto bailout;
	}

	ret = slapi_register_plugin("internalpostoperation", 1 /* Enabled */,
	                      "cos_internalpostop_init", cos_internalpostop_init,
	                      "Class of Service internalpostoperation plugin", NULL,
	                      plugin_identity);

bailout:
	LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_init\n",0,0,0);
	return ret;
}

/*
	cos_start
	---------
	This function registers the computed attribute evaluator
	and inits the cos cache.
	It is called after cos_init.
*/
int cos_start( Slapi_PBlock *pb )
{
	int ret = 0;

	LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_start\n",0,0,0);

	if( !cos_cache_init() )
	{
		LDAPDebug( LDAP_DEBUG_PLUGIN, "cos: ready for service\n",0,0,0);
	}
	else
	{

		/* problems we are hosed */
		cos_cache_stop();
		LDAPDebug( LDAP_DEBUG_ANY, "cos_start: failed to initialise\n",0,0,0);
		ret = -1;
	}

	LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_start\n",0,0,0);
	return ret;
}

/*
	cos_close
	---------
	closes down the cache
*/
int cos_close( Slapi_PBlock *pb )
{
	LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_close\n",0,0,0);

	cos_cache_stop();

	LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_close\n",0,0,0);

	return 0;
}

/*
	cos_compute
	-----------
	called when evaluating named attributes in a search
	and attributes remain unfound in the entry,
	this function checks the attribute for a match with
	those in the class of service definitions, and if a
	match is found, adds the attribute and value to the
	output list

	returns 
		0 on success
		1 on outright failure
		-1 when doesn't know about attribute
*/
int cos_compute(computed_attr_context *c,char* type,Slapi_Entry *e,slapi_compute_output_t outputfn)
{
	int ret = -1;

	return ret;
}


/*
	cos_post_op
	-----------
	Catch all for all post operations that change entries
	in some way - this simply notifies the cache of a
	change - the cache decides if action is necessary
*/
int cos_post_op( Slapi_PBlock *pb )
{
	LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_post_op\n",0,0,0);

	cos_cache_change_notify(pb);

	LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_post_op\n",0,0,0);

	return SLAPI_PLUGIN_SUCCESS; /* always succeed */
}

