/**
 * File:	modules/Lan.ycp
 * Package:	Network configuration
 * Summary:	Network card data
 * Authors:	Michal Svec <msvec@suse.cz>
 *
 * $Id: Lan.ycp 22684 2005-03-18 22:03:18Z nashif $
 *
 * Representation of the configuration of network cards.
 * Input and output routines.
 */

{

module "Lan";
textdomain "network";

import "Arch";
import "DNS";
import "Detection";
import "Host";
import "Hostname";
import "IP";
import "Map";
import "Mode";
import "NetworkConfig";
import "NetworkDevices";
import "NetworkModules";
import "ProductFeatures";
import "Routing";
import "Progress";
import "Service";
import "String";
import "Summary";
import "SuSEFirewall4Network";

include "network/complex.ycp";
include "network/runtime.ycp";

/*-------------*/
/* GLOBAL DATA */

/**
 * Current module information
 */
// FIXME: MOD global map Module = $[];


global map autoinstall_settings = $[];
global string description = "";
global string unique = "";

global string type = "";
global string device = "";
global string alias = "";

/**
 * Hotplug type ("" if not hot pluggable)
 */
global string hotplug = "";

global list<string> Requires = [];

/* address options */
global string bootproto = "static";
global string ipaddr = "";
global string remoteip = "";
global string netmask = "";

global string startmode = "auto";
global boolean usercontrol = false;
global string mtu = "";

/* wireless options */
global string wl_mode = "";
global string wl_essid = "";
global string wl_nwid = "";
global string wl_auth_mode = "";
// when adding another key, don't forget the chmod 600 in NetworkDevices
global string wl_wpa_psk = "";
global string wl_key_length = "";
global list<string> wl_key = [];
global integer wl_default_key = 0;
global string wl_nick = "";

global string wl_channel = "";
global string wl_frequency = "";
global string wl_bitrate = "";
global string wl_accesspoint = "";
global boolean wl_power = true;

// Card Features from hwinfo
// if not provided, we use the default full list
global list<string> wl_auth_modes = nil;
global list<string> wl_enc_modes = nil;
global list<string> wl_channels = nil;
global list<string> wl_bitrates = nil;
list<string> nilliststring = nil; // to save some casting

/* s390 options */
global string read_channel = "";
global string write_channel = "";
global string data_channel = "";
global string portname = "";
global string chan_mode = "0";
global string qeth_options = "";
global boolean ipa_takeover = false;

/* aliases */
global map aliases = $[];

/* propose options */
global boolean proposal_valid = false;
boolean write_only = false;

/* current selected HW */
map hw = $[];

/* NetworkModules:: name */
global string nm_name = "";
global string nm_name_old = nil;

/**
 * Abort function
 * return boolean return true if abort
 */
global block<boolean> AbortFunction = nil;

/*--------------*/
/* PRIVATE DATA */

/**
 * Hardware information
 * @see ReadHardware
 */
list<map> Hardware = [];

/**
 * Modules information
 * @see ReadModules
 */
// FIXME: MOD map Modules = $[];

/**
 * Deleted modules
 */
// FIXME: MOD list<string> DeletedModules = [];

/**
 * /etc/install.inf data
 */
map InstallInf = $[];

/**
 * Data was modified?
 */
boolean modified = false;

/**
 * Which operation is pending?
 */
global symbol operation = nil;
// FIXME: used in lan/address.ycp (#17346) -> "global"

/*------------------*/
/* GLOBAL FUNCTIONS */

/**
 * Data was modified?
 * @return true if modified
 */
global define boolean Modified() {
    y2debug("modified=%1",modified);
    return modified;
}

include "network/hardware.ycp";
include "network/install.ycp";
include "network/routines.ycp";
include "network/runtime.ycp";

/**
 * Read all network settings from the SCR
 * @return true on success
 */
global define boolean Read() {

    /* Read dialog caption */
    string caption = _("Initializing Network Configuration");
    integer steps = 9;

    integer sl = 0; /* 1000; /* TESTING */
    sleep(sl);

    Progress::New(caption, " ", steps, [
	/* Progress stage 1/9 */
	_("Detect network devices"),
	/* Progress stage 2/9 */
	_("Read driver information"),
	/* Progress stage 3/9 - multiple devices may be present, really plural*/
	_("Read device configuration"),
	/* Progress stage 4/9 */
	_("Read network configuration"),
	/* Progress stage 5/9 */
	_("Read routing configuration"),
	/* Progress stage 6/9 */
	_("Read host name and DNS configuration"),
	/* Progress stage 7/9 */
	_("Read installation information"),
	/* Progress stage 8/9 */
	_("Read firewall settings"),
	/* Progress stage 9/9 */
	_("Detect current status"),
    ], [], "");

    if(Abort()) return false;
    /* Progress step 1/9 */
    ProgressNextStage(_("Detecting network devices..."));
    // if(!Arch::s390 ()) /* FIXME: Temporary hack until the detection is functional */

    // Dont read hardware data in config mode
    if(!Mode::config ()) {
	if(!Detection::running) Detection::Start();
	Hardware = ReadHardware("netcard");
    }

    /* ReadHardware(""); /* TESTING */
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 2/9 */
    ProgressNextStage(_("Reading /etc/modprobe.conf..."));
    // FIXME: MOD // ReadModules(NetworkDevices::CardRegex["netcard"]:"");
    NetworkModules::Read();
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 3/9 - multiple devices may be present, really plural*/
    ProgressNextStage(_("Reading device configuration..."));
    NetworkDevices::Read();
    NetworkDevices::CleanHotplugSymlink();
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 4/9 */
    ProgressNextStage(_("Reading network configuration..."));
    NetworkConfig::Read();
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 5/9 */
    ProgressNextStage(_("Reading routing configuration..."));
    Routing::Read();
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 6/9 */
    ProgressNextStage(_("Reading host name and DNS configuration..."));
    DNS::Read();
    Host::Read();
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 7/9 */
    ProgressNextStage(_("Reading installation information..."));
    ReadInstallInf();
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 8/9 */
    ProgressNextStage(_("Reading firewall settings..."));
    boolean orig = Progress::set (false);
    SuSEFirewall4Network::Read ();
    Progress::set (orig);
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 9/9 */
    ProgressNextStage(_("Detecting current status..."));
    ReadStatus();
    sleep(sl);

    if(Abort()) return false;
    /* Final progress step */
    ProgressNextStage(_("Finished"));
    sleep(sl);

    if(Abort()) return false;
    modified = false;
    return true;
}

/**
 * Update the SCR according to network settings
 * @return true on success
 */
global define boolean Write() {

    y2milestone("Writing configuration");
    if(!write_only && Detection::running) Detection::Stop();

    /* Stop the detection */
    if(!modified) {
	y2milestone("No changes to network setup -> nothing to write");
	return true;
    }

    /* Write dialog caption */
    string caption = _("Saving Network Configuration");
    integer steps = 11;

    integer sl = 0; /* 1000; /* TESTING */
    sleep(sl);

    Progress::New(caption, " ", steps, [
	/* Progress stage 2 */
	_("Write drivers information"),
	/* Progress stage 3 - multiple devices may be present,really plural*/
	_("Write device configuration"),
	/* Progress stage 4 */
	_("Write network configuration"),
	/* Progress stage 5 */
	_("Write routing configuration"),
	/* Progress stage 6 */
	_("Write host name and DNS configuration"),
	/* Progress stage 7 */
	_("Set up network services"),
	/* Progress stage 8 */
	_("Write firewall settings"),
	/* Progress stage 9 */
	_("Activate network services"),
	/* Progress stage 10 */
	_("Run SuSEconfig"),
	/* Progress stage 11 */
	_("Set up smpppd"),
	/* Progress stage 12 */
	_("Run KInternet"),
    ], [], "");

    if(Abort()) return false;
    /* Progress step 2 */
    ProgressNextStage(_("Writing /etc/modprobe.conf..."));
    // FIXME: MOD WriteModules();
    NetworkModules::Write("netcard");
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 3 - multiple devices may be present, really plural*/
    ProgressNextStage(_("Writing device configuration..."));
    NetworkDevices::Write("netcard");
    // WriteDevices();
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 4 */
    ProgressNextStage(_("Writing network configuration..."));
    NetworkConfig::Write();
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 5 */
    ProgressNextStage(_("Writing routing configuration..."));
    Routing::Write();
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 6 */
    ProgressNextStage(_("Writing host name and DNS configuration..."));
    DNS::Write();
    Host::Write();
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 7 */
    ProgressNextStage(_("Setting up network services..."));
    EnableNetwork();
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 8 */
    ProgressNextStage(_("Writing firewall settings..."));
    boolean orig = Progress::set (false);
    SuSEFirewall4Network::Write ();
    Progress::set (orig);
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 9 */
    ProgressNextStage(_("Activating network services..."));
    if(!write_only) {
	NetworkModules::HwUp ();
	ReloadOrStartNetwork();
    }
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 10 */
    ProgressNextStage(_("Running SuSEconfig..."));
    if(!write_only) RunSuSEconfig();
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 11 */
    ProgressNextStage(_("Setting up smpppd(8)..."));
    // takes care of autoinst by itself
    SetupSMPPPD();
    sleep(sl);

    if(Abort()) return false;
    /* Progress step 12 */
    ProgressNextStage(_("Running KInternet..."));
    if (!write_only)
    {
	RunKInternet();
    }
    sleep(sl);

    if(Abort()) return false;
    /* Final progress step */
    ProgressNextStage(_("Finished"));
    sleep(sl);

    if(Abort()) return false;
    return true;
}

/**
 * Only write configuration without starting any init scripts and SuSEconfig
 * @return true on success
 */
global define boolean WriteOnly() {
    write_only = true;
    return Write();
}

/**
 * Import data
 * @param settings settings to be imported
 * @return true on success
 */
global define boolean Import(map settings) {

    if (!Mode::autoinst()) {
        // FIXME: MOD Modules = (map) eval(settings["modules"]:$[]);
        NetworkDevices::Import("netcard", (map<string, map>) settings["devices"]:$[]);
        NetworkModules::Import("netcard", (map<string, map>) settings["hwcfg"]:$[]);
    } else {
        autoinstall_settings = settings;
    }

    NetworkConfig::Import(settings["config"]:$[]);
    DNS::Import((map) eval(settings["dns"]:$[]));
    Routing::Import((map) eval(settings["routing"]:$[]));

    modified = true;
    return true;
}

/**
 * Export data
 * @return dumped settings (later acceptable by Import())
 */
global define map Export() {
    return $[
	"dns"		: DNS::Export(),
	// FIXME: MOD "modules"	: Modules,
	"config"	: NetworkConfig::Export(),
	"devices"	: NetworkDevices::Export("netcard"),
	"hwcfg"		: NetworkModules::Export("netcard"),
	"routing"	: Routing::Export(),
    ];
}

/**
 * Select the hardware component
 * @param hw the component
 */
void SelectHWMap (map hw) {
    SelectHardwareMap (hw);

    if(hotplug == "") {
	// FIXME: MOD Module["module"] = hw["module"]:"";
	// FIXME: MOD Module["options"] = hw["options"]:"";
	NetworkModules::Alias = hw["module"]:"";
	NetworkModules::Options = hw["options"]:"";
    }
    else
	y2milestone("HOTPLUG(%1) -> ignoring modprobe.conf", hotplug);

    // Wireless Card Features
    wl_auth_modes = hw["wl_auth_modes"]:nilliststring;
    wl_enc_modes = hw["wl_enc_modes"]:nilliststring;
    wl_channels = hw["wl_channels"]:nilliststring;
    wl_bitrates = hw["wl_bitrates"]:nilliststring;
    string mac = hw["mac"]:"";
    string busid = hw["busid"]:"";

    if(busid != nil && busid != "") {
	nm_name = "bus-" + hw["bus"]:"" + "-" + hw["busid"]:"";
	if(Arch::s390 ()) nm_name = type + "-" + nm_name;
    }
    else {
	// #42421
	y2milestone ("No bus id info found, skipping hwcfg: %1", hw);
	nm_name = "";
    }

    /* eth, tr, not on s390 (#38819) */
    if(!Arch::s390 () && mac != nil && mac != "" && mac != "00:00:00:00:00:00")
	device = "id-" + hw["mac"]:"";
    /* iucv already filled in from lan/hardware.ycp (#42212) */
    else if(type == "iucv")
	y2debug("IUCV: %1", device);
    /* other devs */
    else if(busid != nil && busid != "")
	device = "bus-" + hw["bus"]:"" + "-" + hw["busid"]:"";
    /* USB, PCMCIA */
    else if(hw["hotplug"]:"" != "")
	device = "bus-" + hw["hotplug"]:"";
    /* dummy */
    else
	y2milestone("No detailed HW info: %1", device);

    y2milestone("hw=%1", hw);
    y2milestone("device=%1", device);
}

/**
 * Select the hardware component
 * @param which index of the component
 */
global define void SelectHW(integer which) {
    SelectHWMap (FindHardware (Hardware, which));
}


/**
 * Create a textual summary and a list of unconfigured devices
 * @param mode "split": split configured and unconfigured?<br />
 *             "summary": add resolver and routing symmary
 * @return summary of the current configuration
 */
global define list Summary(string mode) {
    boolean split = (mode == "split");

    list sum = BuildSummary("netcard", Hardware, split);

    /* Testing improved summary */
    if(mode == "summary")
	sum[0] = sum[0]:"" + DNS::Summary() + Routing::Summary();

    return sum;
}

/**
 * Create an overview table with all configured devices
 * @return table items
 */
global define list Overview() {
    return BuildOverview("netcard", Hardware);
}

/*-------------------*/
/* PRIVATE FUNCTIONS */

/**
 * Return 10 free devices
 * @param type device type
 * @return list of 10 free devices
 */
global define list FreeDevices(string type) {
    return NetworkDevices::GetFreeDevices(type, 10);
}

/**
 * Return 10 free aliases
 * @param type device type
 * @param num device number
 * @return list of 10 free devices
 */
global define list FreeAliases(string type, integer num) {
    // FIXME: NI y2debug("Devices=%1", Devices);
    map Devices_1 = $[]; // FIXME: NI Devices[type, sformat("%1",num)]:$[];
    y2debug("Devices=%1", Devices_1);
    return NetworkDevices::GetFreeDevices("_aliases", 10);
}


/**
 * Set various device variables
 * @param devmap map with variables
 * @return void
 */
void SetDeviceVars(map devmap) {


    /* address options */
    bootproto = devmap["BOOTPROTO"]:"static";
    ipaddr = devmap["IPADDR"]:"";
    remoteip = devmap["REMOTE_IPADDR"]:"";
    netmask = devmap["NETMASK"]:"";

    mtu = devmap["MTU"]:"";
    startmode = devmap["STARTMODE"]:"manual";
    usercontrol = devmap["USERCONTROL"]:"no" == "yes";

    /* wireless options */
    wl_mode = devmap["WIRELESS_MODE"]:"Managed";
    wl_essid = devmap["WIRELESS_ESSID"]:"";
    wl_nwid = devmap["WIRELESS_NWID"]:"";
    wl_auth_mode = devmap["WIRELESS_AUTH_MODE"]:"open";
    wl_wpa_psk = devmap["WIRELESS_WPA_PSK"]:"";
    wl_key_length = devmap["WIRELESS_KEY_LENGTH"]:"128";
    wl_key = []; // ensure exactly 4 entries
    wl_key[0] = devmap["WIRELESS_KEY_0"]:devmap["WIRELESS_KEY"]:"";
    wl_key[1] = devmap["WIRELESS_KEY_1"]:"";
    wl_key[2] = devmap["WIRELESS_KEY_2"]:"";
    wl_key[3] = devmap["WIRELESS_KEY_3"]:"";
    wl_default_key = tointeger (devmap["WIRELESS_DEFAULT_KEY"]:"0");
    wl_nick = devmap["WIRELESS_NICK"]:"";

    wl_channel = devmap["WIRELESS_CHANNEL"]:"";
    wl_frequency = devmap["WIRELESS_FREQUENCY"]:"";
    wl_bitrate = devmap["WIRELESS_BITRATE"]:"auto";
    wl_accesspoint = devmap["WIRELESS_AP"]:"";
    wl_power = devmap["WIRELESS_POWER"]:"yes" == "yes";

    /* s/390 options */
    read_channel = devmap["READ_CHANNEL"]:"";
    write_channel = devmap["WRITE_CHANNEL"]:"";
    data_channel = devmap["DATA_CHANNEL"]:"";
    portname = devmap["PORTNAME"]:"";
    
    aliases = devmap["_aliases"]:$[];

    return;
}

/**
 * Select the given device
 * @param dev device to select ("" for new device, default values)
 * @return true if success
 */
global define boolean Select(string dev) {

    y2debug("dev=%1", dev);
    // defaults for a new device
    map devmap = $[
	"STARTMODE": "auto",
	"NETMASK": (Detection::result["NETMASK"]:"255.255.255.0"), // #31369
	];

    /* dev=="" -> Add */
    if(dev == "") {
	type = "eth";
	device = NetworkDevices::GetFreeDevice(type);

	// FIXME: MOD AddModule(type);
	NetworkModules::AddM();
	nm_name = NetworkModules::GetFreeName("static-");

	/* FIXME: alias: how to prefill new alias? */
	alias = "";
    }
    /* dev!="" -> Edit */
    else {
	/* FIXME: NI:
	if(!CheckDevice(dev)) {
	    y2error("Device not found: %1", dev);
	    return false;
	}
	*/
	NetworkDevices::Edit(dev);
	devmap = NetworkDevices::Current;
	type = NetworkDevices::device_type(dev);
	device = NetworkDevices::device_num(dev);
	alias = NetworkDevices::alias_num(dev);
	nm_name = devmap["_nm_name"]:"";
    }

    /* general stuff */
    description = BuildDescription (type, device, devmap, Hardware);

    unique = devmap["UNIQUE"]:"";
    SetDeviceVars(devmap);


    hotplug = "";
    /*
    if(issubstring(device, "bus-pcmcia"))
	hotplug = "pcmcia";
    else if(issubstring(device, "bus-usb"))
	hotplug = "usb";
    */

    y2debug("type=%1", type);
    if(issubstring(type, "-"))
	type = regexpsub(type, "([^-]+)-.*$", "\\1");
    y2debug("type=%1", type);

    /* kernel options */
    if(hotplug == "" && alias == "" && nm_name != "") {
	// FIXME: MOD SelectModule(dev);
	NetworkModules::SelectM(nm_name);
    }
    else {
	// FIXME: MOD SelectModule("");
	NetworkModules::AddM();
    }

    qeth_options = NetworkModules::QETH_OPTIONS;
    chan_mode = NetworkModules::CCW_CHAN_MODE;

    if (NetworkModules::QETH_IPA_TAKEOVER == nil ||
	NetworkModules::QETH_IPA_TAKEOVER == "" ||
	NetworkModules::QETH_IPA_TAKEOVER == "0")
	ipa_takeover = false;
    else
	ipa_takeover = true;

    if(bootproto == "none") bootproto = "static";

    return true;
}

/**
 * Add a new device
 * @return true if success
 */
global define boolean Add() {
    operation = nil;
    if(Select("") != true) return false;
    NetworkDevices::Add();
    operation = `add;
    return true;
}

/**
 * Edit the given device
 * @param name device to edit
 * @return true if success
 */
global define boolean Edit(string name) {
    operation = nil;
    if(Select(name) != true) return false;
    NetworkDevices::Edit(name);
    operation = `edit;
    return true;
}

/**
 * Delete the given device
 * @param name device to delete
 * @return true if success
 */
global define boolean Delete(string name) {
    operation = nil;
    if(Select(name) != true) return false;
    NetworkDevices::Delete(name);
    operation = `delete;
    return true;
}

map <string, string> ifplugd_priorities = $[
    "eth": "20",
    "wlan": "10",
    ];

/**
 * Commit pending operation
 * @return true if success
 */
global define boolean Commit() {
    y2debug("Commit(%1)", operation);

    if(operation == `add || operation == `edit) {
	map<string,any> newdev = $[];

	//if(bootproto == "static") {
	if(ipaddr != "") {
	    newdev["IPADDR"] = ipaddr;
	    newdev["NETMASK"] = netmask;
	    newdev["BROADCAST"] = IP::ComputeBroadcast(ipaddr, netmask);
	    newdev["NETWORK"] = IP::ComputeNetwork(ipaddr, netmask);
	}
	newdev["REMOTE_IPADDR"] = remoteip;

	if(alias == "") {
	    newdev["MTU"] = mtu;
	    newdev["STARTMODE"] = startmode;
	    // it is not in Select yet because we don't have a widget for it
	    if (startmode == "ifplugd")
	    {
		string prio = ifplugd_priorities[type]:"";
		newdev["IFPLUGD_PRIORITY"] = prio;
	    }
	    newdev["USERCONTROL"] = usercontrol? "yes": "no";
	    newdev["BOOTPROTO"] = bootproto;
	    newdev["NAME"] = description;
	}

	if(unique != "" && unique != nil)
	    newdev["UNIQUE"] = unique;

	if(hotplug == "") {
	    if(alias == "") {
		// FIXME: MOD ChangeModule(NetworkDevices::device_name(type, device), operation == `add);
		y2milestone("HWCFG name %1 (%2)", nm_name, nm_name_old);
		if(nm_name != nil && nm_name != "" && operation == `edit &&
			nm_name_old != nil && nm_name_old != "" &&
			nm_name != nm_name_old) {
		    y2milestone("HWCFG name change (%1) -> deleting old (%2)", nm_name, nm_name_old);
		    NetworkModules::DeleteM(nm_name_old);
		    nm_name_old = nil;
		}

		if(Arch::s390 ()) {
		    // handle HSI like qeth, #40692#c15
		    string type2 = (type == "hsi")? "qeth": type;
		    // handle tr like lcs
		    if (type == "tr" || type == "eth")
			type2 = "lcs";
		    if(type2 == "iucv") {
			NetworkModules::SCRIPTUP = "hwup-iucv";
		    }
		    else {
			NetworkModules::SCRIPTUP = "hwup-ccw";
			NetworkModules::SCRIPTUP_ccw = "hwup-ccw";
			NetworkModules::SCRIPTDOWN = "hwdown-ccw";
			NetworkModules::SCRIPTUP_ccwgroup = "hwup-" + type2;
		    }

		    integer devid = 0;
		    string devstr = "";

		    y2milestone("nm_name=%1", nm_name);
		    string s390chanid = "[0-9]+\\.[0-9]+\\.";
		    if(regexpmatch(nm_name, s390chanid)) {
			devid = tointeger("0x" + regexpsub(nm_name, s390chanid + "(.*)", "\\1"));
			devstr = regexpsub(nm_name, ".*[^0-9](" + s390chanid + ").*", "\\1");
		    }

		    y2milestone("devid=%1(%2)", devid, devstr);
		    if(devid == nil) devid = 0;
		    string devid0 = String::PadZeros(regexpsub(tohexstring(devid), "0x(.*)", "\\1"), 4);
		    string devid1 = String::PadZeros(regexpsub(tohexstring(devid+1), "0x(.*)", "\\1"), 4);
		    string devid2 = String::PadZeros(regexpsub(tohexstring(devid+2), "0x(.*)", "\\1"), 4);

		    if(type2 == "ctc" || type2 == "lcs") {
			NetworkModules::CCW_CHAN_IDS = sformat("%1%2 %1%3", devstr, devid0, devid1);
			NetworkModules::CCW_CHAN_MODE = chan_mode;
			NetworkModules::CCW_CHAN_NUM = "2";
		    }
		    else if(type2 == "qeth") {
			NetworkModules::CCW_CHAN_IDS = sformat("%1%2 %1%3 %1%4", devstr, devid0, devid1, devid2);
			NetworkModules::CCW_CHAN_MODE = chan_mode;
			NetworkModules::CCW_CHAN_NUM = "3";
			NetworkModules::QETH_OPTIONS = qeth_options;
			if (ipa_takeover)
			    NetworkModules::QETH_IPA_TAKEOVER = "1";
		    }

		}

		// If "", no hwcfg is created. See also #42421.
		NetworkModules::ChangeM(nm_name);
	    }
	}

	if(hotplug == "pcmcia")
	    newdev["DHCLIENT_SET_DOWN_LINK"] = "yes";

	if(type == "wlan") {
	    newdev["WIRELESS_MODE"] = wl_mode;
	    newdev["WIRELESS_ESSID"] = wl_essid;
	    newdev["WIRELESS_NWID"] = wl_nwid;
	    newdev["WIRELESS_AUTH_MODE"] = wl_auth_mode;
	    newdev["WIRELESS_WPA_PSK"] = wl_wpa_psk;
	    newdev["WIRELESS_KEY_LENGTH"] = wl_key_length;
	    // obsoleted by WIRELESS_KEY_0
	    newdev["WIRELESS_KEY"] = ""; // TODO: delete the varlable
	    newdev["WIRELESS_KEY_0"] = wl_key[0]:"";
	    newdev["WIRELESS_KEY_1"] = wl_key[1]:"";
	    newdev["WIRELESS_KEY_2"] = wl_key[2]:"";
	    newdev["WIRELESS_KEY_3"] = wl_key[3]:"";
	    newdev["WIRELESS_DEFAULT_KEY"] = tostring (wl_default_key);
	    newdev["WIRELESS_NICK"] = wl_nick;

	    newdev["WIRELESS_CHANNEL"] = wl_channel;
	    newdev["WIRELESS_FREQUENCY"] = wl_frequency;
	    newdev["WIRELESS_BITRATE"] = wl_bitrate;
	    newdev["WIRELESS_AP"] = wl_accesspoint;
	    newdev["WIRELESS_POWER"] = wl_power ? "yes" : "no";
	}

	if(nm_name != nil && nm_name != "")
	    newdev["_nm_name"] = nm_name;

	if(type == "ctc" || type == "escon")
	    if(NetworkConfig::Config["WAIT_FOR_INTERFACES"]:nil == nil || NetworkConfig::Config["WAIT_FOR_INTERFACES"]:0 < 40)
		NetworkConfig::Config["WAIT_FOR_INTERFACES"] = 40;

	/* FIXME: MOD
	if(type == "hsi" || type == "qeth")
	    if(NetworkConfig::Config["WAIT_FOR_INTERFACES"]:nil == nil || NetworkConfig::Config["WAIT_FOR_INTERFACES"]:0 < 15)
		NetworkConfig::Config["WAIT_FOR_INTERFACES"] = 15;
	*/

	if(alias == "") {
	    newdev["_aliases"] = aliases;
	    y2milestone("aliases %1", aliases);
	    NetworkDevices::Name = NetworkDevices::device_name(NetworkDevices::RealType(type, hotplug), device);
	}
	else
	    NetworkDevices::Name = NetworkDevices::alias_name(NetworkDevices::RealType(type, hotplug), device, alias);

	NetworkDevices::Current = newdev;
	NetworkDevices::Commit();
    }
    else if(operation == `delete) {
	NetworkDevices::Commit();
	if(alias == "") {
	    if(hotplug == "") {
		// FIXME: MOD DeleteModule(NetworkDevices::device_name(type, device));
		NetworkModules::DeleteM(nm_name);
	    // FIXME: NI DeleteDevice2(NetworkDevices::device_name(type, device));
	    }
	}
	else {
	    // FIXME: NI DeleteDevice2(NetworkDevices::device_name(type, device) + ":" + alias);
	}
    }
    else {
	y2error("Unknown operation: %1", operation);
	return false;
    }

    nm_name_old = nil;
    modified = true;
    operation = nil;
    return true;
}

