/**
 * File:
 *   ui.ycp
 *
 * Module:
 *   NFS server
 *
 * Summary:
 *   Network NFS server dialogs
 *
 * Authors:
 *   Jan Holesovky <kendy@suse.cz>
 *   Dan Vesely (dan@suse.cz)
 *   Martin Vidner <mvidner@suse.cz>
 *
 * $Id: ui.ycp 20276 2004-11-10 12:09:38Z mvidner $
 *
 * Network NFS server dialogs
 *
 */

{

    textdomain "nfs_server";

    import "CWMFirewallInterfaces";
    import "Label";
    import "Mode";
    import "NfsServer";
    import "Popup";
    import "Sequencer";
    import "Wizard";
    include "nfs_server/routines.ycp";

    /**
     * The kernel-based server can handle spaces in paths,
     * the user-mode server cannot.
     */
    boolean spaces_allowed = false;

    /**
     * Ask user for a directory to export. Allow browsing.
     * @param mountpoint	default value
     * @param exports		exports list to check for duplicates
     * @return			a path to export or nil if cancelled
     */
    define string GetDirectory(string mountpoint, list<map> exports) ``{
	Wizard::SetScreenShotName ("nfs-server-2a-dir");

	if (mountpoint == nil)
	    mountpoint = "";

	UI::OpenDialog(
	    `opt(`decorated),
	    `HBox(
		`HSpacing(1),
		`VBox(
		    `VSpacing(0.2),
		    `HBox (
			`TextEntry(`id(`mpent),
				   // text entry label
				   _("&Directory to export:"), mountpoint),
			`HSpacing (1),
			`VBox (
			    // button label
			    `Bottom(`PushButton (`id (`browse), `opt (`key_F6), _("&Browse")))
			    )
			),
		    `VSpacing(0.2),
		    `HBox(
			`PushButton(`id(`ok), `opt(`default, `key_F10), Label::OKButton()),
			`PushButton(`id(`cancel), `opt (`key_F9), Label::CancelButton())
			),
		    `VSpacing(0.2)),
		`HSpacing(1)
		));
	UI::SetFocus (`id (`mpent));

	any ret = nil;
	do {
	    ret = UI::UserInput();

	    mountpoint = (string) UI::QueryWidget(`id(`mpent), `Value);
	    if (ret == `ok)
	    {
		list allowed = FindAllowed(exports, mountpoint);
		if (allowed != nil) {
		    // error popup message
		    Popup::Message(_("The exports table already
contains this directory."));
		    ret = nil;
		}
		else if (!spaces_allowed && findfirstof (mountpoint, " \t") != nil)
		{
		    Popup::Message(sformat (
// message popup; %1, %2 are package names
_("The user-mode NFS server (%1) cannot export directories
with spaces in their names.
Use the kernel-based server (%2) to do that."), "nfs-server", "nfs-utils"));
		    ret = nil;
		}
		else if (SCR::Read (.target.size, mountpoint) < 0 && !Mode::config ()) {
		    // the dir does not exist
		    ret = Popup::YesNo(_("The directory does not exist.
Create it?")) ? `ok : nil;
		}
	    }
	    else if (ret == `browse)
	    {
		string dir = (string) UI::QueryWidget (`id (`mpent), `Value);
		if (size (dir) == 0)
		{
		    dir = "/";
		}

		// title in the file selection dialog
		dir = UI::AskForExistingDirectory(dir, _("Select Directory to Export"));

		if (size (dir) > 0)
		{
		    UI::ChangeWidget (`id (`mpent), `Value, dir);
		}

	    }

	} while (ret != `ok && ret != `cancel);

	UI::CloseDialog();
	Wizard::RestoreScreenShotName ();

	if (ret == `ok)
	    return mountpoint;
	return nil;
    }

    /**
     * The kernel based server complains about an empty host specification
     * and wants us to use "*". But the user mode server would match it
     * only with an unqualified name. We must not use "*" there
     * to mean an anonymous client.
     */
    boolean use_star_for_anonymous = false;

    /**
     * Ask user for an entry for the allowed hosts list.
     * @param hosts	hosts default value
     * @param opts	options default value
     * @param allowed	current list, to check for duplicates
     * @return		[newhosts, newopts] or nil if cancelled. Options without parentheses.
     */
    define list<string> GetAllowedHosts(string hosts, string opts, list<string> allowed) ``{
	Wizard::SetScreenShotName ("nfs-server-2b-hosts");

	if (hosts == nil)   hosts = "";
	if (opts == nil)    opts = "";
	if (allowed == nil) allowed = [];
	list<string> allowed_names = maplist(string str, allowed, ``{
	    integer brpos = findfirstof(str, "(");
	    if (str != nil)
	    str = substring(str, 0, brpos);
	    return str;
	});

	UI::OpenDialog(`opt(`decorated),
		       `HBox(`HSpacing(1),
			     `VBox(`VSpacing(0.2),
				   // make at least the default options fit
				   `HSpacing (30),
				   // text entry label
				   `TextEntry(`id(`hostsent), _("&Hosts wildcard:"), hosts),
				   // text entry label
				   `TextEntry(`id(`optsent), _("O&ptions:"), opts),
				   `VSpacing(0.2),
				   // ok pushbutton: confirm the dialog
				   `HBox(`PushButton(`id(`ok), `opt(`default, `key_F10), Label::OKButton()),
					 `PushButton(`id(`cancel), `opt (`key_F9), Label::CancelButton())),
				   `VSpacing(0.2)),
			     `HSpacing(1)
			   ));

	UI::SetFocus (`id (`hostsent));
	any ret = nil;
	do {
	    ret = UI::UserInput();

	    hosts = (string) UI::QueryWidget(`id(`hostsent), `Value);
	    if (use_star_for_anonymous && size (hosts) == 0)
	    {
		hosts = "*";
		UI::ChangeWidget (`id(`hostsent), `Value, hosts);
	    }
	    opts = (string) UI::QueryWidget(`id(`optsent), `Value);
	    opts = deletechars (opts, " ()");
	    UI::ChangeWidget (`id(`optsent), `Value, opts);
	    if (ret == `ok &&
		(!CheckNoSpaces(hosts) || !CheckExportOptions(opts)))
		ret = nil;
	    if (ret == `ok && contains(allowed_names, hosts)) {
		// error popup message
		Popup::Message(_("Options for this wildcard
are already set."));
		ret = nil;
	    }
	} while (ret != `ok && ret != `cancel);

	UI::CloseDialog();
	Wizard::RestoreScreenShotName ();

	if (ret == `ok)
	    return [ hosts, opts ];
	return nil;
    }


    /**
     * Opening NFS server dialog
     * @return `back, `abort, `next `or finish
     */
    define symbol BeginDialog () ``{
	Wizard::SetScreenShotName ("nfs-server-1-start");

	boolean start_nfs_server = NfsServer::start;

	boolean changed = false;

	// firewall widget using CWM
	map<string, any> fw_settings = $[
	    "services": [ "nfs-server" ],
	    "display_details": true,
	    ];
	map<string,any> fw_cwm_widget = CWMFirewallInterfaces::CreateOpenFirewallWidget (fw_settings);

	string help_text =
	// Help, part 1 of 2
_("<P>Here you can choose whether you want to start NFS server on your computer
and export some of your directories to the others.</P>");

	help_text = help_text +
	// Help, part 2 of 2
_("<P>If you choose <B>Start NFS server</B>, the <B>Next</B> button will open
a configuration dialog where you can specify the directories to export.</P>");

	help_text = help_text + fw_cwm_widget["help"]:"";

	/* The end of the definitions */

	term nfs_contents =
		// frame label
		`Frame(_("NFS server"),
		       `VBox(`VSpacing(0.2),
			     `RadioButtonGroup(`id(`rbgroup),
				// radio button label
					       `VBox(`Left(`RadioButton(`id(`servyes), `opt (`notify), _("&Start NFS server"), start_nfs_server)),
				//radio button label
						     `Left(`RadioButton(`id(`servno), `opt (`notify), _("&Don't start NFS server"), !start_nfs_server)))),
			     `VSpacing(0.2)));
	term fw_contents =
	    // frame label
	    `Frame (_("Firewall"),
		    `VBox(`VSpacing(0.2),
			  fw_cwm_widget["custom_widget"]:`Empty (),
			  `VSpacing(0.2)));

	term contents = `HVSquash (`VBox (
				       nfs_contents,
				       `VSpacing (1),
				       fw_contents));

	// dialog title
	Wizard::SetContents(_("Configuration of the NFS server"),
			      contents, help_text, true, true);

	// initialize the widget (set the current value)
	CWMFirewallInterfaces::OpenFirewallInit (fw_cwm_widget, "");

	map event = nil;
	any ret = nil;
	do {
	    start_nfs_server = (UI::QueryWidget(`id(`rbgroup), `CurrentButton) == `servyes);
	    if (!start_nfs_server)
	    {
		Wizard::SetNextButton(`next, Label::FinishButton() );

	    }
	    else
	    {
		Wizard::RestoreNextButton();
		Wizard::SetFocusToNextButton();
	    }

	    event = UI::WaitForEvent ();	   
	    ret = event["ID"]:nil;
	    if (ret == `cancel)
	    {
		ret = `abort;
	    }

	    // handle the events, enable/disable the button, show the popup if button clicked
	    CWMFirewallInterfaces::OpenFirewallHandle (fw_cwm_widget, "", event);
	    changed =
		true ||		// TODO: fw changed, #44106
		start_nfs_server != NfsServer::start;

	    if (ret == `abort && changed && !Popup::ReallyAbort (changed))
	    {
		ret = `again;
	    }
	} while (ret != `back && ret != `next && ret != `abort);

	if (ret == `next)
	{
	    // grab current settings, store them to SuSEFirewall::
	    CWMFirewallInterfaces::OpenFirewallStore (fw_cwm_widget, "", event);
	    NfsServer::start = start_nfs_server;

	    if (!start_nfs_server)
	    {
		return `finish;
	    }
	}

	Wizard::RestoreScreenShotName ();
	return (symbol) ret;
    }

    /**
     * Exports dialog itself
     * @return `back, `abort, `next
     */
    define symbol ExportsDialog () ``{
	Wizard::SetScreenShotName ("nfs-server-2-exports");

	// Help, part 1 of 3
	string help_text = _("<P>The upper box contains all the directories which will be exported.
If a directory is selected, the lower box shows the hosts allowed to
mount this directory.</P>
");

	// Help, part 2 of 3
	help_text = help_text + _("<P>Hosts wildcard sets which hosts can access the selected directory.
It can be <I>single host</I>, <I>netgroups</I>, <I>wildcards</I> or
<I>IP networks</I>.</P>
");

	// Help, part 3 of 3
	help_text = help_text + _("<P>Please have a look at <B>man exports</B> for further information.</P>
");

	list<map<string,any> > exports = NfsServer::exports;

	term contents =
	    `VBox(
		`ReplacePoint(`id(`exportsrep), ExportsSelBox(exports)),
		// push button label
		`HBox(`PushButton(`id(`mpnewbut), `opt (`key_F3), _("Add &directory")),
		      // push button label
		      `PushButton(`id(`mpeditbut), `opt (`key_F4), _("&Edit")),
		      // push button label
		      `PushButton(`id(`mpdelbut), `opt (`key_F5), _("De&lete"))),
		// push button label
		`VBox(`Left(`Label(`id(`allowedlab), `opt(`hstretch), "")),
		      `Table(`id(`allowedtab), `opt(`notify, `immediate),
			     // table header
			     `header(_("Hosts wildcard")+"  ",
			     // table header
				     _("Options")+"  "),
			     [])),
		      // push button label
		`HBox(`PushButton(`id(`alwnewbut), _("Add &host")),
		      // push button label
		      `PushButton(`id(`alweditbut), _("Ed&it")),
		      // push button label
		      `PushButton(`id(`alwdelbut), _("Dele&te")))
		);

	Wizard::SetContentsButtons (
	    // dialog title
	    _("Directories to export to the others"),
	    contents, help_text,
	    Label::BackButton (), Label::FinishButton());

	any ret = nil;
	any simulated = nil; // simulated user input
	string oldmp = nil;
	// preselect an item - convenience, button enabling
	if (size (exports) > 0)
	{
	    UI::ChangeWidget (`id (`exportsbox), `CurrentItem, exports[0,"mountpoint"]:"");
	}
	do {
	    string mountpoint = (string) UI::QueryWidget (`id (`exportsbox), `CurrentItem);
	    boolean anymp = mountpoint != nil;

	    UI::ChangeWidget (`id (`mpeditbut), `Enabled, anymp);
	    UI::ChangeWidget (`id (`mpdelbut), `Enabled, anymp);
	    UI::ChangeWidget (`id (`alwnewbut), `Enabled, anymp);
	    if (mountpoint != oldmp)
	    {
		if (mountpoint == nil)
		{
		    mountpoint = "";
		}
		UI::ChangeWidget (`id(`allowedlab), `Value, mountpoint);
		oldmp = mountpoint;
		list<string> allowed = FindAllowed (exports, mountpoint);
		UI::ChangeWidget(`id (`allowedtab), `Items,
				 AllowedTableItems (allowed != nil? allowed: []));
	    }
	    boolean anyalw = UI::QueryWidget (`id (`allowedtab), `CurrentItem) != nil;
	    UI::ChangeWidget (`id (`alweditbut), `Enabled, anyalw);
	    UI::ChangeWidget (`id (`alwdelbut), `Enabled, anyalw);

	    // Kludge, because a `Table still does not have a shortcut.
	    UI::SetFocus (`id (`allowedtab));

	    // simulated input,
	    // used for `alweditbut afted `mpnewbut
	    if (simulated == nil)
	    {
		ret = UI::UserInput();
		if (ret == `cancel)
		{
		    ret = `abort;
		}
	    }
	    else
	    {
		ret = simulated;
		simulated = nil;
	    }

	    if (ret == `mpnewbut)
	    {
		string mountpoint = GetDirectory(nil, exports);

		if (mountpoint != nil)
		{
		    list default_allowed = [
			sformat ("%1(%2)",
				 use_star_for_anonymous? "*":"",
				 // these are the default anyway
				 // Exportfs of nfs-utils-1.0.1 gives a warning
				 // if neither of sync, async is specified.
				 "ro,root_squash,sync")
			];
		    exports = add(exports, $[ "mountpoint" : mountpoint,
					    "allowed"    : default_allowed ]);
		    UI::ReplaceWidget(`id(`exportsrep), ExportsSelBox(exports));
		    UI::ChangeWidget(`id(`exportsbox), `CurrentItem, mountpoint);
		    simulated = `alweditbut;
		}
	    }
	    else if (ret == `mpeditbut)
	    {
		string mp = (string) UI::QueryWidget(`id(`exportsbox), `CurrentItem);
		if (mp != nil) {
		    string mountpoint = GetDirectory(mp,
						     (list<map>) filter(map ent, exports, ``{
							 return ent["mountpoint"]:"" != mp;
						     }));

		    if (mountpoint != nil) {
			exports = maplist(map<string,any> ent, exports, ``{
			    string tmp = ent["mountpoint"]:"";
			    if (tmp == mp)
			    return add(ent, "mountpoint", mountpoint);
			    return ent;
			});
			UI::ReplaceWidget(`id(`exportsrep), ExportsSelBox(exports));
			UI::ChangeWidget(`id(`exportsbox), `CurrentItem, mountpoint);
		    }
		}
	    }
	    else if (ret == `mpdelbut)
	    {
		string mountpoint = (string) UI::QueryWidget(`id(`exportsbox), `CurrentItem);
		if (mountpoint != nil)
		    exports = filter(map<string,any> entry, exports,
				     ``(entry["mountpoint"]:"" != mountpoint));

		UI::ReplaceWidget(`id(`exportsrep), ExportsSelBox(exports));
		if (size (exports) > 0)
		{
		    UI::ChangeWidget (`id (`exportsbox), `CurrentItem,
				      exports[0,"mountpoint"]:"");
		}
	    }
	    else if (ret == `alwnewbut)
	    {
		string mountpoint = (string) UI::QueryWidget(`id(`exportsbox), `CurrentItem);
		if (mountpoint != nil) {
		    list<string> allowed = FindAllowed(exports, mountpoint);

		    list<string> hostopt = GetAllowedHosts(nil, nil, allowed);
		    if (hostopt != nil) {
			allowed = add(allowed, hostopt[0]:"" + "(" + hostopt[1]:"" + ")");
			exports = ReplaceInExports(exports, mountpoint, allowed);

			UI::ChangeWidget(`id(`allowedtab), `Items,
					 AllowedTableItems(allowed));
		    }
		}
	    }
	    else if (ret == `alweditbut)
	    {
		string mountpoint = (string) UI::QueryWidget(`id(`exportsbox), `CurrentItem);
		if (mountpoint != nil) {
		    list<string> allowed = FindAllowed(exports, mountpoint);
		    string hosts = "";
		    string opts = "";
		    if (allowed != nil) {
			integer alw_no = (integer) UI::QueryWidget(`id(`allowedtab), `CurrentItem);
			if (alw_no != nil) {
			    list<string> ho = AllowedToHostsOpts(allowed[alw_no]:"");
			    hosts = ho[0]:"";
			    opts = ho[1]:"";
			}
			allowed = remove (allowed, alw_no);
		    }
		    list<string> hostopt = GetAllowedHosts(hosts, opts, allowed);
		    if (hostopt != nil) {
			allowed = add(allowed, hostopt[0]:"" + "(" + hostopt[1]:"" + ")");
			exports = ReplaceInExports(exports, mountpoint, allowed);

			UI::ChangeWidget(`id(`allowedtab), `Items,
					 AllowedTableItems(allowed));
		    }
		}
	    }
	    else if (ret == `alwdelbut)
	    {
		string mountpoint = (string) UI::QueryWidget(`id(`exportsbox), `CurrentItem);
		if (mountpoint != nil) {
		    list<string> allowed = FindAllowed(exports, mountpoint);
		    integer alwno = (integer) UI::QueryWidget(`id(`allowedtab), `CurrentItem);
		    if (allowed != nil && alwno != nil) {
			allowed = remove (allowed, alwno);
			exports = maplist(map<string,any> entry, exports, ``{
			    if (entry["mountpoint"]:"" == mountpoint)
			    entry = add(entry, "allowed", allowed);
			    return entry;
			});

			UI::ChangeWidget(`id(`allowedtab), `Items,
					 AllowedTableItems(allowed));
		    }
		}
	    }
	    else if (ret == `abort && !Popup::ReallyAbort (true))
	    {
		ret = `again;
	    }
	} while (ret != `back && ret != `next && ret != `abort);

	if (ret == `next)
	{
	    NfsServer::exports = exports;
	}

	Wizard::RestoreScreenShotName ();
	return (symbol) ret;
    }


	/**
	 * Whole configuration of NfsServer but without reading and writing.
	 * For use with autoinstallation.
	 * @return sequence result
	 */
	define symbol NfsServerAutoSequence() ``{

    map Aliases = $[
    "begin"   : ``(BeginDialog ()),
    "exports"   : ``(ExportsDialog ())
    ];

    map Sequence = $[
    "ws_start"  : "begin",
    "begin"     : $[
	`next   : "exports",
	`finish : `next,
	`abort  : `abort
	],
    "exports"       : $[
	`next   : `next,
	`abort  : `abort
	]
    ];

    Wizard::CreateDialog ();
    Wizard::SetDesktopIcon("nfs_server");

	symbol ret = Sequencer::Run(Aliases, Sequence);
	UI::CloseDialog ();
	return ret;
	}
}
