/*
  gyrus-admin-mailbox.c

  GYRUS -- GNOME Cyrus Administrator. Administrator Mailboxes Modules.

  Copyright (C) 2003-2004 Alejandro Valdés Jiménez <avaldes@utalca.cl>
  Copyright (C) 2003-2004 Jorge Bustos Bustos <jbustos@utalca.cl>
  Copyright (C) 2003-2005 Claudio Saavedra Valdés <csaavedra@alumnos.utalca.cl>
  
  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, see <http://www.gnu.org/licenses/>.
  
*/

#include <config.h>

#include <gtk/gtk.h>
#include <glib/gprintf.h>
#include <string.h>

#include "glib/gi18n.h"
#include "gyrus-main-app.h"
#include "gyrus-admin.h"
#include "gyrus-common.h"
#include "gyrus-admin-private.h"
#include "gyrus-admin-mailbox.h"
#include "gyrus-admin-acl.h"

#define BUFFER_SIZE 1024

void
gyrus_admin_acl_set_tree_view (GyrusAdmin *admin, const gchar *mailbox_path);

gboolean
gyrus_admin_mailbox_get_quota (GyrusAdmin *admin, const gchar *mailbox_path,
			       gint *quota_limit, gint *quota_used,
			       gchar **error)
{
	gchar *msg, **parts;
	GyrusImapStatus status;

	msg = g_strdup_printf (". getquota \"%s\"\n", mailbox_path);
	gyrus_admin_write_channel (admin, msg);
	g_free (msg);

	*error = NULL;
	
	status = gyrus_admin_listen_channel (admin, &msg, NULL);
	if (status == GYRUS_IMAP_STATUS_BYE) {
		g_free (msg);
		return FALSE;
	}

	parts = g_strsplit (msg, " ", -1);
	if (g_ascii_strcasecmp (parts[1], "QUOTA") == 0) {
		g_strfreev (parts);
		parts = g_strsplit (msg, "(", -1);
		sscanf (parts[1], "STORAGE %d %d)",
			quota_used, quota_limit);
		g_free (msg);
		gyrus_admin_listen_channel (admin, &msg, NULL);
	}
	else if (g_ascii_strcasecmp (parts[1], "NO") == 0) {
		*quota_used = -1;
		*quota_limit = -1;
		if (strcmp (parts [2], "Quota") == 0)
			*error = g_strdup (_("Quota does not exist"));
		else if (strcmp (parts [2], "Permission") == 0)
			*error = g_strdup (_("Permission denied"));
	}
	g_free (msg);	
	g_strfreev (parts);
	return TRUE;
}

static void
gyrus_admin_mailbox_get_human_quota (gint quota, gchar *str_quota)
{
	const gchar* prefix;
	gdouble f_quota = quota;
	gint iter = 0;
     
	while (f_quota > 1024.0 && iter < 4) {
		f_quota /= 1024.0;
		iter ++;
	}

	switch (iter) {
	case 0:
		prefix = "K";
		break;
	case 1:
		prefix = "M";
		break;
	case 2:
		prefix = "G";
		break;
	default:
		prefix = "T";
	}

	g_sprintf (str_quota, "%.1f %sB", f_quota, prefix);
}


gboolean
gyrus_admin_mailbox_set_quota (GyrusAdmin *admin, const gchar *mailbox_path,
			       gint new_quota)
{
	GyrusImapStatus status;
	gchar *msg;

	msg = g_strdup_printf (". setquota \"%s\" (STORAGE %d)\n",
			       mailbox_path, new_quota);
	gyrus_admin_write_channel (admin, msg);
	g_free (msg);
	
	status = gyrus_admin_listen_channel (admin, &msg, NULL);
	g_free (msg);

	return (status == GYRUS_IMAP_STATUS_OK);
	
}

void
gyrus_admin_mailbox_set_sensitive (GyrusAdmin *admin, gboolean status)
{
	gtk_widget_set_sensitive (admin->priv->expander_modify_quota, status);
	gtk_widget_set_sensitive (admin->priv->expander_modify_acl, status);
}

