/*
 * Copyright (c)2004 The DragonFly Project.  All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 *   Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * 
 *   Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 * 
 *   Neither the name of the DragonFly Project nor the names of its
 *   contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

/*
 * fn_configure.c
 * Configuration functions for installer.
 * This includes both Configure the LiveCD Environment, and
 * Configure an Installed System (there is considerable overlap.)
 * $Id: fn_configure.c,v 1.14 2004/07/11 16:58:21 cpressey Exp $
 */

#include <sys/types.h>

#include <dirent.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <dfui/dfui.h>
#include <dfui/fspred.h>
#include <dfui/extbuf.h>
#include <dfui/mem.h>

#include <dfui/fspred.h>

#include "functions.h"
#include "diskutil.h"
#include "uiutil.h"
#include "commands.h"
#include "package.h"
#include "pathnames.h"
#include "confed.h"
#include "cmds.h"

/*** EXTERNS ***/

extern struct dfui_connection *c;
extern struct system_storage *ss;
extern char dfly_root[];

/** CONFIGURE FUNCTIONS **/

#define	PW_NOT_ALLOWED		":;,`~!@#$%^&*()+={}[]\\|/?<>'\" "
#define	GECOS_NOT_ALLOWED	":,\\\""
#define	FILENAME_NOT_ALLOWED	":;`~!#$^&*()={}[]\\|?<>'\" "
#define	MEMBERSHIPS_NOT_ALLOWED	":;`~!@#$%^&*()+={}[]\\|/?<>'\" "

void
fn_add_user(void)
{
	struct dfui_form *f;
	struct dfui_response *r;
	struct dfui_dataset *ds, *new_ds;
	char *username, *home, *passwd_1, *passwd_2, *gecos,
	     *shell, *uid, *group, *groups;
	struct commands *cmds;
	struct command *cmd;
	int done = 0;

	f = dfui_form_create(
		"add_user",
		"Add user",
		"Here you can add a user to an installed system.\n\n"
		"You can leave the Home Directory, User ID, and Login Group "
		"fields empty if you want these items to be automatically "
		"allocated by the system.",
		"",
		"f",	"username",	"Username",
			"Enter the username the user will log in as", "",
		"f",	"gecos",	"Real Name",
			"Enter the real name (or GECOS field) of this user", "",
		"f",	"passwd_1",	"Password",
			"Enter the user's password (will not be displayed)", "",
			"h",	"obscured", "true",
		"f",	"passwd_2",	"Password (Again)",
			"Re-enter the user's password to confirm", "",
			"h",	"obscured", "true",
		"f",	"shell",	"Shell",
			"Enter the full path to the user's shell program", "",
		"f",	"home",		"Home Directory",
			"Enter the full path to the user's home directory, or leave blank", "",
		"f",	"uid",		"User ID",
			"Enter this account's numeric user id, or leave blank", "",
		"f",	"group",	"Login Group",
			"Enter the primary group for this account, or leave blank", "",
		"f",	"groups",	"Other Group Memberships",
			"Enter a comma-seperated list of other groups "
			"that this user should belong to", "",
		"a",	"ok", 		"Accept and Add",		"", "",
		"a",	"cancel", 	"Return to Configure Menu",	"", "",
		NULL
	);

	ds = dfui_dataset_new();
	dfui_dataset_celldata_add(ds, dfui_id_new("username"), "");
	dfui_dataset_celldata_add(ds, dfui_id_new("gecos"), "");
	dfui_dataset_celldata_add(ds, dfui_id_new("passwd_1"), "");
	dfui_dataset_celldata_add(ds, dfui_id_new("passwd_2"), "");
	dfui_dataset_celldata_add(ds, dfui_id_new("shell"), "/bin/tcsh");
	dfui_dataset_celldata_add(ds, dfui_id_new("home"), "");
	dfui_dataset_celldata_add(ds, dfui_id_new("uid"), "");
	dfui_dataset_celldata_add(ds, dfui_id_new("group"), "");
	dfui_dataset_celldata_add(ds, dfui_id_new("groups"), "");
	dfui_form_dataset_add(f, ds);

	while (!done) {
		if (!dfui_be_present(c, f, &r)) abort_backend();

		if (dfui_id_is(dfui_response_get_action_id(r), "cancel")) {
			done = 1;
			break;
		}

		new_ds = dfui_dataset_dup(dfui_response_dataset_get_first(r));
		dfui_form_datasets_free(f);
		dfui_form_dataset_add(f, new_ds);

		/* Fetch form field values. */

		username = dfui_dataset_get_value(new_ds, "username");
		home = dfui_dataset_get_value(new_ds, "home");
		gecos = dfui_dataset_get_value(new_ds, "gecos");
		shell = dfui_dataset_get_value(new_ds, "shell");
		passwd_1 = dfui_dataset_get_value(new_ds, "passwd_1");
		passwd_2 = dfui_dataset_get_value(new_ds, "passwd_2");
		uid = dfui_dataset_get_value(new_ds, "uid");
		group = dfui_dataset_get_value(new_ds, "group");
		groups = dfui_dataset_get_value(new_ds, "groups");

		if (strlen(username) == 0) {
			inform("You must enter a username.");
			done = 0;			
		} else if (strcmp(passwd_1, passwd_2) != 0) {
			/* Passwords don't match; tell the user. */
			inform("The passwords do not match.");
			done = 0;
		} else if (!assert_clean("Username", username, PW_NOT_ALLOWED) ||
		    !assert_clean("Real Name", gecos, GECOS_NOT_ALLOWED) ||
		    !assert_clean("Password", passwd_1, PW_NOT_ALLOWED) ||
		    !assert_clean("Shell", shell, FILENAME_NOT_ALLOWED) ||
		    !assert_clean("Home Directory", home, FILENAME_NOT_ALLOWED) ||
		    !assert_clean("User ID", uid, PW_NOT_ALLOWED) ||
		    !assert_clean("Login Group", group, PW_NOT_ALLOWED) ||
		    !assert_clean("Group Memberships", groups, MEMBERSHIPS_NOT_ALLOWED)) {
			done = 0;
		} else if (!is_program("%s%s", dfly_root, shell) &&
		    strcmp(shell, "/nonexistent") != 0) {
			inform(	"Chosen shell does not exist on the system.");
			done = 0;
			continue;
		} else {
			cmds = commands_new();

			command_add(cmds, "chroot %smnt/ /usr/sbin/pw useradd "
			    "'%s' %s%s %s%s -c \"%s\" %s%s -s %s %s%s %s",
			    dfly_root, username,
			    strlen(uid) == 0 ? "" : "-u ", uid,
			    strlen(group) == 0 ? "" : "-g ", group,
			    gecos,
			    strlen(home) == 0 ? "" : "-d ", home,
			    shell,
			    strlen(groups) == 0 ? "" : "-G ", groups,
			    (strlen(home) == 0 || !is_dir(home)) ?
				"-m -k /usr/share/skel" : "");

			cmd = command_add(cmds, "echo '%s' | "
			    "chroot %smnt/ /usr/sbin/pw usermod '%s' -h 0",
			    passwd_1, dfly_root, username);
			command_set_desc(cmd, "Setting password...");

			if (commands_execute(c, cmds)) {
				inform("User `%s' was added.", username);
				done = 1;
			} else {
				inform("User was not successfully added.");
				done = 0;
			}
			commands_free(cmds);
		}

		free(home);
	}

	dfui_form_free(f);
	dfui_response_free(r);
}