/**
 * Get the module configuration for the modules configured in the 
 * interface section
 * @param ay_device Device, for example eth0
 * @param ay_modules list of modules from the AY profile
 * @return map the module map with module name and options
 */
define map GetModuleForInterface(string ay_device ,
				    list<map> ay_modules) {
    map ayret = $[];
    list<map> ay_filtered = filter(map ay_m, ay_modules,
				  ``(
				     ay_m["device"]:"" == ay_device ));
    
    if (size(ay_filtered) > 0 ) {
	ayret = ay_filtered[0]:$[];
    }

    return ayret;
}


/**
 * Find matching device 
 * Find a device, optionally with some predefined values
 * @param interface interface map 
 * @return map The map of the matching device.
 */
define map FindMatchingDevice(map interface) {
    map tosel = nil;
    list<string> devs = NetworkDevices::List("netcard");
    y2milestone("Configured devices: %1", devs );

    // this condition is always true for SLES9, HEAD uses $[] for proposal
    if (interface != $[])
    {
	// Notes for comments about matching:
	// - interface["device"] is the key which we look for in the actual hw
	// - H iterates over Hardware
	// - patterns are shell-like

        /* code for eth-id-00:80:c8:f6:48:4c configurations */
	// *-id-$ID => find H["mac"] == $ID
        list device_id = splitstring(interface["device"]:"", "-");
        if (size(device_id)> 1 && issubstring(interface["device"]:"", "id"))
        {
            string hwaddr = device_id[2]:"";
            if(hwaddr != nil && hwaddr != "") {
                tosel = find (map h, Hardware, ``( h["mac"]:"" == hwaddr ));
            }			
            y2milestone("Rule: matching mac in device name");
        }
        /* code for eth-bus-pci-0000:00:0d.0 configurations */
	// *-bus-pci-$ID => find H["bus"] == "pci" & H["busid"] == $ID
        else if ((size(device_id)> 1 &&
                    issubstring(interface["device"]:"", "bus-pci")))
        {
            string busid = device_id[3]:"";
            if(busid != nil && busid != "") {
                tosel = find (map h, Hardware, ``( h["busid"]:"" == busid
                            && h["bus"]:"" == "pci" ));
            }	
            y2milestone("Rule: matching pci id in device name");
        }

        /* code for module configuration */
	// join with the modules list of the ay profile according to "device"
	// if exists => find H["module"] == AH["module"]
        map aymodule = GetModuleForInterface(interface["device"]:"", 
                autoinstall_settings["modules"]:[]);
        y2milestone("module data: %1", aymodule );
        if (tosel == nil && aymodule != $[]) {
            if(aymodule != nil && aymodule["module"]:"" != "") {
                tosel = find (map h, Hardware, ``( h["module"]:"" == aymodule["module"]:"" ));
            }
            y2milestone("Rule: matching module configuration");
        }
    }
    
    // First device was already configured, we are now looking for 
    // a second (third,...) one
    if (size(devs) > 0 )
    {
	// go thru all devices, check whether there's one that does
	// not have a configuration yet
	// and has the same type as the current profile item
	foreach (map h, Hardware, {
            y2milestone("Checking for device=%1", h);
            SelectHWMap(h);
            string _device_name = NetworkDevices::device_name(NetworkDevices::RealType(type, hotplug), device);
            if (!NetworkDevices::Check(_device_name) &&
                type == NetworkDevices::device_type(interface["device"]:"")
                )
            {
                y2milestone("Selected: %1", h );
                tosel = h;
                break;
            }
        });
        if (tosel == nil )
        {
            y2error("Nothing found");
        }

    } else {
	// this is the first interface, match the hardware with install.inf

        /* Select the one from install.inf with the same MAC */
	// find H["mac"] == InstallInf["hwaddr"]
        if (tosel == nil  ) {
            string hwaddr = InstallInf["hwaddr"]:"";
            if(hwaddr != nil && hwaddr != "") {
                tosel = find (map h, Hardware, ``( h["mac"]:"" == hwaddr ));
            }
            y2milestone("Rule: matching hwaddr in install.inf");
        }

        /* Select the one from install.inf with the same module */
	// find H["module"] == InstallInf["module"]
        if (tosel == nil) {
            string infmodule = InstallInf["module"]:"";
            if(infmodule != nil && infmodule != "") {
                tosel = find (map h, Hardware, ``( h["module"]:"" == infmodule ));
            }
            y2milestone("Rule: matching module in install.inf");
        }

        /* No install.inf driver -> select the first active */
        // but not wlan, #44951
	// find H["active"] == true && H["type"] != "wlan"
        if (tosel == nil) {
            tosel = find (map h, Hardware, ``(
                        h["active"]:false
                        && h["type"]:"" != "wlan"
                        ));
            y2milestone("Rule: first active");
        }

        /* No active driver -> select the first with a driver */
	// find H["module"] != "" && H["type"] != "wlan"
        if (tosel == nil) {
            y2milestone("No active driver found, trying further.");
            tosel = find (map h, Hardware, ``(
                        h["module"]:"" != ""
                        && h["type"]:"" != "wlan"
                        && y2milestone("Using driver: %1", h) == nil
                        ));
            y2milestone("Rule: first with driver");
        }
    }

    return tosel;
}

