/* sunone-account.c
 *
 * Copyright (C) 2002-2004 Sun Microsystems, Inc
 *
 * AUTHORS
 *     Jack Jia <jack.jia@sun.com>
 *     Harry Lu <harry.lu@sun.com>
 *     Alfred Peng <alfred.peng@sun.com>
 *     Jedy Wang <jedy.wang@sun.com>
 *     Rodrigo Moya <rodrigo@ximian.com>
 *
 */

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

#include <string.h>
#include <glib/gi18n.h>
#include <libedataserverui/e-passwords.h>
#include <camel/camel-url.h>
#include <e-util/e-dialog-utils.h>

#include "sunone-util.h"
#include "sunone-account.h"
#include "sunone-component.h"

struct _SunOneAccountPrivate {
	char *name;
	char *uid;
	char *server;
	char *protocol;
	char *user;
	char *password;
	char *id_email;
	char *proxy;
	int poll_interval;

	gboolean save_password;
	gboolean dont_ask_password;
	gboolean account_online;
	gboolean connecting;

	SunOneConnection *cnc;
};

static void sunone_account_dispose (GObject *object);
static void sunone_account_finalize (GObject *object);
static SunOneConnection * sunone_account_connect (SunOneAccount *account);

#define	DEFAULT_POLL_INTERVAL	30

static GObjectClass *parent_class = NULL;

G_DEFINE_TYPE (SunOneAccount, sunone_account, G_TYPE_OBJECT)

static void
sunone_account_class_init (SunOneAccountClass *klass)
{
	GObjectClass *object_class = (GObjectClass *) klass;
	parent_class = g_type_class_ref (G_TYPE_OBJECT);

	object_class->dispose  = sunone_account_dispose;
	object_class->finalize = sunone_account_finalize;
}

static void
sunone_account_init (SunOneAccount *object)
{
	SunOneAccount *account = SUNONE_ACCOUNT (object);
	SunOneAccountPrivate *priv = account->priv;

	account->priv = g_new0 (SunOneAccountPrivate, 1);
	priv = account->priv;

	/* initialize private members */
	priv->poll_interval = DEFAULT_POLL_INTERVAL;
	priv->account_online = TRUE;
	priv->connecting = FALSE;
}