void
fn_root_passwd(void)
{
	struct dfui_form *f;
	struct dfui_response *r;
	char *root_passwd_1, *root_passwd_2;
	struct dfui_dataset *ds, *new_ds;
	struct commands *cmds;
	struct command *cmd;

	f = dfui_form_create(
		"root_passwd",
		"Set Root Password",
		"Here you can set the super-user (root) password.",
		"",
		"f",	"root_passwd_1",		"Root password",
			"Enter the root password you would like to use", "",
			"h",	"obscured", "true",
		"f",	"root_passwd_2",		"Root password again",
			"Enter the root password again to confirm", "",
			"h",	"obscured", "true",
		"a",	"ok", "Accept and Set Password", "", "",
		"a",	"cancel", "Return to Configure Menu", "", "",
		NULL
	);

	ds = dfui_dataset_new();
	dfui_dataset_celldata_add(ds, dfui_id_new("root_passwd_1"), "");
	dfui_dataset_celldata_add(ds, dfui_id_new("root_passwd_2"), "");
	dfui_form_dataset_add(f, ds);

	if (!dfui_be_present(c, f, &r)) abort_backend();

	if (dfui_id_is(dfui_response_get_action_id(r), "ok")) {
		new_ds = dfui_response_dataset_get_first(r);

		/*
		 * Fetch form field values.
		 */

		root_passwd_1 = dfui_dataset_get_value(new_ds, "root_passwd_1");
		root_passwd_2 = dfui_dataset_get_value(new_ds, "root_passwd_2");

		if (strcmp(root_passwd_1, root_passwd_2) == 0) {
			/*
			 * Passwords match, so set the root password.
			 */
			cmds = commands_new();
			cmd = command_add(cmds, "echo '%s' | "
			    "chroot %smnt/ /usr/sbin/pw usermod root -h 0",
			    root_passwd_1, dfly_root);
			command_set_desc(cmd, "Setting password...");
			if (commands_execute(c, cmds)) {
				inform("The root password has been changed.");
			} else {
				inform(	"An error occurred when "
					"setting the root password.");
			}
			commands_free(cmds);
		} else {
			/*
			 * Passwords don't match - tell the user, let them try again.
			 */
			inform("The passwords do not match.");
		}
	}

	dfui_response_free(r);
	dfui_form_free(f);
}

