/***************************************************************************
 *   Copyright (C) 2012 by Timothy Pearson                                 *
 *   kb9vqf@pearsoncomputing.net                                           *
 *                                                                         *
 *   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; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   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.             *
 ***************************************************************************/

#include "config.h"

#include "plugin.h"

#include <sys/wait.h>

#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <ctype.h>
#include <syslog.h>

#include <tqstringlist.h>
#include <tqfile.h>

#include <libtdeldap.h>

static char **plugin_arguments = NULL;

static TQString admingroup_dn;
static TQString realmname;
static TQString aclfilename;
static TQString rootaccountname;
static LDAPManager* ldapManagerObject = 0;

void log_plugin (const char* msg, ...)
{
	va_list ap;
	va_start (ap, msg);
	vprintf(msg, ap);
	va_end (ap);
}

static int
post_modify (Slapi_PBlock *pb)
{
	LDAPMod **mods;
	char *dn;
	int rc, code;
	
	return_val_if_fail (pb, -1);
	
	/* Make sure it was successful, don't process errors */
	rc = slapi_pblock_get (pb, SLAPI_RESULT_CODE, &code);
	return_val_if_fail (rc >= 0, -1);
	if (code != LDAP_SUCCESS) {
		return 0;
	}

	/* Get out the DN and normalize it */
	rc = slapi_pblock_get (pb, SLAPI_MODIFY_TARGET, &dn);
	return_val_if_fail (rc >= 0 && dn, -1);
	dn = slapi_ch_strdup (dn);
	slapi_dn_normalize_case (dn);

	if (dn == admingroup_dn) {
		TQString errorstring;
		LDAPGroupInfo admininfo = ldapManagerObject->getGroupByDistinguishedName(admingroup_dn, &errorstring);
		if (errorstring == "") {
			TQFile file(aclfilename);
			if (file.open(IO_WriteOnly)) {
				TQTextStream stream( &file );
		
				stream << "# This file was automatically generated by TDE\n";
				stream << "# All changes will be lost!\n";
				stream << "\n\n";
	
				stream << "# Internal Kerberos administration account\n";
				stream << TQString("kadmin/%1@%2\tall,get-keys").arg(rootaccountname).arg(realmname);
				stream << "\n\n";
	
				stream << "# Configured realm administrators\n";
				for ( TQStringList::Iterator it = admininfo.userlist.begin(); it != admininfo.userlist.end(); ++it ) {
					TQString krbConvertedUser = *it;
					int eqpos = krbConvertedUser.find("=")+1;
					int cmpos = krbConvertedUser.find(",", eqpos);
					krbConvertedUser.truncate(cmpos);
					krbConvertedUser.remove(0, eqpos);
					krbConvertedUser.append("@"+realmname);
					stream << krbConvertedUser << "\tall,get-keys\n";
				}
				file.close();
			}
		}
	}
	
	rc = slapi_pblock_get (pb, SLAPI_MODIFY_MODS, &mods);
	return_val_if_fail (rc >= 0 && mods, -1);
	
	slapi_ch_free_string (&dn);
	return 0;
}

static const char * plugin_compat_ver = SLAPI_PLUGIN_VERSION_03;

static Slapi_PluginDesc plugin_description = {
	PLUGIN_NAME,                                              /* plug-in identifier */
	"kb9vqf@pearsoncomputing.net",                            /* vendor name */
	VERSION,                                                  /* plug-in revision number */
	"Updates kadmind ACL list on group update"                /* plug-in description */
};

static int
plugin_destroy (Slapi_PBlock *pb) 
{
	slapi_ch_array_free (plugin_arguments);
	plugin_arguments = NULL;

	if (ldapManagerObject) delete ldapManagerObject;

	return 0;
}

extern "C" int internal_plugin_init (Slapi_PBlock *pb)
{
	char **argv = NULL;
	int argc = 0;
	int rc, i;

	return_val_if_fail (pb, -1);

	rc = slapi_pblock_get (pb, SLAPI_PLUGIN_ARGV, &argv);
	return_val_if_fail (rc >= 0, -1);
	slapi_pblock_get (pb, SLAPI_PLUGIN_ARGC, &argc);
	return_val_if_fail (rc >= 0, -1);

	/* 
	 * Copy all the arguments, until we get destroyed, and 
	 * send the arguments to the components to configure 
	 * themselves.
	 */
	plugin_arguments = (char**)slapi_ch_calloc (argc + 1, sizeof (char*));
	for (i = 0; i < argc; ++i) {
		plugin_arguments[i] = slapi_ch_strdup (argv[i]);
		TQStringList argComponents = TQStringList::split(":=", plugin_arguments[i]);
		if (argComponents[0] == "admingroup-dn") {
			admingroup_dn = argComponents[1];
		}
		else if (argComponents[0] == "realm") {
			realmname = argComponents[1];
		}
		else if (argComponents[0] == "aclfile") {
			aclfilename = argComponents[1];
		}
		else if (argComponents[0] == "builtinadmin") {
			rootaccountname = argComponents[1];
		}
	}

	/* Null terminate */
	plugin_arguments[i] = NULL;

	if (slapi_pblock_set (pb, SLAPI_PLUGIN_VERSION, (void*)plugin_compat_ver) != 0 ||
	    slapi_pblock_set (pb, SLAPI_PLUGIN_DESCRIPTION, (void*)(&plugin_description)) != 0 ||
	    slapi_pblock_set (pb, SLAPI_PLUGIN_DESTROY_FN, (void*)plugin_destroy)) {
		log_plugin ("error registering plugin %s\n", PLUGIN_NAME);
		return -1;
	}

	ldapManagerObject = new LDAPManager(realmname, "ldapi://");

	/* Setup the entry add/modify functions */
	if (slapi_pblock_set (pb, SLAPI_PLUGIN_POST_MODIFY_FN, (void*)post_modify) != 0) {
		log_plugin ("error registering plugin hooks for %s\n", PLUGIN_NAME);
		return -1;
	}

	log_plugin ("%s initialized\n", PLUGIN_NAME);
	return 0;
}

