/* PureAdmin
 * Copyright (C) 2003 Isak Savo
 *
 *  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, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/*
 * Handles Pre-requistities for the user manager
 *
 * Copyright (C) 2006 - Isak Savo
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <gtk/gtk.h>

#include "cfg.h"
#include "helper.h"
#include "gui_helper.h"
#include "globals.h"
#include "system_accounts.h"
#include "prereq_usrmanager.h"

/* The columns in the GUI list */
enum {
	COL_CHANGETYPE, /* Not visible in the GUI */
	COL_DESCRIPTION,
	COL_VALUE,
	COL_VALUE2,
	N_COLUMNS
};
/* Callbacks */
static void cell_value1_edited_cb (GtkCellRendererText *cellrenderertext, gchar *path, gchar *new_text, gpointer user_data);
static void cell_value2_edited_cb (GtkCellRendererText *cellrenderertext, gchar *path, gchar *new_text, gpointer user_data);

static void init_prereq_window (void)
{
	static GtkWidget *tree = NULL;
	GtkCellRenderer *renderer;
	GtkTreeViewColumn *column;
	GtkListStore *model;
	
	if (tree)
		return;
	tree = DW("tree_system_changes");
	
	model  = gtk_list_store_new (N_COLUMNS, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
   
	gtk_tree_view_set_model (GTK_TREE_VIEW (tree), GTK_TREE_MODEL (model));
	g_object_unref (G_OBJECT (model));
	
	gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree)),
				     GTK_SELECTION_SINGLE);

	renderer = gtk_cell_renderer_text_new();
	column = gtk_tree_view_column_new_with_attributes (_("Action"), renderer,
							   "text", COL_DESCRIPTION,
							   NULL);
	gtk_tree_view_append_column (GTK_TREE_VIEW(tree), column);

	renderer = gtk_cell_renderer_text_new();
	column = gtk_tree_view_column_new_with_attributes (_("Value"), renderer,
							   "text", COL_VALUE,
							   NULL);
	g_object_set(renderer, "editable", TRUE, NULL);
	gtk_tree_view_append_column (GTK_TREE_VIEW(tree), column);
	g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(cell_value1_edited_cb), GINT_TO_POINTER(COL_VALUE));
	
	renderer = gtk_cell_renderer_text_new();
	column = gtk_tree_view_column_new_with_attributes (_("Numerical Value"), renderer,
							   "text", COL_VALUE2,
							   NULL);
	g_object_set(renderer, "editable", TRUE, NULL);
	gtk_tree_view_append_column (GTK_TREE_VIEW(tree), column);
	g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(cell_value2_edited_cb), GINT_TO_POINTER(COL_VALUE2));
	
}
static gboolean is_pwfile_ok()
{
	return g_file_test(cfg.pwfile, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR);
}
static gboolean is_default_home_ok ()
{
	return g_file_test (cfg.default_home, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR);
}

static gboolean is_default_gid_ok()
{
	return (cfg.default_gid > 1 && sys_get_gid_exists(cfg.default_gid));
}
static gboolean is_default_uid_ok()
{
	return (cfg.default_uid > 1 && sys_get_uid_exists(cfg.default_uid));
}

static SystemActions *get_required_actions (void)
{
	SystemActions *rv = g_new0 (SystemActions, 1);
	
	if (!is_pwfile_ok())
	{
		// It doesn't exist, get the guessed default value
		rv->pwfile = cfg_get_pftp_default_pwfile();
		rv->changes |= CHANGES_CREATE_PWFILE;
	}
	if (!is_default_home_ok())
	{
		rv->ftpdir = cfg_find_default_home();
		rv->changes |= CHANGES_CREATE_HOMEDIR;
	}
	if (!is_default_gid_ok())
	{
		rv->grp_id = sys_get_available_group_id(100);
		rv->ftpgroup = g_strdup ("ftpgroup");
		rv->changes |= CHANGES_ADD_GROUP;
	}
	if (!is_default_uid_ok())
	{
		rv->usr_id = sys_get_available_user_id(100);
		rv->ftpuser = g_strdup ("ftpuser");
		rv->changes |= CHANGES_ADD_USER;
	}
	return rv;
}

static void append_row (SystemChangesType change, const gchar *description, const gchar *value, const gchar *value2)
{
	static GtkWidget *tree = NULL;
	static GtkListStore *model;
	GtkTreeIter iter;
	if (tree == NULL)
	{
		tree = DW("tree_system_changes");
		model = (GtkListStore*) gtk_tree_view_get_model(GTK_TREE_VIEW (tree));
	}
	gtk_list_store_append(GTK_LIST_STORE(model), &iter);
	gtk_list_store_set(model, &iter, 
						COL_CHANGETYPE, change,
						COL_DESCRIPTION, description,
						COL_VALUE, value,
						COL_VALUE2, value2,
						-1);
}