void
fn_cvsup_sources(void)
{
	struct commands *cmds;
	
    	inform("The system has issued a cvsup command in the background.\n\nPlease tail /tmp/cvsupdate.log if you wish to follow the progress.");

	// TODO: This needs a lot more work.

	cmds = commands_new();
	command_add(cmds, "/usr/local/bin/cvsup -h cvsup.dragonflybsd.com -b /usr /usr/share/examples/cvsup/DragonFly-supfile >/tmp/cvsupdate.log");
	if (!commands_execute(c, cmds)) {
	    inform("Warning: could not launch cvsup");
	}
	commands_free(cmds);	
}

void
fn_install_packages(void)
{
	struct dfui_form *f;
	struct dfui_response *r;
	struct dfui_field *fi;
	struct dfui_dataset *ds;
	struct dfui_celldata *cd;
	FILE *pipe;
	char command[256];
	char pkg_name[256];
	struct commands *cmds;
	int pkg_count = 0;

	f = dfui_form_create(
		"install_packages",
		"Install Packages",
		"Select optional software packages that you want "
		"installed on this system.  This form lists only the "
		"software packages installed on the LiveCD; thousands "
		"more are available via the internet once DragonFly "
		"is installed.",
		"",

		"h",	"special", "nympha_install_packages",

		NULL
	);

	ds = dfui_dataset_new();
	snprintf(command, 256, "ls %svar/db/pkg", dfly_root);
	if ((pipe = popen(command, "r")) != NULL) {
		while (fgets(pkg_name, 255, pipe) != NULL) {
			pkg_name[strlen(pkg_name) - 1] = '\0';
			fi = dfui_form_field_add(f, dfui_id_new(pkg_name),
			    dfui_info_new(pkg_name, "", ""));
			dfui_field_hint_add(fi, "control", "checkbox");
			dfui_dataset_celldata_add(ds,
			    dfui_id_new(pkg_name), "Y");
		}
		pclose(pipe);
	}
	dfui_form_dataset_add(f, ds);

	dfui_form_action_add(f, dfui_id_new("ok"),
	    dfui_info_new("Accept and Install", "", ""));
	dfui_form_action_add(f, dfui_id_new("cancel"),
	    dfui_info_new("Return to Configure Menu", "", ""));

	if (!dfui_be_present(c, f, &r)) abort_backend();

	if (dfui_id_is(dfui_response_get_action_id(r), "ok")) {
		cmds = commands_new();

		cd = dfui_dataset_celldata_get_first(dfui_response_dataset_get_first(r));

		while (cd != NULL) {
			strlcpy(pkg_name, dfui_celldata_get_field_id(cd), 256);
			if (!strcasecmp(dfui_celldata_get_value(cd), "Y")) {
#ifdef DEBUG
				inform(	"pkg_copy `%s' (#%d).", pkg_name, pkg_count + 1);
#endif DEBUG
				if (!pkg_copy(cmds, pkg_name)) {
					inform(	"Couldn't install package `%s'.", pkg_name);
					break;
				} else {
					pkg_count++;
				}
			}
			cd = dfui_celldata_get_next(cd);
		}

		if (!commands_execute(c, cmds)) {
			inform(	"Packages were not fully installed.");
		} else {
			inform(	"%d packages were successfully installed!",
				pkg_count);
		}
		commands_free(cmds);
	}

	dfui_form_free(f);
	dfui_response_free(r);
}