static void
sunone_account_dispose (GObject *object)
{
	SunOneAccount *account = SUNONE_ACCOUNT (object);
	SunOneAccountPrivate *priv = account->priv;

	g_return_if_fail (IS_SUNONE_ACCOUNT (account));

	if (priv) {
		if (priv->name) {
			g_free (priv->name);
			priv->name = NULL;
		}
		if (priv->uid) {
			g_free (priv->uid);
			priv->uid = NULL;
		}
		if (priv->server) {
			g_free (priv->server);
			priv->server = NULL;
		}
		if (priv->protocol) {
			g_free (priv->protocol);
			priv->protocol = NULL;
		}
		if (priv->user) {
			g_free (priv->user);
			priv->user = NULL;
		}
		if (priv->password) {
			g_free (priv->password);
			priv->password = NULL;
		}

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

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

		if (priv->cnc) {
			g_object_unref (priv->cnc);
			priv->cnc = NULL;
		}

		g_free (priv);
		account->priv = NULL;
	}

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

static void
sunone_account_finalize (GObject *object)
{
	if (G_OBJECT_CLASS (parent_class)->finalize)
		(* G_OBJECT_CLASS (parent_class)->finalize) (object);
}

gboolean
sunone_account_get_dont_ask_password (SunOneAccount *account)
{
	SunOneAccountPrivate *priv = account->priv;

	g_return_val_if_fail (IS_SUNONE_ACCOUNT (account), FALSE);
	return priv->dont_ask_password;
}

void
sunone_account_set_dont_ask_password (SunOneAccount *account, gboolean dont_ask_password)
{
	SunOneAccountPrivate *priv = account->priv;

	g_return_if_fail (IS_SUNONE_ACCOUNT (account));
	priv->dont_ask_password = dont_ask_password;
}

const char *
sunone_account_get_email (SunOneAccount *account)
{
	SunOneAccountPrivate *priv = account->priv;

	g_return_val_if_fail (IS_SUNONE_ACCOUNT (account), NULL);
	return (const char *) priv->id_email;
}

void
sunone_account_set_email (SunOneAccount *account, const char *id_email)
{
	SunOneAccountPrivate *priv = account->priv;

	g_return_if_fail (IS_SUNONE_ACCOUNT (account));
	if (priv->id_email)
		g_free (priv->id_email);
	priv->id_email = g_strdup (id_email);
}

const char *
sunone_account_get_name (SunOneAccount *account)
{
	SunOneAccountPrivate *priv = account->priv;

	g_return_val_if_fail (IS_SUNONE_ACCOUNT (account), NULL);
	return (const char *) priv->name;
}

void
sunone_account_set_name (SunOneAccount *account, const char *name)
{
	SunOneAccountPrivate *priv = account->priv;

	g_return_if_fail (IS_SUNONE_ACCOUNT (account));

	if (priv->name)
		g_free (priv->name);
	priv->name = g_strdup (name);
}

const char *
sunone_account_get_uid (SunOneAccount *account)
{
	g_return_val_if_fail (IS_SUNONE_ACCOUNT (account), NULL);
	return (const char *) account->priv->uid;
}

void
sunone_account_set_uid (SunOneAccount *account, const char *uid)
{
	g_return_if_fail (IS_SUNONE_ACCOUNT (account));

	if (account->priv->uid)
		g_free (account->priv->uid);
	account->priv->uid = g_strdup (uid);
}

const char *
sunone_account_get_server (SunOneAccount *account)
{
	SunOneAccountPrivate *priv = account->priv;

	g_return_val_if_fail (IS_SUNONE_ACCOUNT (account), NULL);
	return (const char *) priv->server;
}

void
sunone_account_set_server (SunOneAccount *account, const char *server)
{
	SunOneAccountPrivate *priv = account->priv;

	g_return_if_fail (IS_SUNONE_ACCOUNT (account));

	if (priv->server)
		g_free (priv->server);
#ifdef ENABLE_IDN
	priv->server = sunone_util_get_encoded_server_port_name (server);
#else
	priv->server = g_strdup (server);
#endif
}

const char *
sunone_account_get_protocol (SunOneAccount *account)
{
	SunOneAccountPrivate *priv = account->priv;

	g_return_val_if_fail (IS_SUNONE_ACCOUNT (account), NULL);
	return (const char *) priv->protocol;
}

void
sunone_account_set_protocol (SunOneAccount *account, const char *protocol)
{
	SunOneAccountPrivate *priv = account->priv;

	g_return_if_fail (IS_SUNONE_ACCOUNT (account));

	if (priv->protocol)
		g_free (priv->protocol);
	priv->protocol = g_strdup (protocol);
}

const char *
sunone_account_get_user (SunOneAccount *account)
{
	SunOneAccountPrivate *priv = account->priv;

	g_return_val_if_fail (IS_SUNONE_ACCOUNT (account), NULL);
	return (const char *) priv->user;
}

void
sunone_account_set_user (SunOneAccount *account, const char *user)
{
	SunOneAccountPrivate *priv = account->priv;

	g_return_if_fail (IS_SUNONE_ACCOUNT (account));

	if (priv->user)
		g_free (priv->user);
	priv->user = g_strdup (user);
}

int
sunone_account_get_poll_interval (SunOneAccount *account)
{
	SunOneAccountPrivate *priv = account->priv;

	g_return_val_if_fail (IS_SUNONE_ACCOUNT (account), DEFAULT_POLL_INTERVAL);

	return priv->poll_interval;
}

void 
sunone_account_set_poll_interval (SunOneAccount *account, int poll_interval)
{
	SunOneAccountPrivate *priv = account->priv;

	g_return_if_fail (IS_SUNONE_ACCOUNT (account));

	priv->poll_interval = poll_interval;
}

const char *
sunone_account_get_password (SunOneAccount *account, gboolean is_interactive, const char *prompt)
{
	SunOneAccountPrivate *priv = account->priv;

	g_return_val_if_fail (IS_SUNONE_ACCOUNT (account), NULL);


	if (!priv->password) {
		gboolean remember;
		char *password;
		char *key = g_strdup_printf ("wcap://%s@%s",
					     priv->user,
					     priv->server);

		password = e_passwords_get_password ("Evolution-jescs", key);
		if (password) {
			priv->password = g_strdup (password);
			g_free (password);
		} else if (is_interactive) {

			if (priv->dont_ask_password) {
				g_free (key);
				return NULL;
			}

			if (priv->password) {
				g_free (key);
				return (const char *) priv->password;
			}

			remember = priv->save_password;

			priv->password = e_passwords_ask_password (
				_("Enter password"), "Evolution-jescs", key, prompt,
				E_PASSWORDS_REMEMBER_FOREVER | E_PASSWORDS_SECRET, &remember,
				NULL);

			if (priv->password == NULL) {
				/*user canceled the dialog*/
				priv->dont_ask_password = TRUE;
			}

			sunone_account_set_save_password (account, remember);
		}

		g_free (key);
	}

	return (const char *) priv->password;
}

void
sunone_account_set_password (SunOneAccount *account, const char *password)
{
	SunOneAccountPrivate *priv = account->priv;

	g_return_if_fail (IS_SUNONE_ACCOUNT (account));

	if (priv->password)
		g_free (priv->password);
	priv->password = g_strdup (password);
}

gboolean
sunone_account_get_save_password (SunOneAccount *account)
{
	SunOneAccountPrivate *priv = account->priv;

	g_return_val_if_fail (IS_SUNONE_ACCOUNT (account), FALSE);
	return priv->save_password;
}

void
sunone_account_set_save_password (SunOneAccount *account, gboolean save)
{
	SunOneAccountPrivate *priv = account->priv;

	g_return_if_fail (IS_SUNONE_ACCOUNT (account));
	priv->save_password = save;
}

void
sunone_account_forget_password (SunOneAccount *account)
{
	gchar *key;
	SunOneAccountPrivate *priv = account->priv;

	g_return_if_fail (IS_SUNONE_ACCOUNT (account));

	key = g_strdup_printf ("wcap://%s@%s", priv->user, priv->server);
	e_passwords_forget_password ("Evolution-jescs", key);

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

void
sunone_account_set_proxy (SunOneAccount *account, const char *proxy)
{
	g_return_if_fail (IS_SUNONE_ACCOUNT (account));

	if (account->priv->proxy)
		g_free (account->priv->proxy);
	account->priv->proxy = g_strdup (proxy);
}

const char *
sunone_account_get_proxy (SunOneAccount *account)
{
	SunOneAccountPrivate *priv = account->priv;

	g_return_val_if_fail (IS_SUNONE_ACCOUNT (account), NULL);
	return (const char *) priv->proxy;
}

static char *
get_proxy_url ()
{
	GConfClient *conf_client;
	char *proxy_uri = NULL;

	conf_client = gconf_client_get_default ();
	if (gconf_client_get_bool (conf_client, "/system/http_proxy/use_http_proxy", NULL)) {
		char *server;
		int port;

		server = gconf_client_get_string (conf_client, "/system/http_proxy/host", NULL);
		port = gconf_client_get_int (conf_client, "/system/http_proxy/port", NULL);

		if (server && server[0]) {
			if (gconf_client_get_bool (conf_client, "/system/http_proxy/use_authentication", NULL)) {
				char *user, *password;

				user = gconf_client_get_string (conf_client,
									"/system/http_proxy/authentication_user", NULL);
				password = gconf_client_get_string (conf_client,
									"/system/http_proxy/authentication_password", NULL);
				proxy_uri = g_strdup_printf("http://%s:%s@%s:%d", user, password, server, port);

				g_free (user);
				g_free (password);
			} else
				proxy_uri = g_strdup_printf ("http://%s:%d", server, port);

			g_free (server);
		}
	}

	g_object_unref (conf_client);
	return proxy_uri;
}


SunOneAccount *
sunone_account_new (EAccountList *account_list, EAccount *edata)
{
	SunOneAccount *sunone_account;
	SunOneAccountPrivate *priv;
	const char *use_proxy;
	char *new_url;
	int port;
	CamelURL *url;
	CamelURL *server_url;

	url = camel_url_new (edata->source->url, NULL);
	if (!url) {
		g_warning ("Could not parse sunone uri '%s'", edata->source->url);
		return NULL;
	}

	new_url = g_strdup (camel_url_get_param (url, "serverurl"));
	if (!new_url) {
		/*we have no new url, so convert from old settings*/
		const char * use_ssl = camel_url_get_param (url, "use_ssl");

		if (use_ssl && !strcmp (use_ssl, "always"))
			new_url = g_strdup_printf ("https://%s:%d", url->host ? url->host : "", url->port);
		else
			new_url = g_strdup_printf ("http://%s:%d", url->host ? url->host : "", url->port);
	} else if (strncasecmp (new_url, "http", 4) && strncasecmp (new_url, "https", 5)){
		char *tmp_url;

		tmp_url = g_strconcat ("http://", new_url, NULL);
		g_free (new_url);
		new_url = tmp_url;
	}

	server_url = camel_url_new (new_url, NULL);
	g_free (new_url);
	if (!server_url) {
		g_warning ("Could not parse sunone uri '%s'", edata->source->url);
		return NULL;
	}

	sunone_account = g_object_new (SUNONE_ACCOUNT_TYPE, NULL);
	priv = sunone_account->priv;
	priv->name = g_strdup (edata->name);
	priv->uid = g_strdup (edata->uid);
	priv->user = g_strdup (url->user);
	priv->id_email = g_strdup (edata->id->address);

	port = server_url->port;
	if (!strcmp (server_url->protocol, "https")) {
		priv->protocol = g_strdup ("https");
		if (port == 0)
			port = 443;
	} else {
		priv->protocol = g_strdup ("http");
		if (port == 0)
			port = 80;
	}

	priv->server = g_strdup_printf ("%s:%d", server_url->host ? server_url->host : "", port);

	use_proxy = camel_url_get_param (url, "useproxy");
	if (use_proxy)
		priv->proxy = get_proxy_url ();

	if (edata->source->auto_check)
		priv->poll_interval = edata->source->auto_check_time;

	camel_url_free (server_url);
	camel_url_free (url);
	
	return sunone_account;
}


gboolean
sunone_account_set_offline (SunOneAccount *account)
{
	SunOneAccountPrivate *priv;

	g_return_val_if_fail (account != NULL, FALSE);
	g_return_val_if_fail (IS_SUNONE_ACCOUNT (account), FALSE);

	priv = account->priv;
	if (priv->cnc) {
		g_object_unref (priv->cnc);
		priv->cnc = NULL;
	}

	priv->account_online = FALSE;

	return TRUE;
}

gboolean
sunone_account_set_online (SunOneAccount *account)
{
	SunOneAccountPrivate *priv = account->priv;

	g_return_val_if_fail (IS_SUNONE_ACCOUNT (account), FALSE);

	if (!priv->account_online) {
		if (sunone_account_get_connection (account))
			return TRUE;
		else 
			return FALSE;
	} else {
		return TRUE;
	}
}

void
sunone_account_is_offline (SunOneAccount *account, int *state)
{
	g_return_if_fail (IS_SUNONE_ACCOUNT (account));

	sunone_component_is_offline (global_sunone_component, state);
}

SunOneConnection *
sunone_account_get_connection (SunOneAccount *account)
{
	SunOneAccountPrivate *priv = account->priv;
	static GStaticMutex mutex = G_STATIC_MUTEX_INIT;

	g_return_val_if_fail (IS_SUNONE_ACCOUNT (account), NULL);

	if (!priv->cnc) {
		g_static_mutex_lock (&mutex);
		priv->cnc = sunone_account_connect (account);
		g_static_mutex_unlock (&mutex);
	}
	return priv->cnc;
}

void
sunone_account_set_connecting (SunOneAccount *account, gboolean status)
{
	g_return_if_fail (IS_SUNONE_ACCOUNT (account));

	account->priv->connecting = status;
}

gboolean
sunone_account_is_connecting (SunOneAccount *account)
{
	g_return_val_if_fail (IS_SUNONE_ACCOUNT (account), FALSE);

	return account->priv->connecting;
}

static SunOneConnection *
sunone_account_connect (SunOneAccount *account)
{
	SunOneAccountPrivate *priv = account->priv;
	SunOneConnection *so_cnc;
	const char *password;
#ifdef ENABLE_IDN
	const char *t;
#endif
	char *full_uri;
	char *prompt;
	char *hname;
	guint error_code;
	int state;

	g_return_val_if_fail (IS_SUNONE_ACCOUNT (account), NULL);

	sunone_account_is_offline (account, &state);
	if (state == OFFLINE_MODE) {
		e_notice (NULL, GTK_MESSAGE_ERROR, _("Sun JESCS Account is offline"));
		return NULL;
	}

	/*try to connect to calendar server unless user canceled password dialog*/
#ifdef ENABLE_IDN
	t = sunone_account_get_server (account);
	hname = sunone_util_get_decoded_server_port_name (t);
#else
	hname = g_strdup (sunone_account_get_server (account));
#endif
	prompt = g_strdup_printf (_("Please enter the password for %s@%s"),
						sunone_account_get_user (account), hname);
	do {
		password = sunone_account_get_password (account, TRUE, prompt);
		g_free (prompt);
		if (!password) {
			/*password dialog is canceled*/
			g_free (hname);
			return NULL;
		}

		full_uri = g_strdup_printf ("%s://%s", sunone_account_get_protocol (account),
						sunone_account_get_server (account));

		so_cnc = sunone_connection_new (full_uri, sunone_account_get_proxy (account),
						sunone_account_get_user (account),
						password);
		g_free (full_uri);
		error_code = sunone_connection_login (so_cnc);

		if (SUNONE_ERROR_IS_SUCCESSFUL (error_code))
			break;
		sunone_account_forget_password (account);
		g_object_unref (G_OBJECT (so_cnc)); 
		prompt = g_strdup_printf (_("Unable to authenticate to WCAP server.\n"
						"Wrong password or could not connect to server.\n"
						"Please enter the password for %s@%s\n"
						"or click Cancel button to cancel"),
						sunone_account_get_user (account), hname);
	} while (1);
	g_free (hname);

	error_code = sunone_connection_version (so_cnc);
	if (!SUNONE_ERROR_IS_SUCCESSFUL (error_code)) {
		/* do nothing for now */
	}
	
	priv->cnc = so_cnc;
	priv->account_online = TRUE;

	return so_cnc;

}

char *
sunone_account_get_physical_uri (SunOneAccount *account, const char *calid, const char *type)
{
	return g_strdup_printf ("wcap://%s@%s/?protocol=%s&uid=%s&calid=%s&type=%s",
				sunone_account_get_user (account),
				sunone_account_get_server (account),
				sunone_account_get_protocol (account),
				sunone_account_get_uid (account),
				calid, type);
}
