/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) version 3.
 *
 * 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with the program; if not, see <http://www.gnu.org/licenses/>
 *
 *
 * Authors:
 *		Shreyas Srinivasan <sshreyas@novell.com>
 *		Sankar P <psankar@novell.com>
 *
 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string.h>
#include <glib/gi18n.h>
#include <glade/glade.h>

#include <libedataserverui/e-passwords.h>
#include <mail/mail-component.h>
#include <mail/em-folder-tree.h>
#include <mail/mail-config.h>
#include <mail/em-folder-selector.h>
#include <mail/em-popup.h>
#include <mail/em-account-editor.h>
#include <camel/camel-url.h>
#include <camel/camel-store.h>
#include <mail/mail-ops.h>
#include <libedataserver/e-account.h>
#include <e-util/e-error.h>
#include <e-util/e-icon-factory.h>
#include <e-util/e-util-private.h>

#include <e-gw-container.h>
#include <e-gw-connection.h>
#include <e-gw-message.h>
#include <libedataserverui/e-name-selector.h>
#include "proxy-login.h"

#define GW(name) glade_xml_get_widget (priv->xml, name)

#define ACCOUNT_PICTURE 0
#define ACCOUNT_NAME 1

proxyLogin *pld = NULL;
static GObjectClass *parent_class = NULL;

struct _proxyLoginPrivate {
	/* Glade XML data for the Add/Edit Proxy dialog*/
	GladeXML *xml;
	/* Widgets */
	GtkWidget *main;

	/*Tree Store*/
	GtkTreeStore *store;
	/*Tree View*/
	GtkTreeView *tree;

	gchar *help_section;
};

static void
proxy_login_finalize (GObject *object)
{
	proxyLogin *prd = (proxyLogin *) object;
	proxyLoginPrivate *priv;

	g_return_if_fail (IS_PROXY_LOGIN (prd));
	priv = prd->priv;
	g_list_foreach (prd->proxy_list, (GFunc)g_free, NULL);
	g_list_free (prd->proxy_list);
	prd->proxy_list = NULL;
	g_object_unref (priv->xml);
	g_free (priv->help_section);

	if (prd->priv) {
		g_free (prd->priv);
		prd->priv = NULL;
	}

	if (parent_class->finalize)
		(* parent_class->finalize) (object);
}

static void
proxy_login_dispose (GObject *object)
{
	proxyLogin *prd = (proxyLogin *) object;

	g_return_if_fail (IS_PROXY_LOGIN (prd));

	if (parent_class->dispose)
		(* parent_class->dispose) (object);
}

/* Class initialization function for the Send Options */
static void
proxy_login_class_init (GObjectClass *object)
{
	proxyLoginClass *klass;
	GObjectClass *object_class;

	klass = PROXY_LOGIN_CLASS (object);
	parent_class = g_type_class_peek_parent (klass);
	object_class = G_OBJECT_CLASS (klass);

	object_class->finalize = proxy_login_finalize;
	object_class->dispose = proxy_login_dispose;
}

static void
proxy_login_init (GObject *object)
{
	proxyLogin *prd;
	proxyLoginPrivate *priv;

	prd = PROXY_LOGIN (object);
	priv = g_new0 (proxyLoginPrivate, 1);
	prd->priv = priv;

	prd->proxy_list = NULL;
	priv->xml = NULL;
	priv->main = NULL;
	priv->store = NULL;
	priv->tree = NULL;
}

GType
proxy_login_get_type (void)
{
  static GType type = 0;

  if (type == 0) {
    static const GTypeInfo info = {
      sizeof (proxyLoginClass),
      NULL,   /* base_init */
      NULL,   /* base_finalize */
      (GClassInitFunc) proxy_login_class_init,   /* class_init */
      NULL,   /* class_finalize */
      NULL,   /* class_data */
      sizeof (proxyLogin),
     0,      /* n_preallocs */
     (GInstanceInitFunc) proxy_login_init,
	NULL    /* instance_init */
    };
    type = g_type_register_static (G_TYPE_OBJECT,
                                   "proxyLoginType",
                                   &info, 0);
  }

  return type;
}

proxyLogin *
proxy_login_new (void)
{
	proxyLogin *prd;

	prd = g_object_new (TYPE_PROXY_LOGIN, NULL);

	return prd;
}