void
fn_remove_packages(void)
{
	struct dfui_form *f;
	struct dfui_response *r;
	struct dfui_field *fi;
	struct dfui_dataset *ds;
	struct dfui_celldata *cd;
	FILE *pipe;
	char command[256];
	char pkg_name[256];
	struct commands *cmds;
	int pkg_count = 0;

	f = dfui_form_create(
		"remove_packages",
		"Remove Packages",
		"Select the intsalled software packages that you want "
		"removed from this system.",
		"",

		"h",	"special", "nympha_remove_packages",

		NULL
	);

	ds = dfui_dataset_new();
	snprintf(command, 256, "ls %smnt/var/db/pkg", dfly_root);
	if ((pipe = popen(command, "r")) != NULL) {
		while (fgets(pkg_name, 255, pipe)) {
			pkg_name[strlen(pkg_name) - 1] = '\0';
			fi = dfui_form_field_add(f, dfui_id_new(pkg_name),
			    dfui_info_new(pkg_name, "", ""));
			dfui_field_hint_add(fi, "control", "checkbox");
			dfui_dataset_celldata_add(ds,
			    dfui_id_new(pkg_name), "N");
		}
		pclose(pipe);
	}
	dfui_form_dataset_add(f, ds);

	dfui_form_action_add(f, dfui_id_new("ok"),
	    dfui_info_new("Accept and Remove", "", ""));
	dfui_form_action_add(f, dfui_id_new("cancel"),
	    dfui_info_new("Return to Configure Menu", "", ""));

	if (!dfui_be_present(c, f, &r)) abort_backend();

	if (dfui_id_is(dfui_response_get_action_id(r), "ok")) {
		cmds = commands_new();

		cd = dfui_dataset_celldata_get_first(dfui_response_dataset_get_first(r));

		while (cd != NULL) {
			strlcpy(pkg_name, dfui_celldata_get_field_id(cd), 256);
			if (!strcasecmp(dfui_celldata_get_value(cd), "Y")) {
#ifdef DEBUG
				inform(	"pkg_remove `%s' (#%d).", pkg_name, pkg_count + 1);
#endif DEBUG
				if (!pkg_remove(cmds, pkg_name)) {
					inform(	"Couldn't remove package `%s'.", pkg_name);
					break;
				} else {
					pkg_count++;
				}
			}
			cd = dfui_celldata_get_next(cd);
		}

		if (!commands_execute(c, cmds)) {
			inform(	"Packages were not fully removed.");
		} else {
			inform(	"%d packages were successfully removed.",
				pkg_count);
		}
		commands_free(cmds);
	}

	dfui_form_free(f);
	dfui_response_free(r);
}

/** LIVECD UTILITIES FUNCTIONS **/

/*
 * String returned by this function must be deallocated by the caller.
 */
char *
fn_select_file(char *title, char *desc, char *help, char *cancel,
		char *dir, char *ext)
{
	struct dfui_form *f;
	struct dfui_response *r;
	struct dirent *de;
	DIR *d;
	char *s;

	f = dfui_form_create(
		"select_file",
		title, desc, help,
		"h",	"role", 	"menu",
		NULL
	);

	d = opendir(dir);
	while ((de = readdir(d)) != NULL) {
		if (strcmp(de->d_name, ".") == 0 ||
		    strcmp(de->d_name, "..") == 0 ||
		    strstr(de->d_name, ext) == NULL)
			continue;
		dfui_form_action_add(f, dfui_id_new(de->d_name),
		    dfui_info_new(de->d_name, "", ""));
	}
	closedir(d);

	dfui_form_action_add(f, dfui_id_new("cancel"),
	    dfui_info_new(cancel, "", ""));

	if (!dfui_be_present(c, f, &r)) abort_backend();

	s = dfui_strdup(dfui_response_get_action_id(r));

	dfui_form_free(f);
	dfui_response_free(r);

	return(s);
}

void
fn_set_kbdmap(char *cfg_root)
{
	struct commands *cmds;
	char *s;
	struct config_vars *rc_conf;
	char filename[256];

	rc_conf = config_vars_new();

	s = fn_select_file("Select Keyboard Map",
	    "Select a keyboard map appropriate to your keyboard layout.",
	    "", "Return to Utilities Menu", "/usr/share/syscons/keymaps",
	    ".kbd");

	if (strcmp(s, "cancel") != 0) {
		cmds = commands_new();
		command_add(cmds, "kbdcontrol -l "
		    "/usr/share/syscons/keymaps/%s < /dev/ttyv0", s);
		if (commands_execute(c, cmds)) {
			snprintf(filename, 256, "/usr/share/syscons/keymaps/%s", s);
			config_var_set(rc_conf, "keymap", filename);
			config_vars_write(rc_conf, CONFIG_TYPE_SH,
			    "%s%setc/rc.conf", dfly_root, cfg_root);
		} else {
			inform("Keyboard map not successfully set.");
		}
		commands_free(cmds);
	}

	free(s);
	config_vars_free(rc_conf);
}

void
fn_set_vidfont(char *cfg_root)
{
	struct commands *cmds;
	char *s;
	int by = 0;
	struct config_vars *rc_conf;
	char variable[256], filename[256];

	rc_conf = config_vars_new();

	s = fn_select_file("Select Console Font",
	    "Select a font appropriate to your video monitor and language.",
	    "", "Return to Utilities Menu", "/usr/share/syscons/fonts",
	    ".fnt");

	if (strcmp(s, "cancel") != 0) {
		cmds = commands_new();
		command_add(cmds, "vidcontrol -f "
		    "/usr/share/syscons/fonts/%s < /dev/ttyv0", s);
		if (commands_execute(c, cmds)) {
			if (strstr(s, "8x16") != NULL) {
				by = 16;
			} else if (strstr(s, "8x14") != NULL) {
				by = 14;
			} else {
				by = 8;
			}
			snprintf(variable, 256, "font8x%d", by);
			snprintf(filename, 256, "/usr/share/syscons/fonts/%s", s);
			config_var_set(rc_conf, variable, filename);
			config_vars_write(rc_conf, CONFIG_TYPE_SH,
			    "%s%setc/rc.conf", dfly_root, cfg_root);
			
		} else {
			inform("Video font not successfully set.");
		}
		commands_free(cmds);
	}

	free(s);
	config_vars_free(rc_conf);
}