void
gyrus_admin_mailbox_show_info (GyrusAdmin *admin, const gchar *user,
			       const gchar *mailbox_path)
{
	gint quota_limit, quota_used, quota_free;
	gchar str_quota_limit[BUFFER_SIZE], str_quota_free[BUFFER_SIZE];
	gchar *msg;
	
	/* shows the name of the current mailbox */
	gtk_label_set_text (GTK_LABEL (admin->priv->label_mailbox_name),
			    mailbox_path);

	if (!gyrus_admin_mailbox_get_quota (admin, mailbox_path,
					    &quota_limit, &quota_used, &msg)) {
		gyrus_admin_logged_out (admin);
		return;
	}
	
	quota_free = quota_limit - quota_used;
	
	/* sets the quota info in the screen */
	if (quota_limit < 0)
		g_sprintf (str_quota_limit,
			   "%s", msg);
	else
		gyrus_admin_mailbox_get_human_quota (quota_limit,
						     str_quota_limit);
	
	if (quota_free < 0)
		g_sprintf (str_quota_free, _("Quota overloaded"));
	else
		gyrus_admin_mailbox_get_human_quota (quota_free,
						     str_quota_free);

	gtk_label_set_text (GTK_LABEL (admin->priv->label_mailbox_owner),
			    user);
	gtk_label_set_text (GTK_LABEL (admin->priv->label_quota_limit),
			    str_quota_limit);
	gtk_label_set_text (GTK_LABEL (admin->priv->label_quota_free),
			    str_quota_free);

	gyrus_admin_acl_set_tree_view (admin, mailbox_path);
/* 	gyrus_admin_acl_get (admin, mailbox_path, NULL); */

	if (msg != NULL)
		g_free (msg);
}

void
gyrus_admin_mailbox_clear_info (GyrusAdmin *admin)
{
	GtkListStore *store;

	gtk_label_set_text (GTK_LABEL (admin->priv->label_mailbox_name), "");
	gtk_label_set_text (GTK_LABEL (admin->priv->label_mailbox_owner), "");
	gtk_label_set_text (GTK_LABEL (admin->priv->label_quota_limit), "");
	gtk_label_set_text (GTK_LABEL (admin->priv->label_quota_free), "");

	store = GTK_LIST_STORE
		(gtk_tree_view_get_model (admin->priv->treeview_acl));
	gtk_list_store_clear (store);
}

void
gyrus_admin_mailbox_on_button_quota_apply_clicked (GtkButton *button,
						   gpointer userdata)
{
	GyrusAdmin * admin = GYRUS_ADMIN (userdata);
	gchar **tokens;
	const gchar *new_quota_str;
	gint new_quota;
	gchar *new_quota_invalid;
	gchar *mailbox_path;

	new_quota_str =
		gtk_entry_get_text (GTK_ENTRY (admin->priv->entry_quota_new));

	if (!g_ascii_strcasecmp (new_quota_str, ""))
		return;
	
	mailbox_path = g_strdup (gtk_label_get_text
				 (GTK_LABEL (admin->priv->label_mailbox_name)));

	new_quota = 1024 * g_strtod (new_quota_str, &new_quota_invalid);

	if (*new_quota_invalid) {
		gyrus_common_show_message (NULL, GTK_MESSAGE_ERROR,
					   _("Quota not valid. Please try again."));
		g_free (mailbox_path);
		return;
	}

	if (gyrus_admin_mailbox_set_quota (admin, mailbox_path, new_quota)) {
		gtk_entry_set_text (GTK_ENTRY (admin->priv->entry_quota_new),
				    "");
		tokens = g_strsplit (mailbox_path, gyrus_admin_get_separator_char (admin), 3);
		gyrus_admin_mailbox_show_info (admin, tokens[1], mailbox_path);
		g_strfreev (tokens);
	}
	else
		gyrus_common_show_message (NULL, GTK_MESSAGE_ERROR,
					   _("Unable to change quota. "
					     "Are you sure do you have the appropriate permissions?"));

	g_free (mailbox_path);

}

