#include <stdlib.h>
#include "ldap_plugin.h"
#include "interface.h"
#include "support.h"
#include <gnome.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>


GtkWidget *wnd_options = NULL;
ldap_connection *ldapconn = NULL;

int messageBox(GtkMessageType msg_type, GtkButtonsType btn_type, char *message, ...)
{
	va_list arglist;
	char buffer[1024];
	GtkWidget *dialog;
	int ret = 0;

	va_start(arglist, message);
	vsprintf(buffer, message, arglist);
	dialog = gtk_message_dialog_new ((GtkWindow *)wnd_options, GTK_DIALOG_DESTROY_WITH_PARENT, msg_type, btn_type, buffer);
	ret = gtk_dialog_run (GTK_DIALOG (dialog));
	gtk_widget_destroy (dialog);
	va_end(arglist);
	return ret;
}

static const gchar *scopeList[] = {"One Level", "Subtree", NULL};
static const gchar *encryptionList[] = {"Never", "If Available", "Always", NULL};

/***********************************************************************
 *
 * Function:    fill_option_menu
 *
 * Summary:   helper function to fill in option menus
 *
 ***********************************************************************/
void fill_option_menu (GtkOptionMenu *option_menu, int type, gchar **List)
{
	gint i = 0, n = 0;
	GtkWidget *menu ,*menu_item;

	menu = gtk_menu_new ();

	while (List[i]) {
		menu_item = gtk_menu_item_new_with_label (List[i]);
		gtk_widget_show (menu_item);
		gtk_object_set_data (GTK_OBJECT (menu_item), "type", GINT_TO_POINTER (i));
		gtk_menu_append (GTK_MENU (menu),menu_item);
		if (i == type)
			n = i;
		i++;
	}
	gtk_option_menu_set_menu (option_menu, menu);
	gtk_option_menu_set_history (option_menu, n);
}

/***********************************************************************
 *
 * Function:    set_ldap_connection
 *
 * Summary:   fills out the global conn struct based on the users setting
 *
 ***********************************************************************/
