/***************************************************************************
 *   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 <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <pwd.h>

#include <tquuid.h>
#include <tqlayout.h>
#include <tqtabwidget.h>

#include <tdelocale.h>
#include <tdeglobal.h>
#include <tdeparts/genericfactory.h>
#include <ksimpleconfig.h>
#include <tdeglobalsettings.h>
#include <kstandarddirs.h>
#include <kurlrequester.h>
#include <tdelistview.h>
#include <kopenwith.h>
#include <kpropertiesdialog.h>
#include <tdeio/job.h>
#include <tqdir.h>
#include <tqheader.h>
#include <kcombobox.h>
#include <tdemessagebox.h>
#include <tqcheckbox.h>
#include <ktempdir.h>
#include <kprocess.h>
#include <knuminput.h>
#include <tdesu/process.h>
#include <libtdeldap.h>
#include <tdefiledialog.h>
#include <kpassdlg.h>

#include "sha1.h"

#include "ldapcontroller.h"
#include "primaryrealmwizard/primaryrealmwizard.h"
#include "secondaryrealmwizard/secondaryrealmwizard.h"
#include "processingdialog.h"
#include "multimasterreplicationconfigdlg.h"

#include "ldapcontrollerconfigbase.h"

#ifndef KDE_CONFDIR
#define KDE_CONFDIR "/etc/trinity"
#endif

#ifndef TDE_LIBDIR
#define TDE_LIBDIR "/opt/trinity/lib"
#endif

#ifndef LDAP_KEYTAB_FILE
#define LDAP_KEYTAB_FILE "/etc/ldap/ldap.keytab"
#endif

#ifndef LDAP_DEFAULT_FILE
#define LDAP_DEFAULT_FILE "/etc/default/slapd"
#endif

#ifndef HEIMDAL_DEFAULT_FILE
#define HEIMDAL_DEFAULT_FILE "/etc/default/heimdal-kdc"
#endif

#ifndef SASL_DEFAULT_FILE
#define SASL_DEFAULT_FILE "/etc/default/saslauthd"
#endif

#ifndef SASL_CONTROL_FILE
#define SASL_CONTROL_FILE "/etc/ldap/sasl2/slapd.conf"
#endif

#ifndef HEIMDAL_ACL_FILE
#define HEIMDAL_ACL_FILE "/etc/heimdal-kdc/kadmind.acl"
#endif

#define KEY_STRENGTH 2048

typedef KGenericFactory<LDAPController, TQWidget> ldapFactory;

K_EXPORT_COMPONENT_FACTORY( kcm_ldapcontroller, ldapFactory("kcmldapcontroller"))

LDAPController::LDAPController(TQWidget *parent, const char *name, const TQStringList&)
    : TDECModule(parent, name), myAboutData(0)
{
	TQVBoxLayout *layout = new TQVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint());
	m_systemconfig = new KSimpleConfig( TQString::fromLatin1( KDE_CONFDIR "/ldap/ldapconfigrc" ));
	m_systemconfig->setFileWriteMode(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);

	TDEAboutData* about = new TDEAboutData("ldapcontroller", I18N_NOOP("TDE LDAP Controller"), "0.1",
		I18N_NOOP("TDE LDAP Controller Control Panel Module"),
		TDEAboutData::License_GPL,
		I18N_NOOP("(c) 2012 Timothy Pearson"), 0, 0);
	
	about->addAuthor("Timothy Pearson", 0, "kb9vqf@pearsoncomputing.net");
	setAboutData( about );

	m_base = new LDAPControllerConfigBase(this);
	layout->add(m_base);

	m_base->systemRole->clear();
	m_base->systemRole->insertItem("Workstation", ROLE_WORKSTATION);
	m_base->systemRole->insertItem("Secondary Realm Controller", ROLE_SECONDARY_REALM_CONTROLLER);
	m_base->systemRole->insertItem("Primary Realm Controller", ROLE_PRIMARY_REALM_CONTROLLER);

	m_base->multiMasterReplicationMappings->setAllColumnsShowFocus(true);
	m_base->multiMasterReplicationMappings->setFullWidth(true);
	
	setRootOnlyMsg(i18n("<b>LDAP controller settings take effect system wide, and require administrator access to modify</b><br>To alter the system's realm controller settings, click on the \"Administrator Mode\" button below."));
	setUseRootOnlyMsg(true);

	connect(m_base->systemEnableSupport, TQ_SIGNAL(clicked()), this, TQ_SLOT(changed()));
	connect(m_base->systemEnableSupport, TQ_SIGNAL(clicked()), this, TQ_SLOT(processLockouts()));
	connect(m_base->systemRole, TQ_SIGNAL(activated(const TQString&)), this, TQ_SLOT(systemRoleChanged()));

	connect(m_base->caSetMaster, TQ_SIGNAL(clicked()), this, TQ_SLOT(btncaSetMaster()));

	connect(m_base->caRegenerate, TQ_SIGNAL(clicked()), this, TQ_SLOT(btncaRegenerate()));
	connect(m_base->caExportKey, TQ_SIGNAL(clicked()), this, TQ_SLOT(btncaExportKey()));
	connect(m_base->caExportCert, TQ_SIGNAL(clicked()), this, TQ_SLOT(btncaExportCert()));

	connect(m_base->krbRegenerate, TQ_SIGNAL(clicked()), this, TQ_SLOT(btnkrbRegenerate()));
	connect(m_base->krbExportKey, TQ_SIGNAL(clicked()), this, TQ_SLOT(btnkrbExportKey()));
	connect(m_base->krbExportCert, TQ_SIGNAL(clicked()), this, TQ_SLOT(btnkrbExportCert()));

	connect(m_base->ldapRegenerate, TQ_SIGNAL(clicked()), this, TQ_SLOT(btnldapRegenerate()));
	connect(m_base->ldapExportKey, TQ_SIGNAL(clicked()), this, TQ_SLOT(btnldapExportKey()));
	connect(m_base->ldapExportCert, TQ_SIGNAL(clicked()), this, TQ_SLOT(btnldapExportCert()));

	connect(m_base->crlRegenerate, TQ_SIGNAL(clicked()), this, TQ_SLOT(btncrlRegenerate()));

	connect(m_base->btnChangeLDAPRootPassword, TQ_SIGNAL(clicked()), this, TQ_SLOT(btnChangeLDAPRootPassword()));
	connect(m_base->btnChangeRealmAdminPassword, TQ_SIGNAL(clicked()), this, TQ_SLOT(btnChangeRealmAdminPassword()));

	connect(&m_certRefreshTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(updateCertDisplay()));

	connect(m_base->advancedEnableMultiMasterReplication, TQ_SIGNAL(clicked()), this, TQ_SLOT(changed()));

	connect(m_base->btnAddMultiMasterReplicationMapping, TQ_SIGNAL(clicked()), this, TQ_SLOT(btnAddMultiMasterReplicationMapping()));
	connect(m_base->btnEditMultiMasterReplicationMapping, TQ_SIGNAL(clicked()), this, TQ_SLOT(btnEditMultiMasterReplicationMapping()));
	connect(m_base->btnRemoveMultiMasterReplicationMapping, TQ_SIGNAL(clicked()), this, TQ_SLOT(btnRemoveMultiMasterReplicationMapping()));

	connect(m_base->multiMasterReplicationMappings, TQ_SIGNAL(selectionChanged()), this, TQ_SLOT(multiMasterReplicationHighlighted()));
	connect(m_base->multiMasterReplicationMappings, TQ_SIGNAL(executed(TQListViewItem*)), this, TQ_SLOT(modifySelectedMultiMasterReplication()));

	connect(m_base->advancedCaCertExpiry, TQ_SIGNAL(valueChanged(int)), this, TQ_SLOT(caCertExpiryChanged()));
	connect(m_base->advancedCaCrlExpiry, TQ_SIGNAL(valueChanged(int)), this, TQ_SLOT(caCrlExpiryChanged()));
	connect(m_base->advancedKerberosCertExpiry, TQ_SIGNAL(valueChanged(int)), this, TQ_SLOT(kerberosCertExpiryChanged()));
	connect(m_base->advancedLdapCertExpiry, TQ_SIGNAL(valueChanged(int)), this, TQ_SLOT(ldapCertExpiryChanged()));

	m_fqdn = LDAPManager::getMachineFQDN();
	m_roleFullyConfigured = true;

	// FIXME
	// This assumes Debian!
	m_ldapUserName = "openldap";
	m_ldapGroupName = "openldap";

	load();

	processLockouts();
};

LDAPController::~LDAPController() {
}

void system_safe(const char * cmdstr) {
	if (system(cmdstr) < 0) {
		printf("[ERROR] System call to '%s' failed!\n\r", cmdstr);
	}
}

void chown_safe(const char * file, uid_t user, gid_t group) {
	if (chown(file, user, group) < 0) {
		printf("[ERROR] Chown call to '%s' for %d:%d failed!\n\r", file, user, group);
	}
}

void LDAPController::systemRoleChanged() {
	int previousRole = m_prevRole;

	if (m_base->systemRole->currentItem() != m_prevRole) {
		// Verify that this workstation was not already bonded to an LDAP realm!
		bool bonded = false;
		TQStringList cfgRealms = m_systemconfig->groupList();
		for (TQStringList::Iterator it(cfgRealms.begin()); it != cfgRealms.end(); ++it) {
			if ((*it).startsWith("LDAPRealm-")) {
				m_systemconfig->setGroup(*it);
				if (m_systemconfig->readBoolEntry("bonded", false) == true) {
					bonded = true;
				}
			}
		}

		if (m_base->systemRole->currentItem() == ROLE_PRIMARY_REALM_CONTROLLER) {
			if (previousRole == ROLE_SECONDARY_REALM_CONTROLLER) {
				// TODO FIXME
				KMessageBox::error(0, i18n("<qt>Secondary realm controller promotion is not yet available<p>If you want to see it implemented, contact the Trinity Desktop developers</qt>"), i18n("Feature Not Yet Available"));
				m_base->systemRole->setCurrentItem(previousRole);
			}
			else {
				if (bonded) {
					KMessageBox::error(0, i18n("<qt>You are already bonded to a realm!<p>Please unbond from all realms before selecting a Realm Controller role</qt>"), i18n("Common Sense Failure"));
					m_base->systemRole->setCurrentItem(previousRole);
				}
				else {
					m_roleFullyConfigured = false;

					// Something will probably change
					save();
				
					PrimaryRealmWizard realmwizard(this, m_fqdn, m_certconfig, this);
					if (realmwizard.exec() < 0) {
						// Wizard was cancelled
						// Back out all changes!
						m_base->systemRole->setCurrentItem(previousRole);
						save();
					}
					else {
						// Wizard completed; commit changes
						save();
					}

					m_roleFullyConfigured = true;
				
					// Something probably changed
					load();
				}
			}
		}
		else if (m_base->systemRole->currentItem() == ROLE_SECONDARY_REALM_CONTROLLER) {
#if 1
			// TODO FIXME
			KMessageBox::error(0, i18n("<qt>Secondary realm controller support is not yet available<p>If you want to see it implemented, contact the Trinity Desktop developers</qt>"), i18n("Feature Not Yet Available"));
			m_base->systemRole->setCurrentItem(previousRole);
#else
			if (previousRole == ROLE_PRIMARY_REALM_CONTROLLER) {
				// TODO FIXME
				KMessageBox::error(0, i18n("<qt>Primary realm controller demotion is not yet available<p>If you want to see it implemented, contact the Trinity Desktop developers</qt>"), i18n("Feature Not Yet Available"));
				m_base->systemRole->setCurrentItem(previousRole);
			}
			else {
				if (bonded) {
					KMessageBox::error(0, i18n("<qt>You are already bonded to a realm!<p>Please unbond from all realms before selecting a Realm Controller role</qt>"), i18n("Common Sense Failure"));
					m_base->systemRole->setCurrentItem(previousRole);
				}
				else {
					m_roleFullyConfigured = false;

					// Something will probably change
					save();
		
					SecondaryRealmWizard realmwizard(this, m_fqdn, m_certconfig, this);
					if (realmwizard.exec() < 0) {
						// Wizard was cancelled
						// Back out all changes!
						m_base->systemRole->setCurrentItem(previousRole);
						save();
					}
					else {
						// Wizard completed; commit changes
						save();
					}

					m_roleFullyConfigured = true;
		
					// Something probably changed
					load();
				}
			}
#endif
		}
		else if (m_base->systemRole->currentItem() == ROLE_WORKSTATION) {
			if (KMessageBox::warningYesNo(this, i18n("<qt><b>WARNING</b><br>You are attempting to demote a realm controller<p>This action will <b>PERMANENTLY DESTROY</b> the realm directory stored on this machine<p>If you do not want to do this, select <b>Cancel</b> below</qt>"), i18n("Are you absolutely sure?"), TQString("Continue"), TQString("Cancel")) == KMessageBox::Yes) {
				ProcessingDialog pdialog(this);
				pdialog.setStatusMessage(i18n("Preparing to demote primary realm controller..."));
				pdialog.raise();
				pdialog.setActiveWindow();
				tqApp->processEvents();

				save();

				pdialog.setStatusMessage(i18n("Stopping servers..."));

				// Stop SASL
				if (controlSASLServer(SC_STOP) != 0) {
					//
				}
				// Stop Heimdal
				if (controlHeimdalServer(SC_STOP) != 0) {
					//
				}
				// Stop slapd
				if (controlLDAPServer(SC_STOP) != 0) {
					//
				}

				pdialog.setStatusMessage(i18n("Purging LDAP database..."));
				tqApp->processEvents();
				controlHeimdalServer(SC_PURGE);
				controlLDAPServer(SC_PURGE);

				pdialog.setStatusMessage(i18n("Purging local configuration..."));
				tqApp->processEvents();

				system_safe(TQString("rm -f %1").arg(CRON_UPDATE_PRIMARY_REALM_CERTIFICATES_FILE).local8Bit());
				system_safe(TQString("rm -rf %1").arg(TDE_CERTIFICATE_DIR).local8Bit());

				// Write the TDE realm configuration file
				LDAPRealmConfigList realms;
				LDAPManager::writeTDERealmList(realms, m_systemconfig);
				m_systemconfig->setGroup(NULL);
				m_systemconfig->deleteEntry("DefaultRealm");
				m_systemconfig->deleteGroup("Replication", true, false);
				m_systemconfig->sync();

				pdialog.closeDialog();

				load();
			}
			else {
				m_base->systemRole->setCurrentItem(previousRole);
			}
		}
	}
}

void LDAPController::processLockouts() {
	bool enabled = m_base->systemEnableSupport->isChecked();
	bool canChangeLDAPEnabled = true;

	if (getuid() != 0 || !m_systemconfig->checkConfigFilesWritable( true )) {
		canChangeLDAPEnabled = false;
		enabled = false;
	}

	if (m_base->systemRole->currentItem() != ROLE_WORKSTATION) {
		canChangeLDAPEnabled = false;
	}

	if (m_base->systemRole->currentItem() == ROLE_PRIMARY_REALM_CONTROLLER) {
		TQListViewItem* lvi = m_base->multiMasterReplicationMappings->selectedItem();
		if (lvi) {
			m_base->btnEditMultiMasterReplicationMapping->setEnabled(true);
			m_base->btnRemoveMultiMasterReplicationMapping->setEnabled(true);
		}
		else {
			m_base->btnEditMultiMasterReplicationMapping->setEnabled(false);
			m_base->btnRemoveMultiMasterReplicationMapping->setEnabled(false);
		}
	}

	m_base->systemEnableSupport->setEnabled(canChangeLDAPEnabled);
	m_base->systemRole->setEnabled(enabled);
}

void LDAPController::load() {
	bool thisIsMyMachine;

	m_systemconfig->setGroup(NULL);
	m_base->systemEnableSupport->setChecked(m_systemconfig->readBoolEntry("EnableLDAP", false));
	if (m_fqdn == m_systemconfig->readEntry("HostFQDN", "")) {
		thisIsMyMachine = true;
	}
	else {
		thisIsMyMachine = false;
	}
	TQString ldapRole = m_systemconfig->readEntry("LDAPRole", "Workstation");
	if (!thisIsMyMachine) {
		ldapRole = "Workstation";
	}
	if (ldapRole == "Primary Realm Controller") {
		m_base->systemRole->setCurrentItem(ROLE_PRIMARY_REALM_CONTROLLER);
	}
	else {
		m_base->systemRole->setCurrentItem(ROLE_WORKSTATION);
	}
	m_prevRole = m_base->systemRole->currentItem();

	// Load server-specific replication settings
	m_systemconfig->setGroup("Replication");
	m_base->ignoreReplicationSSLFailures->setChecked(m_systemconfig->readBoolEntry("IgnoreSSLFailures", false));

	// Load cert config
	m_systemconfig->setGroup("Certificates");
	m_certconfig.caExpiryDays = m_systemconfig->readNumEntry("caExpiryDays", KERBEROS_PKI_PEMKEY_EXPIRY_DAYS);
	m_certconfig.caCrlExpiryDays = m_systemconfig->readNumEntry("caCrlExpiryDays", KERBEROS_PKI_CRL_EXPIRY_DAYS);
	m_certconfig.kerberosExpiryDays = m_systemconfig->readNumEntry("kerberosExpiryDays", KERBEROS_PKI_KRB_EXPIRY_DAYS);
	m_certconfig.ldapExpiryDays = m_systemconfig->readNumEntry("ldapExpiryDays", KERBEROS_PKI_LDAP_EXPIRY_DAYS);
	m_certconfig.countryName = m_systemconfig->readEntry("countryName");
	m_certconfig.stateOrProvinceName = m_systemconfig->readEntry("stateOrProvinceName");
	m_certconfig.localityName = m_systemconfig->readEntry("localityName");
	m_certconfig.organizationName = m_systemconfig->readEntry("organizationName");
	m_certconfig.orgUnitName = m_systemconfig->readEntry("orgUnitName");
	m_certconfig.commonName = m_systemconfig->readEntry("commonName");
	m_certconfig.emailAddress = m_systemconfig->readEntry("emailAddress");

	m_realmconfig = LDAPManager::readTDERealmList(m_systemconfig, !thisIsMyMachine);
	if (!thisIsMyMachine) {
		LDAPManager::writeTDERealmList(m_realmconfig, m_systemconfig);
	}

	m_systemconfig->setGroup(NULL);
	m_defaultRealm = m_systemconfig->readEntry("DefaultRealm");

	if (m_base->systemRole->currentItem() == ROLE_PRIMARY_REALM_CONTROLLER) {
		if (m_base->TabWidget2->indexOf(m_base->advancedPrimaryControllerTab) < 0) {
			m_base->TabWidget2->insertTab(m_base->advancedPrimaryControllerTab, i18n("Advanced Configuration"));
		}

		m_base->groupRealmController->show();
		m_base->groupRealmCertificates->show();

		m_base->realmName->setText(m_defaultRealm);

		// Display builtin account and group names, and provide a password reset button for each builtin user (yes, this includes the LDAP admin account!)
		// FIXME
		// root account should not be locked to "admin"!
		// when fixing, please fix the two instances of locked "admin":
		// 1.) in realmwizard.cpp ::accept()
		// 2.) in LDAPManager::setLDAPMasterReplicationSettings()
		m_base->ldapRootUser->setText(TQString("cn=%1,").arg("admin") + LDAPManager::ldapdnForRealm(m_defaultRealm));

		// Connect to LDAP
		TQString realmname = m_defaultRealm.upper();
		LDAPCredentials* credentials = new LDAPCredentials;
		credentials->username = "";
		credentials->password = "";
		credentials->realm = realmname;
		LDAPManager* ldap_mgr = new LDAPManager(realmname, "ldapi://", credentials);
		TQString errorstring;

		// Get builtin TDE account mappings from LDAP
		LDAPTDEBuiltinsInfo builtins = ldap_mgr->getTDEBuiltinMappings(&errorstring);
		if (m_roleFullyConfigured && errorstring != "") {
			KMessageBox::error(0, errorstring);
		}

		// Get replication mappings from LDAP
		LDAPMasterReplicationInfo replicationsettings = ldap_mgr->getLDAPMasterReplicationSettings(&errorstring);
		if (m_roleFullyConfigured && errorstring != "") {
			KMessageBox::error(0, errorstring);
		}
		m_base->advancedEnableMultiMasterReplication->setChecked(replicationsettings.enabled);
		m_base->multiMasterReplicationMappings->clear();
		LDAPMasterReplicationMap::iterator it;
		for (it = replicationsettings.serverIDs.begin(); it != replicationsettings.serverIDs.end(); ++it) {
			new TQListViewItem(m_base->multiMasterReplicationMappings, TQString("%1").arg((*it).id), (*it).fqdn);
		}

		// Get certificate settings from LDAP
		TQString realmCAMaster = ldap_mgr->getRealmCAMaster(&errorstring);
		if (m_roleFullyConfigured && errorstring != "") {
			KMessageBox::error(0, errorstring);
		}

		delete ldap_mgr;
		delete credentials;

		m_base->realmAdminUser->setText(LDAPManager::cnFromDn(builtins.builtinRealmAdminAccount));
		m_base->realmAdminGroup->setText(LDAPManager::cnFromDn(builtins.builtinRealmAdminGroup));
		m_base->realmMachineAdminGroup->setText(LDAPManager::cnFromDn(builtins.builtinMachineAdminGroup));
		m_base->realmStandardUserGroup->setText(LDAPManager::cnFromDn(builtins.builtinStandardUserGroup));

		m_base->caCurrentMaster->setText(realmCAMaster);
		if (m_fqdn == realmCAMaster) {
			m_base->caSetMaster->setEnabled(false);
		}
		else {
			m_base->caSetMaster->setEnabled(true);
		}

		m_base->advancedCaCertExpiry->setValue(m_certconfig.caExpiryDays);
		m_base->advancedCaCrlExpiry->setValue(m_certconfig.caCrlExpiryDays);
		m_base->advancedKerberosCertExpiry->setValue(m_certconfig.kerberosExpiryDays);
		m_base->advancedLdapCertExpiry->setValue(m_certconfig.ldapExpiryDays);

		updateCertDisplay();
		m_certRefreshTimer.start(60*1000);
	}
	else {
		if (m_base->TabWidget2->indexOf(m_base->advancedPrimaryControllerTab) >= 0) {
			m_base->TabWidget2->removePage(m_base->advancedPrimaryControllerTab);
		}

		m_base->groupRealmController->hide();
		m_base->groupRealmCertificates->hide();

		m_certRefreshTimer.stop();
	}

	processLockouts();
}

#define CERT_STATUS_COLOR_ACTIVE TQColor(0, 128, 0)
#define CERT_STATUS_COLOR_STALE TQColor(128, 64, 0)
#define CERT_STATUS_COLOR_EXPIRED TQColor(128, 0, 0)
#define CERT_STATUS_COLOR_NOTFOUND CERT_STATUS_COLOR_EXPIRED

void LDAPController::updateCertDisplay() {
	TQDateTime certExpiry;
	TQDateTime now = TQDateTime::currentDateTime();
	TQDateTime soon = now.addDays(7);	// Keep in sync with cert-updater/main.cpp

	TQString kdc_certfile = KERBEROS_PKI_KDC_FILE;
	kdc_certfile.replace("@@@KDCSERVER@@@", m_realmconfig[m_defaultRealm].name.lower());
	TQString ldap_certfile = LDAP_CERT_FILE;
	ldap_certfile.replace("@@@ADMINSERVER@@@", m_realmconfig[m_defaultRealm].name.lower());

	TQString realmname = m_defaultRealm.upper();
	LDAPCredentials* credentials = new LDAPCredentials;
	credentials->username = "";
	credentials->password = "";
	credentials->realm = realmname;
	LDAPManager* ldap_mgr = NULL;
	if (realmname != "") {
		ldap_mgr = new LDAPManager(realmname, "ldapi://", credentials);
	}

	// Certificate Authority
	if (TQFile::exists(KERBEROS_PKI_PEM_FILE)) {
		certExpiry = LDAPManager::getCertificateExpiration(KERBEROS_PKI_PEM_FILE);
		if (certExpiry >= now) {
			m_base->caExpiryString->setText("Expires " + certExpiry.toString());
			if (certExpiry >= soon) {
				m_base->caExpiryString->setPaletteForegroundColor(CERT_STATUS_COLOR_ACTIVE);
			}
			else {
				m_base->caExpiryString->setPaletteForegroundColor(CERT_STATUS_COLOR_STALE);
			}
		}
		else {
			m_base->caExpiryString->setText("Expired " + certExpiry.toString());
			m_base->caExpiryString->setPaletteForegroundColor(CERT_STATUS_COLOR_EXPIRED);
		}
	}
	else {
		m_base->caExpiryString->setText("File not found");
		m_base->caExpiryString->setPaletteForegroundColor(CERT_STATUS_COLOR_NOTFOUND);
	}

	// Kerberos
	if (TQFile::exists(kdc_certfile)) {
		certExpiry = LDAPManager::getCertificateExpiration(kdc_certfile);
		if (certExpiry >= now) {
			m_base->krbExpiryString->setText("Expires " + certExpiry.toString());
			if (certExpiry >= soon) {
				m_base->krbExpiryString->setPaletteForegroundColor(CERT_STATUS_COLOR_ACTIVE);
			}
			else {
				m_base->krbExpiryString->setPaletteForegroundColor(CERT_STATUS_COLOR_STALE);
			}
		}
		else {
			m_base->krbExpiryString->setText("Expired " + certExpiry.toString());
			m_base->krbExpiryString->setPaletteForegroundColor(CERT_STATUS_COLOR_EXPIRED);
		}
	}
	else {
		m_base->krbExpiryString->setText("File not found");
		m_base->krbExpiryString->setPaletteForegroundColor(CERT_STATUS_COLOR_NOTFOUND);
	}

	// LDAP
	if (TQFile::exists(ldap_certfile)) {
		certExpiry = LDAPManager::getCertificateExpiration(ldap_certfile);
		if (certExpiry >= now) {
			m_base->ldapExpiryString->setText("Expires " + certExpiry.toString());
			if (certExpiry >= soon) {
				m_base->ldapExpiryString->setPaletteForegroundColor(CERT_STATUS_COLOR_ACTIVE);
			}
			else {
				m_base->ldapExpiryString->setPaletteForegroundColor(CERT_STATUS_COLOR_STALE);
			}
		}
		else {
			m_base->ldapExpiryString->setText("Expired " + certExpiry.toString());
			m_base->ldapExpiryString->setPaletteForegroundColor(CERT_STATUS_COLOR_EXPIRED);
		}
	}
	else {
		m_base->ldapExpiryString->setText("File not found");
		m_base->ldapExpiryString->setPaletteForegroundColor(CERT_STATUS_COLOR_NOTFOUND);
	}

	// Certificate Revocation List
	TQByteArray certificateContents;
	if (ldap_mgr && ldap_mgr->getTDECertificate("publicRootCertificateRevocationList", &certificateContents, NULL) == 0) {
		certExpiry = LDAPManager::getCertificateExpiration(certificateContents);
		if (certExpiry >= now) {
			m_base->crlExpiryString->setText("Expires " + certExpiry.toString());
			if (certExpiry >= soon) {
				m_base->crlExpiryString->setPaletteForegroundColor(CERT_STATUS_COLOR_ACTIVE);
			}
			else {
				m_base->crlExpiryString->setPaletteForegroundColor(CERT_STATUS_COLOR_STALE);
			}
		}
		else {
			m_base->crlExpiryString->setText("Expired " + certExpiry.toString());
			m_base->crlExpiryString->setPaletteForegroundColor(CERT_STATUS_COLOR_EXPIRED);
		}
	}
	else {
		m_base->crlExpiryString->setText("File not found");
		m_base->crlExpiryString->setPaletteForegroundColor(CERT_STATUS_COLOR_NOTFOUND);
	}

	if (ldap_mgr) {
		delete ldap_mgr;
	}
}

void LDAPController::btncaSetMaster() {
	if (KMessageBox::warningYesNo(this, i18n("<qt><b>You are about to promote the server '%1' to the role of Certificate Authority Master</b><p>Are you sure you want to proceed?</qt>").arg(m_fqdn), i18n("Confirmation Required")) == KMessageBox::Yes) {
		TQString errorstring;

		TQString realmname = m_defaultRealm.upper();
		LDAPCredentials* credentials = new LDAPCredentials;
		credentials->username = "";
		credentials->password = "";
		credentials->realm = realmname;
		LDAPManager* ldap_mgr = new LDAPManager(realmname, "ldapi://", credentials);

		if (ldap_mgr->setRealmCAMaster(m_fqdn, &errorstring) != 0) {
			KMessageBox::error(0, i18n("<qt>Unable to change certificate authority master server!<p>%1</qt>").arg(errorstring), i18n("Internal Failure"));
			delete ldap_mgr;
			load();
			return;
		}

		LDAPManager::generatePublicKerberosCACertificate(m_certconfig, m_realmconfig[m_defaultRealm]);
	
		// Upload the contents of KERBEROS_PKI_PEM_FILE to the LDAP server
		if (uploadKerberosCAFileToLDAP(ldap_mgr, &errorstring) != 0) {
			KMessageBox::error(0, i18n("<qt>Unable to upload new certificate to LDAP server!<p>%1</qt>").arg(errorstring), i18n("Internal Failure"));
		}
	
		delete ldap_mgr;
	
		load();
	}
}

void LDAPController::btncaRegenerate() {
	LDAPManager::generatePublicKerberosCACertificate(m_certconfig, m_realmconfig[m_defaultRealm]);

	TQString realmname = m_defaultRealm.upper();
	LDAPCredentials* credentials = new LDAPCredentials;
	credentials->username = "";
	credentials->password = "";
	credentials->realm = realmname;
	LDAPManager* ldap_mgr = new LDAPManager(realmname, "ldapi://", credentials);

	// Upload the contents of KERBEROS_PKI_PEM_FILE to the LDAP server
	TQString errorstring;
	if (uploadKerberosCAFileToLDAP(ldap_mgr, &errorstring) != 0) {
		KMessageBox::error(0, i18n("<qt>Unable to upload new certificate to LDAP server!<p>%1</qt>").arg(errorstring), i18n("Internal Failure"));
	}

	delete ldap_mgr;

	load();
}

void LDAPController::btncaExportKey() {
	KURL src = KERBEROS_PKI_PEMKEY_FILE;
	KURL dest = KFileDialog::getSaveURL(TQString::null, "*.key|Private Key (*.key)", this, i18n("Select a location to save a copy of the private key..."));
	if (!dest.isEmpty()) {
		TDEIO::CopyJob* job = TDEIO::copy(src, dest, true);
		connect(job, TQ_SIGNAL(result(TDEIO::Job*)), this, TQ_SLOT(slotCertCopyResult(TDEIO::Job*)));
	}
}

void LDAPController::btncaExportCert() {
	KURL src = KERBEROS_PKI_PEM_FILE;
	KURL dest = KFileDialog::getSaveURL(TQString::null, "*.pem|PKI Certificate Files (*.pem)", this, i18n("Select a location to save a copy of the certificate..."));
	if (!dest.isEmpty()) {
		TDEIO::CopyJob* job = TDEIO::copy(src, dest, true);
		connect(job, TQ_SIGNAL(result(TDEIO::Job*)), this, TQ_SLOT(slotCertCopyResult(TDEIO::Job*)));
	}
}

void LDAPController::btnkrbRegenerate() {
	LDAPManager::generatePublicKerberosCertificate(m_certconfig, m_realmconfig[m_defaultRealm]);

	load();
}

void LDAPController::btnkrbExportKey() {
	TQString kdc_keyfile = KERBEROS_PKI_KDCKEY_FILE;
	kdc_keyfile.replace("@@@KDCSERVER@@@", m_realmconfig[m_defaultRealm].name.lower());

	KURL src = kdc_keyfile;
	KURL dest = KFileDialog::getSaveURL(TQString::null, "*.key|Private Key (*.key)", this, i18n("Select a location to save a copy of the private key..."));
	if (!dest.isEmpty()) {
		TDEIO::CopyJob* job = TDEIO::copy(src, dest, true);
		connect(job, TQ_SIGNAL(result(TDEIO::Job*)), this, TQ_SLOT(slotCertCopyResult(TDEIO::Job*)));
	}
}

void LDAPController::btnkrbExportCert() {
	TQString kdc_certfile = KERBEROS_PKI_KDC_FILE;
	kdc_certfile.replace("@@@KDCSERVER@@@", m_realmconfig[m_defaultRealm].name.lower());

	KURL src = kdc_certfile;
	KURL dest = KFileDialog::getSaveURL(TQString::null, "*.pem|PKI Certificate Files (*.pem)", this, i18n("Select a location to save a copy of the certificate..."));
	if (!dest.isEmpty()) {
		TDEIO::CopyJob* job = TDEIO::copy(src, dest, true);
		connect(job, TQ_SIGNAL(result(TDEIO::Job*)), this, TQ_SLOT(slotCertCopyResult(TDEIO::Job*)));
	}
}

void LDAPController::btnldapRegenerate() {
	uid_t slapd_uid = 0;
	gid_t slapd_gid = 0;

	// Get LDAP user uid/gid
	struct passwd *pwd;
	pwd = getpwnam(m_ldapUserName.local8Bit());
	slapd_uid = pwd->pw_uid;
	slapd_gid = pwd->pw_gid;

	LDAPManager::generatePublicLDAPCertificate(m_certconfig, m_realmconfig[m_defaultRealm], slapd_uid, slapd_gid);
	controlLDAPServer(SC_RESTART);

	load();
}

void LDAPController::btnldapExportKey() {
	TQString ldap_keyfile = LDAP_CERTKEY_FILE;
	ldap_keyfile.replace("@@@ADMINSERVER@@@", m_realmconfig[m_defaultRealm].name.lower());

	KURL src = ldap_keyfile;
	KURL dest = KFileDialog::getSaveURL(TQString::null, "*.key|Private Key (*.key)", this, i18n("Select a location to save a copy of the private key..."));
	if (!dest.isEmpty()) {
		TDEIO::CopyJob* job = TDEIO::copy(src, dest, true);
		connect(job, TQ_SIGNAL(result(TDEIO::Job*)), this, TQ_SLOT(slotCertCopyResult(TDEIO::Job*)));
	}
}

void LDAPController::btnldapExportCert() {
	TQString ldap_certfile = LDAP_CERT_FILE;
	ldap_certfile.replace("@@@ADMINSERVER@@@", m_realmconfig[m_defaultRealm].name.lower());

	KURL src = ldap_certfile;
	KURL dest = KFileDialog::getSaveURL(TQString::null, "*.pem|PKI Certificate Files (*.pem)", this, i18n("Select a location to save a copy of the certificate..."));
	if (!dest.isEmpty()) {
		TDEIO::CopyJob* job = TDEIO::copy(src, dest, true);
		connect(job, TQ_SIGNAL(result(TDEIO::Job*)), this, TQ_SLOT(slotCertCopyResult(TDEIO::Job*)));
	}
}

void LDAPController::btncrlRegenerate() {
	TQString errstr;

	// Bind to realm
	TQString realmname = m_defaultRealm.upper();
	LDAPCredentials* credentials = new LDAPCredentials;
	credentials->username = "";
	credentials->password = "";
	credentials->realm = realmname;
	LDAPManager* ldap_mgr = new LDAPManager(realmname, "ldapi://", credentials);

	if (ldap_mgr->generatePKICRL(m_certconfig.caCrlExpiryDays, m_realmconfig[m_defaultRealm], KERBEROS_PKI_CRL_FILE, KERBEROS_PKI_PEMKEY_FILE, KERBEROS_PKI_CRLDB_FILE, &errstr) != 0) {
		KMessageBox::error(this, i18n("<qt><b>Unable to regenerate CRL</b><p>Details: %1</qt>").arg(errstr), i18n("Unable to Regenerate CRL"));
	}

	delete ldap_mgr;

	load();
}

void LDAPController::slotCertCopyResult(TDEIO::Job* job) {
	if (job->error()) {
		job->showErrorDialog(this);
	}
}

void LDAPController::btnChangeLDAPRootPassword() {
	// NOTE
	// There is (currently) no good way to replace the root password
	// This convoluted procedure is (currently) the best I can do...

	bool ret = false;

	TQString rootPassword;
	int result = KPasswordDialog::getNewPassword(rootPassword, i18n("Please enter the new LDAP root password:"));
	if (result == KPasswordDialog::Accepted) {
		SHA1 sha;
		TQCString rootPassword2 = rootPassword.utf8();  // utf8 length could be different from TQString length
		sha.process(rootPassword2, rootPassword2.length());
		TQString rootpw_hash = sha.base64Hash();
	
		TQString oldconfigfilename = "/etc/ldap/slapd.d/cn=config/" + TQString("olcDatabase={%1}mdb.ldif.bkp").arg(1);
		TQString newconfigfilename = "/etc/ldap/slapd.d/cn=config/" + TQString("olcDatabase={%1}mdb.ldif").arg(1);
	
		if (controlLDAPServer(SC_STOP) == 0) {
			rename(newconfigfilename.ascii(), oldconfigfilename.ascii());
			TQFile ifile(oldconfigfilename);
			TQFile ofile(newconfigfilename);
		
			if (ifile.open(IO_ReadOnly)) {
				if (ofile.open(IO_WriteOnly)) {
					TQString line;
					TQTextStream istream(&ifile);
					TQTextStream ostream(&ofile);
					while (!istream.atEnd()) {
						line = istream.readLine();
						if (line.startsWith("olcRootPW:")) {
							ostream << "olcRootPW: {SHA}" << rootpw_hash << "\n";
						}
						else {
							ostream << line << "\n";
						}
					}
					ifile.close();
					unlink(oldconfigfilename.local8Bit());
					ofile.close();
					if (controlLDAPServer(SC_START) == 0) {
						ret = true;
					}
				}
				else {
					ifile.close();
					rename(oldconfigfilename.ascii(), newconfigfilename.ascii());
				}
			}
			else {
				rename(oldconfigfilename.ascii(), newconfigfilename.ascii());
			}
		}
	
		if (!ret) {
			KMessageBox::error(0, i18n("<qt>Unable to modify LDAP root password<p>Your LDAP server may now be in an inconsistent or disabled state</qt>"), i18n("Internal Failure"));
		}
	}
}

void LDAPController::btnChangeRealmAdminPassword() {
	TQString adminPassword;
	int result = KPasswordDialog::getNewPassword(adminPassword, i18n("Please enter the new realm administrator password:"));
	if (result == KPasswordDialog::Accepted) {
		TQString realmname = m_defaultRealm.upper();
		LDAPCredentials* credentials = new LDAPCredentials;
		credentials->username = "";
		credentials->password = "";
		credentials->realm = realmname;
		LDAPManager* ldap_mgr = new LDAPManager(realmname, "ldapi://", credentials);
		TQString errorstring;
		LDAPTDEBuiltinsInfo builtins = ldap_mgr->getTDEBuiltinMappings(&errorstring);
		
		LDAPUserInfo adminuserinfo = ldap_mgr->getUserByDistinguishedName(builtins.builtinRealmAdminAccount);
		if (adminuserinfo.informationValid) {
			adminuserinfo.new_password = adminPassword;
			ldap_mgr->setPasswordForUser(adminuserinfo, &errorstring);

			m_systemconfig->setGroup("Replication");
			m_systemconfig->writeEntry("Password", adminPassword);
			m_systemconfig->setGroup(NULL);
			m_systemconfig->sync();
		}
		
		delete ldap_mgr;
		delete credentials;
	}
}

void LDAPController::btnAddMultiMasterReplicationMapping() {
	// Launch a dialog to add the mapping
	LDAPMasterReplicationMapping mapping;

	bool run = true;
	MultiMasterReplicationConfigDialog mappingconfigdlg(mapping, m_defaultRealm, this);
	while (run && (mappingconfigdlg.exec() == TQDialog::Accepted)) {
		mapping = mappingconfigdlg.m_replicationProperties;
		// Make sure the provided FQDN and/or UID do not already exist
		bool conflict = false;
		TQPtrList<TQListViewItem> lst;
		TQListViewItemIterator it(m_base->multiMasterReplicationMappings);
		while (it.current()) {
			if (it.current()->text(0).toInt() == mapping.id) {
				conflict = true;
				KMessageBox::error(0, i18n("<qt>Unable to add new multi-master replication!<p>The provided ID '%1' conflicts with an existing replication mapping.</qt>").arg(mapping.id), i18n("Invalid Configuration"));
				break;
			}
			if (it.current()->text(1) == mapping.fqdn) {
				conflict = true;
				KMessageBox::error(0, i18n("<qt>Unable to add new multi-master replication!<p>The provided FQDN '%1' conflicts with an existing replication mapping.</qt>").arg(mapping.fqdn), i18n("Invalid Configuration"));
				break;
			}
			++it;
		}
		if (conflict) {
			run = true;
		}
		else {
			run = false;
			new TQListViewItem(m_base->multiMasterReplicationMappings, TQString("%1").arg(mapping.id), mapping.fqdn);
			changed();
		}
	}
}

void LDAPController::btnEditMultiMasterReplicationMapping() {
	// Launch a dialog to edit the mapping
	LDAPMasterReplicationMapping mapping;

	TQListViewItem* lvi = m_base->multiMasterReplicationMappings->selectedItem();
	if (!lvi) {
		return;
	}
	mapping.id = lvi->text(0).toInt();
	mapping.fqdn = lvi->text(1);

	bool run = true;
	MultiMasterReplicationConfigDialog mappingconfigdlg(mapping, m_defaultRealm, this);
	while (run && (mappingconfigdlg.exec() == TQDialog::Accepted)) {
		mapping = mappingconfigdlg.m_replicationProperties;
		// Make sure the provided FQDN and/or UID do not already exist
		bool conflict = false;
		TQPtrList<TQListViewItem> lst;
		TQListViewItemIterator it(m_base->multiMasterReplicationMappings);
		while (it.current()) {
			if (it.current() == lvi) {
				// The selected item will be removed on update, so ignore any conflicts with it...
				++it;
				continue;
			}
			if (it.current()->text(0).toInt() == mapping.id) {
				conflict = true;
				KMessageBox::error(0, i18n("<qt>Unable to add new multi-master replication!<p>The provided ID '%1' conflicts with an existing replication mapping.</qt>").arg(mapping.id), i18n("Invalid Configuration"));
				break;
			}
			if (it.current()->text(1) == mapping.fqdn) {
				conflict = true;
				KMessageBox::error(0, i18n("<qt>Unable to add new multi-master replication!<p>The provided FQDN '%1' conflicts with an existing replication mapping.</qt>").arg(mapping.fqdn), i18n("Invalid Configuration"));
				break;
			}
			++it;
		}
		if (conflict) {
			run = true;
		}
		else {
			run = false;
			if (lvi) {
				delete lvi;
			}
			new TQListViewItem(m_base->multiMasterReplicationMappings, TQString("%1").arg(mapping.id), mapping.fqdn);
			changed();
		}
	}
}

void LDAPController::btnRemoveMultiMasterReplicationMapping() {
	LDAPMasterReplicationMapping mapping;

	TQListViewItem* lvi = m_base->multiMasterReplicationMappings->selectedItem();
	if (!lvi) {
		return;
	}
	mapping.id = lvi->text(0).toInt();
	mapping.fqdn = lvi->text(1);

	if (KMessageBox::warningYesNo(this, i18n("<qt><b>You are about to remove the muli-master replication mapping for '%1'</b><br>This action cannot be undone once committed<p>Are you sure you want to proceed?</qt>").arg(mapping.fqdn), i18n("Confirmation Required")) == KMessageBox::Yes) {
		delete lvi;
		changed();
	}
}

void LDAPController::multiMasterReplicationHighlighted() {
	processLockouts();
}

void LDAPController::modifySelectedMultiMasterReplication() {
	btnEditMultiMasterReplicationMapping();
}

void LDAPController::defaults() {
	//
}

void LDAPController::caCertExpiryChanged() {
	m_certconfig.caExpiryDays = m_base->advancedCaCertExpiry->value();

	emit(changed());
}

void LDAPController::caCrlExpiryChanged() {
	m_certconfig.caCrlExpiryDays = m_base->advancedCaCrlExpiry->value();

	emit(changed());
}

void LDAPController::kerberosCertExpiryChanged() {
	m_certconfig.kerberosExpiryDays = m_base->advancedKerberosCertExpiry->value();

	emit(changed());
}

void LDAPController::ldapCertExpiryChanged() {
	m_certconfig.ldapExpiryDays = m_base->advancedLdapCertExpiry->value();

	emit(changed());
}

void LDAPController::save() {
	TQString prevRole = m_systemconfig->readEntry("LDAPRole", "Workstation");

	m_systemconfig->setGroup(NULL);
	m_systemconfig->writeEntry("EnableLDAP", m_base->systemEnableSupport->isChecked());
	m_systemconfig->writeEntry("HostFQDN", m_fqdn);
	m_systemconfig->writeEntry("LDAPRole", m_base->systemRole->currentText());

	// Write server-specific replication settings
	m_systemconfig->setGroup("Replication");
	m_systemconfig->writeEntry("IgnoreSSLFailures", m_base->ignoreReplicationSSLFailures->isChecked());

	// Write cert config
	m_systemconfig->setGroup("Certificates");
	m_systemconfig->writeEntry("caExpiryDays", m_certconfig.caExpiryDays);
	m_systemconfig->writeEntry("caCrlExpiryDays", m_certconfig.caCrlExpiryDays);
	m_systemconfig->writeEntry("kerberosExpiryDays", m_certconfig.kerberosExpiryDays);
	m_systemconfig->writeEntry("ldapExpiryDays", m_certconfig.ldapExpiryDays);
	m_systemconfig->writeEntry("countryName", m_certconfig.countryName);
	m_systemconfig->writeEntry("stateOrProvinceName", m_certconfig.stateOrProvinceName);
	m_systemconfig->writeEntry("localityName", m_certconfig.localityName);
	m_systemconfig->writeEntry("organizationName", m_certconfig.organizationName);
	m_systemconfig->writeEntry("orgUnitName", m_certconfig.orgUnitName);
	m_systemconfig->writeEntry("commonName", m_certconfig.commonName);
	m_systemconfig->writeEntry("emailAddress", m_certconfig.emailAddress);

	m_systemconfig->setGroup(NULL);

	m_systemconfig->sync();

	TQString errorstring;
	TQString realmname = m_defaultRealm.upper();
	LDAPCredentials* credentials = new LDAPCredentials;
	credentials->username = "";
	credentials->password = "";
	credentials->realm = realmname;
	LDAPManager* ldap_mgr = NULL;

	if (realmname != "") {
		ldap_mgr = new LDAPManager(realmname, "ldapi://", credentials);
	}

	if (ldap_mgr && ldap_mgr->setLdapCertificateStoreAttribute("publicRootCRLIntervalDays", TQString("%1").arg(m_certconfig.caCrlExpiryDays), &errorstring) != 0) {
		KMessageBox::error(this, i18n("<qt><b>Unable to update CRL interval entry in LDAP database</b><p>Details: %1</qt>").arg(errorstring), i18n("LDAP Update Failure"));
	}

	// If role was not changed, update any role-specific advanced settings
	if (prevRole == m_systemconfig->readEntry("LDAPRole", "Workstation")) {
		if (m_base->systemRole->currentItem() == ROLE_PRIMARY_REALM_CONTROLLER) {
			// Write multi-master replication settings
			LDAPMasterReplicationInfo replicationSettings;
			replicationSettings.enabled = m_base->advancedEnableMultiMasterReplication->isChecked();
			replicationSettings.serverIDs.clear();
			TQPtrList<TQListViewItem> lst;
			TQListViewItemIterator it(m_base->multiMasterReplicationMappings);
			while (it.current()) {
				LDAPMasterReplicationMapping mapping;
				mapping.id = it.current()->text(0).toInt();
				mapping.fqdn = it.current()->text(1);
				replicationSettings.serverIDs.append(mapping);
				++it;
			}
			// Use the local password for inter-master authentication
			// All realm controllers in a realm must (obviously) use the same admin/config password!
			m_systemconfig->setGroup("Replication");
			replicationSettings.syncPassword = m_systemconfig->readEntry("Password");
			m_systemconfig->setGroup(NULL);
			// Use the TDE LDAP CA for replication TLS
			replicationSettings.caCertificateFile = KERBEROS_PKI_PEM_FILE;

			replicationSettings.ignore_ssl_failure = m_base->ignoreReplicationSSLFailures->isChecked();

			if (ldap_mgr && ldap_mgr->setLDAPMasterReplicationSettings(replicationSettings, NULL) != 0) {
				// ERROR
			}
		}
	}

	if (ldap_mgr) {
		delete ldap_mgr;
	}

	load();
}

void replacePlaceholdersInFile(TQString infile, TQString outfile, LDAPRealmConfig realmconfig, TQString adminUserName, TQString adminGroupName, TQString machineAdminGroupName, TQString standardUserGroupName, const char * adminPassword, TQString rootUserName, const char * rootPassword, int ldifSchemaNumber=-1, uid_t userid=-1, gid_t groupid=-1, TQString ldapusername=TQString::null, TQString ldapgroupname=TQString::null) {
	SHA1 sha;
	sha.process(rootPassword, strlen(rootPassword));
	TQString rootpw_hash = sha.base64Hash();
	sha.reset();
	sha.process(adminPassword, strlen(rootPassword));
	TQString adminpw_hash = sha.base64Hash();

	// Created needed strings
	TQStringList domainChunks = TQStringList::split(".", realmconfig.name.lower());
	TQString basedcname = "dc=" + domainChunks.join(",dc=");
	TQString simpledcname = domainChunks[0];
	TQString simpledcnamecap = simpledcname.lower();
	simpledcnamecap[0] = simpledcnamecap[0].upper();
	TQDateTime currentDateTime = TQDateTime::currentDateTime();
	TQString timestamp = currentDateTime.toString(TQt::ISODate);
	timestamp.replace("-", "");
	timestamp.replace(":", "");
	timestamp.replace("T", "");
	TQString uuid;
	TQUuid randomUUID = TQUuid::createUuid();
	uuid = randomUUID.toString();
	uuid.replace("{", "");
	uuid.replace("}", "");
	TQString timestamp_us;
	timestamp_us = timestamp_us.sprintf("%06d", currentDateTime.time().msec()*1000);

	TQString kdc_certfile = KERBEROS_PKI_KDC_FILE;
	TQString kdc_keyfile = KERBEROS_PKI_KDCKEY_FILE;
	TQString ldap_certfile = LDAP_CERT_FILE;
	TQString ldap_keyfile = LDAP_CERTKEY_FILE;
	kdc_certfile.replace("@@@KDCSERVER@@@", realmconfig.name.lower());
	kdc_keyfile.replace("@@@KDCSERVER@@@", realmconfig.name.lower());
	ldap_certfile.replace("@@@ADMINSERVER@@@", realmconfig.name.lower());
	ldap_keyfile.replace("@@@ADMINSERVER@@@", realmconfig.name.lower());

	TQFile ifile(infile);
	TQFile ofile(outfile);
	if (ifile.open(IO_ReadOnly)) {
		if (ofile.open(IO_WriteOnly)) {
			TQString line;
			TQTextStream istream(&ifile);
			TQTextStream ostream(&ofile);
			while (!istream.atEnd()) {
				line = istream.readLine();
				line.replace("@@@REALM_DCNAME@@@", basedcname);
				line.replace("@@@REALM_UCNAME@@@", realmconfig.name.upper());
				line.replace("@@@REALM_LCNAME@@@", realmconfig.name.lower());
				line.replace("@@@ADMINSERVER@@@", realmconfig.admin_server);
				line.replace("@@@ADMINPORT@@@", TQString("%1").arg(realmconfig.admin_server_port));
				line.replace("@@@KDCSERVER@@@", realmconfig.kdc);
				line.replace("@@@KDCPORT@@@", TQString("%1").arg(realmconfig.kdc_port));
				line.replace("@@@ROOTUSER@@@", rootUserName);
				line.replace("@@@ROOTPW_SHA@@@", rootpw_hash);
				line.replace("@@@ADMINUSER@@@", adminUserName);
				line.replace("@@@ADMINGROUP@@@", adminGroupName);
				line.replace("@@@LOCALADMINGROUP@@@", machineAdminGroupName);
				line.replace("@@@STANDARDUSERGROUP@@@", standardUserGroupName);
				line.replace("@@@ADMINPW_SHA@@@", adminpw_hash);
				line.replace("@@@PKINIT_REQUIRE_EKU@@@", (realmconfig.pkinit_require_eku)?"yes":"no");
				line.replace("@@@PKINIT_REQUIRE_KRBTGT_OTHERNAME@@@", (realmconfig.pkinit_require_krbtgt_otherName)?"yes":"no");
				line.replace("@@@WIN2K_PKINIT@@@", (realmconfig.win2k_pkinit)?"yes":"no");
				line.replace("@@@WIN2K_PKINIT_REQUIRE_BINDING@@@", (realmconfig.win2k_pkinit_require_binding)?"yes":"no");
				line.replace("@@@REALM_SIMPLE_CP_NAME@@@", simpledcnamecap);
				line.replace("@@@REALM_SIMPLE_LC_NAME@@@", simpledcname.lower());
				line.replace("@@@TIMESTAMP@@@", timestamp);
				line.replace("@@@TIMESTAMP_MICROSECONDS@@@", timestamp_us);
				line.replace("@@@ENTRYUUID@@@", uuid);
				line.replace("@@@LDAP_KEYTAB_FILE@@@", LDAP_KEYTAB_FILE);
				line.replace("@@@LDAP_USER_NAME@@@", ldapusername);
				line.replace("@@@LDAP_GROUP_NAME@@@", ldapgroupname);
				line.replace("@@@TDELIBDIR@@@", TDE_LIBDIR);
				line.replace("@@@HEIMDALACLFILE@@@", HEIMDAL_ACL_FILE);
				line.replace("@@@KRBPKIPEMFILE@@@", KERBEROS_PKI_PEM_FILE);
				line.replace("@@@KRBPKIPEMKEYFILE@@@", KERBEROS_PKI_PEMKEY_FILE);
				line.replace("@@@KRBKDCPEMFILE@@@", kdc_certfile);
				line.replace("@@@KRBKDCPEMKEYFILE@@@", kdc_keyfile);
				line.replace("@@@LDAPPEMFILE@@@", ldap_certfile);
				line.replace("@@@LDAPPEMKEYFILE@@@", ldap_keyfile);
				if (ldifSchemaNumber >= 0) {
					line.replace("@@@LDIFSCHEMANUMBER@@@", TQString("%1").arg(ldifSchemaNumber));
				}
				ostream << line << "\n";
			}
			ofile.close();
	
			// Set permissions
			if ((userid > 0) && (groupid > 0)) {
				chown_safe(outfile.ascii(), userid, groupid);
			}
		}
		else {
			//KMessageBox::error(0, i18n("<qt>Unable to open output schema file %1 for writing</qt>").arg(outfile), i18n("Internal Failure"));
			printf("[INTERNAL FAILURE] Unable to open output schema file %s for writing\n\r", outfile.ascii()); fflush(stdout);
		}
		ifile.close();
	}
	else {
		//KMessageBox::error(0, i18n("<qt>Unable to open template schema file %1</qt>").arg(infile), i18n("Internal Failure"));
		printf("[INTERNAL FAILURE] Unable to open template schema file %s\n\r", infile.ascii()); fflush(stdout);
	}

	// Keep UI responsive
	tqApp->processEvents();
}

int LDAPController::controlKAdminDaemon(sc_command command) {
	if (command == SC_RESTART) {
		// FIXME
		// This assumes Debian!
		return system("/etc/init.d/openbsd-inetd restart");
	}
	return -2;
}

int LDAPController::controlSASLServer(sc_command command) {
	if (command == SC_START) {
		// FIXME
		// This assumes Debian!
		return system("/etc/init.d/saslauthd start");
	}
	if (command == SC_STOP) {
		// FIXME
		// This assumes Debian!
		return system("/etc/init.d/saslauthd stop");
	}
	if (command == SC_RESTART) {
		// FIXME
		// This assumes Debian!
		return system("/etc/init.d/saslauthd restart");
	}
	return -2;
}

int LDAPController::controlHeimdalServer(sc_command command, uid_t userid, gid_t groupid) {
	if (command == SC_START) {
		// FIXME
		// This assumes Debian!
		return system("/etc/init.d/heimdal-kdc start");
	}
	if (command == SC_STOP) {
		// FIXME
		// This assumes Debian!
		return system("/etc/init.d/heimdal-kdc stop");
	}
	if (command == SC_RESTART) {
		// FIXME
		// This assumes Debian!
		return system("/etc/init.d/heimdal-kdc restart");
	}
	if (command == SC_PURGE) {
		controlHeimdalServer(SC_STOP);
		system_safe(TQString("rm -f %1").arg(LDAP_KEYTAB_FILE).local8Bit());
		// FIXME
		// This assumes Debian
		system_safe("rm -f /etc/krb5.keytab");
		system_safe("rm -rf /var/lib/heimdal-kdc/*");
	}
	if (command == SC_SETDBPERMS) {
		if ((userid > 0) && (groupid > 0)) {
			TQString command;
			command = TQString("chgrp %1 " + TQString(LDAP_KEYTAB_FILE)).arg(groupid);
			system_safe(command.ascii());
			chmod(LDAP_KEYTAB_FILE, S_IRUSR|S_IWUSR|S_IRGRP);
		}
	}
	return -2;
}

int LDAPController::controlLDAPServer(sc_command command, uid_t userid, gid_t groupid) {
	if (command == SC_START) {
		// FIXME
		// This assumes Debian!
		return system("/etc/init.d/slapd start");
	}
	if (command == SC_STOP) {
		// FIXME
		// This assumes Debian!
		return system("/etc/init.d/slapd stop");
	}
	if (command == SC_RESTART) {
		// FIXME
		// This assumes Debian!
		return system("/etc/init.d/slapd restart");
	}
	if (command == SC_PURGE) {
		controlLDAPServer(SC_STOP);
		// FIXME
		// This assumes Debian!
		system_safe("rm -rf /var/lib/ldap/*");
		system_safe("rm -rf /etc/ldap/slapd.d/*");
	}
	if (command == SC_SETDBPERMS) {
		if ((userid > 0) && (groupid > 0)) {
			// FIXME
			// This assumes Debian!
			TQString command;
			command = TQString("chown -R %1 /var/lib/ldap/*").arg(userid);
			system_safe(command.ascii());
			command = TQString("chgrp -R %1 /var/lib/ldap/*").arg(groupid);
			system_safe(command.ascii());
			command = TQString("chown -R %1 /etc/ldap/slapd.d/*").arg(userid);
			system_safe(command.ascii());
			command = TQString("chgrp -R %1 /etc/ldap/slapd.d/*").arg(groupid);
			system_safe(command.ascii());
		}
	}
	return -2;
}

int LDAPController::initializeNewKerberosRealm(TQString realmName, TQString *errstr) {
	TQCString command = "kadmin";
	QCStringList args;
	args << TQCString("-l");

	TQString prompt;
	PtyProcess kadminProc;
	kadminProc.enableLocalEcho(false);
	kadminProc.exec(command, args);
	prompt = LDAPManager::readFullLineFromPtyProcess(&kadminProc);
	prompt = prompt.stripWhiteSpace();
	if (prompt == "kadmin>") {
		command = TQCString("init ")+realmName.local8Bit();
		kadminProc.enableLocalEcho(false);
		kadminProc.writeLine(command, true);
		do { // Discard our own input
			prompt = LDAPManager::readFullLineFromPtyProcess(&kadminProc);
			printf("(kadmin) '%s'\n\r", prompt.ascii());
		} while (prompt == TQString(command));
		prompt = prompt.stripWhiteSpace();
		if (prompt.contains("authentication failed")) {
			if (errstr) *errstr = LDAPManager::detailedKAdminErrorMessage(prompt);
			kadminProc.enableLocalEcho(false);
			kadminProc.writeLine("quit", true);
			return 1;
		}
		else if (prompt.startsWith("Realm max")) {
			command = "unlimited";
			kadminProc.enableLocalEcho(false);
			kadminProc.writeLine(command, true);
			do { // Discard our own input
				prompt = LDAPManager::readFullLineFromPtyProcess(&kadminProc);
				printf("(kadmin) '%s'\n\r", prompt.ascii());
			} while (prompt == TQString(command));
			prompt = prompt.stripWhiteSpace();
			if (prompt.startsWith("Realm max")) {
				command = "unlimited";
				kadminProc.enableLocalEcho(false);
				kadminProc.writeLine(command, true);
				do { // Discard our own input
					prompt = LDAPManager::readFullLineFromPtyProcess(&kadminProc);
					printf("(kadmin) '%s'\n\r", prompt.ascii());
				} while (prompt == TQString(command));
				prompt = prompt.stripWhiteSpace();
			}
			if (prompt != "kadmin>") {
				if (errstr) *errstr = LDAPManager::detailedKAdminErrorMessage(prompt);
				kadminProc.enableLocalEcho(false);
				kadminProc.writeLine("quit", true);
				return 1;
			}

			// Success!
			kadminProc.enableLocalEcho(false);
			kadminProc.writeLine("quit", true);
			return 0;
		}

		// Failure
		if (errstr) *errstr = LDAPManager::detailedKAdminErrorMessage(prompt);
		kadminProc.enableLocalEcho(false);
		kadminProc.writeLine("quit", true);
		return 1;
	}

	if (errstr) *errstr = "Internal error.  Verify that kadmin exists and can be executed.";
	return 1;	// Failure
}

int LDAPController::addHostEntryToKerberosRealm(TQString kerberosHost, TQString *errstr) {
	TQCString command = "kadmin";
	QCStringList args;
	args << TQCString("-l");

	TQString hoststring = "host/"+kerberosHost;

	TQString prompt;
	PtyProcess kadminProc;
	kadminProc.exec(command, args);
	prompt = LDAPManager::readFullLineFromPtyProcess(&kadminProc);
	prompt = prompt.stripWhiteSpace();
	if (prompt == "kadmin>") {
		command = TQCString("ext ")+hoststring.local8Bit();
		kadminProc.enableLocalEcho(false);
		kadminProc.writeLine(command, true);
		do { // Discard our own input
			prompt = LDAPManager::readFullLineFromPtyProcess(&kadminProc);
			printf("(kadmin) '%s'\n\r", prompt.ascii());
		} while (prompt == TQString(command));
		prompt = prompt.stripWhiteSpace();
		if (prompt.contains("authentication failed")) {
			if (errstr) *errstr = LDAPManager::detailedKAdminErrorMessage(prompt);
			kadminProc.enableLocalEcho(false);
			kadminProc.writeLine("quit", true);
			return 1;
		}
		else if (prompt.endsWith("Principal does not exist")) {
			prompt = LDAPManager::readFullLineFromPtyProcess(&kadminProc);
			prompt = prompt.stripWhiteSpace();
			if (prompt != "kadmin>") {
				if (errstr) *errstr = LDAPManager::detailedKAdminErrorMessage(prompt);
				kadminProc.enableLocalEcho(false);
				kadminProc.writeLine("quit", true);
				return 1;
			}
			command = TQCString("ank --random-key ")+hoststring.local8Bit();
			kadminProc.enableLocalEcho(false);
			kadminProc.writeLine(command, true);
			do { // Discard our own input
				prompt = LDAPManager::readFullLineFromPtyProcess(&kadminProc);
				printf("(kadmin) '%s'\n\r", prompt.ascii());
			} while (prompt == TQString(command));
			prompt = prompt.stripWhiteSpace();
			// Use all defaults
			while (prompt != "kadmin>") {
				if (prompt.contains("authentication failed")) {
					if (errstr) *errstr = LDAPManager::detailedKAdminErrorMessage(prompt);
					kadminProc.enableLocalEcho(false);
					kadminProc.writeLine("quit", true);
					return 1;
				}
				else {
					// Extract whatever default is in the [brackets] and feed it back to kadmin
					TQString defaultParam;
					int leftbracket = prompt.find("[");
					int rightbracket = prompt.find("]");
					if ((leftbracket >= 0) && (rightbracket >= 0)) {
						leftbracket++;
						defaultParam = prompt.mid(leftbracket, rightbracket-leftbracket);
					}
					command = defaultParam.local8Bit();
					kadminProc.enableLocalEcho(false);
					kadminProc.writeLine(command, true);
					do { // Discard our own input
						prompt = LDAPManager::readFullLineFromPtyProcess(&kadminProc);
						printf("(kadmin) '%s'\n\r", prompt.ascii());
					} while (prompt == TQString(command));
					prompt = prompt.stripWhiteSpace();
				}
			}
			command = TQCString("ext ")+hoststring.local8Bit();
			kadminProc.enableLocalEcho(false);
			kadminProc.writeLine(command, true);
			do { // Discard our own input
				prompt = LDAPManager::readFullLineFromPtyProcess(&kadminProc);
				printf("(kadmin) '%s'\n\r", prompt.ascii());
			} while (prompt == TQString(command));
			prompt = prompt.stripWhiteSpace();
			if (prompt != "kadmin>") {
				if (errstr) *errstr = LDAPManager::detailedKAdminErrorMessage(prompt);
				kadminProc.enableLocalEcho(false);
				kadminProc.writeLine("quit", true);
				return 1;
			}

			// Success!
			kadminProc.enableLocalEcho(false);
			kadminProc.writeLine("quit", true);
			return 0;
		}
		else if (prompt == "kadmin>") {
			// Success!
			kadminProc.enableLocalEcho(false);
			kadminProc.writeLine("quit", true);
			return 0;
		}

		// Failure
		if (errstr) *errstr = LDAPManager::detailedKAdminErrorMessage(prompt);
		kadminProc.enableLocalEcho(false);
		kadminProc.writeLine("quit", true);
		return 1;
	}

	if (errstr) *errstr = "Internal error.  Verify that kadmin exists and can be executed.";
	return 1;	// Failure
}

int LDAPController::addLDAPEntryToKerberosRealm(TQString ldapProcessOwnerName, TQString ldapHost, TQString *errstr) {
	TQCString command = "kadmin";
	QCStringList args;
	args << TQCString("-l");

	TQString hoststring = ldapProcessOwnerName+"/"+ldapHost;

	TQString prompt;
	PtyProcess kadminProc;
	kadminProc.exec(command, args);
	prompt = LDAPManager::readFullLineFromPtyProcess(&kadminProc);
	prompt = prompt.stripWhiteSpace();
	if (prompt == "kadmin>") {
		command = TQCString("ext --keytab=")+LDAP_KEYTAB_FILE+" "+hoststring.local8Bit();
		kadminProc.enableLocalEcho(false);
		kadminProc.writeLine(command, true);
		do { // Discard our own input
			prompt = LDAPManager::readFullLineFromPtyProcess(&kadminProc);
			printf("(kadmin) '%s'\n\r", prompt.ascii());
		} while (prompt.startsWith("ext --keytab="));
		prompt = prompt.stripWhiteSpace();
		if (prompt.contains("authentication failed")) {
			if (errstr) *errstr = LDAPManager::detailedKAdminErrorMessage(prompt);
			kadminProc.enableLocalEcho(false);
			kadminProc.writeLine("quit", true);
			return 1;
		}
		else if (prompt.endsWith("Principal does not exist")) {
			prompt = LDAPManager::readFullLineFromPtyProcess(&kadminProc);
			prompt = prompt.stripWhiteSpace();
			if (prompt != "kadmin>") {
				if (errstr) *errstr = LDAPManager::detailedKAdminErrorMessage(prompt);
				kadminProc.enableLocalEcho(false);
				kadminProc.writeLine("quit", true);
				return 1;
			}
			command = TQCString("ank --random-key ")+hoststring.local8Bit();
			kadminProc.enableLocalEcho(false);
			kadminProc.writeLine(command, true);
			do { // Discard our own input
				prompt = LDAPManager::readFullLineFromPtyProcess(&kadminProc);
				printf("(kadmin) '%s'\n\r", prompt.ascii());
			} while (prompt == TQString(command));
			prompt = prompt.stripWhiteSpace();
			// Use all defaults
			while (prompt != "kadmin>") {
				if (prompt.contains("authentication failed")) {
					if (errstr) *errstr = LDAPManager::detailedKAdminErrorMessage(prompt);
					kadminProc.enableLocalEcho(false);
					kadminProc.writeLine("quit", true);
					return 1;
				}
				else {
					// Extract whatever default is in the [brackets] and feed it back to kadmin
					TQString defaultParam;
					int leftbracket = prompt.find("[");
					int rightbracket = prompt.find("]");
					if ((leftbracket >= 0) && (rightbracket >= 0)) {
						leftbracket++;
						defaultParam = prompt.mid(leftbracket, rightbracket-leftbracket);
					}
					command = defaultParam.local8Bit();
					kadminProc.enableLocalEcho(false);
					kadminProc.writeLine(command, true);
					do { // Discard our own input
						prompt = LDAPManager::readFullLineFromPtyProcess(&kadminProc);
						printf("(kadmin) '%s'\n\r", prompt.ascii());
					} while (prompt == TQString(command));
					prompt = prompt.stripWhiteSpace();
				}
			}
			command = TQCString("ext --keytab=")+LDAP_KEYTAB_FILE+" "+hoststring.local8Bit();
			kadminProc.enableLocalEcho(false);
			kadminProc.writeLine(command, true);
			do { // Discard our own input
				prompt = LDAPManager::readFullLineFromPtyProcess(&kadminProc);
				printf("(kadmin) '%s'\n\r", prompt.ascii());
			} while (prompt.startsWith("ext --keytab="));
			prompt = prompt.stripWhiteSpace();
			if (prompt != "kadmin>") {
				if (errstr) *errstr = LDAPManager::detailedKAdminErrorMessage(prompt);
				kadminProc.enableLocalEcho(false);
				kadminProc.writeLine("quit", true);
				return 1;
			}

			// Success!
			kadminProc.enableLocalEcho(false);
			kadminProc.writeLine("quit", true);
			return 0;
		}
		else if (prompt == "kadmin>") {
			// Success!
			kadminProc.enableLocalEcho(false);
			kadminProc.writeLine("quit", true);
			return 0;
		}

		// Failure
		if (errstr) *errstr = LDAPManager::detailedKAdminErrorMessage(prompt);
		kadminProc.enableLocalEcho(false);
		kadminProc.writeLine("quit", true);
		return 1;
	}

	if (errstr) *errstr = "Internal error.  Verify that kadmin exists and can be executed.";
	return 1;	// Failure
}

int LDAPController::setKerberosPasswordForUser(LDAPCredentials user, TQString *errstr) {
	if (user.password == "") {
		return 0;
	}

	TQCString command = "kadmin";
	QCStringList args;
	args << TQCString("-l") << TQCString("-r") << user.realm.upper().local8Bit();

	TQString prompt;
	PtyProcess kadminProc;
	kadminProc.exec(command, args);
	prompt = LDAPManager::readFullLineFromPtyProcess(&kadminProc);
	prompt = prompt.stripWhiteSpace();
	if (prompt == "kadmin>") {
		command = TQCString("passwd ")+user.username.local8Bit();
		kadminProc.enableLocalEcho(false);
		kadminProc.writeLine(command, true);
		do { // Discard our own input
			prompt = LDAPManager::readFullLineFromPtyProcess(&kadminProc);
			printf("(kadmin) '%s'\n\r", prompt.ascii());
		} while (prompt == TQString(command));
		prompt = prompt.stripWhiteSpace();
		if (prompt.contains("authentication failed")) {
			if (errstr) *errstr = LDAPManager::detailedKAdminErrorMessage(prompt);
			kadminProc.enableLocalEcho(false);
			kadminProc.writeLine("quit", true);
			return 1;
		}
		else if ((prompt.endsWith(" Password:")) && (prompt.startsWith(TQString(user.username + "@")))) {
			kadminProc.enableLocalEcho(false);
			kadminProc.writeLine(user.password.utf8(), true);
			do { // Discard our own input
				prompt = LDAPManager::readFullLineFromPtyProcess(&kadminProc);
				printf("(kadmin) '%s'\n\r", prompt.ascii());
			} while (prompt == "");
			prompt = prompt.stripWhiteSpace();
			if ((prompt.endsWith(" Password:")) && (prompt.startsWith("Verify"))) {
				kadminProc.enableLocalEcho(false);
				kadminProc.writeLine(user.password.utf8(), true);
				do { // Discard our own input
					prompt = LDAPManager::readFullLineFromPtyProcess(&kadminProc);
					printf("(kadmin) '%s'\n\r", prompt.ascii());
				} while (prompt == "");
				prompt = prompt.stripWhiteSpace();
			}
			if (prompt != "kadmin>") {
				if (errstr) *errstr = LDAPManager::detailedKAdminErrorMessage(prompt);
				kadminProc.enableLocalEcho(false);
				kadminProc.writeLine("quit", true);
				return 1;
			}

			// Success!
			kadminProc.enableLocalEcho(false);
			kadminProc.writeLine("quit", true);
			return 0;
		}
		else if (prompt == "kadmin>") {
			// Success!
			kadminProc.enableLocalEcho(false);
			kadminProc.writeLine("quit", true);
			return 0;
		}

		// Failure
		if (errstr) *errstr = LDAPManager::detailedKAdminErrorMessage(prompt);
		kadminProc.enableLocalEcho(false);
		kadminProc.writeLine("quit", true);
		return 1;
	}

	if (errstr) *errstr = "Internal error.  Verify that kadmin exists and can be executed.";
	return 1;	// Failure
}

int LDAPController::createRealmCertificates(LDAPCertConfig certinfo, LDAPRealmConfig realmconfig, uid_t ldap_uid, gid_t ldap_gid) {
	// Certificate authority certificate
	TQString command;
	command = TQString("openssl genrsa -out %1 %2").arg(KERBEROS_PKI_PEMKEY_FILE).arg(KEY_STRENGTH);
	system_safe(command.local8Bit());
	chmod(KERBEROS_PKI_PEMKEY_FILE, S_IRUSR|S_IWUSR);
	chown_safe(KERBEROS_PKI_PEMKEY_FILE, 0, 0);

	LDAPManager::generatePublicKerberosCACertificate(certinfo, m_realmconfig[m_defaultRealm]);

	// KDC certificate
	TQString kdc_certfile = KERBEROS_PKI_KDC_FILE;
	TQString kdc_keyfile = KERBEROS_PKI_KDCKEY_FILE;
	TQString kdc_reqfile = KERBEROS_PKI_KDCREQ_FILE;
	kdc_certfile.replace("@@@KDCSERVER@@@", realmconfig.name.lower());
	kdc_keyfile.replace("@@@KDCSERVER@@@", realmconfig.name.lower());
	kdc_reqfile.replace("@@@KDCSERVER@@@", realmconfig.name.lower());
	command = TQString("openssl genrsa -out %1 %2").arg(kdc_keyfile).arg(KEY_STRENGTH);
	system_safe(command.local8Bit());
	chmod(kdc_keyfile.ascii(), S_IRUSR|S_IWUSR);
	chown_safe(kdc_keyfile.ascii(), 0, 0);

	LDAPManager::generatePublicKerberosCertificate(certinfo, realmconfig);

	// LDAP certificate
	TQString ldap_certfile = LDAP_CERT_FILE;
	TQString ldap_keyfile = LDAP_CERTKEY_FILE;
	TQString ldap_reqfile = LDAP_CERTREQ_FILE;
	ldap_certfile.replace("@@@ADMINSERVER@@@", realmconfig.name.lower());
	ldap_keyfile.replace("@@@ADMINSERVER@@@", realmconfig.name.lower());
	ldap_reqfile.replace("@@@ADMINSERVER@@@", realmconfig.name.lower());
	command = TQString("openssl genrsa -out %1 %2").arg(ldap_keyfile).arg(KEY_STRENGTH);
	system_safe(command.local8Bit());
	chmod(ldap_keyfile.ascii(), S_IRUSR|S_IWUSR);
	chown_safe(ldap_keyfile.ascii(), ldap_uid, ldap_gid);

	LDAPManager::generatePublicLDAPCertificate(certinfo, realmconfig, ldap_uid, ldap_gid);

	return 0;
}

int LDAPController::uploadKerberosCAFileToLDAP(LDAPManager* ldap_mgr, TQString* errstr) {
	// Upload the contents of KERBEROS_PKI_PEM_FILE to the LDAP server
	TQFile cafile(KERBEROS_PKI_PEM_FILE);
	if (cafile.open(IO_ReadOnly)) {
		TQByteArray cafiledata = cafile.readAll();
		if (ldap_mgr->writeCertificateFileIntoDirectory(cafiledata, "publicRootCertificate", errstr) != 0) {
			return -1;
		}
		return 0;
	}
	return -1;
}

int LDAPController::uploadKerberosCAKeyFileToLDAP(LDAPManager* ldap_mgr, TQString* errstr) {
	// Upload the contents of KERBEROS_PKI_PEMKEY_FILE to the LDAP server
	TQFile cafile(KERBEROS_PKI_PEMKEY_FILE);
	if (cafile.open(IO_ReadOnly)) {
		TQByteArray cafiledata = cafile.readAll();
		if (ldap_mgr->writeCertificateFileIntoDirectory(cafiledata, "privateRootCertificateKey", errstr) != 0) {
			return -1;
		}
		return 0;
	}
	return -1;
}

// #define STRICT_SETUP 1

int LDAPController::createNewSecondaryController(TQWidget* dialogparent, LDAPRealmConfig realmconfig, TQString adminUserName, const char * adminPassword, TQString adminRealm, TQString *errstr) {
	// Fortunately this is somewhat simpler than createNewLDAPRealm(...)!
	ProcessingDialog pdialog(dialogparent);
	pdialog.setStatusMessage(i18n("Loading data for secondary controller..."));
	pdialog.raise();
	pdialog.setActiveWindow();
	tqApp->processEvents();

	// TODO FIXME
	// Threading would be a good idea here, to keep the GUI responsive while the backend code works

	// Reset improperly uninitialized variables
	realmconfig.bonded = true;

	// Find the templates
	TQString templateDir = locate("data", "kcmldapcontroller/skel/heimdal/heimdal.defaults");
	templateDir.replace("heimdal/heimdal.defaults", "");
	if (templateDir == "") {
		if (errstr) *errstr = i18n("Unable to locate required template files");
		pdialog.closeDialog();
		return -1;
	}

	KTempDir configTempDir;
	configTempDir.setAutoDelete(true);
	TQString destDir = "/etc/";

	pdialog.setStatusMessage(i18n("Stopping servers..."));

	// Stop SASL
	if (controlSASLServer(SC_STOP) != 0) {
#ifdef STRICT_SETUP
		if (errstr) *errstr = i18n("Unable to stop SASL server");
		pdialog.closeDialog();
		return -1;
#endif // STRICT_SETUP
	}
	// Stop Heimdal
	if (controlHeimdalServer(SC_STOP) != 0) {
#ifdef STRICT_SETUP
		if (errstr) *errstr = i18n("Unable to stop Kerberos server");
		pdialog.closeDialog();
		return -1;
#endif // STRICT_SETUP
	}
	// Stop slapd
	if (controlLDAPServer(SC_STOP) != 0) {
#ifdef STRICT_SETUP
		if (errstr) *errstr = i18n("Unable to stop LDAP server");
		pdialog.closeDialog();
		return -1;
#endif // STRICT_SETUP
	}

	// TODO FIXME
	// 1.) Fetch CA private/public certificates from master LDAP server, save them, and also use the public certificate to fill a certificate information structure
	// 2.) Bond machine to Kerberos
	// 3.) Set up LDAP replication
	// 4.) Point local Kerberos and SASL instances to this LDAP server

	return -1;
}

int LDAPController::createNewLDAPRealm(TQWidget* dialogparent, LDAPRealmConfig realmconfig, TQString adminUserName, TQString adminGroupName, TQString machineAdminGroupName, TQString standardUserGroupName, const char * adminPassword, TQString rootUserName, const char * rootPassword, TQString adminRealm, LDAPCertConfig certinfo, TQString *errstr) {
	Q_UNUSED(adminRealm)

	int ldifSchemaNumber;
	int ldifConfigSchemaNumber;

	ProcessingDialog pdialog(dialogparent);
	pdialog.setStatusMessage(i18n("Loading data for realm deployment..."));
	pdialog.raise();
	pdialog.setActiveWindow();
	tqApp->processEvents();

	// TODO FIXME
	// Threading would be a good idea here, to keep the GUI responsive while the backend code works

	// Reset improperly uninitialized variables
	realmconfig.bonded = true;

	// Find the templates
	TQString templateDir = locate("data", "kcmldapcontroller/skel/heimdal/heimdal.defaults");
	templateDir.replace("heimdal/heimdal.defaults", "");
	if (templateDir == "") {
		if (errstr) *errstr = i18n("Unable to locate required template files");
		pdialog.closeDialog();
		return -1;
	}

	KTempDir configTempDir;
	configTempDir.setAutoDelete(true);
	TQString destDir = "/etc/";

	pdialog.setStatusMessage(i18n("Stopping servers..."));

	// Stop SASL
	if (controlSASLServer(SC_STOP) != 0) {
#ifdef STRICT_SETUP
		if (errstr) *errstr = i18n("Unable to stop SASL server");
		pdialog.closeDialog();
		return -1;
#endif // STRICT_SETUP
	}
	// Stop Heimdal
	if (controlHeimdalServer(SC_STOP) != 0) {
#ifdef STRICT_SETUP
		if (errstr) *errstr = i18n("Unable to stop Kerberos server");
		pdialog.closeDialog();
		return -1;
#endif // STRICT_SETUP
	}
	// Stop slapd
	if (controlLDAPServer(SC_STOP) != 0) {
#ifdef STRICT_SETUP
		if (errstr) *errstr = i18n("Unable to stop LDAP server");
		pdialog.closeDialog();
		return -1;
#endif // STRICT_SETUP
	}

	pdialog.setStatusMessage(i18n("Purging existing LDAP database..."));
	tqApp->processEvents();
	controlHeimdalServer(SC_PURGE);
	controlLDAPServer(SC_PURGE);

	pdialog.setStatusMessage(i18n("Installing new LDAP schema..."));
	tqApp->processEvents();

	mkdir(TQString(destDir + "heimdal-kdc").ascii(), S_IRUSR|S_IWUSR|S_IXUSR);
	mkdir(TQString(destDir + "ldap").ascii(), S_IRUSR|S_IWUSR|S_IXUSR);
	mkdir(TQString(destDir + "ldap/slapd.d").ascii(), S_IRUSR|S_IWUSR|S_IXUSR);
	mkdir(TQString(destDir + "ldap/slapd.d/cn=config").ascii(), S_IRUSR|S_IWUSR|S_IXUSR);
	mkdir(TQString(destDir + "ldap/slapd.d/cn=config/cn=schema").ascii(), S_IRUSR|S_IWUSR|S_IXUSR);

	mkdir(TDE_CERTIFICATE_DIR, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
	mkdir(KERBEROS_PKI_ANCHORDIR, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
	mkdir(KERBEROS_PKI_PRIVATEDIR, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
	mkdir(KERBEROS_PKI_PUBLICDIR, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);

	// Heimdal
	replacePlaceholdersInFile(templateDir + "heimdal/heimdal.defaults", HEIMDAL_DEFAULT_FILE, realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword);
	replacePlaceholdersInFile(templateDir + "heimdal/kadmind.acl", HEIMDAL_ACL_FILE, realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword);
	replacePlaceholdersInFile(templateDir + "heimdal/kdc.conf", destDir + "heimdal-kdc/kdc.conf", realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword);
	replacePlaceholdersInFile(templateDir + "heimdal/krb5.conf", destDir + "krb5.conf", realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword);

	// OpenLDAP
	replacePlaceholdersInFile(templateDir + "openldap/skel.ldif", configTempDir.name() + "skel.ldif", realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword);
// 	replacePlaceholdersInFile(templateDir + "openldap/ldap/slapd.conf", destDir + "ldap/slapd.conf", realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword);
	replacePlaceholdersInFile(templateDir + "openldap/ldap/slapd.defaults", LDAP_DEFAULT_FILE, realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword, -1, -1, -1, m_ldapUserName, m_ldapGroupName);

	// SASL
	replacePlaceholdersInFile(templateDir + "sasl/saslauthd.defaults", SASL_DEFAULT_FILE, realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword);
	replacePlaceholdersInFile(templateDir + "sasl/slapd.conf", SASL_CONTROL_FILE, realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword);

	// OpenSSL
	LDAPManager::writeOpenSSLConfigurationFile(realmconfig);

	// FIXME
	// This assumes Debian!
	// Grant LDAP access to SASL mux pipe
	system_safe("dpkg-statoverride --remove --quiet /var/run/saslauthd");
	system_safe(TQString("dpkg-statoverride --add root %1 710 /var/run/saslauthd").arg(m_ldapGroupName).ascii());

	// FIXME
	// This assumes Debian!
	system_safe("ln -s /etc/heimdal-kdc/kadmind.acl /var/lib/heimdal-kdc/kadmind.acl");
	system_safe("ln -s /etc/heimdal-kdc/kdc.conf /var/lib/heimdal-kdc/kdc.conf");

	uid_t slapd_uid = 0;
	gid_t slapd_gid = 0;

	// Get LDAP user uid/gid
	struct passwd *pwd;
	pwd = getpwnam(m_ldapUserName.local8Bit());
	slapd_uid = pwd->pw_uid;
	slapd_gid = pwd->pw_gid;

	// SECURITY
	// Make sure that the ldapi:/// socket in /var/run/slapd/ldapi is NOT world readable/writable (technically the permissions are for the directory containing the ldapi socket)
	// This would mean that anyone with access to the server running LDAP can dump the KRB5 keys!
	// FIXME
	// Can we do anything about this now?

	// Base database configuration
	replacePlaceholdersInFile(templateDir + "openldap/ldif/config.ldif", destDir + "ldap/slapd.d/" + TQString("cn=config.ldif"), realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword, -1, slapd_uid, slapd_gid);
	replacePlaceholdersInFile(templateDir + "openldap/ldif/schema.ldif", destDir + "ldap/slapd.d/cn=config/" + TQString("cn=schema.ldif"), realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword, -1, slapd_uid, slapd_gid);
	ldifSchemaNumber = 0;
	replacePlaceholdersInFile(templateDir + "openldap/ldif/olcConfig.ldif", destDir + "ldap/slapd.d/cn=config/" + TQString("olcDatabase={%1}config.ldif").arg(ldifSchemaNumber), realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword, ldifSchemaNumber, slapd_uid, slapd_gid);
	replacePlaceholdersInFile(templateDir + "openldap/ldif/moduleConfig.ldif", destDir + "ldap/slapd.d/cn=config/" + TQString("cn=module{%1}.ldif").arg(ldifSchemaNumber), realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword, ldifSchemaNumber, slapd_uid, slapd_gid);
	ldifSchemaNumber = 1;
	replacePlaceholdersInFile(templateDir + "openldap/ldif/olcDatabase.ldif", destDir + "ldap/slapd.d/cn=config/" + TQString("olcDatabase={%1}mdb.ldif").arg(ldifSchemaNumber), realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword, ldifSchemaNumber, slapd_uid, slapd_gid);
	ldifConfigSchemaNumber = 0;
	mkdir(TQString(destDir + "ldap/slapd.d/cn=config/olcDatabase={%1}mdb").arg(ldifSchemaNumber).ascii(), S_IRUSR|S_IWUSR|S_IXUSR);
	replacePlaceholdersInFile(templateDir + "openldap/ldif/olcDatabaseConfigMemberOf.ldif", destDir + "ldap/slapd.d/cn=config/olcDatabase={%1}mdb/" + TQString("olcOverlay={%2}memberof.ldif").arg(ldifSchemaNumber).arg(ldifConfigSchemaNumber), realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword, ldifConfigSchemaNumber, slapd_uid, slapd_gid);

	// Schema files
	ldifSchemaNumber = 0;
	replacePlaceholdersInFile(templateDir + "openldap/ldif/core.ldif", destDir + "ldap/slapd.d/cn=config/cn=schema/" + TQString("cn={%1}core.ldif").arg(ldifSchemaNumber), realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword, ldifSchemaNumber, slapd_uid, slapd_gid);
	ldifSchemaNumber = 1;
	replacePlaceholdersInFile(templateDir + "openldap/ldif/cosine.ldif", destDir + "ldap/slapd.d/cn=config/cn=schema/" + TQString("cn={%1}cosine.ldif").arg(ldifSchemaNumber), realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword, ldifSchemaNumber, slapd_uid, slapd_gid);
	ldifSchemaNumber = 2;
	replacePlaceholdersInFile(templateDir + "openldap/ldif/inetorgperson.ldif", destDir + "ldap/slapd.d/cn=config/cn=schema/" + TQString("cn={%1}inetorgperson.ldif").arg(ldifSchemaNumber), realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword, ldifSchemaNumber, slapd_uid, slapd_gid);
	ldifSchemaNumber = 3;
	replacePlaceholdersInFile(templateDir + "openldap/ldif/rfc2307bis.ldif", destDir + "ldap/slapd.d/cn=config/cn=schema/" + TQString("cn={%1}rfc2307bis.ldif").arg(ldifSchemaNumber), realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword, ldifSchemaNumber, slapd_uid, slapd_gid);
	ldifSchemaNumber = 4;
	replacePlaceholdersInFile(templateDir + "openldap/ldif/rfc2739.ldif", destDir + "ldap/slapd.d/cn=config/cn=schema/" + TQString("cn={%1}rfc2739.ldif").arg(ldifSchemaNumber), realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword, ldifSchemaNumber, slapd_uid, slapd_gid);
	ldifSchemaNumber = 5;
	replacePlaceholdersInFile(templateDir + "openldap/ldif/ems-core.ldif", destDir + "ldap/slapd.d/cn=config/cn=schema/" + TQString("cn={%1}ems-core.ldif").arg(ldifSchemaNumber), realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword, ldifSchemaNumber, slapd_uid, slapd_gid);
	ldifSchemaNumber = 6;
	replacePlaceholdersInFile(templateDir + "openldap/ldif/hdb.ldif", destDir + "ldap/slapd.d/cn=config/cn=schema/" + TQString("cn={%1}hdb.ldif").arg(ldifSchemaNumber), realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword, ldifSchemaNumber, slapd_uid, slapd_gid);
	ldifSchemaNumber = 7;
	replacePlaceholdersInFile(templateDir + "openldap/ldif/tde-core.ldif", destDir + "ldap/slapd.d/cn=config/cn=schema/" + TQString("cn={%1}tde-core.ldif").arg(ldifSchemaNumber), realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword, ldifSchemaNumber, slapd_uid, slapd_gid);
// 	ldifSchemaNumber = 8;
// 	replacePlaceholdersInFile(templateDir + "openldap/ldif/samba.ldif", destDir + "ldap/slapd.d/cn=config/cn=schema/" + TQString("cn={%1}samba.ldif").arg(ldifSchemaNumber), realmconfig, adminUserName, adminGroupName, machineAdminGroupName, standardUserGroupName, adminPassword, rootUserName, rootPassword, ldifSchemaNumber, slapd_uid, slapd_gid);

	// Set permissions
	chmod(TQString(HEIMDAL_DEFAULT_FILE).ascii(), S_IRUSR|S_IWUSR|S_IRGRP);
	chmod(TQString(HEIMDAL_ACL_FILE).ascii(), S_IRUSR|S_IWUSR|S_IRGRP);
	chown_safe(TQString(HEIMDAL_ACL_FILE).ascii(), slapd_uid, 0);
	chmod(TQString(destDir + "heimdal-kdc/kdc.conf").ascii(), S_IRUSR|S_IWUSR|S_IRGRP);
	chmod(TQString(destDir + "krb5.conf").ascii(), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);

	chmod(TQString(configTempDir.name() + "skel.ldif").ascii(), S_IRUSR|S_IWUSR);
// 	chmod(TQString(destDir + "ldap/slapd.conf").ascii(), S_IRUSR|S_IWUSR);
	chmod(TQString(LDAP_DEFAULT_FILE).ascii(), S_IRUSR|S_IWUSR|S_IRGRP);

	chmod(TQString(SASL_DEFAULT_FILE).ascii(), S_IRUSR|S_IWUSR|S_IRGRP);
	chmod(TQString(SASL_CONTROL_FILE).ascii(), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);

	chmod(TQString(OPENSSL_EXTENSIONS_FILE).ascii(), S_IRUSR|S_IWUSR);

	pdialog.setStatusMessage(i18n("Installing realm certificates..."));
	tqApp->processEvents();

	if (certinfo.generate_certs) {
		// Generate certificates
		if (createRealmCertificates(certinfo, realmconfig, slapd_uid, slapd_gid) != 0) {
			if (errstr) *errstr = i18n("Unable to install realm certificates");
			pdialog.closeDialog();
			return -1;
		}
		m_certconfig = certinfo;
	}
	else {
		// Copy certificates
		TQString kdc_certfile = KERBEROS_PKI_KDC_FILE;
		TQString kdc_keyfile = KERBEROS_PKI_KDCKEY_FILE;
		kdc_certfile.replace("@@@KDCSERVER@@@", realmconfig.name.lower());
		kdc_keyfile.replace("@@@KDCSERVER@@@", realmconfig.name.lower());
		TQString ldap_certfile = LDAP_CERT_FILE;
		TQString ldap_keyfile = LDAP_CERTKEY_FILE;
		ldap_certfile.replace("@@@ADMINSERVER@@@", realmconfig.name.lower());
		ldap_keyfile.replace("@@@ADMINSERVER@@@", realmconfig.name.lower());

		// Copy files
		// FIXME
		// There has GOT to be a better way to do this than system()!!!
		TQString command;
		command = TQString("cp %1 %2").arg(certinfo.provided_kerberos_pem).arg(KERBEROS_PKI_PEMKEY_FILE);
		system_safe(command.local8Bit());
		command = TQString("cp %1 %2").arg(certinfo.provided_kerberos_pemkey).arg(KERBEROS_PKI_PEM_FILE);
		system_safe(command.local8Bit());
		command = TQString("cp %1 %2").arg(certinfo.provided_kerberos_crt).arg(kdc_certfile);
		system_safe(command.local8Bit());
		command = TQString("cp %1 %2").arg(certinfo.provided_kerberos_key).arg(kdc_keyfile);
		system_safe(command.local8Bit());
		command = TQString("cp %1 %2").arg(certinfo.provided_ldap_crt).arg(ldap_certfile);
		system_safe(command.local8Bit());
		command = TQString("cp %1 %2").arg(certinfo.provided_ldap_key).arg(ldap_keyfile);
		system_safe(command.local8Bit());

		// Set permissions
		chmod(KERBEROS_PKI_PEMKEY_FILE, S_IRUSR|S_IWUSR);
		chown_safe(KERBEROS_PKI_PEMKEY_FILE, 0, 0);
		chmod(KERBEROS_PKI_PEM_FILE, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
		chown_safe(KERBEROS_PKI_PEM_FILE, 0, 0);
		chmod(kdc_keyfile.ascii(), S_IRUSR|S_IWUSR);
		chown_safe(kdc_keyfile.ascii(), 0, 0);
		chmod(kdc_certfile.ascii(), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
		chown_safe(kdc_certfile.ascii(), 0, 0);
		chmod(ldap_keyfile.ascii(), S_IRUSR|S_IWUSR);
		chown_safe(ldap_keyfile.ascii(), slapd_uid, slapd_gid);
		chmod(ldap_certfile.ascii(), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
		chown_safe(ldap_certfile.ascii(), slapd_uid, slapd_gid);
	}

	pdialog.setStatusMessage(i18n("Loading initial database into LDAP..."));
	tqApp->processEvents();

	// Load database
	TDEProcess slapadd;
	slapadd << "slapadd" << "-l" << configTempDir.name() + "skel.ldif";
	slapadd.start();
	while (slapadd.isRunning()) {
		tqApp->processEvents();
	}
	if (slapadd.exitStatus() != 0) {
		if (errstr) *errstr = i18n("Unable to import initial database into LDAP");
		pdialog.closeDialog();
		return -1;
	}

	controlLDAPServer(SC_SETDBPERMS, slapd_uid, slapd_gid);

	pdialog.setStatusMessage(i18n("Starting servers..."));
	tqApp->processEvents();

	// Start slapd
	if (controlLDAPServer(SC_START) != 0) {
		if (errstr) *errstr = i18n("Unable to start LDAP server");
		pdialog.closeDialog();
		return -1;
	}
	// Start Heimdal
	if (controlHeimdalServer(SC_START) != 0) {
		if (errstr) *errstr = i18n("Unable to start Kerberos server");
		pdialog.closeDialog();
		return -1;
	}

	pdialog.setStatusMessage(i18n("Initializing Kerberos database..."));
	tqApp->processEvents();

	TQString errorstring;
	if (initializeNewKerberosRealm(realmconfig.name.upper(), &errorstring) != 0) {
		if (errstr) *errstr = i18n("Unable to initialize Kerberos database<p>").append(errorstring);
		pdialog.closeDialog();
		return -1;
	}

	if (addHostEntryToKerberosRealm(realmconfig.kdc, &errorstring) != 0) {
		if (errstr) *errstr = i18n("Unable to add KDC server entry to Kerberos database<p>").append(errorstring);
		pdialog.closeDialog();
		return -1;
	}

	if (addLDAPEntryToKerberosRealm(m_ldapUserName, realmconfig.admin_server, &errorstring) != 0) {
		if (errstr) *errstr = i18n("Unable to add %1 entry to Kerberos database<p>").arg(m_ldapUserName).append(errorstring);
		pdialog.closeDialog();
		return -1;
	}

	if (addLDAPEntryToKerberosRealm("ldap", realmconfig.admin_server, &errorstring) != 0) {
		if (errstr) *errstr = i18n("Unable to add LDAP entry to Kerberos database<p>").append(errorstring);
		pdialog.closeDialog();
		return -1;
	}

	controlHeimdalServer(SC_SETDBPERMS, slapd_uid, slapd_gid);

	// Move all those new Heimdal entries to the correct tree/branch
	TQStringList domainChunks = TQStringList::split(".", realmconfig.name.lower());
	TQString basedcname = "dc=" + domainChunks.join(",dc=");
	LDAPCredentials* credentials = new LDAPCredentials;
	credentials->username = "";
	credentials->password = "";
	credentials->realm = realmconfig.name.upper();
	LDAPManager* ldap_mgr = new LDAPManager(realmconfig.name.upper(), "ldapi://", credentials);
	if (ldap_mgr->moveKerberosEntries("o=kerberos,cn=kerberos control,ou=master services,ou=core,ou=realm," + basedcname, &errorstring) != 0) {
		delete ldap_mgr;
		delete credentials;
		if (errstr) *errstr = errorstring;
		pdialog.closeDialog();
		return -1;
	}

	// Set CA master
	if (ldap_mgr->setRealmCAMaster(m_fqdn, &errorstring) != 0) {
		delete ldap_mgr;
		delete credentials;
		if (errstr) *errstr = errorstring;
		pdialog.closeDialog();
		return -1;
	}

	// Upload the contents of KERBEROS_PKI_PEM_FILE to the LDAP server
	if (uploadKerberosCAFileToLDAP(ldap_mgr, &errorstring) != 0) {
		delete ldap_mgr;
		delete credentials;
		if (errstr) *errstr = errorstring;
		pdialog.closeDialog();
		return -1;
	}

	// Upload the contents of KERBEROS_PKI_PEMKEY_FILE to the LDAP server
	if (uploadKerberosCAKeyFileToLDAP(ldap_mgr, &errorstring) != 0) {
		delete ldap_mgr;
		delete credentials;
		if (errstr) *errstr = errorstring;
		pdialog.closeDialog();
		return -1;
	}

	// Set @@@ADMINUSER@@@ password in kadmin
	LDAPCredentials adminuser;
	adminuser.username = adminUserName;
	adminuser.password = adminPassword;
	adminuser.realm = realmconfig.name.upper();
	if (setKerberosPasswordForUser(adminuser, &errorstring) != 0) {
		delete ldap_mgr;
		delete credentials;
		if (errstr) *errstr = i18n("Unable to set user password in Kerberos database<p>").append(errorstring);
		pdialog.closeDialog();
		return -1;
	}

	pdialog.setStatusMessage(i18n("Configuring local system..."));
	tqApp->processEvents();

	// Write the TDE realm configuration file
	LDAPRealmConfigList realms;
	realms.insert(realmconfig.name, realmconfig);
	LDAPManager::writeTDERealmList(realms, m_systemconfig);
	m_systemconfig->setGroup(NULL);
	m_systemconfig->writeEntry("DefaultRealm", realmconfig.name);

	m_systemconfig->setGroup("Replication");
	m_systemconfig->writeEntry("Password", adminPassword);
	m_systemconfig->setGroup(NULL);

	m_systemconfig->sync();

	LDAPManager::writeLDAPConfFile(realmconfig, ROLE_PRIMARY_REALM_CONTROLLER);

	// Write the sudoers file
	if (ldap_mgr->writeSudoersConfFile(&errorstring) != 0) {
		delete ldap_mgr;
		delete credentials;
		if (errstr) *errstr = i18n("Unable to set local sudo rights<p>").append(errorstring);
		pdialog.closeDialog();
		return -1;
	}

	LDAPManager::writePrimaryRealmCertificateUpdateCronFile();

	delete ldap_mgr;
	delete credentials;

	pdialog.setStatusMessage(i18n("(Re)starting servers..."));
	tqApp->processEvents();

	// Restart slapd
	if (controlLDAPServer(SC_RESTART) != 0) {
		if (errstr) *errstr = i18n("Unable to restart LDAP server");
		pdialog.closeDialog();
		return -1;
	}
	// Restart Heimdal
	if (controlHeimdalServer(SC_RESTART) != 0) {
		if (errstr) *errstr = i18n("Unable to restart Kerberos server");
		pdialog.closeDialog();
		return -1;
	}
	// Restart kadmind
	if (controlKAdminDaemon(SC_RESTART) != 0) {
		if (errstr) *errstr = i18n("Unable to restart Kerberos Administration Service");
		pdialog.closeDialog();
		return -1;
	}

	// Start SASL
	if (controlSASLServer(SC_START) != 0) {
		if (errstr) *errstr = i18n("Unable to start SASL server");
		pdialog.closeDialog();
		return -1;
	}

	// Write NSS configuration file
	LDAPManager::writeNSSwitchFile();

	// Write the NSS update crontab file and update NSS database
	LDAPManager::writeClientCronFiles();

	pdialog.closeDialog();

	return 0;
}

int LDAPController::buttons() {
	return TDECModule::Apply|TDECModule::Help;
}

TQString LDAPController::quickHelp() const
{
	return i18n("This module configures an LDAP Realm Controller.");
}

#include "ldapcontroller.moc"