void
gyrus_admin_mailbox_on_entry_quota_new_activate (GtkEntry *entry,
						 gpointer user_data)
{
	GtkButton *button = GTK_BUTTON (user_data);
	gtk_button_clicked (button);
}

static gboolean
gyrus_admin_mailbox_exists (GyrusAdmin *admin,
			    const gchar *mailbox)
{
	GyrusImapStatus status;
	
	gchar *msg = g_strdup_printf (". list \"\" \"%s\"\n", mailbox);
	gyrus_admin_write_channel (admin, msg);
	g_free (msg);

	status = gyrus_admin_listen_channel (admin, &msg, NULL);
	g_free (msg);
	if (status == GYRUS_IMAP_STATUS_LIST) {
		gyrus_admin_listen_channel (admin, &msg, NULL);
		g_free (msg);

		return TRUE;
	}

	return FALSE;
}

static gboolean 
gyrus_admin_mailbox_name_is_valid (GyrusAdmin *admin, const gchar *mailbox)
{
	return (/*!g_strrstr (mailbox, " ") &&*/
		!g_strrstr (mailbox, gyrus_admin_get_separator_char (admin)));
}


/*
  Create a new mailbox in server managed by @admin. If @quota is not negative,
  the maximun amount of space that can be used by the mailbox is @quota KB.

  The root for the mailbox is @path.

  If there is any error and the mailbox can not be created, returns FALSE and
  a error message is allocated in @error.

  @admin: The #GyrusAdmin related to the server where the mailbox must be
  created.

  @mailbox: The name of the mailbox. Must be a valid mailbox name for IMAP
  standard.

  @path: The base path for the new mailbox. Must be existent.

  @quota: The quota limit in KB, or a negative value for unquoted mailboxes.

  @error: If gyrus_admin_new_mailbox () returns FALSE, a newly allocated error
  message.

  Returns: TRUE if successful, FALSE in any other case.
  
*/

gboolean
gyrus_admin_mailbox_new (GyrusAdmin *admin, const gchar *mailbox,
			 const gchar *path, gint quota, gchar **error)
{
	GyrusImapStatus status;
	gchar *new_mailbox;
	gchar *msg;

	if (!gyrus_admin_mailbox_name_is_valid (admin, mailbox)) {
		*error = g_strdup_printf (_("'%s' is not a valid "
					    "mailbox name. Please try a different one."),
					  mailbox);
		return FALSE;
	}

	if (strcmp (path, "user") != 0 &&
	    !gyrus_admin_mailbox_exists (admin, path)) {
		*error = g_strdup_printf (_("Parent mailbox '%s' does not exist. "
					    "Please refresh the mailboxes list and try again."),
					  path);
		return FALSE;
	}
	
	new_mailbox = g_strconcat (path, gyrus_admin_get_separator_char (admin), 
					  mailbox, NULL);

	if (gyrus_admin_mailbox_exists (admin, new_mailbox)) {
		*error = g_strdup_printf (_("Mailbox '%s' already exists. "
					    "Please try a different name."),
					  new_mailbox);
		return FALSE;
	}

	msg = g_strdup_printf (". create \"%s\"\n", new_mailbox);
	gyrus_admin_write_channel (admin, msg);
	g_free (msg);

	status = gyrus_admin_listen_channel (admin, &msg, NULL);
	if (status != GYRUS_IMAP_STATUS_OK) {
		g_free (msg);
		*error = g_strdup (_("Unable to create the mailbox. "
				     "Are you sure do you have the appropriate permissions?"));
		g_free (new_mailbox);
		return FALSE;
	}

	g_free (msg);

	if (quota >= 0) {
		if (!gyrus_admin_mailbox_set_quota (admin, new_mailbox,
						    quota)) {
			*error = g_strdup (_("Mailbox created, "
					     "but could not set quota."));
			g_free (new_mailbox);
			return FALSE;
		}
	}

	g_free (new_mailbox);
	return TRUE;
}