int set_ldap_connection(void)
{
	GtkEntry *txt_server = (GtkEntry *)lookup_widget(wnd_options, "txt_server");
	GtkEntry *txt_port = (GtkEntry *)lookup_widget(wnd_options, "txt_port");
	GtkEntry *txt_binddn = (GtkEntry *)lookup_widget(wnd_options, "txt_binddn");
	GtkEntry *txt_passwd = (GtkEntry *)lookup_widget(wnd_options, "txt_passwd");
	GtkEntry *txt_searchbase = (GtkEntry *)lookup_widget(wnd_options, "txt_searchbase");
	GtkEntry *txt_filter = (GtkEntry *)lookup_widget(wnd_options, "txt_filter");
	GtkEntry *txt_authmech = (GtkEntry *)lookup_widget(wnd_options, "txt_authmech");
	GtkToggleButton *chk_write = (GtkToggleButton *)lookup_widget(wnd_options, "chk_write");
	GtkToggleButton *chk_anonymous = (GtkToggleButton *)lookup_widget(wnd_options, "chk_anonymous");
	gchar *statefile = NULL;
	GtkWidget *item;

	//Sanity checks
	if (!strlen(gtk_entry_get_text(txt_server))) {
		//No server given
		messageBox(GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "No Server given");
		return 1;
	}
	if (!strlen(gtk_entry_get_text(txt_port))) {
		//No Port given
		messageBox(GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "No Port given");
		return 1;
	}
	if (!strlen(gtk_entry_get_text(txt_binddn)) && !gtk_toggle_button_get_active(chk_anonymous)) {
		//No server given
		messageBox(GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "No Username given.");
		return 1;
	}
	if (!strlen(gtk_entry_get_text(txt_searchbase))) {
		//No server given
		messageBox(GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "No Searchbase given");
		return 1;
	}
	if (strlen(gtk_entry_get_text(txt_searchbase)) && strlen(ldapconn->searchbase) && strcmp(gtk_entry_get_text(txt_searchbase) ,ldapconn->searchbase)) {
		if (messageBox(GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, "Switch Searchbase is a really bad idea if you have already synced.\nDo you want to abort?") == GTK_RESPONSE_YES) {
			return 1;
		}
	}
	if (!strlen(gtk_entry_get_text(txt_authmech))) {
		//No server given
		messageBox(GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "No Auth Mechanism given");
		return 1;
	}

	statefile = g_strdup(ldapconn->statefile);
	free(ldapconn);
	ldapconn = malloc(sizeof(ldap_connection));
	memset(ldapconn, 0, sizeof(ldapconn));

	ldapconn->servername = strdup(gtk_entry_get_text(txt_server));
	ldapconn->serverport = atoi(gtk_entry_get_text(txt_port));
	ldapconn->binddn =  strdup(gtk_entry_get_text(txt_binddn));
	ldapconn->pwd =  strdup(gtk_entry_get_text(txt_passwd));
	ldapconn->searchbase =  strdup(gtk_entry_get_text(txt_searchbase));
	ldapconn->filter =  strdup(gtk_entry_get_text(txt_filter));
	ldapconn->authmech = strdup(gtk_entry_get_text(txt_authmech));
	ldapconn->ldap_version = LDAP_VERSION3;
	ldapconn->handle = NULL;
	ldapconn->write = 1;
	ldapconn->anonymous = 0;
	strcpy(ldapconn->statefile, statefile);
	g_free(statefile);

	item = gtk_option_menu_get_menu (GTK_OPTION_MENU (lookup_widget(wnd_options, "opt_scope")));
	item = gtk_menu_get_active (GTK_MENU (item));
	if (!GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (item), "type"))) {
		ldapconn->scope = LDAP_SCOPE_ONELEVEL;
	} else {
		ldapconn->scope = LDAP_SCOPE_SUBTREE;
	}

	if (!strcmp(gtk_entry_get_text((GtkEntry *)((GtkCombo *)lookup_widget(wnd_options, "cmb_debug"))->entry), "Errors Only")) {
		ldapconn->debug_level = 0;
	}
	if (!strcmp(gtk_entry_get_text((GtkEntry *)((GtkCombo *)lookup_widget(wnd_options, "cmb_debug"))->entry), "Errors and Warnings")) {
		ldapconn->debug_level = 1;
	}
	if (!strcmp(gtk_entry_get_text((GtkEntry *)((GtkCombo *)lookup_widget(wnd_options, "cmb_debug"))->entry), "Information")) {
		ldapconn->debug_level = 2;
	}
	if (!strcmp(gtk_entry_get_text((GtkEntry *)((GtkCombo *)lookup_widget(wnd_options, "cmb_debug"))->entry), "Debug")) {
		ldapconn->debug_level = 3;
	}
	if (!strcmp(gtk_entry_get_text((GtkEntry *)((GtkCombo *)lookup_widget(wnd_options, "cmb_debug"))->entry), "Full Debug")) {
		ldapconn->debug_level = 4;
	}

	item = gtk_option_menu_get_menu (GTK_OPTION_MENU (lookup_widget(wnd_options, "opt_encryption")));
	item = gtk_menu_get_active (GTK_MENU (item));
	ldapconn->encryption = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (item), "type"));

	if (gtk_toggle_button_get_active(chk_write) == FALSE) {
		ldapconn->write = 0;
	}

	if (gtk_toggle_button_get_active(chk_anonymous)) {
		ldapconn->anonymous = 1;
	}

	return 0;
}

/***********************************************************************
 *
 * Function:    open_option_window
 *
 * Summary:   get called by multisync when the options window is displayed
 *
 ***********************************************************************/
