/**
 * File:
 *   modules/Nis.ycp
 *
 * Module:
 *   Configuration of NIS client
 *
 * Summary:
 *   NIS client configuration data, I/O functions.
 *
 * Authors:
 *   Jan Holesovsky <kendy@suse.cz>
 *   Dan Vesely <dan@suse.cz>
 *   Martin Vidner <mvidner@suse.cz>
 *
 * $Id: Nis.ycp 21715 2005-02-18 13:47:07Z jsuchome $
 *
 */

{
    module "Nis";
    textdomain "nis";

    import "Address";
    import "Autologin";
    import "IP";
    import "Message";
    import "Nsswitch";
    import "Package";
    import "Progress";
    import "Report";
    import "Service";
    import "Summary";
    import "SuSEFirewall";
    import "Wizard";

    /* default value of settings modified */
    global boolean modified = false;


    /**
     * Function sets internal variable, which indicates, that any
     * settings were modified, to "true"
     */
    global define void SetModified () {
        modified = true;
    }

    /**
     * Functions which returns if the settings were modified
     * @return boolean  settings were modified
     */
    global define boolean GetModified () {
        return modified;
    }
    /**
     * Required packages for this module to operate
     *
     */
    global list<string> required_packages = ["ypbind"];

    /**
     * Should ypbind be started at boot?
     * If not, other settings are not touched.
     */
    global boolean start = false;

    /**
     * IP addresses of NIS servers.
     */
    global list<string> servers = [];

    /**
     * @return Access the servers as a string
     */
    global define string GetServers () ``{
	return mergestring (servers, " ");
    }

    /**
     * Set the servers from a string
     * @param servers_s a whitespace separated list
     */
    global define void SetServers (string servers_s) ``{
	servers = filter (string s, splitstring (servers_s, " \t"), ``(s != ""));
    }

    /**
     * Broadcast for the default domain?
     * (New in ypbind-1.12)
     */
    global boolean default_broadcast = false;

    /**
     * Servers for a multiple-domain configuration.
     * Keys are domains, values are lists of servers (strings).
     * The domains must be the same as for multidomain_broadcast
     * @see multidomain_broadcast
     */
    global map<string,list<string> > multidomain_servers = $[];

    /**
     * Servers for a multiple-domain configuration.
     * Whether a broadcast will be done if the servers don't respond.
     * Keys are domains, values are booleans.
     * The domains must be the same as for multidomain_servers
     * @see multidomain_servers
     * @see global_broadcast
     */
    global map multidomain_broadcast = $[];

    global map slp_domain = $[];

    string domain = "";
    string old_domain = nil;
    boolean domain_changed = false;

    /**
     * If the domain has changed from a nonempty one, it may only be
     * changed at boot time. Use this to warn the user.
     * @return whether changed by SetDomain
     */
    global define boolean DomainChanged () ``{
	return domain_changed;
    }

    /**
     * @return Get the NIS domain.
     */
    global define string GetDomain () ``{
	return domain;
    }

    /**
     * Set the NIS domain.
     * @param new_domain a new domain
     */
    global define void SetDomain (string new_domain) ``{
	domain = new_domain;
	if (domain != old_domain && old_domain != "")
	{
	    domain_changed = true;
	}
    }

    // DHCP cooperation

    /**
     * Whether sysconfig/network/dhcp says
     * to get the domain AND yp.conf by DHCP.
     * It will be turned off if dhcp_possible is off
     * (the user can override it)
     */
    global boolean dhcp_wanted = false;

    /**
     * #35654: if the server is running and sysconfig wants NIS data,
     * it's ok to FitIntoSingle
     */
    global boolean dhcpcd_running = false;

    /**
     * Is the domain AND yp.conf served by DHCP?
     * This is independent of dhcp_wanted:
     * dhcpcd*.pid exists and dhcpcd*.info contains NISSERVERS and NISDOMAIN.
     * It is used to warn the user if he wants to turn DHCP on but the config
     * is not provided.
     */
    global boolean dhcp_possible = false;
    /**
     * If dhcp_wanted changes, we need to restart the DHCP client
     */
    global boolean dhcp_restart = false;

    // The following four are from sysconfig/ypbind; the comments are
    // taken from there. The dialog help texts have "user friendlier"
    // descriptions.

    /**
     * If this option is set, ypbind will only bind to the loopback
     * interface and remote hosts cannot query it.
     */
    global boolean local_only = false;

    /**
     * If this option is set, ypbind will ignore /etc/yp.conf and use
     * a broadcast call to find a NIS server in the local subnet. You
     * should avoid to use this, it is a big security risk.
     * @see multidomain_broadcast
     * @see default_broadcast
     */
    global boolean global_broadcast = false;

    /**
     * You should set this to "yes" if you have a NIS server in your
     * network, which binds only to high ports over 1024. Since this
     * is a security risk, you should consider to replace the NIS
     * server with another implementation.
     */
    global boolean broken_server = false;

    /**
     * Extra options for ypbind. Here you can add options like
     * "-ypset", "-ypsetme", "-p port" or "-no-ping".
     */
    global string options = "";

    /**
     * If no, automounter will not be affected.
     */
    global boolean _autofs_allowed = true;

    /**
     * Start automounter and import the settings from NIS. (Bug 6092)
     */
    global boolean _start_autofs = false;

    /**
     * Output of "rcypbind start", if there was an error.
     * Read only.
     * This is currently used only in nis-server for its more advanced
     * error reporting. (Bug 14706)
     */
    global string YpbindErrors = "";

    /* ---------------------------------------------------------------- */
    // used also for nis-server

    /**
     * Check syntax of a NIS domain name
     * @param domain	a domain name
     * @return		true if correct
     */
    global define boolean check_nisdomainname (string domain) ``{
	// TODO
	// disallow whitespace and special characters...
	return domain != "" && domain != "(none)" && size (domain) <= 64;
    }

    /**
     * @return describe a valid NIS domain name
     */
    global define string valid_nisdomainname () ``{
	// Translators: do not translate (none)!
	return _("A NIS domain name must not be empty,
it must not be \"(none)\",
and it must be at most 64 characters long.
");
    }

    /**
     * If the hostname resolution is done over NIS,
     * names cannot be used to specify servers.
     */
    boolean hosts_by_nis = false;

    /**
     * Using NIS and LDAP simultaneously is not supported (#36981).
     */
    boolean users_by_ldap = false;

    /**
     * Used in the UI when NIS is turned on.
     */
    global define boolean UsersByLdap () ``{
	return users_by_ldap;
    }

    /**
     * Describe a valid address - ip4 or name, names only if
     * nsswitch.conf does not have hosts: nis
     * @return a description
     */
    global define string valid_address_nis () ``{
	y2debug ("hosts_by_nis %1", hosts_by_nis);
	if (hosts_by_nis)
	{
	    // message popup
	    return _("Only an IP address can be used
because host names are resolved using NIS.\n\n") + IP::Valid4();
	}
	else
	{
	    return Address::Valid4();
	}
    }

    /**
     * Check syntax of a network address (ip4 or name), names only if
     * nsswitch.conf does not have hosts: nis
     * @param a an address
     * @return true if correct
     */
    global define boolean check_address_nis (string a) ``{
	y2debug ("hosts_by_nis %1", hosts_by_nis);
	if (hosts_by_nis)
	{
	    return IP::Check4(a);
	}
	else
	{
	    return Address::Check(a);
	}
    }

    /* ---------------------------------------------------------------- */

    /**
     * Has the configuration been changed?
     * Can be used as an argument to Popup::ReallyAbort
     */
    global boolean touched = false;

    /**
     * A convenient shortcut for setting touched.
     * @param really	if true, set Nis::touched
     * @example Nis::Touch (Nis::var != ui_var);
     */
    global define void Touch (boolean really) ``{
	touched = touched || really;
    }

    /* ---------------------------------------------------------------- */

    /**
     * Read only, set by ProbePackages.
     * Use as an argument to DoInstallAndRemove
     */
    global list<string> install_packages = [];

    /**
     * Detect which packages have to be installed
     * and return a descriptive string for a plain text pop-up.
     * @return "" or "Foo will be installed.\nBar will be installed.\n"
     */
    global define string ProbePackages () {
	string message = "";
	install_packages = [];

	if (_autofs_allowed && _start_autofs)
	{
	    string pkg = "autofs4";
	    if (! Package::Installed (pkg) && ! Package::Installed ("autofs"))
	    {
		install_packages = add (install_packages, pkg);
		// Translators: popup message part, ends with a newline
		message = message + _("The automounter package will be installed.\n");
	    }
	}

	return message;
    }

    /* ---------------------------------------------------------------- */

    /**
     * Set module data
     * @return void
     */
    global define void Set (map settings) ``{
	start = settings["start_nis"]:false;

	servers = settings["nis_servers"]:[];
	default_broadcast = settings["nis_broadcast"]:false;
	domain = settings["nis_domain"]:"";
	old_domain = domain;

	dhcp_wanted = settings["nis_by_dhcp"]:false;
	// we have to hope the config will be available
	dhcp_possible = true;
	// we don't know what the state will be before Write, so restart it
	dhcp_restart = true;

	list<map> other_domains = settings["nis_other_domains"]:[];
	foreach (map other_domain, other_domains, ``{
	    string d = other_domain["nis_domain"]:"";
	    list<string> s = other_domain["nis_servers"]:[];
	    boolean b = other_domain["nis_broadcast"]:false;
	    multidomain_servers = add (multidomain_servers, d, s);
	    multidomain_broadcast = add (multidomain_broadcast, d, b);
	});

	local_only = settings["nis_local_only"]:false;
	broken_server = settings["nis_broken_server"]:false;
	options = settings["nis_options"]:"";

	// autofs is not touched in Write if the map does not want it
	_autofs_allowed = haskey (settings, "start_autofs");
	_start_autofs = settings["start_autofs"]:false;
	if (_start_autofs)
		required_packages = add(required_packages, "autofs4");

	touched	= true;
    }

    // TODO update the map keys
    // better still: link to a current interface description
    /**
     * Get all the NIS configuration from a map.
     * When called by nis_auto (preparing autoinstallation data)
     * the map may be empty.
     * @param settings	$["start": "domain": "servers":[...] ]
     * @return	success
     */
    global define boolean Import (map settings) ``{
	if (size (settings) == 0)
	{
	    //Provide defaults for autoinstallation editing:
	    //Leave empty.
	    old_domain = domain;
	    // enable _autofs_allowed
	    // Injecting it into the defaults for the GUI
	    // but leaving the check in Set makes it possible
	    // to delete the element manually from the xml profile
	    // and leave autofs untouched
	    settings["start_autofs"] = false;
	    Set(settings);
	    return true;
	}

	boolean missing = false;
	// "nis_domain" can be omitted if nis_by_dhcp is true
	foreach (string k, ["start_nis"], ``{
	    if (! haskey (settings, k))
	    {
		y2error ("Missing at Import: '%1'.", k);
		missing = true;
	    }
	});
	if (missing)
	{
	    return false;
	}

	Set(settings);
	return true;
    }

    // TODO update the map keys
    // better still: link to a current interface description
    /**
     * Dump the NIS settings to a map, for autoinstallation use.
     * @return $["start":, "servers":[...], "domain":]
     */
    global define map Export () ``{
	list other_domains = maplist (string d, list s, multidomain_servers, ``{
	    return $[
		"nis_domain": d,
		"nis_servers": s,
		"nis_broadcast": multidomain_broadcast[d]:false,
		];
	});

	if (global_broadcast)
	{
	    y2error ("Attempt to export Nis::global_broadcast");
	}

	return $[
	    "start_nis": start,

	    "nis_servers": servers,
	    "nis_domain": domain,
	    "nis_broadcast": default_broadcast,

	    "nis_other_domains": other_domains,

	    "nis_by_dhcp": dhcp_wanted,

	    "nis_local_only": local_only,
	    "nis_broken_server": broken_server,
	    "nis_options": options,

	    "start_autofs": _start_autofs,
	    ];
    }

    // copied from Mail.ycp
    // replace with a custom list
    /**
     * Summarizes a list of data
     * @param title passed to Summary::AddHeader
     * @param value a list (of scalars, lists or maps)
     * @return Summary-formatted description
     */
    define string ListItem(string title, any value) ``{
	string summary = "";
	summary = Summary::AddHeader(summary, title);
	//enhancement BEGIN
	if (is (value, map))
	{
	    value = maplist (any k, any v, (map) value, ``(k));
	}
	//enhancement END
	if (is(value,list) && size((list) value)>0) {
	    summary = Summary::OpenList(summary);
	    foreach (any d, (list) value, ``{
		string entry = "";
		if (is(d,map) || is (d,list))
		    entry = sformat("%1 Entries configured", is(d, map) ? size((map) value) : size((list) value));
		else
		    entry = (string) d;

		summary = Summary::AddListItem(summary, entry);
	    });
	    summary = Summary::CloseList(summary);
	} else {
	    summary = Summary::AddLine(summary,Summary::NotConfigured ());
	}
	return summary;
    }

    /**
     * @return Html formatted configuration summary
     */
    global define string Summary () ``{
	// TODO multidomain_servers, multidomain_broadcast
	// OK, a dumb mapping is possible, but wouldn't it be
	// too complicated to write by hand?
	string summary = "";
	string nc = Summary::NotConfigured ();

	// summary: Domain or servers are retrieved by the
	// Dynamic Host Configuration Protocol.
	// Will be placed after NIS Domain/NIS Servers instead of the
	// actual settings.
	string dhcp = _("by DHCP");

	// summary header
	summary = Summary::AddHeader(summary, _("NIS Client enabled"));
	// summary item: an option is turned on
	summary = Summary::AddLine(summary, (start) ? _("Yes") : nc);
	// summary header
	summary = Summary::AddHeader(summary, _("NIS Domain"));
	summary = Summary::AddLine(summary,
				   dhcp_wanted ? dhcp :
				   ((domain != "") ? domain : nc));
	// summary header
	summary = Summary::AddHeader(summary, _("NIS Servers"));
	summary = Summary::AddLine(summary,
				   dhcp_wanted ? dhcp :
				   ((servers !=[]) ? mergestring(servers,"<br>") : nc));
	// summary header
	summary = Summary::AddHeader(summary, _("Broadcast"));
	// summary item: an option is turned on
	summary = Summary::AddLine(summary, (default_broadcast) ? _("Yes") : nc);
	// TODO: a full list
	summary = summary + ListItem (_("Other domains"), multidomain_servers);
	// summary header
	summary = Summary::AddHeader(summary, _("Answer to local host only"));
	// summary item: an option is turned on
	summary = Summary::AddLine(summary, (local_only) ? _("Yes") : nc);
	// summary header
	summary = Summary::AddHeader(summary, _("Broken server"));
	// summary item: an option is turned on
	summary = Summary::AddLine(summary, (broken_server) ? _("Yes") : nc);
	// summary header
	summary = Summary::AddHeader(summary, _("ypbind options"));
	summary = Summary::AddLine(summary, (options!="") ? options : nc);
	// summary header
	summary = Summary::AddHeader(summary, _("Automounter enabled"));
	// summary item: an option is turned on
	summary = Summary::AddLine(summary, (_start_autofs) ? _("Yes") : nc);

	return summary;
    }

    /**
     * Makes an item for the short summary. I guess the users module
     * wants to avoid paragraph breaks.
     * @param title
     * @param value
     * @return [b]title[/b]: value[br]
     */
    global define string BrItem (string title, string value) ``{
	return sformat ("<b>%1</b>: %2<br>", title, value);
    }

    /**
     * Create a short textual summary with configuration abstract
     * It is called by "authentication/user sources" dialog in yast2-users
     * @return summary of the current configuration
     */
    global define string ShortSummary() ``{

	string summary = "";
	// summary value, used in  "Servers: by DHCP" or "Domain: by DHCP"
	string dhcp = _("by DHCP");
	string nc = Summary::NotConfigured ();
	summary =
	    // summary item
	    BrItem (_("Servers"), dhcp_wanted ? dhcp :
		    ((servers !=[]) ? GetServers() : nc)) +
	    // summary item
	    BrItem (_("Domain"), dhcp_wanted ? dhcp :
		    ((domain != "") ? domain : nc)) +
	    // summary item (yes/no follows)
	    BrItem (_("Client Enabled"), start ? _("Yes"): _("No"));

	return summary;
    }

    /**
     * Reads NIS settings from the SCR
     * @return success
     */
    global define boolean Read () ``{
	start = Service::Enabled("ypbind");

	servers = (list<string>) SCR::Read (.etc.yp_conf.servers);
	default_broadcast = (boolean) SCR::Read (.etc.yp_conf.defaultbroadcast);
	multidomain_servers = (map<string,list<string> >) SCR::Read (.etc.yp_conf.domainservers);
	multidomain_broadcast = (map) SCR::Read (.etc.yp_conf.broadcast);
	slp_domain = (map) SCR::Read (.etc.yp_conf.slp);

	map out = (map) SCR::Execute (.target.bash_output, "/bin/ypdomainname");
	domain = deletechars (out["stdout"]:"", "\n");
	old_domain = domain;

	boolean servers_by_dhcp_wanted = SCR::Read (
	    .sysconfig.network.dhcp.DHCLIENT_MODIFY_NIS_CONF) == "yes";
	boolean domain_by_dhcp_wanted = SCR::Read (
	    .sysconfig.network.dhcp.DHCLIENT_SET_DOMAINNAME) == "yes";
	dhcp_wanted = servers_by_dhcp_wanted && domain_by_dhcp_wanted;

	dhcpcd_running =
	    SCR::Execute (.target.bash, "ls /var/run/dhcpcd-*.pid") == 0;
	string di = "/var/lib/dhcpcd/dhcpcd-*.info";
	boolean servers_by_dhcp_possible = dhcpcd_running && SCR::Execute (
	    .target.bash, "/bin/grep -qs '^NISSERVERS' " + di) == 0;
	boolean domain_by_dhcp_possible = dhcpcd_running && SCR::Execute (
	    .target.bash, "/bin/grep -qs '^NISDOMAIN' " + di) == 0;
	dhcp_possible = servers_by_dhcp_possible && domain_by_dhcp_possible;
	// we do it here instead of in the ui so that the user can override it
	// in the ui or switch single/multiple domains
	if (dhcp_wanted && !dhcp_possible)
	{
	    dhcp_wanted = false;
	    Touch (true);
	}

	local_only = SCR::Read (.sysconfig.ypbind.YPBIND_LOCAL_ONLY) == "yes";
	global_broadcast = SCR::Read (.sysconfig.ypbind.YPBIND_BROADCAST) == "yes";
	broken_server = SCR::Read (.sysconfig.ypbind.YPBIND_BROKEN_SERVER) == "yes";
	options = (string) SCR::Read (.sysconfig.ypbind.YPBIND_OPTIONS);

	// install on demand
	_start_autofs = _autofs_allowed && Service::Enabled("autofs");

	hosts_by_nis = contains (Nsswitch::ReadDb ("hosts"), "nis");

	list<string> nss_passwd = Nsswitch::ReadDb ("passwd");
	users_by_ldap =
	    contains (nss_passwd, "ldap") ||
	    (contains (nss_passwd, "compat") &&
	     contains (Nsswitch::ReadDb ("passwd_compat"), "ldap"));

	Autologin::Read ();

	boolean progress_orig = Progress::set (false);
	SuSEFirewall::Read ();
	Progress::set (progress_orig);

	return true;
    }

    /**
     * Make up data for screnshots.
     * To be used instead of @ref Read .
     */
    global define void Fake () ``{
	y2milestone ("Faking data for screenshots");
	start = true;
	servers = ["10.42.0.1"];
	default_broadcast = false;
	multidomain_servers = $[
	    "printer.example.com": [],
	    "test.example.com": ["10.42.1.1", "10.42.1.2"],
	    ];
	multidomain_broadcast = $[
	    "printer.example.com": true,
	    "test.example.com": false,
	    ];
	domain = "example.com";
	old_domain = domain;
	dhcp_wanted = false;
	dhcp_possible = false; // to see the warning popup
	local_only = false;
	global_broadcast = false;
	broken_server = false;
	options = "";
	_autofs_allowed = true;
	_start_autofs = true;
	hosts_by_nis = false;
    }

    /**
     * @param file a pathname
     * @return is there a nis inclusion?
     */
    define boolean HasPlus (string file) ``{
	// does the file have a plus?
	y2milestone("file %1 has pluses", file);
	return (0 == SCR::Execute (.target.bash, "/usr/bin/grep -q '^[+-]' " + file));
    }

    /**
     * If a file does not contain a NIS entry, add it.
     * @param	file	pathname
     * @param	what	a "+" line without a '\n'
     * @return success?
     */
    define boolean WritePlusesTo (string file, string what) ``{
	boolean ok = true;
	if (!HasPlus (file))
	{
	    // backup the file:
	    SCR::Execute (.target.bash,sformat("/bin/cp %1 %1.YaST2save",file));
	    if (SCR::Execute (.target.bash, sformat ("/bin/echo '%1' >> %2", what, file)) != 0)
	    {
		ok = false;
	    }
	}
	// TODO only for passwd?
	// replace the 'nologin' occurence (#40571)
	else if (SCR::Execute (.target.bash, sformat ("/bin/grep -q '^%1/sbin/nologin' %2", what, file)) == 0)
	{
            ok = SCR::Execute (.target.bash, sformat ("/usr/bin/sed -i.YaST2save -e 's@%1/sbin/nologin@%1@' %2", what, file)) == 0;
	}
	if (!ok)
	{
	    Report::Error (Message::ErrorWritingFile (file));
	}
	return ok;
    }

    /**
     * Do we need compat? Is there a plus in any of the user databases?
     * @return true/false
     */
    define boolean HavePlus () ``{
	list<string> files = [
	    "/etc/passwd",
	    "/etc/shadow",
	    "/etc/group",
	    ];
	// find a file having a plus
	return nil != find (string file, files, ``( HasPlus (file) ));
    }

    /**
     * Add "+" lines to system files so that NIS entries get merged in.
     * (Formerly this was done in SuSEconfig.ypclient)
     * @return success?
     */
    define boolean WritePluses () ``{
	list<string> files = ["passwd", "shadow", "group"];
	//don't forget a newline
	map what_to_write = $[
	    "passwd":	"+::::::",
	    "group":	"+:::",
	    "shadow":	"+",
	    ];
	foreach (string f, files, {
	    y2milestone("Writing pluses to %1", f);
	    if (! WritePlusesTo (sformat ("/etc/%1", f), what_to_write[f]:""))
	    {
		return false;
	    }
	});
	return true;
    }

    /**
     * Configures the name service switch for the user databases
     * according to chosen settings
     * @return success?
     */
    global define boolean WriteNssUsers () ``{
	list<string> dbs = ["passwd", "group", "shadow"];
	// Why bother with both compat and nis?
	// If there's no plus, we don't have to write passwd etc.
	// And it's supposed to be faster.
	// But then programs have to reread nsswitch :( #23203
	// so we stick with compat.
	if (start)
	{
		// we want to switch to "compat"
		foreach (string db, dbs, ``{
		    // what if a db is not mentioned?
		    // We get [] meaning compat, so it's ok to make it explicit
		    list<string> db_l = Nsswitch::ReadDb (db);

		    if (!contains (db_l, "compat"))
		    {
			// remove "files" and "nis", if there;
			db_l = filter (string s, db_l, ``(
					   s != "files" && s != "nis"));
			// put "compat" and the rest;
			db_l = prepend (db_l, "compat");
			Nsswitch::WriteDb (db, db_l);
		    }

		    // *_compat may be set to nisplus, nuke it (#16168)
		    string db_c = db + "_compat";
		    Nsswitch::WriteDb (db_c, []);
		});
		y2milestone("Writing pulses");
		WritePluses ();
	}
	else // not start
	{

	y2milestone("not writing pluses");
	    boolean have_plus = HavePlus ();

	    if (!have_plus)
	    {
		foreach (string db, dbs, ``{
		    list<string> db_l = Nsswitch::ReadDb (db);

		    // remove "nis" if there;
		    db_l = filter (string s, db_l, ``(s != "nis"));
		    // if nothing left, put "files";
		    // NOT. just remove it, meaning compat. #35299
		    Nsswitch::WriteDb (db, db_l);
		});
	    }
	}

	if (!SCR::Write (.etc.nsswitch_conf, nil))
	{
	    Report::Error (Message::ErrorWritingFile ("/etc/nsswitch.conf"));
	    return false;
	}
	return true;
    }

    /**
     * Only write new configuration w/o starting any scripts
     * @return true on success
     */
    global define boolean WriteOnly()``{
	if (start)
	{
	    Service::Enable("portmap");
	    Service::Enable("ypbind");

	    if (!dhcp_wanted)
	    {
		if (!SCR::Write (.etc.defaultdomain, domain))
		{
		    Report::Error (
			Message::ErrorWritingFile ("/etc/defaultdomain"));
		    return false;
		}

		// so that dhcpcd cannot restore it
		SCR::Execute (.target.remove, "/etc/yp.conf.sv");

		SCR::Write (.etc.yp_conf.servers, servers);
		SCR::Write (.etc.yp_conf.defaultbroadcast, default_broadcast);
		SCR::Write (.etc.yp_conf.domainservers, multidomain_servers);
		SCR::Write (.etc.yp_conf.broadcast, multidomain_broadcast);
		SCR::Write (.etc.yp_conf.slp, slp_domain);
		if (! SCR::Write (.etc.yp_conf, nil))
		{
		    Report::Error (Message::ErrorWritingFile ("/etc/yp.conf"));
		    return false;
		}
	    }
	    SCR::Write (.sysconfig.ypbind.YPBIND_LOCAL_ONLY, local_only? "yes":"no");
	    SCR::Write (.sysconfig.ypbind.YPBIND_BROADCAST, global_broadcast? "yes":"no");
	    SCR::Write (.sysconfig.ypbind.YPBIND_BROKEN_SERVER, broken_server? "yes":"no");
	    SCR::Write (.sysconfig.ypbind.YPBIND_OPTIONS, options);
	    if (! SCR::Write (.sysconfig.ypbind, nil))
	    {
		Report::Error (Message::ErrorWritingFile (
		    "/etc/sysconfig/ypbind"));
		return false;
	    }

	    SCR::Write (.sysconfig.network.dhcp.DHCLIENT_SET_DOMAINNAME,
			dhcp_wanted? "yes":"no");
	    SCR::Write (.sysconfig.network.dhcp.DHCLIENT_MODIFY_NIS_CONF,
			dhcp_wanted? "yes":"no");
	    if (! SCR::Write (.sysconfig.network.dhcp, nil))
	    {
		Report::Error (Message::ErrorWritingFile (
		    "/etc/sysconfig/network/dhcp"));
		return false;
	    }
	    Autologin::Write (false);
	}
	else
	{
	    Service::Disable("ypbind");
	}

	// TODO do as much as possible if one thing fails
	// especially WRT nis/autofs independence
	WriteNssUsers ();

	if (_autofs_allowed)
	{
	    if (! Nsswitch::WriteAutofs (start && _start_autofs,"nis"))
	    {
	        return false;
	    }

	    if (_start_autofs)
	    {
	        Service::Enable("autofs");
	    }
	    else
	    {
	        Service::Disable("autofs");
	    }
	}

	boolean progress_orig = Progress::set (false);
	SuSEFirewall::WriteOnly ();
	Progress::set (progress_orig);

	return true;
    }

    /**
     * Saves NIS configuration.
     * @return true on success
     */
    global define boolean Write () ``{
	if (!WriteOnly ())
	{
	    return false;
	}

	// dialog label
	Progress::New (_("Writing NIS Configuration..."), " ", 2, [
	    // progress stage label
	    _("Stop services"),
	    // progress stage label
	    _("Start services"),
	   ], [
	    // progress step label
	    _("Stopping services..."),
	    // progress step label
	    _("Starting services..."),
	    // final progress step label
	    _("Finished") ],
	    "" );

	// help text
	Wizard::RestoreHelp(_("Writing NIS client settings"));

	Progress::NextStage ();

	if (dhcp_restart)
	{
	    // Restart the dhcp client, if it is running, to parse the changed
	    // options
	    Service::RunInitScript ("network", "restart-all-dhcp-clients");
	}

	Service::Stop("ypbind");

	Progress::NextStage ();

	if (start)
	{
	    if (Service::Status("portmap") != 0 )
	    {
		// portmap must not be started if it is running already (see bug # 9999)
		if (Service::Start("portmap") == false)
		{
		    // error popup message
		    Report::Error (_("Error while running portmapper."));
		    return false;
		}
	    }
	    sleep(1000);	// workaround for bug #10428, ypbind restart

	    map out = (map) SCR::Execute (.target.bash_output, "/etc/init.d/ypbind start", $["TERM": "raw"]);
	    if (out["exit"]:1 == 0)
	    {
		YpbindErrors = "";
	    }
	    else
	    {
		YpbindErrors = out["stdout"]:"internal error";
		// error popup message
		Report::Error (_("Error while running ypclient."));
		return false;
	    }

	    // only test for a server if domain not changed
	    if (! domain_changed)
	    {
		if (SCR::Execute(.target.bash, "/usr/bin/ypwhich >/dev/null") != 0)
		{
		    // error popup message
		    Report::Error (_("NIS server not found."));
		    return false;
		}
	    }
	    if (Autologin::modified)
		SCR::Execute (.target.bash, "/sbin/SuSEconfig --module kdm3");
	}

	// if the name service cache daemon is running, restart it
	if (Service::Status("nscd") == 0) {
	    Service::Restart("nscd");
	}

	if (_autofs_allowed && touched)
	{
	    Service::Stop("autofs");

	    if (_start_autofs)
	    {
		Service::Start("autofs");
	    }
	}

	SuSEFirewall::ActivateConfiguration ();

	// final stage
	Progress::NextStage ();

	return true;
    }

    /**
     * Return needed packages and packages to be removed
     * during autoinstallation.
     * @return map of lists.
     *
     **/

    global  define map AutoPackages() ``{
      list install_pkgs = required_packages;
      list remove_pkgs = [];
      return ($["install": install_pkgs, "remove": remove_pkgs]);
    }


}