/**
 * Propose a configuration
 * @return true if something was proposed
 */
global define boolean Propose() {

    y2milestone("Hardware=%1", Hardware);
    y2milestone("InstallInf=%1", InstallInf);

    y2milestone("NetworkConfig::Config=%1", NetworkConfig::Config);
    y2milestone("NetworkConfig::DHCP=%1", NetworkConfig::DHCP);

    /* Something is already configured -> do nothing */
    list<string> devs = NetworkDevices::List("netcard");
    // workaround for #50164:
    // better do it in ND::List, when we know what we can expect from HAL
    devs = filter (string d, devs, ``( d != "usb" && d != "pcmcia"));
    if(size(devs) > 0) {
	y2milestone("Something already configured: don't propose.");
	y2milestone("devs=%1", devs);
	return false;
    }

    Add();

    map tosel = nil;

    /* Some HW found -> use it for proposal */
    if(size(Hardware) > 0) {
        tosel =  FindMatchingDevice($[]);
    }
        

    y2debug("Hardware=%1", Hardware);
    y2milestone("tosel=%1", tosel);
    // do not select a card without a driver, #29296
    if (tosel != nil)
    {
	SelectHWMap(tosel);
    }
    /* Nothing detected -> use InstallInf */
    else if(size(InstallInf) > 0) {

	if(!haskey(InstallInf, "module")) {
	    y2milestone("No hardware, no install.inf module -> no proposal.");
	    return false;
	}

	// FIXME: MOD Module["module"] = InstallInf["module"]:"";
	// FIXME: MOD Module["options"] = InstallInf["options"]:"";

	NetworkModules::Alias = InstallInf["module"]:"";
	NetworkModules::Options = InstallInf["options"]:"";

	type = InstallInf["type"]:"eth";
	device = NetworkDevices::GetFreeDevice(type);
    }
    else {
	y2milestone("No hardware, no install.inf -> no proposal.");
	return false;
    }

    ipaddr = InstallInf["ipaddr"]:"";
    bootproto = InstallInf["bootproto"]:"static";
    y2debug("ipaddr,bootproto=%1,%2", ipaddr, bootproto);
    if(bootproto == "static" && ipaddr != "" && ipaddr != nil) {

	y2milestone("static proposal");

	/* propose address setup from /etc/install.inf */
	netmask = InstallInf["netmask"]:"";
	if(netmask == nil || netmask == "")
	    netmask = "255.255.255.0";

	/* propose default gateway from /etc/install.inf */
	string gw = InstallInf["gateway"]:"";
	if(gw != "" && gw != nil) {
	    Routing::Routes = [ $[
		"destination"       : "default",
		"gateway"           : gw,
		"netmask"           : "-",
		"device"            : "-"
	    ] ];
	}

	/* propose DNS setup from /etc/install.inf */
	string ns = InstallInf["nameserver"]:"";
	string hn = InstallInf["hostname"]:"";
	string dn = InstallInf["domain"]:"";

	/* try to get hostname from the network */
	if(hn == "") {
	    string hnent = Detection::ResolveIP(ipaddr);
	    y2debug("%1", hnent);
	    list hnsplit = Hostname::SplitFQ(hnent);
	    hn = hnsplit[0]:hn;
	    y2debug("%1", hn);
	    hnent = hnsplit[1]:"";
	    if(hnent != "" && hnent != nil)
		dn = hnent;
	    y2debug("hn=%1", hn);
	    y2debug("dn=%1", dn);
	}

	/* commit the DNS setup */
	if(ns != "" && ns != nil) {
	    DNS::nameservers = [ ns ];
	    DNS::modified = true;
	}
	if(hn != "" && hn != nil) {
	    if(dn != "" && dn != nil)
		DNS::domain = dn;
	    DNS::hostname = hn;
	    DNS::modified = true;
	}

    }
    else {
	/* Propose static config in case of DSL hardware is found */
	list pppoe = (list) SCR::Read(.probe.pppoe);
	// FIXME: testing pppoe = [ $["a" : "b"] ];
	map pppoedev = pppoe[0]:$[];
	y2debug("pppoe=%1", pppoe);
	if(pppoe != nil && size(pppoe) > 0) {
	    y2milestone("DSL proposal");
	    ipaddr = "";
	    bootproto = "static";
	}
	else {
	    // Open Enterprise Server does not like DHCP (#50524).
	    // In that case, we cannot propose.
	    if (ProductFeatures::force_static_ip)
	    {
		y2milestone ("Static config forced but no info available -> no proposal.");
		return false;
	    }
	    y2milestone("DHCP proposal");
	    ipaddr = "";
	    bootproto = "dhcp";
	    NetworkConfig::DHCP["DHCLIENT_SET_HOSTNAME"] = true;
	}

    }

    Commit();

    y2milestone("NetworkConfig::Config=%1", NetworkConfig::Config);
    y2milestone("NetworkConfig::DHCP=%1", NetworkConfig::DHCP);

    return true;
}