/* Returns a GList of all the mailboxes under a given mailbox.
   This list must be freed, and also it's elements.
 */
static GList *
gyrus_admin_get_submailboxes (GyrusAdmin *admin,
			      const gchar *mailbox)
{
	GList *list = NULL;
	gchar *c_mailbox;
	gchar *msg;

	g_assert (GYRUS_IS_ADMIN (admin));

	msg = g_strdup_printf (". list \"%s\" *\n", mailbox);
	gyrus_admin_write_channel (admin, msg);
	g_free (msg);

	while (gyrus_admin_listen_channel (admin, &msg, NULL) ==
	       GYRUS_IMAP_STATUS_LIST) {
		
		/* this cuts the name of the mailbox of the readed buffer,
		 being the buffer of the form:
		 
		* LIST () "." "user.mymailbox"\n\0
		
		*/
		c_mailbox = gyrus_admin_get_mailbox_from_list_message (msg);
		list = g_list_prepend (list, c_mailbox);
		g_free (msg);
	}
	g_free (msg);

	return list;
}

static gboolean
gyrus_admin_mailbox_delete (GyrusAdmin *admin,
			    const gchar *mailbox,
			    gchar **error)
{
	GyrusImapStatus status;
	gchar *msg;
	
	g_assert (GYRUS_IS_ADMIN (admin));

	msg = g_strdup_printf (". delete \"%s\"\n",
			       mailbox);

	gyrus_admin_write_channel (admin, msg);
	g_free (msg);

	status = gyrus_admin_listen_channel (admin, &msg, NULL);
	g_free (msg);

	if (status == GYRUS_IMAP_STATUS_NO) {
		*error = g_strdup_printf
			(_("Unable to delete '%s'. Permission denied."),
			 mailbox);
		return FALSE;
	}
	return TRUE;
}

void
gyrus_admin_mailbox_delete_all (GyrusAdmin *admin,
				const gchar *mailbox)
{
	GList *list;
	GList *iter = NULL;
	gchar *error;

	g_assert (GYRUS_IS_ADMIN (admin));
	
	list = gyrus_admin_get_submailboxes (admin, mailbox);

#ifdef GYRUS_DELETE_AUTO_ACL
	const gchar *user = gyrus_admin_get_current_user (admin);
#endif

	for (iter = list; iter != NULL; iter = g_list_next (iter)) {

#ifdef GYRUS_DELETE_AUTO_ACL
		
		if (!gyrus_admin_acl_set_entry (admin, (gchar *)iter->data,
						user, "+d", &error)) {
			gyrus_common_show_message
				(NULL, GTK_MESSAGE_ERROR, error);
			g_free (error);
		}

#endif
		
		if (!gyrus_admin_mailbox_delete (admin, (gchar *)iter->data,
						 &error)) {
			gyrus_common_show_message
				(NULL, GTK_MESSAGE_ERROR, error);
			g_free (error);
		}
	}
	
	for (iter = list; iter != NULL; iter = g_list_next (iter))
		g_free (iter->data);
	
	g_list_free (list);
}


gboolean
gyrus_admin_acl_rights_have_right (const gchar *rights,
				   const gchar right)
{
	return (strchr (rights, right) != NULL) ? TRUE : FALSE;
}

