/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/* e-book-auth-util.c - Lame helper to load addressbooks with authentication.
 *
 * Copyright (C) 2005 Novell, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of version 2 of the GNU Lesser General Public
 * License as published by the Free Software Foundation.
 *
 * 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 Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * Authors: Hans Petter Jansson <hpj@novell.com>
 *
 * Mostly taken from Evolution's addressbook/gui/component/addressbook.c
 */

#include <config.h>
#include <string.h>
#include <gtk/gtkmessagedialog.h>
#include <glib/gi18n-lib.h>
#include <libebook/e-book.h>
#include <libedataserverui/e-passwords.h>
#include <libedataserver/e-url.h>
#include "e-book-auth-util.h"

static void addressbook_authenticate (EBook *book, gboolean previous_failure,
				      ESource *source, EBookCallback cb, gpointer closure);
static void auth_required_cb (EBook *book, gpointer data);
typedef struct {
	ESource       *source;
	EBook         *book;

	EBookCallback  open_func;
	gpointer       open_func_data;
} LoadSourceData;

static void
free_load_source_data (LoadSourceData *data)
{
	if (data->source)
		g_object_unref (data->source);
	if (data->book) {
		g_signal_handlers_disconnect_by_func (data->book, auth_required_cb, NULL);
		g_object_unref (data->book);
	}
	g_free (data);
}

static gchar *
remove_parameters_from_uri (const gchar *uri)
{
	char *euri_str;
	EUri *euri;

	euri = e_uri_new (uri);
	euri_str = e_uri_to_string (euri, FALSE);
	e_uri_free (euri);
	return euri_str;
}

static void
load_source_auth_cb (EBook *book, EBookStatus status, gpointer closure)
{
	LoadSourceData *data = closure;

	if (status != E_BOOK_ERROR_OK) {
		/* the user clicked cancel in the password dialog */
		if (status == E_BOOK_ERROR_CANCELLED) {
			if (e_book_check_static_capability (book, "anon-access")) {
				GtkWidget *dialog;

				/* XXX "LDAP" has to be removed from the folowing message
				   so that it wil valid for other servers which provide 
				   anonymous access*/

				dialog = gtk_message_dialog_new (NULL,
								 0,
								 GTK_MESSAGE_WARNING,
								 GTK_BUTTONS_OK,
								 _("Accessing LDAP Server anonymously"));
				g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
				gtk_widget_show (dialog);
				if (data->open_func)
					data->open_func (book, E_BOOK_ERROR_OK, data->open_func_data);
				free_load_source_data (data);
				return;
			}
		} else if (status == E_BOOK_ERROR_INVALID_SERVER_VERSION) {
#if 0
			e_error_run (NULL, "addressbook:server-version", NULL);
#endif
			status = E_BOOK_ERROR_OK;
			if (data->open_func)
				data->open_func (book, status, data->open_func_data);
			free_load_source_data (data);
			return;
		} else {
			const gchar *uri = e_book_get_uri (book);
			gchar *stripped_uri = remove_parameters_from_uri (uri);
			const gchar *auth_domain = e_source_get_property (data->source, "auth-domain");
			const gchar *component_name;

			component_name = auth_domain ? auth_domain : "Addressbook";

			e_passwords_forget_password (component_name, stripped_uri);
			addressbook_authenticate (book, TRUE, data->source, load_source_auth_cb, closure);

			g_free (stripped_uri);
		       
			return;
		}
	}

	if (data->open_func)
		data->open_func (book, status, data->open_func_data);

	free_load_source_data (data);
}

static gboolean
get_remember_password (ESource *source)
{
	const gchar *value;

	value = e_source_get_property (source, "remember_password");
	if (value && !g_ascii_strcasecmp (value, "true"))
		return TRUE;

	return FALSE;
}

static void
set_remember_password (ESource *source, gboolean value)
{
	e_source_set_property (source, "remember_password",
			       value ? "true" : "false");
}