void
fn_set_scrnmap(char *cfg_root)
{
	struct commands *cmds;
	char *s;
	struct config_vars *rc_conf;
	char filename[256];

	rc_conf = config_vars_new();

	s = fn_select_file("Select Screen Map",
	    "Select a mapping for translating characters as they appear "
	    "on your video console screen.",
	    "", "Return to Utilities Menu", "/usr/share/syscons/scrnmaps",
	    ".scm");

	if (strcmp(s, "cancel") != 0) {
		cmds = commands_new();
		command_add(cmds, "vidcontrol -l "
		    "/usr/share/syscons/scrnmaps/%s < /dev/ttyv0", s);
		if (commands_execute(c, cmds)) {
			snprintf(filename, 256, "/usr/share/syscons/scrnmaps/%s", s);
			config_var_set(rc_conf, "scrnmap", filename);
			config_vars_write(rc_conf, CONFIG_TYPE_SH,
			    "%s%setc/rc.conf", dfly_root, cfg_root);
		} else {
			inform("Video font not successfully set.");
		}
		commands_free(cmds);
	}
	free(s);
}

void 
fn_set_timezone(char *cfg_root)
{
        char *s = NULL;
        struct commands *cmds;
	int found_file = 0;
	char temp[256], current_path[256], selection[256];

	cmds = commands_new();

	snprintf(current_path, 256, "/usr/share/zoneinfo");
	while(found_file == 0) {
		if (s != NULL)
			free(s);
		s = fn_select_file("Select Time Zone",
		    "Select a Time Zone appropriate to your physical location.",
		    "", "Return to Utilities Menu", current_path,
		    "");
		if(is_dir("%s/%s", current_path, s)) {
			snprintf(temp, 256, "%s/%s", current_path, s);
			strlcpy(current_path, temp, 256);
		} else {
			if(is_file("%s/%s", current_path, s)) {
				snprintf(selection, 256, "%s/%s", current_path, s);
				found_file = 1;
			}
			if (strcmp(s, "cancel") == 0) {
				strlcpy(selection, "cancel", 256);
				found_file = 1;
			}
		}
	}
	free(s);

        if (strcmp(selection, "cancel") != 0) {
                command_add(cmds, "cp "
                    "%s%s%s %s%setc/localtime",
		    dfly_root, cfg_root, selection, dfly_root, cfg_root);
                if (commands_execute(c, cmds)) {
			inform("The Time Zone has been set to %s.", selection);
		}
	}
	commands_free(cmds);
}

void 
fn_assign_datetime(char *cfg_root) 
{
        struct dfui_form *f;
        struct dfui_response *r;
	struct commands *cmds;
        struct dfui_dataset *ds, *new_ds;
	char datecommand[256];
	int year, month, dayofmonth, hour, minutes;
	int valid = 1;

        f = dfui_form_create(
                "Set Time/Date",
                "Set Time/Date",
                "Enter the current time and date.",
                "",

                "f",        "year",             "Enter year",
                        "Enter the current year (e.g. `04')", "",
                "f",        "month",               "Month",
                        "Enter the current month (e.g. `07')", "",
                "f",        "dayofmonth",               "dayofmonth",
                        "Enter the current day of month (e.g. `30')", "",
                "f",        "hour",               "hour",
                        "Enter the current hour (e.g. `07')", "",
                "f",        "minutes",               "minutes",
                        "Enter the current minutes (e.g. `59')", "",
                "a",    "ok", "OK", "", "",
		"a",    "cancel", "Cancel", "", "",

                NULL
        );

        ds = dfui_dataset_new();
        dfui_dataset_celldata_add(ds, dfui_id_new("year"), "");
        dfui_dataset_celldata_add(ds, dfui_id_new("month"), "");
	dfui_dataset_celldata_add(ds, dfui_id_new("dayofmonth"), "");
	dfui_dataset_celldata_add(ds, dfui_id_new("hour"), "");
	dfui_dataset_celldata_add(ds, dfui_id_new("minutes"), "");
        dfui_form_dataset_add(f, ds);

        if (!dfui_be_present(c, f, &r)) abort_backend();

        if (dfui_id_is(dfui_response_get_action_id(r), "ok")) {
                new_ds = dfui_response_dataset_get_first(r);

                if ((year = atoi(dfui_dataset_get_value(new_ds, "year"))) <= 0)
			valid = 0;
                if ((month = atoi(dfui_dataset_get_value(new_ds, "month"))) <= 0)
			valid = 0;
		if ((dayofmonth = atoi(dfui_dataset_get_value(new_ds, "dayofmonth"))) <= 0)
			valid = 0;
		if ((hour = atoi(dfui_dataset_get_value(new_ds, "hour"))) <= 0)
			valid = 0;
		if ((minutes = atoi(dfui_dataset_get_value(new_ds, "minutes"))) <= 0)
			valid = 0;

		if (valid) {
			snprintf(datecommand, 256, "date -n %04d%02d%02d%02d%02d",
			    year, month, dayofmonth, hour, minutes);
			cmds = commands_new();
			command_add(cmds, "%s", datecommand);
			if (commands_execute(c, cmds)) {
				inform("The date and time have been set.");
			}
			commands_free(cmds);
		} else {
			inform(	"Please enter numbers within acceptable ranges "
				"for year, month, day of month, hour, and minute.");
		}
	}
}