static gint
proxy_get_password (EAccount *account, gchar **user_name, gchar **password)
{
	const gchar *failed_auth;
	gchar *uri, *key, *prompt;
	CamelURL *url;
	const gchar *poa_address, *use_ssl = NULL, *soap_port;

	url = camel_url_new (account->source->url, NULL);
	if (url == NULL)
		return 0;
	*user_name = g_strdup (url->user);
	poa_address = url->host;
	if (!poa_address || strlen (poa_address) ==0)
		return 0;

        soap_port = camel_url_get_param (url, "soap_port");
        if (!soap_port || strlen (soap_port) == 0)
                soap_port = "7191";
	use_ssl = camel_url_get_param (url, "use_ssl");

	key =  g_strdup_printf ("groupwise://%s@%s/", url->user, poa_address);

	if (use_ssl && !g_str_equal (use_ssl, "never"))
		uri = g_strdup_printf ("https://%s:%s/soap", poa_address, soap_port);
	else
		uri = g_strdup_printf ("http://%s:%s/soap", poa_address, soap_port);

	failed_auth = "";

	prompt = g_strdup_printf (_("%sEnter password for %s (user %s)"),
			failed_auth, poa_address, url->user);

	*password = e_passwords_get_password ("Groupwise", key);

	g_free (key);
	g_free (prompt);
	g_free (uri);
	camel_url_free (url);

	return 1;
}

static EGwConnection *
proxy_login_get_cnc (EAccount *account, GtkWindow *password_dlg_parrent)
{
	EGwConnection *cnc;
	CamelURL *url;
	const gchar *failed_auth;
	gchar *uri = NULL, *key = NULL, *prompt = NULL, *password = NULL;
	const gchar *use_ssl = NULL, *soap_port;
	gboolean remember;

	url = camel_url_new (account->source->url, NULL);
	if (url == NULL)
		return NULL;
	if (!url->host || strlen (url->host) ==0)
		return NULL;

        soap_port = camel_url_get_param (url, "soap_port");
        if (!soap_port || strlen (soap_port) == 0)
                soap_port = "7191";
	use_ssl = camel_url_get_param (url, "use_ssl");

	key =  g_strdup_printf ("groupwise://%s@%s/", url->user, url->host);
	if (use_ssl && !g_str_equal (use_ssl, "never"))
		uri = g_strdup_printf ("https://%s:%s/soap", url->host, soap_port);
	else
		uri = g_strdup_printf ("http://%s:%s/soap", url->host, soap_port);

	failed_auth = "";
	cnc = NULL;

	prompt = g_strdup_printf (_("%sEnter password for %s (user %s)"),
			failed_auth, url->host, url->user);

	password = e_passwords_get_password ("Groupwise", key);

	if (!password)
		password = e_passwords_ask_password (prompt, "Groupwise", key, prompt,
				E_PASSWORDS_REMEMBER_FOREVER|E_PASSWORDS_SECRET, &remember, password_dlg_parrent);

	g_free (prompt);
	cnc = e_gw_connection_new (uri, url->user, password);
	if (!E_IS_GW_CONNECTION(cnc) && use_ssl && g_str_equal (use_ssl, "when-possible")) {
		gchar *http_uri = g_strconcat ("http://", uri + 8, NULL);
		cnc = e_gw_connection_new (http_uri, url->user, password);
		g_free (http_uri);
	}

	g_free (key);
	g_free (password);
	g_free (uri);
	camel_url_free (url);

	return cnc;
}

static void
proxy_login_cb (GtkDialog *dialog, gint state)
{
	GtkWidget *account_name_tbox;
	proxyLoginPrivate *priv;
	gchar *proxy_name;

	priv = pld->priv;
	account_name_tbox = glade_xml_get_widget (priv->xml, "account_name");
	proxy_name = g_strdup ((gchar *) gtk_entry_get_text ((GtkEntry *) account_name_tbox));

	switch (state) {
	    case GTK_RESPONSE_OK:
		    gtk_widget_destroy (priv->main);
		    proxy_soap_login (proxy_name);
		    g_object_unref (pld);
		    break;
	    case GTK_RESPONSE_CANCEL:
		    gtk_widget_destroy (priv->main);
		    g_object_unref (pld);
		    break;
	    case GTK_RESPONSE_HELP:
		    break;
	}

	g_free (proxy_name);
}