GtkWidget* open_option_window(sync_pair *pair, connection_type type)
{
	char port[1024];

	//Create and show the Options window
	wnd_options = create_wnd_options();
	gtk_widget_show (wnd_options);

	//Create a new ldapconnection
	ldapconn = malloc(sizeof(ldap_connection));
	memset(ldapconn, 0, sizeof(ldapconn));
	ldapconn->debug_level = 0;
	ldapconn->handle = NULL;
	sprintf(ldapconn->statefile, "%s/%sldap", sync_get_datapath(pair), (type==CONNECTION_TYPE_LOCAL?"local":"remote"));

	if (!load_ldap_state(ldapconn)) {
		sprintf(port, "%i", ldapconn->serverport);
		gtk_entry_set_text((GtkEntry *)lookup_widget(wnd_options, "txt_server"), ldapconn->servername);
		gtk_entry_set_text((GtkEntry *)lookup_widget(wnd_options, "txt_port"), port);
		gtk_entry_set_text((GtkEntry *)lookup_widget(wnd_options, "txt_binddn"), ldapconn->binddn);
		gtk_entry_set_text((GtkEntry *)lookup_widget(wnd_options, "txt_passwd"), ldapconn->pwd);
		gtk_entry_set_text((GtkEntry *)lookup_widget(wnd_options, "txt_searchbase"), ldapconn->searchbase);
		gtk_entry_set_text((GtkEntry *)lookup_widget(wnd_options, "txt_filter"), ldapconn->filter);
		gtk_entry_set_text((GtkEntry *)lookup_widget(wnd_options, "txt_authmech"), ldapconn->authmech);

		if (ldapconn->write) {
			gtk_toggle_button_set_active((GtkToggleButton *)lookup_widget(wnd_options, "chk_write"), TRUE);
		} else {
			gtk_toggle_button_set_active((GtkToggleButton *)lookup_widget(wnd_options, "chk_write"), FALSE);
		}

		if (ldapconn->anonymous) {
			gtk_toggle_button_set_active((GtkToggleButton *)lookup_widget(wnd_options, "chk_anonymous"), TRUE);
			on_chk_anonymous_toggled(NULL, NULL);
		}

		switch (ldapconn->debug_level) {
			case 0:
				gtk_entry_set_text((GtkEntry *)((GtkCombo *)lookup_widget(wnd_options, "cmb_debug"))->entry, "Errors Only");
				break;
			case 1:
				gtk_entry_set_text((GtkEntry *)((GtkCombo *)lookup_widget(wnd_options, "cmb_debug"))->entry, "Errors and Warnings");
				break;
			case 2:
				gtk_entry_set_text((GtkEntry *)((GtkCombo *)lookup_widget(wnd_options, "cmb_debug"))->entry, "Information");
				break;
			case 3:
				gtk_entry_set_text((GtkEntry *)((GtkCombo *)lookup_widget(wnd_options, "cmb_debug"))->entry, "Debug");
				break;
			case 4:
				gtk_entry_set_text((GtkEntry *)((GtkCombo *)lookup_widget(wnd_options, "cmb_debug"))->entry, "Full Debug");
				break;
		}

		if (ldapconn->scope == LDAP_SCOPE_ONELEVEL) {
			fill_option_menu ((GtkOptionMenu *)lookup_widget(wnd_options, "opt_scope"), 0, (gchar **)scopeList);
		} else {
			fill_option_menu ((GtkOptionMenu *)lookup_widget(wnd_options, "opt_scope"), 1, (gchar **)scopeList);
		}
		fill_option_menu ((GtkOptionMenu *)lookup_widget(wnd_options, "opt_encryption"), ldapconn->encryption, (gchar **)encryptionList);
	} else {
		fill_option_menu ((GtkOptionMenu *)lookup_widget(wnd_options, "opt_scope"), 0, (gchar **)scopeList);
		fill_option_menu ((GtkOptionMenu *)lookup_widget(wnd_options, "opt_encryption"), 1, (gchar **)encryptionList);
	}

	return(wnd_options);
}