void 
fn_assign_hostname_domain(char *cfg_root) 
{
        struct dfui_form *f;
        struct dfui_response *r;
	struct dfui_dataset *ds, *new_ds;
	struct config_vars *rc_conf, *resolv_conf;
	char *fqdn, *hostname, *domain;

        f = dfui_form_create(
                "Set Hostname/Domain",
                "Set Hostname/Domain",
                "",
                "",

		"f",        "hostname",             "Hostname",
			"Enter the Hostname (e.g. `machine')", "",
		"f",        "domain",               "Domain",
			"Enter the Domain Name (e.g. `network.lan')", "",

                "a",    "ok", "OK", "", "",
                NULL
        );

	ds = dfui_dataset_new();
	dfui_dataset_celldata_add(ds, dfui_id_new("hostname"), "");
	dfui_dataset_celldata_add(ds, dfui_id_new("domain"), "");
	dfui_form_dataset_add(f, ds);

        if (!dfui_be_present(c, f, &r)) abort_backend();

	if (dfui_id_is(dfui_response_get_action_id(r), "ok")) {
		new_ds = dfui_response_dataset_get_first(r);

		hostname = dfui_dataset_get_value(new_ds, "hostname");
		domain = dfui_dataset_get_value(new_ds, "domain");
		asprintf(&fqdn, "%s.%s", hostname, domain);

		rc_conf = config_vars_new();
		resolv_conf = config_vars_new();

		config_var_set(rc_conf, "hostname", fqdn);
		config_var_set(resolv_conf, "search", domain);
                config_vars_write(resolv_conf, CONFIG_TYPE_RESOLV,
                    "%s%setc/resolv.conf", "/", cfg_root);
		config_vars_write(rc_conf, CONFIG_TYPE_SH,
		    "%s%setc/rc.conf", "/", cfg_root);

		config_vars_free(rc_conf);
		config_vars_free(resolv_conf);

		free(fqdn);
	}

        dfui_form_free(f);
        dfui_response_free(r);
}