void
gyrus_admin_acl_set_tree_view (GyrusAdmin *admin, const gchar *mailbox_path)
{
	GtkTreeIter iter;
	GList *list = NULL;	
	GList *l_iter;
	GtkTreeModel *model;
	gchar *error;
	
	model = gtk_tree_view_get_model (admin->priv->treeview_acl);
	gtk_list_store_clear (GTK_LIST_STORE (model));
	
	list = gyrus_admin_acl_get (admin, mailbox_path, &error);

/*	if (GYRUS_IS_MAIN_APP (admin->priv->parent_app))
		gyrus_main_app_menu_item_set_sensitive
			(GYRUS_MAIN_APP (admin->priv->parent_app),
			 "EntryNew", list != NULL);
*/
	if (list == NULL) {
		gtk_label_set_text (GTK_LABEL (admin->priv->label_acl),
				    error);
		gtk_widget_hide (GTK_WIDGET (admin->priv->scrolled_acl));
		gtk_widget_show (admin->priv->label_acl);
		g_free (error);
		return;
	}
	
	gtk_widget_hide (admin->priv->label_acl);
	gtk_widget_show (GTK_WIDGET (admin->priv->scrolled_acl));
	
	for (l_iter = list; l_iter != NULL; l_iter = g_list_next (l_iter)) {
		gtk_list_store_append (GTK_LIST_STORE (model), &iter);
		gtk_list_store_set (GTK_LIST_STORE (model), &iter,
				    COL_ACL_IDENTIFIER,
				    GYRUS_IMAP_ACL_ENTRY (l_iter->data)->identifier,
				    COL_ACL_RIGHT_L,
				    gyrus_admin_acl_rights_have_right (GYRUS_IMAP_ACL_ENTRY(l_iter->data)->rights, 'l'),
				    COL_ACL_RIGHT_R,
				    gyrus_admin_acl_rights_have_right (GYRUS_IMAP_ACL_ENTRY(l_iter->data)->rights, 'r'),
				    COL_ACL_RIGHT_S,
				    gyrus_admin_acl_rights_have_right (GYRUS_IMAP_ACL_ENTRY(l_iter->data)->rights, 's'),
				    COL_ACL_RIGHT_W,
				    gyrus_admin_acl_rights_have_right (GYRUS_IMAP_ACL_ENTRY(l_iter->data)->rights, 'w'),
				    COL_ACL_RIGHT_I,
				    gyrus_admin_acl_rights_have_right (GYRUS_IMAP_ACL_ENTRY(l_iter->data)->rights, 'i'),
				    COL_ACL_RIGHT_P,
				    gyrus_admin_acl_rights_have_right (GYRUS_IMAP_ACL_ENTRY(l_iter->data)->rights, 'p'),
				    COL_ACL_RIGHT_C,
				    gyrus_admin_acl_rights_have_right (GYRUS_IMAP_ACL_ENTRY(l_iter->data)->rights, 'c'),
				    COL_ACL_RIGHT_D,
				    gyrus_admin_acl_rights_have_right (GYRUS_IMAP_ACL_ENTRY(l_iter->data)->rights, 'd'),
				    COL_ACL_RIGHT_A,
				    gyrus_admin_acl_rights_have_right (GYRUS_IMAP_ACL_ENTRY(l_iter->data)->rights, 'a'),
				    COL_ACL_MODIFIED, FALSE,
				    -1);
		
/*		GYRUS_IMAP_ACL_ENTRY (l_iter->data)->rights); */
		
	}

	gyrus_admin_acl_list_free (list);
}

inline gboolean
gyrus_admin_acl_has_selection (GyrusAdmin *admin)
{
	GtkTreeSelection *selection;
	selection  = gtk_tree_view_get_selection (admin->priv->treeview_acl);
	return gtk_tree_selection_get_selected (selection, NULL, NULL);
}