static void
proxy_soap_login (gchar *email)
{
	EAccountList *accounts = mail_config_get_accounts();
	EAccount *srcAccount;
	EAccount *dstAccount;
	EGwConnection *proxy_cnc, *cnc;
	CamelURL *uri = NULL, *parent = NULL;
	gchar *password = NULL, *user_name = NULL;
	gchar *proxy_source_url = NULL, *parent_source_url = NULL;
	gchar *name;
	gint i;
	gint permissions = 0;

	for (i=0; email[i]!='\0' && email[i]!='@' ; i++);
	if (email[i]=='@')
		name = g_strndup(email, i);
	else {
		e_error_run (NULL, "org.gnome.evolution.proxy-login:invalid-user",email ,NULL);
		return;
	}

	/* README: There should not be the weird scenario of the proxy itself configured as an account.
	   If so, it is violating the (li)unix philosophy of User creation. So dont care about that scenario*/

	if (e_account_list_find (accounts, E_ACCOUNT_FIND_ID_ADDRESS, email) != NULL) {
		e_error_run (NULL, "org.gnome.evolution.proxy-login:already-loggedin", email, NULL);
		g_free (name);
		return;
	}

	srcAccount = pld->account;
	cnc = proxy_login_get_cnc (srcAccount, NULL);
	proxy_get_password (srcAccount, &user_name, &password);

	proxy_cnc = e_gw_connection_get_proxy_connection (cnc, user_name, password, email, &permissions);

	if (proxy_cnc) {
		parent = camel_url_new (e_account_get_string(srcAccount, E_ACCOUNT_SOURCE_URL), NULL);
		parent_source_url = camel_url_to_string (parent, CAMEL_URL_HIDE_PASSWORD);
		uri = camel_url_copy (parent);
		camel_url_set_user (uri, name);
		proxy_source_url = camel_url_to_string (uri, CAMEL_URL_HIDE_PASSWORD);
		dstAccount = e_account_new();
		e_account_set_string(dstAccount, E_ACCOUNT_ID_ADDRESS, email);
		dstAccount->enabled = TRUE;
		e_account_set_string(dstAccount, E_ACCOUNT_SOURCE_URL, proxy_source_url);
		e_account_set_string (dstAccount, E_ACCOUNT_TRANSPORT_URL, proxy_source_url);
		e_account_set_string (dstAccount, E_ACCOUNT_NAME, email);
		e_account_set_string (dstAccount, E_ACCOUNT_ID_NAME, name);
		e_account_set_string (dstAccount, E_ACCOUNT_PROXY_PARENT_UID, srcAccount->uid);
		e_account_list_add(accounts, dstAccount);
		e_account_list_change (accounts, srcAccount);
		e_account_list_save(accounts);
		g_object_set_data ((GObject *)dstAccount, "permissions", GINT_TO_POINTER(permissions));
		mail_get_store(e_account_get_string(dstAccount, E_ACCOUNT_SOURCE_URL), NULL, proxy_login_add_new_store, dstAccount);

		g_free (proxy_source_url);
		g_free (parent_source_url);
		camel_url_free (parent);
	} else {
		e_error_run (NULL, "org.gnome.evolution.proxy-login:invalid-user",email ,NULL);
		return;
	}

	g_object_unref (cnc);
	g_free (name);
	g_free (user_name);
	g_free (password);
}

static void
proxy_login_add_new_store (gchar *uri, CamelStore *store, gpointer user_data)
{
	MailComponent *component = mail_component_peek ();
	EAccount *account = user_data;
	gint permissions = GPOINTER_TO_INT(g_object_get_data ((GObject *)account, "permissions"));

	if (store == NULL)
		return;

	if (!(permissions & E_GW_PROXY_MAIL_WRITE))
	    store->mode &= !CAMEL_STORE_WRITE;

	store->flags |= CAMEL_STORE_PROXY;
	mail_component_add_store (component, store, account->name);
}

static void
proxy_login_tree_view_changed_cb(GtkDialog *dialog)
{
	proxyLoginPrivate *priv = pld->priv;
	GtkTreeSelection* account_select;
	GtkTreeIter iter;
	GtkWidget *account_name_tbox;
	GtkTreeModel *model;
	gchar *account_mailid;

	account_select = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree));
	gtk_tree_selection_get_selected (account_select, &model, &iter);
        gtk_tree_model_get (model, &iter, ACCOUNT_NAME, &account_mailid, -1);
	account_mailid = g_strrstr (account_mailid, "\n") + 1;
	account_name_tbox = glade_xml_get_widget (priv->xml, "account_name");
	gtk_entry_set_text((GtkEntry*) account_name_tbox,account_mailid);
}

static void
proxy_login_setup_tree_view (void)
{
	proxyLoginPrivate *priv;
	GtkTreeSelection *selection;
	GtkCellRenderer *renderer;
	GtkTreeViewColumn *column;

	priv = pld->priv;
	renderer = g_object_new (GTK_TYPE_CELL_RENDERER_PIXBUF,
				 "xpad", 4,
				 "ypad", 4,
				 NULL);
	column = gtk_tree_view_column_new_with_attributes ("Picture", renderer, "pixbuf", ACCOUNT_PICTURE, NULL);
	gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree), column);
	renderer = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes ("Name", renderer, "text", ACCOUNT_NAME, NULL);
	gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree), column);
	gtk_tree_view_set_model (priv->tree, GTK_TREE_MODEL (priv->store));
	selection = gtk_tree_view_get_selection (priv->tree);
	gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
	g_signal_connect (G_OBJECT (selection), "changed", G_CALLBACK(proxy_login_tree_view_changed_cb), NULL);
}