void
fn_assign_ip(char *cfg_root)
{
	FILE *p;
	char *word;
	char line[256];
	char *string, *string1;
	char interface[256];
	char *interface_ip, *interface_netmask, *defaultrouter, *dns_resolver;
	char *hostname, *domain;
	struct dfui_dataset *ds, *new_ds;
	struct dfui_form *f;
	struct dfui_response *r;
	struct commands *cmds;
	struct config_vars *rc_conf, *resolv_conf;

	/*
	 * Get interface list.
	 */
	p = popen("/sbin/ifconfig -l", "r");
	/* XXX it's possible (though extremely unlikely) this will fail. */
	while (fgets(line, 255, p) != NULL) {
		line[strlen(line) - 1] = '\0';
	}
	pclose(p);

	f = dfui_form_create(
		"assign_ip",
		"Assign IP Address",
		"Please select which interface you would like to configure:",
		"",
		"h",	"role", "menu",
		NULL
	);
	
	/* Loop through array. */
	word = strtok(line, " \t");
	while (word != NULL) {
		dfui_form_action_add(f, dfui_id_new(word),
			dfui_info_new(word, "", ""));
		word = strtok(NULL, " ");
	}

	dfui_form_action_add(f, dfui_id_new("cancel"),
	    dfui_info_new("Cancel", "", ""));

	if (!dfui_be_present(c, f, &r))
		abort_backend();
	
	if (dfui_id_is(dfui_response_get_action_id(r), "cancel")) {
		dfui_form_free(f);
		dfui_response_free(r);
		return;
	}

	strlcpy(interface, dfui_response_get_action_id(r), 256);

	rc_conf = config_vars_new();
	resolv_conf = config_vars_new();

	switch (dfui_be_present_dialog(c, "Use DHCP?",
	    "Use DHCP|Configure Manually",
	    "DHCP allows the interface to automatically obtain "
	    "an IP address from a nearby DHCP server.\n\n"
	    "Would you like to enable DHCP for this interface?")) {
	case 1:
		cmds = commands_new();
		system("killall dhclient");
		command_add(cmds, "dhclient -1 %s", interface);
		if (commands_execute(c, cmds)) {
			show_ifconfig(interface);
		} else {
			inform(	"Warning: could not enable dhclient "
				"for %s.", interface);
		}
		commands_free(cmds);
		asprintf(&string, "ifconfig_%s", interface);
		config_var_set(rc_conf, string, "DHCP");
		free(string);
		break;
	case 2:
		dfui_form_free(f);
		dfui_response_free(r);
		f = dfui_form_create(
		    "assign_ip",
		    "Assign IP Address",
		    "Configuring Interface:",
		    "",
		    "f",	"interface_ip",		"IP Address",
			"Enter the IP Address you would like to use", "",
		    "f",	"interface_netmask",	"Netmask",
			"Enter the netmask of the IP address", "",		    
		    "f",	"defaultrouter",	"Default Router",
			"Enter the IP address of the default router", "",		    
		    "f",	"dns_resolver",		"Primary DNS Server",
			"Enter the IP address of primary DNS Server", "",
		    "f",	"hostname",		"Hostname",
			"Enter the Hostname", "",
		    "f",	"domain",		"Domain",
			"Enter the Domain Name", "",		    
		    "a",	"ok", 			"Configure Interface",
			"", "",
		    "a",	"cancel", 		"Return to Utilities Menu",
			"", "", 
		    NULL
		);

		ds = dfui_dataset_new();
		dfui_dataset_celldata_add(ds, dfui_id_new("interface_netmask"), "");		
		dfui_dataset_celldata_add(ds, dfui_id_new("defaultrouter"), "");
		dfui_dataset_celldata_add(ds, dfui_id_new("dns_resolver"), "");
		dfui_dataset_celldata_add(ds, dfui_id_new("hostname"), "");
		dfui_dataset_celldata_add(ds, dfui_id_new("domain"), "");
		dfui_dataset_celldata_add(ds, dfui_id_new("interface_ip"), "");
		dfui_form_dataset_add(f, ds);

		if (!dfui_be_present(c, f, &r)) abort_backend();

		if (dfui_id_is(dfui_response_get_action_id(r), "ok")) {
			new_ds = dfui_response_dataset_get_first(r);		

			interface_ip = dfui_dataset_get_value(new_ds, "interface_ip");
			interface_netmask = dfui_dataset_get_value(new_ds, "interface_netmask");
			defaultrouter = dfui_dataset_get_value(new_ds, "defaultrouter");
			dns_resolver = dfui_dataset_get_value(new_ds, "dns_resolver");
			hostname = dfui_dataset_get_value(new_ds, "hostname");
			domain = dfui_dataset_get_value(new_ds, "domain");
    		
			cmds = commands_new();
			command_add(cmds, "/sbin/ifconfig %s %s netmask %s",
			    interface, interface_ip, interface_netmask);
			command_add(cmds, "/sbin/route add default %s",
			    defaultrouter);
		    
			if (commands_execute(c, cmds)) {
				show_ifconfig(interface);
			} else {
				inform(	"Warning: could not assign IP or default gateway.");
			}
			commands_free(cmds);

			asprintf(&string, "ifconfig_%s", interface);
			asprintf(&string1, "inet %s netmask %s",
			    interface_ip, interface_netmask);

			config_var_set(rc_conf, string, string1);
			config_var_set(rc_conf, "defaultrouter", defaultrouter);

			free(string);
			free(string1);

			asprintf(&string, "%s.%s", hostname, domain);
			config_var_set(rc_conf, "hostname", string);
			free(string);

			config_var_set(resolv_conf, "search", domain);
			config_var_set(resolv_conf, "nameserver", dns_resolver);
		}
		break;
	default:
		abort_backend();
	}

	/*
	 * Save out changes to /etc/rc.conf and /etc/resolv.conf.
	 */
	config_vars_write(rc_conf, CONFIG_TYPE_SH,
	    "%s%setc/rc.conf", dfly_root, cfg_root);
	config_vars_write(resolv_conf, CONFIG_TYPE_RESOLV,
	    "%s%setc/resolv.conf", dfly_root, cfg_root);

	config_vars_free(rc_conf);
	config_vars_free(resolv_conf);

	dfui_form_free(f);
	dfui_response_free(r);
}