/**
 * Create a configuration for autoyast
 * @return true if something was proposed
 */
global define boolean Autoinstall() {

    y2milestone("Hardware=%1", Hardware);
    map tosel = nil;

    /* Some HW found -> use it for proposal */
    if(size(Hardware) > 0
       && size(autoinstall_settings["interfaces"]:[]) > 0) {

        foreach(map<string, string> interface, autoinstall_settings["interfaces"]:[], ``{
                
            list<string> devs = NetworkDevices::List("netcard");
            y2milestone("devs: %1", devs);
            tosel = nil;

            Add();

    	    tosel = FindMatchingDevice(interface);

	    y2milestone("tosel=%1", tosel);

            // Read module data from autoyast
            map aymodule = GetModuleForInterface(interface["device"]:"",
                autoinstall_settings["modules"]:[]);


	    if (tosel != nil)
	    {
		tosel["module"] = (aymodule["module"]:""!= "")?
		    aymodule["module"]:"" : tosel["module"]:"";
		tosel["options"] = (aymodule["options"]:""!= "")?
		    aymodule["options"]:"" : tosel["options"]:"";
		
		SelectHWMap(tosel);
	    }
	    /* Nothing detected -> use InstallInf */
	    else if(size(InstallInf) > 0) {

		if(!haskey(InstallInf, "module")) {
		    y2milestone("No hardware, no install.inf module -> no proposal.");
		    return false;
		}
		NetworkModules::Alias = InstallInf["module"]:"";
		NetworkModules::Options = InstallInf["options"]:"";

		type = InstallInf["type"]:"eth";
		device = NetworkDevices::GetFreeDevice(type);
	    }
	    else {
		y2milestone("No hardware, no install.inf -> no autoinstallation possible.");
		return false;
	    }

	    // The uppercasing is also done in lan_auto::FromAY
	    // but the output goes to "devices" whereas here
	    // we use "interfaces". FIXME.
            string newk = nil;
            interface =  mapmap(string k, string v, interface, ``{
                    newk = toupper(k);
                    return $[newk: v];
                    });


            // Set interface variables
            SetDeviceVars(interface);
	    
	    y2debug("ipaddr,bootproto=%1,%2", ipaddr, bootproto);
            if(bootproto == "static" && ipaddr != "" && ipaddr != nil) 
            {

                y2milestone("static configuration");

		if(netmask == nil || netmask == "")
		    netmask = "255.255.255.0";
	
	    }
	    Commit();
	});
    } 
    else 
    {
        y2milestone("no interface configuration, taking everything from install.inf");
        Propose();
    }

    return true;
}