static void
proxy_login_update_tree (void)
{
	GtkTreeIter iter;
	gint i,n;
	GdkPixbuf *broken_image = NULL;
	GList *proxy_list = NULL;
	gchar *proxy_name;
	gchar *proxy_email;
	EGwConnection *cnc;
	proxyLoginPrivate *priv = pld->priv;
	gchar *file_name = e_icon_factory_get_icon_filename ("stock_person", GTK_ICON_SIZE_DIALOG);
	broken_image = file_name ? gdk_pixbuf_new_from_file (file_name, NULL) : NULL;

	cnc = proxy_login_get_cnc (pld->account, priv->main ? (GTK_WINDOW (gtk_widget_get_toplevel (priv->main))) : NULL);
	if (cnc)
		e_gw_connection_get_proxy_list (cnc, &proxy_list);

	gtk_tree_store_clear (priv->store);
	if (proxy_list != NULL) {
		n = g_list_length(proxy_list);
		for (i=0;i<n;i=i+2) {
			proxy_name = g_list_nth_data(proxy_list,i);
			proxy_email = g_list_nth_data(proxy_list,i+1);
			gtk_tree_store_append (priv->store, &iter, NULL);
			gtk_tree_store_set (priv->store, &iter, 0, broken_image, 1, g_strconcat(proxy_name, "\n", proxy_email, NULL), -1);
		}
		gtk_tree_view_set_model (GTK_TREE_VIEW(priv->tree),GTK_TREE_MODEL (priv->store));
	}

	g_free (file_name);
	if (broken_image)
		g_object_unref (broken_image);

	if (cnc)
		g_object_unref (cnc);
}

void
org_gnome_proxy_account_login (EPopup *ep, EPopupItem *p, gpointer data)
{
	gchar *uri = data;
	proxyLoginPrivate *priv;
	EGwConnection *cnc;
	gchar *gladefile;

	/* This pops-up the password dialog in case the User has forgot-passwords explicitly */
	cnc = proxy_login_get_cnc (mail_config_get_account_by_source_url (uri), NULL);
	if (cnc)
		g_object_unref (cnc);

	pld = proxy_login_new();
	priv = pld->priv;

	gladefile = g_build_filename (EVOLUTION_GLADEDIR,
				      "proxy-login-dialog.glade",
				      NULL);
	priv->xml = glade_xml_new (gladefile, NULL, NULL);
	g_free (gladefile);

	priv->main = glade_xml_get_widget (priv->xml, "proxy_login_dialog");
	pld->account = mail_config_get_account_by_source_url (uri);
	priv->tree = GTK_TREE_VIEW (glade_xml_get_widget (priv->xml, "proxy_login_treeview"));
	priv->store =  gtk_tree_store_new (2,
					   GDK_TYPE_PIXBUF,
					   G_TYPE_STRING
					   );
	proxy_login_setup_tree_view ();
	proxy_login_update_tree ();
	g_signal_connect (GTK_DIALOG (priv->main), "response", G_CALLBACK(proxy_login_cb), NULL);
	gtk_widget_show (GTK_WIDGET (priv->main));
 }

static EPopupItem popup_items[] = {
/* To Translators: In this case, Proxy does not mean something like 'HTTP Proxy', but a groupwise
 * feature by which one person can send/read mails/appointments using another person's identity
 * without knowing his password, for example if that other person is on vacation */
	{ E_POPUP_ITEM, (gchar *) "20.emc.04", (gchar *) N_("_Proxy Login..."), org_gnome_proxy_account_login, NULL, NULL, 0, EM_POPUP_FOLDER_STORE }
};

static void
popup_free (EPopup *ep, GSList *items, gpointer data)
{
	g_slist_free (items);
}

void
org_gnome_create_proxy_login_option (EPlugin *ep, EMPopupTargetFolder *t)
{
	EAccount *account;
	GSList *menus = NULL;
	gint i;

	account = mail_config_get_account_by_source_url (t->uri);
	if (g_strrstr (t->uri,"groupwise://") && !account->parent_uid) {
		popup_items[0].label =  _(popup_items[0].label);
		for (i = 0; i < sizeof (popup_items) / sizeof (popup_items[0]); i++)
			menus = g_slist_prepend (menus, &popup_items[i]);
		e_popup_add_items (t->target.popup, menus, NULL, popup_free, t->uri);
	}
	return;

}