static void
addressbook_authenticate (EBook *book, gboolean previous_failure, ESource *source,
			  EBookCallback cb, gpointer closure)
{
	const gchar *auth;
	const gchar *user;
	const gchar *component_name;
	const char *password     = NULL;
	char *pass_dup           = NULL;
	const gchar *uri               = e_book_get_uri (book);
        gchar *stripped_uri      = remove_parameters_from_uri (uri);
	const gchar *auth_domain = e_source_get_property (source, "auth-domain");
			
	component_name = auth_domain ? auth_domain : "Addressbook";
	uri = stripped_uri;

	password = e_passwords_get_password (component_name, uri);

	auth = e_source_get_property (source, "auth");

	if (auth && !strcmp ("ldap/simple-binddn", auth)) {
		user = e_source_get_property (source, "binddn");
	}
	else if (auth && !strcmp ("plain/password", auth)) {
		user = e_source_get_property (source, "user");
		if (!user) {
			user = e_source_get_property (source, "username");
		}
	}
	else {
		user = e_source_get_property (source, "email_addr");
	}
	if (!user)
		user = "";

	if (!password) {
		char *prompt;
		char *password_prompt;
		gboolean remember;
		char *failed_auth;
		guint32 flags = E_PASSWORDS_REMEMBER_FOREVER|E_PASSWORDS_SECRET|E_PASSWORDS_ONLINE;

		if (previous_failure) {
			failed_auth = _("Failed to authenticate.\n");
			flags |= E_PASSWORDS_REPROMPT;
		}
		else {
			failed_auth = "";
		}

		password_prompt = g_strdup_printf (_("Enter password for %s (user %s)"),
						   e_source_peek_name (source), user);

		prompt = g_strconcat (failed_auth, password_prompt, NULL);
		g_free (password_prompt);

		remember = get_remember_password (source);
		pass_dup = e_passwords_ask_password (prompt, component_name, uri, prompt,
						     flags, &remember,
						     NULL);
		if (remember != get_remember_password (source))
			set_remember_password (source, remember);

		g_free (prompt);
	}

	if (password || pass_dup) {
		e_book_async_authenticate_user (book, user, password ? password : pass_dup,
						e_source_get_property (source, "auth"),
						cb, closure);
		g_free (pass_dup);
	}
	else {
		/* they hit cancel */
		cb (book, E_BOOK_ERROR_CANCELLED, closure);
	}

	g_free (stripped_uri);
}

static void
auth_required_cb (EBook *book, gpointer data)
{
	LoadSourceData *load_source_data = g_new0 (LoadSourceData, 1);
	
	load_source_data->source = g_object_ref (g_object_ref (e_book_get_source (book)));

	addressbook_authenticate (book, FALSE, load_source_data->source,
				  load_source_auth_cb, load_source_data);
}

static void
load_source_cb (EBook *book, EBookStatus status, gpointer closure)
{
	LoadSourceData *load_source_data = closure;

	if (status == E_BOOK_ERROR_OK && book != NULL) {
		const gchar *auth;

		auth = e_source_get_property (load_source_data->source, "auth");
		if (auth && strcmp (auth, "none")) {
			g_signal_connect (book, "auth_required", (GCallback) auth_required_cb, NULL);
			
			if (e_book_is_online (book)) {
				addressbook_authenticate (book, FALSE, load_source_data->source,
							  load_source_auth_cb, closure);
				return;
			}
		}
	}

	if (load_source_data->open_func)
		load_source_data->open_func (book, status, load_source_data->open_func_data);

	free_load_source_data (load_source_data);
}

/**
 * e_load_book_source:
 * @source: an #ESource
 * @open_func: a function to call when the operation finishes, or %NULL
 * @user_data: data to pass to callback function
 *
 * Creates a new #EBook specified by @source, and starts a non-blocking
 * open operation on it. If the book requires authorization, presents
 * a window asking the user for such.
 *
 * When the operation finishes, calls the callback function indicating
 * if it succeeded or not. If you don't care, you can pass %NULL for
 * @open_func, and no action will be taken on completion.
 *
 * Return value: A new #EBook that is being opened.
 **/
EBook *
e_load_book_source (ESource *source, EBookCallback open_func, gpointer user_data)
{
	EBook          *book;
	LoadSourceData *load_source_data = g_new0 (LoadSourceData, 1);

	load_source_data->source = g_object_ref (source);
	load_source_data->open_func = open_func;
	load_source_data->open_func_data = user_data;

	book = e_book_new (source, NULL);
	if (!book)
		return NULL;

	load_source_data->book = book;
	g_object_ref (book);
	e_book_async_open (book, FALSE, load_source_cb, load_source_data);
	return book;
}