void on_btn_cancel_clicked(GtkButton *button, gpointer user_data)
{
	gtk_widget_destroy(wnd_options);
	wnd_options = NULL;
}

void on_wnd_options_destroy(GtkObject *object, gpointer user_data)
{
	sync_plugin_window_closed();
}

void on_btn_ok_clicked (GtkButton *button, gpointer user_data)
{
	if (set_ldap_connection()) {
		return;
	}

	save_ldap_state(ldapconn);

	gtk_widget_destroy(wnd_options);
	wnd_options = NULL;
}

/***********************************************************************
 *
 * Function:    on_btn_auth_mechs_clicked
 *
 * Summary:   call back function for the "Get From Server"
 *
 ***********************************************************************/
void on_btn_auth_mechs_clicked (GtkButton *button, gpointer user_data)
{
	LDAPMessage *res, *res2;
	char *attrs[] = {"supportedSASLMechanisms", NULL};
	int i = 0;
	char **entries;
	GList *items = NULL;
	char *oldvalue;
	GtkEntry *txt_authmech = (GtkEntry *)lookup_widget(wnd_options, "txt_authmech");

	//Check entries
	if (set_ldap_connection()) {
		return;
	}

	if (ldap_start(ldapconn)) {
		messageBox(GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "Could not connect to %s on port %i", ldapconn->servername,  ldapconn->serverport);
		return;
	}

	if (ldap_set_version(ldapconn)) {
		messageBox(GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "Could not set Ldap Version to 3");
		return;
	}

	if (ldapconn->encryption) {
		ldap_encrypt(ldapconn);
	}

	ldapconn->anonymous = 1;
	if (ldap_makebind(ldapconn)) {
		messageBox(GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "Could bind to server");
		return;
	}

	if (ldap_search_s(ldapconn->ld, "", LDAP_SCOPE_BASE, "(objectClass=*)", attrs, 0, &res)) {
		messageBox(GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "Unable to search for supportedSASLMechanisms");
		return;
	}

	res2 = ldap_first_entry(ldapconn->ld, res);
	if (!res2) {
		messageBox(GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "No entries found");
		return;
	}

	items = g_list_append (items, "SIMPLE");

	entries = ldap_get_values(ldapconn->ld, res2, "supportedSASLMechanisms");
	if (!entries) {
		messageBox(GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,  "Supported Mechanisms:\n\nNo Mechanisms found!");
	} else {
		while (entries[i]) {
			items = g_list_append (items, strdup(entries[i]));
			i++;
		}
		ldap_value_free(entries);
	}

	oldvalue =  strdup(gtk_entry_get_text(txt_authmech));
	gtk_combo_set_popdown_strings (GTK_COMBO (lookup_widget(wnd_options, "cmb_authmech")), items);
	gtk_entry_set_text(txt_authmech, oldvalue);
	free(oldvalue);

	ldap_unbind_s(ldapconn->ld);
	//ldap_memfree(ldapconn->ld);
}

/***********************************************************************
 *
 * Function:    on_btn_test_clicked
 *
 * Summary:   call back function for the "Test Server Features"
 *
 ***********************************************************************/