static void clear_list(void)
{
	GtkListStore *model = (GtkListStore*) gtk_tree_view_get_model (GTK_TREE_VIEW (DW("tree_system_changes")));
	gtk_list_store_clear(model);
}

static gboolean take_required_actions (SystemActions *actions, GList **errors)
{
	GError *err = NULL;
	gboolean ok, retval = TRUE;
	guint grp = cfg.default_gid;
	guint usr = cfg.default_uid;
	
	g_return_val_if_fail (errors != NULL && *errors == NULL, FALSE);
	
	if (actions->changes & CHANGES_ADD_GROUP)
	{
		ok = sys_create_group(actions->ftpgroup, actions->grp_id, &err);
		if (!ok)
		{
			*errors = g_list_append (*errors, g_strdup (err->message));
			g_error_free(err);
			err = NULL;
			retval = FALSE;
		}
		else
		{
			cfg.default_gid = cfg_find_ftpgroup_gid();
			grp = actions->grp_id;
		}
	}
	if (actions->changes & CHANGES_ADD_USER)
	{
		ok = sys_create_user(actions->ftpuser, actions->usr_id, grp, &err);
		if (!ok)
		{
			*errors = g_list_append (*errors, g_strdup (err->message));
			g_error_free(err);
			err = NULL;
			retval = FALSE;
		}
		else
		{
			cfg.default_uid = cfg_find_ftpuser_uid();
			usr = actions->usr_id;
		}
	}
	if (actions->changes & CHANGES_CREATE_HOMEDIR)
	{
		ok = misc_create_directory(actions->ftpdir, (usr > 0 ? usr : -1), (grp > 0 ? grp : -1), &err);
		if (!ok)
		{
			*errors = g_list_append (*errors, g_strdup (err->message));
			g_error_free(err);
			err = NULL;
			retval = FALSE;
		}
		g_free (cfg.default_home);
		cfg.default_home = g_strdup (actions->ftpdir);
	}
	if (actions->changes & CHANGES_CREATE_PWFILE)
	{
		ok = misc_create_passwd_file(actions->pwfile);
		if (!ok)
		{
			*errors = g_list_append (*errors, g_strdup (err->message));
			g_error_free(err);
			err = NULL;
			retval = FALSE;
		}
		g_free (cfg.pwfile);
		g_free (cfg.pdbfile);
		cfg.pwfile = g_strdup (actions->pwfile);
		gchar *fnamebase = filename_without_extension(actions->pwfile);
		cfg.pdbfile = g_strconcat (fnamebase, ".pdb", NULL);
	}
	
	return retval;
}

static void populate_actions (SystemActions *actions)
{
	if (actions->changes & CHANGES_ADD_GROUP)
	{
		gchar newgid[10];
		g_snprintf(newgid, 10, "%u", actions->grp_id);
		append_row(CHANGES_ADD_GROUP, _("Add a dedicated ftp system group"), actions->ftpgroup, newgid);
	}
	if (actions->changes & CHANGES_ADD_USER)
	{
		gchar newuid[10];
		g_snprintf(newuid, 10, "%u", actions->usr_id);
		append_row(CHANGES_ADD_USER, _("Add a dedicated ftp system user"), actions->ftpuser, newuid);
	}
	if (actions->changes & CHANGES_CREATE_HOMEDIR)
	{
		append_row (CHANGES_CREATE_HOMEDIR, _("Create a default home directory for ftp users"), actions->ftpdir, NULL);
	}
	if (actions->changes & CHANGES_CREATE_PWFILE)
		append_row(CHANGES_CREATE_PWFILE, _("Create a new password file for ftp users"), actions->pwfile, NULL);
}

/**** Global Functions ****/

gboolean prereq_show_dialog (GtkWindow *parent)
{
	init_prereq_window();
	gboolean rv = FALSE;;
	SystemActions *a = get_required_actions();
	populate_actions(a);
	g_object_set_data_full(G_OBJECT(DW("dlg_prepare_usermanager")), "systemactions", a, (GDestroyNotify) system_actions_free);
	if (parent)
		gtk_window_set_transient_for(GTK_WINDOW (DW("dlg_prepare_usermanager")), parent);
	
	GtkResponseType response = gtk_dialog_run(GTK_DIALOG(DW("dlg_prepare_usermanager")));
	if (response == GTK_RESPONSE_YES)
	{
		a = g_object_get_data(G_OBJECT(DW("dlg_prepare_usermanager")), "systemactions");
		GList *errors = NULL;
		if (!take_required_actions (a, &errors))
		{
			pur_log_wrn ("Couldn't take action:");
			GList *node = errors;
			while (node)
			{
				pur_log_wrn((gchar*)node->data);
				g_free (node->data);
				node = g_list_next(node);
			}
			g_list_free(node);
		}
		else
			rv = TRUE;
	}
	clear_list();
	gtk_widget_hide(DW("dlg_prepare_usermanager"));
	return rv;
}