/*** NON-fn_ FUnCTIONS ***/

char *
convert_swap_options(char *line)
{
	char *result, *word;
	int i;

	result = malloc(256);
	result[0] = '\0';

	for (; (word = strsep(&line, ",")) != NULL; ) {
		if (word[0] == '-') {
			/*
			 * Don't bother trying to honour the -C
			 * option, since we can't copy files from
			 * the right place anyway.
			 */
			if (strcmp(word, "-C") != 0) {
				for (i = 0; word[i] != '\0'; i++) {
					if (word[i] == '=')
						word[i] = ' ';
				}
				strcat(result, word);
				strcat(result, " ");
			}
		}
	}
	
	return(result);
}

/*
 * Uses ss->selected_{disk,slice} as the target system.
 */
int
mount_target_system(char *cfg_root)
{
	struct commands *cmds;
	char *dev_name, *filename, line[256];
	FILE *fstab;
	char *word, *cvtoptions;
	char device[256], mtpt[256], fstype[256], options[256];
	char *try_mtpt[5]  = {"/var", "/tmp", "/usr", "/home", NULL};
	int i;

	/*
	 * Mount subpartitions from this installation if they are
	 * not already mounted.  Tricky, as we need to honour the
	 * installation's fstab.
	 */
	unmount_all_under(NULL, "%smnt", dfly_root);
	cmds = commands_new();

	asprintf(&dev_name, "%ss%d",
	    ss->selected_disk->device, ss->selected_slice->number);
	command_add_function(cmds, cmd_maybe_makedev,
	    (void *)dev_name, "maybe_makedev()");
	    
	command_add(cmds, "mount %sdev/%ss%da %s%s",
		dfly_root,
		ss->selected_disk->device, ss->selected_slice->number,
		dfly_root, cfg_root);
	if (!commands_execute(c, cmds)) {
		commands_free(cmds);
		return(0);
	}
	commands_free(cmds);

	asprintf(&filename, "%s%s/etc/fstab", dfly_root, cfg_root);
	fstab = fopen(filename, "r");
	free(filename);
	if (fstab == NULL) {
		inform("Filesystem table on installed system could not be read.");
		cmds = commands_new();
		command_add(cmds, "umount %s%s", dfly_root, cfg_root);
		if (!commands_execute(c, cmds)) {
			inform("Warning: Installed system was not properly unmounted.");
		}
		commands_free(cmds);
		return(0);
	}

	cmds = commands_new();

	while (fgets(line, 256, fstab) != NULL) {
		/*
		 * Parse the fstab line.
		 */
		if (first_non_space_char_is(line, '#'))
			continue;
		if ((word = strtok(line, " \t")) == NULL)
			continue;
		strlcpy(device, word, 256);
		if ((word = strtok(NULL, " \t")) == NULL)
			continue;
		strlcpy(mtpt, word, 256);
		if ((word = strtok(NULL, " \t")) == NULL)
			continue;
		strlcpy(fstype, word, 256);
		if ((word = strtok(NULL, " \t")) == NULL)
			continue;
		strlcpy(options, word, 256);

		/*
		 * Now, if the mountpoint has /usr, /var, /tmp, or /home
		 * as a prefix, mount it under cfg_root.
		 */
		for (i = 0; try_mtpt[i] != NULL; i++) {
			if (strstr(mtpt, try_mtpt[i]) == mtpt) {
				/*
				 * Don't mount it if it's optional.
				 */
				if (strstr(options, "noauto") != NULL)
					continue;

				/*
				 * Don't mount it if device doesn't start
				 * with /dev/ and it isn't 'swap'.
				 */
				if (strstr(device, "/dev/") == NULL &&
				    strcmp(device, "swap") != 0)
					continue;

				/*
				 * If the device is 'swap', mount_mfs it instead.
				 */
				if (strcmp(device, "swap") == 0) {
					cvtoptions = convert_swap_options(options);
					command_add(cmds,
					    "mount_mfs %s swap %s%s%s",
					    cvtoptions, dfly_root, cfg_root, mtpt);
					free(cvtoptions);
				} else {
					command_add_function(cmds,
					    cmd_maybe_makedev,
					    (void *)dfui_strdup(device),
					    "maybe_makedev()");
					command_add(cmds,
					    "mount -o %s %s%s %s%s%s", options,
					    dfly_root, device, dfly_root,
					    cfg_root, mtpt);
				}
			}
		}
	}
	fclose(fstab);

	if (!commands_execute(c, cmds)) {
		commands_free(cmds);
		return(0);
	}
	commands_free(cmds);

	return(1);
}