void on_btn_test_clicked (GtkButton *button, gpointer user_data)
{
	GtkEntry *txt_server = (GtkEntry *)lookup_widget(wnd_options, "txt_server");
	GtkEntry *txt_port = (GtkEntry *)lookup_widget(wnd_options, "txt_port");

	GtkImage *img_connection = (GtkImage *)lookup_widget(wnd_options, "img_connection");
	GtkImage *img_authenticate = (GtkImage *)lookup_widget(wnd_options, "img_authenticate");
	GtkImage *img_search = (GtkImage *)lookup_widget(wnd_options, "img_search");
	GtkImage *img_write = (GtkImage *)lookup_widget(wnd_options, "img_write");
	GtkImage *img_evolution = (GtkImage *)lookup_widget(wnd_options, "img_evolution");
	GtkImage *img_ldap = (GtkImage *)lookup_widget(wnd_options, "img_ldap");
	GtkImage *img_encrypt = (GtkImage *)lookup_widget(wnd_options, "img_encrypt");
	GtkImage *img_strong_auth = (GtkImage *)lookup_widget(wnd_options, "img_strong_auth");

	LDAP *ld = NULL;
	int ldapResult;
	int ssf = 0;
	LDAPMessage *res, *res2;
	char *attrs[] = {"cn", NULL};
	BerElement *berptr = NULL;
	char *attribute = NULL;
	char **values = NULL;
	int i = 0, found = 0;
	LDAPMod **data;
	char dn[1024];

	//Check entries
	if (set_ldap_connection()) {
		return;
	}

	gtk_image_set_from_stock(img_connection, "gtk-dialog-question", GTK_ICON_SIZE_BUTTON);
	gtk_image_set_from_stock(img_authenticate, "gtk-dialog-question", GTK_ICON_SIZE_BUTTON);
	gtk_image_set_from_stock(img_search, "gtk-dialog-question", GTK_ICON_SIZE_BUTTON);
	gtk_image_set_from_stock(img_write, "gtk-dialog-question", GTK_ICON_SIZE_BUTTON);
	gtk_image_set_from_stock(img_evolution, "gtk-dialog-question", GTK_ICON_SIZE_BUTTON);
	gtk_image_set_from_stock(img_ldap, "gtk-dialog-question", GTK_ICON_SIZE_BUTTON);
	gtk_image_set_from_stock(img_encrypt, "gtk-dialog-question", GTK_ICON_SIZE_BUTTON);
	gtk_image_set_from_stock(img_strong_auth, "gtk-dialog-question", GTK_ICON_SIZE_BUTTON);

	//Check connection
	if (ldap_start(ldapconn)) {
		messageBox(GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "Could not connect to %s on port %s", gtk_entry_get_text(txt_server), gtk_entry_get_text(txt_port) );
		gtk_image_set_from_stock(img_connection, "gtk-dialog-error", GTK_ICON_SIZE_BUTTON);
		return;
	} else {
		gtk_image_set_from_stock(img_connection, "gtk-apply", GTK_ICON_SIZE_BUTTON);
	}

	//Check ldap v3
	if (ldap_set_version(ldapconn)) {
		gtk_image_set_from_stock(img_ldap, "gtk-dialog-warning", GTK_ICON_SIZE_BUTTON);
	} else {
		gtk_image_set_from_stock(img_ldap, "gtk-apply", GTK_ICON_SIZE_BUTTON);
	}

	//Check encryption
	if (!ldapconn->encryption || ldap_encrypt(ldapconn)) {
		gtk_image_set_from_stock(img_encrypt, "gtk-dialog-warning", GTK_ICON_SIZE_BUTTON);
	} else {
		gtk_image_set_from_stock(img_encrypt, "gtk-apply", GTK_ICON_SIZE_BUTTON);
	}

	//Check Authentication
	if (ldap_makebind(ldapconn)) {
		messageBox(GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "Unable to bind to server");
		gtk_image_set_from_stock(img_authenticate, "gtk-dialog-error", GTK_ICON_SIZE_BUTTON);
		return;
	} else {
		ldap_get_option(ldapconn->ld, LDAP_OPT_X_SASL_SSF, &ssf);
		if (ssf <= 0) {
			gtk_image_set_from_stock(img_strong_auth, "gtk-dialog-warning", GTK_ICON_SIZE_BUTTON);
		} else {
			gtk_image_set_from_stock(img_strong_auth, "gtk-apply", GTK_ICON_SIZE_BUTTON);
		}
		gtk_image_set_from_stock(img_authenticate, "gtk-apply", GTK_ICON_SIZE_BUTTON);
	}

	//Try to search
	ldapResult = ldap_search_s(ldapconn->ld, ldapconn->searchbase, LDAP_SCOPE_ONELEVEL, ldapconn->filter, attrs, 0, &res);
	if (ldapResult != LDAP_SUCCESS) {
		messageBox(GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "Unable to search.\nServer said: %s", ldap_err2string(ldapResult));
		gtk_image_set_from_stock(img_search, "gtk-dialog-error", GTK_ICON_SIZE_BUTTON);
		return;
	} else {
		gtk_image_set_from_stock(img_search, "gtk-apply", GTK_ICON_SIZE_BUTTON);
	}

	//Try to add a dn
	data = g_malloc0(1024 * sizeof(LDAPMod *));

	data[i] = g_malloc0(sizeof(LDAPMod));
	data[i]->mod_values = g_malloc0(2 * sizeof(char *));
	data[i]->mod_type = "cn";
	data[i]->mod_values[0] = "65af6f22fhfnbvJasdhasud2374";
	data[i]->mod_values[1] = NULL;
	data[i]->mod_op = LDAP_MOD_ADD;

	i++;
	data[i] = g_malloc0(sizeof(LDAPMod));
	data[i]->mod_values = g_malloc0(2 * sizeof(char *));
	data[i]->mod_type = "sn";
	data[i]->mod_values[0] = "test";
	data[i]->mod_values[1] = NULL;
	data[i]->mod_op = LDAP_MOD_ADD;

	i++;
	data[i] = g_malloc0(sizeof(LDAPMod));
	data[i]->mod_values = g_malloc0(4 * sizeof(char *));
	data[i]->mod_type = "objectClass";
	data[i]->mod_values[0] = "top";
	data[i]->mod_values[1] = "person";
	data[i]->mod_values[2] = "organizationalPerson";
	data[i]->mod_values[3] = NULL;
	data[i]->mod_op = LDAP_MOD_ADD;
	data[i + 1] = NULL;

	sprintf(dn, "cn=65af6f22fhfnbvJasdhasud2374,%s", ldapconn->searchbase);

	if (ldapResult = ldap_add_s(ldapconn->ld, dn, data)) {
		messageBox(GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "Unable to add entry.\nServer said: %s", ldap_err2string(ldapResult));
		gtk_image_set_from_stock(img_write, "gtk-dialog-warning", GTK_ICON_SIZE_BUTTON);
	}

	if (ldapResult = ldap_delete_s(ldapconn->ld, dn)) {
		messageBox(GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "Unable to delete entry.\nServer said: %s", ldap_err2string(ldapResult));
		gtk_image_set_from_stock(img_write, "gtk-dialog-warning", GTK_ICON_SIZE_BUTTON);
	} else {
		gtk_image_set_from_stock(img_write, "gtk-apply", GTK_ICON_SIZE_BUTTON);
	}

	//Check evolution compliance
	if (ldap_check_evolution(ldapconn)) {
		gtk_image_set_from_stock(img_evolution, "gtk-dialog-warning", GTK_ICON_SIZE_BUTTON);
	} else {
		gtk_image_set_from_stock(img_evolution, "gtk-apply", GTK_ICON_SIZE_BUTTON);
	}

	ldap_unbind_s(ldapconn->ld);
	//ldap_memfree(ldapconn->ld);
	free(data);
}

void
on_chk_anonymous_toggled               (GtkToggleButton *togglebutton,
                                        gpointer         user_data)
{
	if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(wnd_options, "chk_anonymous")))) {
		gtk_widget_set_sensitive(lookup_widget(wnd_options, "txt_binddn"), TRUE);
		gtk_widget_set_sensitive(lookup_widget(wnd_options, "txt_passwd"), TRUE);
	} else {
		gtk_widget_set_sensitive(lookup_widget(wnd_options, "txt_binddn"), FALSE);
		gtk_widget_set_sensitive(lookup_widget(wnd_options, "txt_passwd"), FALSE);
	}
}