gboolean prereq_are_all_requirements_met (void)
{
	if (!is_default_gid_ok())
		return FALSE;
	if (!is_default_uid_ok())
		return FALSE;
	if (!is_default_home_ok())
		return FALSE;
	if (!is_pwfile_ok())
		return FALSE;
	
	return TRUE;
}

void system_actions_free (SystemActions *actions)
{
	g_free (actions->ftpuser);
	g_free (actions->ftpgroup);
	g_free (actions->ftpdir);
	g_free (actions->pwfile);
	g_free (actions);
}

static gboolean is_valid_system_name (const gchar *str)
{
	if (!str || *str == '\0')
		return FALSE;
	const gchar *p = str;
	while (*p)
	{
		if (!g_ascii_isalpha(*p))
			return FALSE;
		p++;
	}
	return TRUE;
}

/**** Callbacks ****/
static void cell_value1_edited_cb (GtkCellRendererText *cellrenderertext, gchar *path, gchar *new_text, gpointer user_data)
{
	GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (DW("tree_system_changes")));
	GtkTreeIter iter;
	SystemActions *actions;
	SystemChangesType change_type;
	
	gtk_tree_model_get_iter_from_string(model, &iter, path);
	gtk_tree_model_get(model, &iter, COL_CHANGETYPE, &change_type, -1);
	
	actions = g_object_get_data(G_OBJECT(DW("dlg_prepare_usermanager")), "systemactions");
	g_return_if_fail (actions != NULL);
	
	switch (change_type)
	{
		case CHANGES_ADD_GROUP:
		case CHANGES_ADD_USER:
			// Username column
			if (!is_valid_system_name(new_text))
			{
				gui_display_msgdialog("Invalid Name", "The text you entered contains illegal characters. Only english characters are allowed",
					MSGDLG_TYPE_ERROR, DW("dlg_prepare_usermanager"));
			}
			else 
			{
				gtk_list_store_set (GTK_LIST_STORE(model), &iter, COL_VALUE, new_text, -1);
				/* Update the action object */
				if (change_type == CHANGES_ADD_GROUP) {
					g_free (actions->ftpgroup);
					actions->ftpgroup = g_strdup (new_text);
				} else {
					g_free (actions->ftpuser);
					actions->ftpuser = g_strdup (new_text);
				}
			}
			break;
			
		case CHANGES_CREATE_HOMEDIR:
		case CHANGES_CREATE_PWFILE:
			if (!g_path_is_absolute(new_text))
				gui_display_msgdialog("Invalid Directory", "The entered directory is not a valid location", 
					MSGDLG_TYPE_ERROR, DW("dlg_prepare_usermanager"));
			else
			{
				gtk_list_store_set (GTK_LIST_STORE(model), &iter, COL_VALUE, new_text, -1);
				if (change_type == CHANGES_CREATE_HOMEDIR) {
					g_free (actions->ftpdir);
					actions->ftpdir = g_strdup (new_text);
				} else {
					g_free (actions->pwfile);
					actions->pwfile = g_strdup (new_text);
				}
			}
			break;
	}
}

static void cell_value2_edited_cb (GtkCellRendererText *cellrenderertext, gchar *path, gchar *new_text, gpointer user_data)
{
	GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (DW("tree_system_changes")));
	GtkTreeIter iter;
	SystemActions *actions;
	SystemChangesType change_type;
	
	gtk_tree_model_get_iter_from_string(model, &iter, path);
	gtk_tree_model_get(model, &iter, COL_CHANGETYPE, &change_type, -1);
	
	actions = g_object_get_data(G_OBJECT(DW("dlg_prepare_usermanager")), "systemactions");
	g_return_if_fail (actions != NULL);
	
	guint num = atoi(new_text);
	if (num == 0)
		return;
	
	g_free (new_text);
	new_text = g_strdup_printf ("%d", num);
		
	if (change_type == CHANGES_ADD_GROUP)
		actions->grp_id = num;
	else if (change_type == CHANGES_ADD_USER)
		actions->usr_id = num;
	else
	{
		/* FIXME; Write a better message */
		gui_display_msgdialog("Invalid Setting", "You cannot set this field since this row does not allow it!", MSGDLG_TYPE_ERROR, DW("dlg_prepare_usermanager"));
	}
}


syntax highlighted by Code2HTML, v. 0.9.1