/**
 * Check if any device except the current one is configured with DHCP.
 * The current one is checked separately because there may be uncommitted
 * changes. (#17391)
 * @return true if any DHCP device except the current one is configured
 */
global define boolean AnyDHCPDevice() {
    list<string> devs = NetworkDevices::Locate("BOOTPROTO", "dhcp");
    devs = filter(string dev, devs, {
	return dev != NetworkDevices::device_name(NetworkDevices::RealType(type, hotplug), device);
    });
    return size(devs) > 0;
}

/**
 * Check if the given device has any virtual alias.
 * @param dev device to be checked
 * @return true if there are some aliases
 */
global define boolean HasAliases(string dev) {
    return NetworkDevices::HasAliases(dev);
}


global define list<map> PrepareForAutoinst() {
  
    ReadInstallInf();     
    Hardware = ReadHardware("netcard");
    return Hardware;
}

/**
 * @return list of packages needed when writing the config
 */
global list<string> Packages () {
    list<string> pkgs = [];
    if (NetworkDevices::Locate ("USERCONTROL", "yes") != [])
    {
	pkgs = add (pkgs, "smpppd");
    }
    if (NetworkDevices::Locate ("STARTMODE", "ifplugd") != [])
    {
	pkgs = add (pkgs, "ifplugd");
    }
    // #49880
    if (NetworkDevices::List ("wlan") != [])
    {
	pkgs = add (pkgs, "wireless-tools");
    }
    // #47809
    if (NetworkDevices::Locate ("WIRELESS_AUTH_MODE", "psk") != [])
    {
	pkgs = add (pkgs, "wpa_supplicant");
    }
    return pkgs;
}

/**
 * @return list of packages needed when writing the config in autoinst
 * mode
 */
global map AutoPackages () {
      return ($["install": Packages(), "remove": []]);
}

/**
 * Function which returns if the settings were modified
 * @return boolean  settings were modified
 */
global define boolean GetModified () {
    return modified;
}
/**
 * Function sets internal variable, which indicates, that any
 * settings were modified, to "true"
 */
global define void SetModified () {
    modified = true;
}
/**
 * Function sets internal variable, which indicates, that any
 * settings were modified, to "false"
 */
global define void UnsetModified () {
    modified = false;
}

/* EOF */
}
