/* $Id: e2_ownership_dialog.c 868 2008-05-06 04:42:09Z tpgww $

Copyright (C) 2003-2008 tooar <tooar@gmx.net>
Portions copyright (C) 1999 Michael Clark.

This file is part of emelFM2.
emelFM2 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 3, or (at your option)
any later version.

emelFM2 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 emelFM2; see the file GPL. If not, contact the Free Software
Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "emelfm2.h"
//#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include "e2_dialog.h"
#include "e2_ownership_dialog.h"

typedef struct _E2_OwnersDldRuntime
{
	GtkWidget *user_combo;
	GtkWidget *group_combo;
	GtkWidget *recurse_dirs_button;
	uid_t owner;
	gid_t group;
	gboolean recurse;
	gboolean permission;
	DialogButtons result;
} E2_OwnersDldRuntime;

/**
@brief get user id corresponing to text entered into the dialog

@param rt pointer to dialog data struct

@return the id
*/
static uid_t _e2_ownership_dialog_get_user_id (E2_OwnersDldRuntime *rt)
{
	gchar *user;
	struct passwd *pw_buf;
	uid_t user_id;

	user = (gchar *) gtk_entry_get_text (GTK_ENTRY (GTK_BIN (rt->user_combo)->child));
	user = e2_utf8_to_locale (user);
	if (user != NULL)
	{
		if ((pw_buf = getpwnam (user)) != NULL)
			user_id = pw_buf->pw_uid;
		else
		{
			user_id = (uid_t) strtol (user, NULL, 10);
			if (errno == EINVAL)
			{
				//FIXME user 0 probably not relevant
			}
		}
		g_free (user);
	}
	else
		user_id = 0;	//FIXME

	return user_id;
}
/**
@brief get group id corresponing to text entered into the dialog

@param rt pointer to dialog data struct

@return the id
*/
static gid_t _e2_ownership_dialog_get_group_id (E2_OwnersDldRuntime *rt)
{
	gchar *group;
	struct group *grp_buf;
	gid_t group_id;

	group = (gchar *) gtk_entry_get_text (GTK_ENTRY (GTK_BIN (rt->group_combo)->child));
	group = e2_utf8_to_locale (group);
	if (group != NULL)
	{
		if ((grp_buf = getgrnam (group)) != NULL)
			group_id = grp_buf->gr_gid;
		else
		{
			group_id = (gid_t) strtol (group, NULL, 10);
			if (errno == EINVAL)
			{
				//FIXME group 0 probably not relevant
			}
		}
		g_free (group);
	}
	else
		group_id = 0;

	return group_id;
}
/**
@brief dialog response callback

@param dialog the ownership-dialog
@param response the response for the clicked button
@param rt pointer to dialog data struct

@return
*/
static void _e2_ownership_dialog_response_cb (GtkDialog *dialog,
	gint response, E2_OwnersDldRuntime *rt)
{
	switch (response)
	{
		case GTK_RESPONSE_OK:
		case E2_RESPONSE_APPLYTOALL:
			rt->owner = _e2_ownership_dialog_get_user_id (rt);
			rt->group = _e2_ownership_dialog_get_group_id (rt);
			break;
		default:
		break;
	}
	gtk_widget_destroy (GTK_WIDGET (dialog));
	e2_dialog_response_decode_cb (dialog, response, &rt->result);
}
/**
@brief update recurse flag if permission is valid

@param togglebutton the clicked button
@param rt pointer to dialog data struct

@return
*/
static void _e2_ownership_dialog_toggle_recurse_button_cb
	(GtkToggleButton *togglebutton, E2_OwnersDldRuntime *rt)
{
	if (rt->permission)
		rt->recurse = GTK_TOGGLE_BUTTON (rt->recurse_dirs_button)->active;
	return;
}
/**
@brief show custom message about inadequate permission

@param box the box to hold the data

@return
*/
void e2_ownership_dialog_warn (GtkWidget *box)
{
	GtkWidget *frame = gtk_frame_new ("");
	gtk_box_pack_start (GTK_BOX (box), frame, TRUE, TRUE, E2_PADDING);
	gtk_widget_show (frame);

	GtkWidget *sub_vbox = gtk_vbox_new (FALSE, 0);
    gtk_container_add (GTK_CONTAINER (frame), sub_vbox);
  	gtk_widget_show (sub_vbox);
	GtkWidget *hbox = gtk_hbox_new (FALSE, 0);
    gtk_container_add (GTK_CONTAINER (sub_vbox), hbox);
	e2_widget_add_mid_label (hbox, _("You are not authorised to make any change."), 0.5,
		TRUE, E2_PADDING*2);
	gtk_widget_show (hbox);
	//padding line
	hbox = gtk_hbox_new (FALSE, 0);
	gtk_container_add (GTK_CONTAINER (sub_vbox), hbox);
    e2_widget_add_mid_label (hbox, (" "), 0, FALSE, 0);
  	gtk_widget_show (hbox);
}
/**
@brief create and run a dialog for changing item permissions
This is run from inside a queued thread-function
@param localpath path of item to be processed
@param owner_ret store for returning the new owner
@param group_ret store for returning the new group
@param recurse_ret store for returning whether to recurse the changes
@param permission_ret store for returning whether a change is authorised
@param multi TRUE if this dialog is part of a series for multiple items

@return enumerator corresponing to the clicked dialog button
*/
DialogButtons e2_ownership_dialog_run (VPATH *localpath,
	uid_t *owner_ret, gid_t *group_ret, gboolean *recurse_ret,
	gboolean *permission_ret, gboolean multi)
{
	GtkWidget *ownership_dialog;
	GtkWidget *dialog_vbox;
	GtkWidget *action_area;
	GtkWidget *table, *hbox;

//	E2_OwnersDldRuntime *rt = ALLOCATE (E2_OwnersDldRuntime);
//	CHECKALLOCATEDWARN (rt, return GTK_RESPONSE_CANCEL);
	E2_OwnersDldRuntime rt;

	struct stat statbuf;

	if (e2_fs_lstat (localpath, &statbuf E2_ERR_NONE()))
		return CANCEL;

	GString *label_text = g_string_sized_new (NAME_MAX+20);
	gboolean thisis_dir = e2_fs_is_dir3 (localpath E2_ERR_NONE());

	ownership_dialog = e2_dialog_create (NULL, NULL, _("ownership"),
		_e2_ownership_dialog_response_cb, &rt);	//rt);
	dialog_vbox = GTK_DIALOG (ownership_dialog)->vbox;
	action_area = GTK_DIALOG (ownership_dialog)->action_area; //this is for the buttons
	gtk_container_set_border_width (GTK_CONTAINER (dialog_vbox), E2_PADDING);

//	table = e2_widget_add_table (dialog_vbox, 2, 3, TRUE, TRUE, 0); //2 rows, 3 cols, homogen, fill, no pad
//	gtk_table_set_row_spacing (GTK_TABLE(table), 0, E2_PADDING);
	gchar *label = (thisis_dir) ? _("Directory name") : _("Filename") ;
	gchar *name = g_filename_display_basename (VPSTR (localpath));
	g_string_printf (label_text, "%s: <b>%s</b>", label, name);
	g_free (name);
	//e2_widget_add_mid_label_to_table (table, label_text->str, 0, 1, 2, 0, 1);
	hbox = gtk_hbox_new (FALSE, 0);
	gtk_box_pack_start (GTK_BOX(dialog_vbox), hbox, TRUE, TRUE, E2_PADDING); //top, bottom padding
	e2_widget_add_mid_label (hbox, label_text->str, 0, TRUE, E2_PADDING); //L, R padding
	gtk_widget_show (hbox);

	gchar *s = e2_fs_get_perm_string (statbuf.st_mode);
	g_string_assign (label_text, s);
	g_free (s);
	g_string_insert_c (label_text, e2_utils_get_byte_position (label_text->str, 7), ' ');
	g_string_insert_c (label_text, e2_utils_get_byte_position (label_text->str, 4), ' ');
	g_string_insert_c (label_text, e2_utils_get_byte_position (label_text->str, 1), ' ');
	g_string_prepend (label_text, _("Permissions: "));
	//  e2_widget_add_mid_label_to_table (table, label_text->str, 0, 1, 2, 1, 2);
	hbox = gtk_hbox_new (FALSE, 0);
	e2_widget_add_mid_label (hbox, label_text->str, 0, TRUE, E2_PADDING);
	gtk_box_pack_start (GTK_BOX(dialog_vbox), hbox, TRUE, TRUE, 0);
	gtk_widget_show (hbox);

	g_string_free (label_text, TRUE);

	table = e2_widget_add_table (dialog_vbox, 2, 3, TRUE, TRUE, 0);  //2 rows, 3 cols, homogen, fill, no pad
	gtk_container_set_border_width (GTK_CONTAINER (table), E2_PADDING);
	gtk_table_set_row_spacings (GTK_TABLE (table), E2_PADDING);
	e2_widget_add_mid_label_to_table (table, _("User: "), 0, 0, 1, 0, 1);

	rt.user_combo = e2_combobox_get (NULL, NULL, NULL,
	E2_COMBOBOX_HAS_ENTRY | E2_COMBOBOX_NO_AUTO_HISTORY | E2_COMBOBOX_CYCLE_HISTORY);
	gtk_table_attach_defaults (GTK_TABLE (table), rt.user_combo, 1, 3, 0, 1);  // gint left col, gint right col, gint top row, gint bottom row)

	e2_widget_add_mid_label_to_table (table, _("Group: "), 0, 0, 1, 1, 2);

	rt.group_combo = e2_combobox_get (NULL, NULL, NULL,
	E2_COMBOBOX_HAS_ENTRY | E2_COMBOBOX_NO_AUTO_HISTORY | E2_COMBOBOX_CYCLE_HISTORY);
	gtk_table_attach_defaults (GTK_TABLE (table), rt.group_combo, 1, 3, 1, 2);

  {  //setup the combobox data
    struct passwd *pw_buf;
    struct group *grp_buf;
	gchar *utf;
    gchar id_buf[32];
	gint ctr = 0;	//combo box entry counter, set to 1st value in list, for now
	gint user_index = 0, grp_index = 0;  //default values for combobox display (= 1st entry)

	uid_t src_uid = statbuf.st_uid;
	gint myuid = getuid ();
	setpwent ();

	//path is to current dir
	gboolean writeok = e2_fs_check_write_permission (localpath E2_ERR_NONE());
	if (writeok)
    {
		rt.permission = TRUE;	//confirm ok to proceed
		while ((pw_buf = getpwent ()) != NULL)
	   {	//screen out superfluous names
			if (
				(myuid > 0) //user is not root
				&& ((guint) pw_buf->pw_uid > 0)  //buffer entry is not root's
				&& ((guint) pw_buf->pw_uid < UID_LOWLIMIT)  //but is less than the system-user threshold
			)
				continue;
			utf = e2_utf8_from_locale (pw_buf->pw_name);
			if (utf == NULL)
				utf = g_strdup_printf ("%d", (guint) pw_buf->pw_uid);
			gtk_combo_box_append_text (GTK_COMBO_BOX (rt.user_combo), utf);
			g_free (utf);
			//remember the combo index for the owner
			if ((uid_t) pw_buf->pw_uid == src_uid)
				user_index = ctr;
			else
				ctr++;
		}
		if ((pw_buf = getpwuid (statbuf.st_uid)) == NULL)
		{
			g_snprintf (id_buf, sizeof (id_buf), "%d", (guint) statbuf.st_uid);
			gtk_combo_box_append_text (GTK_COMBO_BOX (rt.user_combo), id_buf);
			user_index = ctr;
		}
		printd (DEBUG, "owner combo index = %d", user_index);

		ctr = 0;
		gid_t src_gid = statbuf.st_gid;
		if (myuid == 0)
		{
			while ((grp_buf = getgrent ()) != NULL)
			{
				utf = e2_utf8_from_locale (grp_buf->gr_name);
				if (utf == NULL)
					utf = g_strdup_printf ("%d", (guint) grp_buf->gr_gid);
				gtk_combo_box_append_text (GTK_COMBO_BOX (rt.group_combo), utf);
				g_free (utf);
				if ((gid_t) grp_buf->gr_gid == src_gid)
					grp_index = ctr;
				else
					ctr++;
			}
		}
		else //not root
		{
			gid_t grp_ids[REPORTABLE_GROUPS];
			gint n = getgroups (REPORTABLE_GROUPS, grp_ids);
			for (ctr = 0; ctr < n; ctr++)
			{
				if ((grp_buf = getgrgid (grp_ids[ctr])) != NULL)
				{
					utf = e2_utf8_from_locale (grp_buf->gr_name);
						if (utf == NULL)
					utf = g_strdup_printf ("%d", (guint) grp_buf->gr_gid);
					gtk_combo_box_append_text (GTK_COMBO_BOX (rt.group_combo), utf);
					g_free (utf);
					if ((gid_t) grp_buf->gr_gid == src_gid)
						grp_index = ctr;
				}
			}
		}
		if ((grp_buf = getgrgid (statbuf.st_gid)) == NULL)
		{
			g_snprintf (id_buf, sizeof (id_buf), "%d", (guint) statbuf.st_gid);
			gtk_combo_box_append_text (GTK_COMBO_BOX (rt.group_combo), id_buf);
			grp_index = ctr;
		}
		printd (DEBUG, "group combo index = %d", grp_index);
	}
	else  //no write permission, no changes allowed, just show current values
    {
		rt.permission = FALSE;	//signal not ok to proceed, even if ok is clicked
    	if ((pw_buf = getpwuid (statbuf.st_uid)) != NULL)
	    	gtk_combo_box_append_text (GTK_COMBO_BOX (rt.user_combo), g_strdup (pw_buf->pw_name));
		else
	 	{
			g_snprintf (id_buf, sizeof (id_buf), "%d", (guint) statbuf.st_uid);
			gtk_combo_box_append_text (GTK_COMBO_BOX (rt.user_combo), id_buf);
		}
    	if ((grp_buf = getgrgid (statbuf.st_gid)) != NULL)
			gtk_combo_box_append_text (GTK_COMBO_BOX (rt.group_combo), g_strdup (grp_buf->gr_name));
		else
		{
			g_snprintf (id_buf, sizeof (id_buf), "%d", (guint) statbuf.st_gid);
			gtk_combo_box_append_text (GTK_COMBO_BOX (rt.group_combo), id_buf);
		}
		gtk_widget_set_sensitive (rt.user_combo, FALSE);
		gtk_widget_set_sensitive (rt.group_combo, FALSE);
	}
    gtk_combo_box_set_active (GTK_COMBO_BOX (rt.user_combo), user_index);
    gtk_combo_box_set_active (GTK_COMBO_BOX (rt.group_combo), grp_index);

    setpwent ();
    setgrent ();
  }

	if (rt.permission && thisis_dir)  //note: no recurse button now if multiple-selection
	{
		table = e2_widget_add_table (dialog_vbox, 1, 3, TRUE, TRUE, 0); //1 row, 3 cols, homogen, fill, no pad
		gtk_table_set_row_spacing (GTK_TABLE(table), 0, E2_PADDING);
		rt.recurse_dirs_button = e2_button_add_toggle_to_table (table, _("Recurse"),
			FALSE, _e2_ownership_dialog_toggle_recurse_button_cb, &rt, //rt,
			1, 2, 0, 1 ); // gint left, gint right, gint top, gint bottom
	}

	if (!rt.permission)
		e2_ownership_dialog_warn (dialog_vbox);

	//add buttons in the order that they will appear
	GtkWidget *btn;
	if (multi)
	{
		e2_dialog_set_negative_response (ownership_dialog, E2_RESPONSE_NOTOALL);
		e2_dialog_add_defined_button (ownership_dialog, &E2_BUTTON_NOTOALL);
		btn = e2_dialog_add_button_custom (ownership_dialog, FALSE,
			&E2_BUTTON_APPLYTOALL, NULL, NULL, NULL);
		if (!rt.permission)
			gtk_widget_set_sensitive (btn, FALSE);
	}

	if (rt.permission || multi)
	{
		btn = e2_dialog_add_defined_button (ownership_dialog, &E2_BUTTON_CANCEL);
		if (!rt.permission)
			gtk_widget_set_sensitive (btn, FALSE);
	}

	e2_dialog_add_button_custom (ownership_dialog, TRUE, &E2_BUTTON_OK,
		NULL, NULL, NULL);

	e2_dialog_setup (ownership_dialog, app.main_window);
/*  non-modal not supported
	e2_dialog_run (ownership_dialog, NULL,
	E2_DIALOG_NONMODAL | E2_DIALOG_THREADED | E2_DIALOG_FREE);
*/
	gdk_threads_enter ();
	e2_dialog_run (ownership_dialog, NULL, 0);
	gtk_main ();
	gdk_threads_leave ();

//	DialogButtons result = rt.result;
//	if (result == OK || result == YES_TO_ALL)
	if (rt.result == OK || rt.result == YES_TO_ALL)
	{
		*owner_ret = rt.owner;
		*group_ret = rt.group;
		*recurse_ret = rt.recurse;
		*permission_ret = rt.permission;
	}
//	DEALLOCATE (E2_OwnersDldRuntime, rt);
//	return result;
	return rt.result;
}