void
gyrus_admin_delete_selected_acl_entry (GyrusAdmin *admin)
{
	GtkTreeIter iter;
	gchar *mailbox, *identifier, *error;
	GtkTreeModel *model;
	GtkTreeSelection *selection;
	
	mailbox = g_strdup (gtk_label_get_text
			    (GTK_LABEL (admin->priv->label_mailbox_name)));
	selection  = gtk_tree_view_get_selection (admin->priv->treeview_acl);
/*	model      = gtk_tree_view_get_model (admin->priv->treeview_acl); */
	
	if (!gtk_tree_selection_get_selected (selection, &model, &iter))
		return;

	gtk_tree_model_get (model, &iter,
			    COL_ACL_IDENTIFIER, &identifier,
			    -1);
	
	if (gyrus_admin_acl_delete_entry (admin, mailbox,
					  identifier, &error))
	{
		selection = gtk_tree_view_get_selection (admin->priv->treeview_acl);
		gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
		
	} else {
		gyrus_common_show_message
			(NULL, GTK_MESSAGE_ERROR, error);
		g_free (error);
	}

	g_free (mailbox);
	g_free (identifier);
}

void
gyrus_admin_start_editing_selected_acl (GyrusAdmin *admin)
{
	GtkTreeSelection *selection;
	GtkTreeModel *model;
	GtkTreeIter iter;
	GtkTreePath *path;
	GtkTreeViewColumn * column;
	
	selection  = gtk_tree_view_get_selection (admin->priv->treeview_acl);
	if (!gtk_tree_selection_get_selected (selection, &model, &iter))
		return;
	
	path = gtk_tree_model_get_path (model, &iter);
	column = GTK_TREE_VIEW_COLUMN (g_object_get_data
				       (G_OBJECT (admin->priv->treeview_acl),
					"column-identifier"));
	gtk_tree_view_set_cursor (admin->priv->treeview_acl, path,
				  column, TRUE);
	gtk_tree_path_free (path);
}

void
gyrus_admin_add_acl_entry (GyrusAdmin *admin)
{
	GtkTreeModel *model;
	GtkTreeIter iter;
	GtkTreeViewColumn *column;
	GtkTreePath *path;
	gboolean duplicated = FALSE;
	GyrusImapAclEntry *entry;
	GList *l_iter;
	gint i;
	const gchar* mailbox = gtk_label_get_text (GTK_LABEL (admin->priv->label_mailbox_name));

	gchar *default_entry_name = g_strdup (_("new entry"));
	gchar *entry_name = g_strdup (default_entry_name);
	
	GList *acl = gyrus_admin_acl_get (admin, mailbox, NULL);

	if (acl == NULL) {
		gtk_widget_show (GTK_WIDGET (admin->priv->scrolled_acl));
		gtk_widget_hide (GTK_WIDGET (admin->priv->label_acl));
	}

	/* check it the new name already exists. */

	i = 0;
	do {
		if (i > 0) {
			g_free (entry_name);
			entry_name = g_strdup_printf ("%s %i", default_entry_name, i);
			duplicated = FALSE;
		}
		l_iter = acl;
		while (l_iter != NULL && !duplicated) {
			entry = GYRUS_IMAP_ACL_ENTRY (l_iter->data);
			if (strcmp (entry->identifier, entry_name) == 0)
				duplicated = TRUE;
			l_iter = g_list_next (l_iter);
		}
		if (duplicated)
			i++;
	} while (duplicated);
	
	gyrus_admin_acl_list_free (acl);
	
	model = gtk_tree_view_get_model (admin->priv->treeview_acl);

	gtk_list_store_append (GTK_LIST_STORE (model), &iter);

	gtk_list_store_set (GTK_LIST_STORE (model), &iter,
			    COL_ACL_IDENTIFIER, entry_name,
			    COL_ACL_RIGHT_L, TRUE,
			    -1);

	gyrus_admin_acl_set_entry (admin,
				   mailbox,
				   entry_name, "l", NULL);
	g_free (default_entry_name);
	g_free (entry_name);
	
	path = gtk_tree_model_get_path (model, &iter);
	column = GTK_TREE_VIEW_COLUMN (g_object_get_data
				       (G_OBJECT (admin->priv->treeview_acl),
					"column-identifier"));
	gtk_tree_view_set_cursor (admin->priv->treeview_acl, path,
				  column, TRUE);
	gtk_tree_path_free (path);
}
