ACC SHELL

Path : /usr/share/YaST2/modules/
File Upload :
Current File : //usr/share/YaST2/modules/SuSEFirewall.ycp

/**
 * Copyright 2004, Novell, Inc.  All rights reserved.
 *
 * File:	modules/SuSEFirewall.ycp
 * Package:	SuSEFirewall configuration
 * Summary:	Interface manipulation of /etc/sysconfig/SuSEFirewall
 * Authors:	Lukas Ocilka <locilka@suse.cz>
 *
 * $Id: SuSEFirewall.ycp 60742 2010-02-08 15:18:49Z locilka $
 *
 * Module for handling SuSEfirewall2.
 */

{
    module "SuSEFirewall";
    textdomain "base";

    import "Mode";
    import "Service";
    import "NetworkInterfaces";
    import "SuSEFirewallServices";
    import "PortAliases";
    import "Report";
    import "Message";
    import "Progress";
    import "PortRanges";
    import "PackageSystem";
    import "FileUtils";
    import "Directory";
    import "Stage";

    // <!-- SuSEFirewall VARIABLES //-->

    string susefirewall_package = "SuSEfirewall2";

    /**
     * configuration hasn't been read for the default
     * this should reduce the readings to only ONE
     */
    boolean configuration_has_been_read = false;

    /**
     * String which includes all interfaces not-defined in any zone
     */
    global string special_all_interface_string = "any";

    /**
     * Maximal number of port number, they are in the interval 1-65535 included
     */
    global integer max_port_number = PortRanges::max_port_number;

    /**
     * Zone which works with the special_all_interface_string string
     */
    global string special_all_interface_zone   = "EXT";

    /* firewall settings map */
    map<string, any> SETTINGS = $[];

    /* configuration was modified when true */
    boolean modified = false;

    /* defines if SuSEFirewall is running */
    boolean is_running = false;

    /* default settings for SuSEFirewall */
    map<string, string> DEFAULT_SETTINGS = $[
	"FW_LOG_ACCEPT_ALL"		: "no",
	"FW_LOG_ACCEPT_CRIT"		: "yes",
	"FW_LOG_DROP_ALL"		: "no",
	"FW_LOG_DROP_CRIT"		: "yes",
	"FW_PROTECT_FROM_INT"		: "no",
	"FW_ROUTE"			: "no",
	"FW_MASQUERADE"			: "no",
	"FW_ALLOW_FW_TRACEROUTE"	: "yes",
	"FW_ALLOW_PING_FW"		: "yes",
	"FW_ALLOW_FW_BROADCAST_EXT"	: "no",
	"FW_ALLOW_FW_BROADCAST_INT"	: "no",
	"FW_ALLOW_FW_BROADCAST_DMZ"	: "no",
	"FW_IGNORE_FW_BROADCAST_EXT"	: "yes",
	"FW_IGNORE_FW_BROADCAST_INT"	: "no",
	"FW_IGNORE_FW_BROADCAST_DMZ"	: "no",
	"FW_IPSEC_TRUST"		: "no",
    ];

    /* verbose_level -> if verbosity is more than 0, be verbose, starting in verbose mode */
    integer verbose_level = 1;

    /* list of known firewall zones */
    list <string> known_firewall_zones = [ "INT", "DMZ", "EXT" ];

    /* map defines zone name for all known firewall zones */
    map <string, string> zone_names = $[
	// TRANSLATORS: Firewall zone name - used in combo box or dialog title
	"EXT" : _("External Zone"),
	// TRANSLATORS: Firewall zone name - used in combo box or dialog title
	"INT" : _("Internal Zone"),
	// TRANSLATORS: Firewall zone name - used in combo box or dialog title
	"DMZ" : _("Demilitarized Zone"),
    ];

    /* internal zone identification - useful for protect-from-internal */
    string int_zone_shortname = "INT";

    /* list of protocols supported in firewall, use only upper-cases */
    list <string> supported_protocols = [ "TCP", "UDP", "RPC", "IP" ];

    /* list of keys in map of definition well-known services */
    list <string> service_defined_by = ["tcp_ports", "udp_ports", "rpc_ports", "ip_protocols", "broadcast_ports"];

    /* list of services currently allowed, which share ports (for instance RPC services) */
    map <string, list <string> > allowed_conflict_services = $[];

    /* services needed for well-running firewall */
    list <string> firewall_services = [ "SuSEfirewall2_init", "SuSEfirewall2_setup" ];
    list <string> firewall_services_reverse = [ "SuSEfirewall2_setup", "SuSEfirewall2_init" ];

    list <string> SuSEFirewall_variables = [
	// zones and interfaces
	"FW_DEV_INT", "FW_DEV_DMZ", "FW_DEV_EXT",

	// services in zones
	"FW_SERVICES_INT_TCP", "FW_SERVICES_INT_UDP", "FW_SERVICES_INT_RPC", "FW_SERVICES_INT_IP",
	"FW_SERVICES_DMZ_TCP", "FW_SERVICES_DMZ_UDP", "FW_SERVICES_DMZ_RPC", "FW_SERVICES_DMZ_IP",
	"FW_SERVICES_EXT_TCP", "FW_SERVICES_EXT_UDP", "FW_SERVICES_EXT_RPC", "FW_SERVICES_EXT_IP",
	"FW_PROTECT_FROM_INT",

	// global routing, masquerading
	"FW_ROUTE", "FW_MASQUERADE", "FW_FORWARD_MASQ", "FW_FORWARD_ALWAYS_INOUT_DEV",

	// broadcast packets
	"FW_ALLOW_FW_BROADCAST_EXT", "FW_ALLOW_FW_BROADCAST_INT", "FW_ALLOW_FW_BROADCAST_DMZ",
	"FW_IGNORE_FW_BROADCAST_EXT", "FW_IGNORE_FW_BROADCAST_INT", "FW_IGNORE_FW_BROADCAST_DMZ",

	// FATE #300970: Support for 'Samba & friends' browsing
	"FW_SERVICES_ACCEPT_RELATED_EXT", "FW_SERVICES_ACCEPT_RELATED_INT", "FW_SERVICES_ACCEPT_RELATED_DMZ",

	// logging
	"FW_LOG_DROP_CRIT", "FW_LOG_DROP_ALL", "FW_LOG_ACCEPT_CRIT", "FW_LOG_ACCEPT_ALL",

	// IPsec support
	"FW_IPSEC_TRUST",
	
	// Custom rulezz
	//     net,protocol[,dport][,sport]
	"FW_SERVICES_ACCEPT_EXT", "FW_SERVICES_ACCEPT_INT", "FW_SERVICES_ACCEPT_DMZ",

	// Custom kernel modules, e.g., for FTP
	"FW_LOAD_MODULES",

	// Services defined in /usr/share/SuSEfirewall2/services/ directory
	// FATE #300687: Ports for SuSEfirewall added via packages
	"FW_CONFIGURATIONS_EXT", "FW_CONFIGURATIONS_INT", "FW_CONFIGURATIONS_DMZ",
    ];

    list <string> one_line_per_record = ["FW_FORWARD_MASQ", "FW_SERVICES_ACCEPT_EXT", "FW_SERVICES_ACCEPT_INT", "FW_SERVICES_ACCEPT_DMZ"];

    // FATE #300970: Firewall support for SMB browsing
    string broadcast_related_module = "nf_conntrack_netbios_ns";

    // <!-- SuSEFirewall VARIABLES //-->

    // <!-- SuSEFirewall GLOBAL FUNCTIONS USED BY LOCAL ONES //-->

    /**
     * Returns whether records in variable should be written one record on one line.
     * @return boolean if wolpr
     */
    boolean WriteOneRecordPerLine (string key_name) {
	if (key_name == nil && key_name == "")
	    return true;

	return (contains (one_line_per_record, key_name));
    }

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

    /**
     * Do not use this function.
     * Only for firewall installation proposal.
     */
    global define void ResetModified () {
	y2milestone("Reseting firewall-modified to 'false'");
	modified = false;
    }

    /**
     * Function returns list of known firewall zones (shortnames)
     *
     * @return	list <string> of firewall zones
     *
     * @example GetKnownFirewallZones() -> ["DMZ", "EXT", "INT"]
     */
    global define list <string> GetKnownFirewallZones () {
	return known_firewall_zones;
    }

    global define boolean IsServiceSupportedInZone (string service, string zone);
    global define list <string> GetSpecialInterfacesInZone (string zone);
    global define void AddSpecialInterfaceIntoZone (string interface, string zone);
    

    /**
     * Variable for ReportOnlyOnce() function
     */
    list <string> report_only_once = [];

    /**
     * Report the error, warning, message only once.
     * Stores the error, warning, message in memory.
     * This is just a helper function that could avoid from filling y2log up with
     * a lot of the very same messages - 'foreach()' is a very powerful builtin.
     *
     * @param string error, warning or message
     * @return boolean whether the message should be reported or not
     *
     * @example
     *	string error = sformat("Port number %1 is invalid.", port_nr);
     *	if (ReportOnlyOnce(error)) y2error(error);
     */
    boolean ReportOnlyOnce (string what_to_report) {
	if (contains(report_only_once, what_to_report)) {
	    return false;
	} else {
	    report_only_once = add (report_only_once, what_to_report);
	    return true;
	}
    }

    // <!-- SuSEFirewall GLOBAL FUNCTIONS USED BY LOCAL ONES //-->

    // <!-- SuSEFirewall LOCAL FUNCTIONS //-->

    /**
     * Function returns whether the feature 'any' network interface is supported in the
     * firewall configuration. The string 'any' must be in the 'EXT' zone.
     *
     * @return boolean is_supported whether the feature is supported or not
     */
    global boolean IsAnyNetworkInterfaceSupported () {
	return contains(GetSpecialInterfacesInZone(special_all_interface_zone), special_all_interface_string);
    }

    /**
     * Function return list of variables needed for SuSEFirewall's settings.
     *
     * @return	list <string> of names of variables
     */
    list <string> GetListOfSuSEFirewallVariables () {
	return SuSEFirewall_variables;
    }

    /**
     * Local function for increasing the verbosity level.
     */
    void IncreaseVerbosity () {
	verbose_level = verbose_level + 1;
    }
    
    /**
     * Local function for decreasing the verbosity level.
     */
    void DecreaseVerbosity () {
	verbose_level = verbose_level - 1;
    }

    /**
     * Local function returns if other functions should produce verbose output.
     * like popups, reporting errors, etc.
     *
     * @return	boolean is_verbose
     */
    boolean IsVerbose () {
	// verbose level must be above zero to be verbose
	return (verbose_level > 0);
    }

    /**
     * Local function for returning default values (if defined) for sysconfig variables.
     *
     * @param	string sysconfig variable
     * @return	string default value
     */
    string GetDefaultValue (string variable) {
	return (string) DEFAULT_SETTINGS[variable]:"";
    }

    /**
     * Local function for reading list of sysconfig variables into internal variables.
     *
     * @param	list <string> of sysconfig variables
     */
    void ReadSysconfigSuSEFirewall (list<string> variables) {
	foreach (string variable, variables, {
	    string value = (string) SCR::Read(add(.sysconfig.SuSEfirewall2,variable));
	    // if variable is undefined, get default value
	    if (value == nil || value == "")
		value = GetDefaultValue(variable);

	    // BNC #426000
	    // backslash at the end
	    if (regexpmatch (value, "[ \t]*\\\\[ \t]*\n")) {
		list <string> rules = splitstring (value, "\\ \t\n");
		rules = filter (string one_rule, rules, {
		    return (one_rule != nil && one_rule != "");
		});
		value = mergestring (rules, " ");
	    }

	    // BNC #194419
	    // replace all "\n" with " " in variables
	    if (regexpmatch(value, "\n"))
		value = mergestring(splitstring(value, "\n"), " ");

	    // replace all "\t" with " " in variables
	    if (regexpmatch(value, "\t"))
		value = mergestring(splitstring(value, "\t"), " ");

	    SETTINGS[variable] = value;
	});
    }

    /**
     * Local function for reseting list of sysconfig variables in internal variables.
     *
     * @param	list <string> of sysconfig variables
     */
    void ResetSysconfigSuSEFirewall (list<string> variables) {
	foreach (string variable, variables, {
	    // reseting means getting default variables
	    SETTINGS[variable] = GetDefaultValue(variable);
	});
    }

    /**
     * Local function for writing the list of internal variables into sysconfig.
     * List of variables is list of keys in SETTINGS map, to sync configuration
     * into the disk, use `nil` as the last list item.
     *
     * @param	list <string> of sysconfig variables
     * @return	boolean if successful
     */
    boolean WriteSysconfigSuSEFirewall (list<string> variables) {
	boolean write_status = true;
	string value = "";

	foreach (string variable, variables, {
	    // if variable is undefined, get default value
	    value = SETTINGS[variable]:GetDefaultValue(variable);

	    if (WriteOneRecordPerLine (variable) == true) {
		value = mergestring (splitstring (value, " "), "\n");
	    }

	    write_status = (boolean) SCR::Write(
		add(.sysconfig.SuSEfirewall2,variable),
		value
	    );

	    if (! write_status) {
		Report::Error(Message::CannotWriteSettingsTo("/etc/sysconfig/SuSEFirewall"));
		break;
	    }
	});

	write_status = SCR::Write(.sysconfig.SuSEfirewall2, nil);
	if (! write_status) {
	    Report::Error(Message::CannotWriteSettingsTo("/etc/sysconfig/SuSEFirewall"));
	}

	return write_status;
    }

    /**
     * Local function returns if protocol is supported by firewall.
     * Protocol name must be in upper-cases.
     *
     * @param	string protocol
     * @return	boolean if protocol is supported
     */
    boolean IsSupportedProtocol (string protocol) {
	return contains(supported_protocols, protocol);
    }

    /**
     * Local function returns if zone (shortname like "EXT") is supported by firewall.
     * Undefined zones are, for sure, unsupported.
     *
     * @param	string zone shortname
     * @return	boolean if zone is known and supported.
     */
    boolean IsKnownZone (string zone) {
	boolean is_zone = false;

	foreach (string known_zone, GetKnownFirewallZones(), {
	    if (known_zone == zone) {
		is_zone = true;
		break;
	    }
	});

	return is_zone;
    }

    /**
     * Local function returns configuration string used in configuration for zone.
     * For instance "ext" for "EXT" zone.
     *
     * @param	string zone shortname
     * @return	string zone configuration string
     */
    string GetZoneConfigurationString (string zone) {
	if (IsKnownZone(zone)) {
	    // zones in SuSEFirewall configuration are identified by lowercased zone shorters
	    return tolower(zone);
	}
	return nil;
    }

    /**
     * Local function returns zone name (shortname) for configuration string.
     * For instance "EXT" for "ext" zone.
     *
     * @param	string zone configuration string
     * @return	string zone shortname
     */
    string GetConfigurationStringZone (string zone_string) {
	if (IsKnownZone(toupper(zone_string))) {
	    // zones in SuSEFirewall configuration are identified by lowercased zone shorters
	    return toupper(zone_string);
	}
	return nil;
    }

    /**
     * Function returns list of allowed services for zone and protocol
     *
     * @param	string zone
     * @param	string protocol
     * @return	list <string> of allowed services/ports
     */
    list <string> GetAllowedServicesForZoneProto (string zone, string protocol) {
	return splitstring(SETTINGS["FW_SERVICES_" + zone + "_" + protocol]:"", " ");
    }

    /**
     * Function sets list of services as allowed ports for zone and protocol
     *
     * @param	list <string> of allowed ports/services
     * @param	string zone
     * @param	string protocol
     */
    void SetAllowedServicesForZoneProto (list <string> allowed_services, string zone, string protocol) {
	SetModified();

	SETTINGS["FW_SERVICES_" + zone + "_" + protocol] = mergestring (toset(allowed_services), " ");
    }

    /**
     * Local function returns configuration string for broadcast packets.
     *
     * @return	string with broadcast configuration
     */
    string GetBroadcastConfiguration (string zone) {

	return SETTINGS["FW_ALLOW_FW_BROADCAST_" + zone]:"no";
    }

    /**
     * Local function saves configuration string for broadcast packets.
     *
     * @param	string with broadcast configuration
     */
    void SetBroadcastConfiguration (string zone, string broadcast_configuration) {
	SetModified();

	SETTINGS["FW_ALLOW_FW_BROADCAST_" + zone] = broadcast_configuration;
    }

    /**
     * Local function return map of allowed ports (without aliases).
     * If any list for zone is defined but empty, all allowed
     * UDP ports for this zone also accept broadcast packets.
     * This function returns only ports that are mentioned in configuration,
     * it doesn't return ports that are listed in some service (defined by package)
     * which is enabled.
     *
     * @return	map <string, list <string> > strings are allowed ports or port ranges
     *
     * @struct $[
     *   "ZONE1" : [ "port1", "port2" ],
     *   "ZONE2" : [ "port3", "port4" ],
     *   "ZONE3" : [ ]
     * ]
     */
    global define map <string, list <string> > GetBroadcastAllowedPorts () {

	map <string, list <string> > allowed_ports = $[];

	foreach (string zone, GetKnownFirewallZones(), {
	    string broadcast = GetBroadcastConfiguration(zone);
	    // no broadcast allowed for this zone
	    if (broadcast == "no") {
		allowed_ports[zone] = [];
	    // all UDP port allowed in zone also allow broadcast
	    } else if (broadcast == "yes") {
		allowed_ports[zone] = GetAllowedServicesForZoneProto (zone, "UDP");
	    // only listed ports allows broadcast
	    } else {
		allowed_ports[zone] = splitstring(broadcast, " ");
		allowed_ports[zone] = filter (string not_space, splitstring(broadcast, " "), {
		    return not_space != "";
		});
	    }
	});

	y2debug("Allowed Broadcast Ports: %1", allowed_ports);

	return allowed_ports;
    }

    /**
     * Function creates allowed-broadcast-ports string from broadcast map and saves it.
     *
     * @param	map <zone_string, list <string> > strings are allowed ports or port ranges
     * @see GetBroadcastAllowedPorts() for an example of data
     */
    global define void SetBroadcastAllowedPorts (map <string, list <string> > broadcast) {
	SetModified();

	foreach (string zone, GetKnownFirewallZones(), {
	    SetBroadcastConfiguration(zone, mergestring(broadcast[zone]:[], " "));
	});
    }

    /**
     * Function returns if broadcast is allowed for needed ports in zone.
     *
     * @param	list <string> ports
     * @param	string zone
     * @return	boolean if is allowed
     *
     * @example IsBroadcastAllowed (["port-xyz", "53"], "EXT") -> true
     */
    boolean IsBroadcastAllowed (list <string> needed_ports, string zone) {
	if (size(needed_ports)==0) {
	    y2warning("Unknown service with no needed ports!");
	    return nil;
	}

	// getting broadcast allowed ports
	map <string, list <string> > allowed_ports_map = GetBroadcastAllowedPorts();

	// Divide allowed port ranges and aliases (also with their port aliases)
	map <string, list <string> > allowed_ports_divided = PortRanges::DividePortsAndPortRanges (
	    allowed_ports_map[zone]:[], true
	);

	// If there are no allowed ports at all
	if (allowed_ports_divided["ports"]:[] == [] && allowed_ports_divided["port_ranges"]:[] == []) {
	    return false;
	}
	// clean up the memory a bit
	allowed_ports_map = nil;

	boolean is_allowed = true;
	// checking all needed ports;
	foreach (string needed_port, needed_ports, {
	    // allowed ports don't contain the needed one and also portranges don't
	    if (
		!contains (allowed_ports_divided["ports"]:[], needed_port) &&
		!PortRanges::PortIsInPortranges (needed_port, allowed_ports_divided["port_ranges"]:[])
	    ) {
		is_allowed = false;
		break;
	    }
	});

	return is_allowed;
    }

    /**
     * Local function removes list of ports from port allowing broadcast packets in zone.
     *
     * @param	list <of ports> to be removed
     * @param	string zone
     */
    void RemoveAllowedBroadcast (list <string> needed_ports, string zone) {
	SetModified();

	map <string, list <string> > allowed_ports = GetBroadcastAllowedPorts();
	list <string> list_ports_allowed = allowed_ports[zone]:[];

	// ports to be allowed one by one
	foreach (string allow_this_port, needed_ports, {
	    // remove all aliases of ports yet mentioned in zone
	    list <string> aliases_of_port = PortAliases::GetListOfServiceAliases(allow_this_port);
	    list_ports_allowed = filter (string just_allowed, list_ports_allowed, {
	        return (! contains(aliases_of_port, just_allowed));
	    });
	});
	allowed_ports[zone] = list_ports_allowed;

	// save it using function
	SetBroadcastAllowedPorts(allowed_ports);
    }

    /**
     * Local function adds list of ports to ports accepting broadcast
     *
     * @param	list <string> of ports
     */
    void AddAllowedBroadcast (list <string> needed_ports, string zone) {
	// changing only if ports are not allowed
	if (! IsBroadcastAllowed (needed_ports, zone)) {
	    SetModified();

	    map <string, list <string> > allowed_ports = GetBroadcastAllowedPorts();
	    list <string> list_ports_allowed = allowed_ports[zone]:[];

	    // ports to be allowed one by one
	    foreach (string allow_this_port, needed_ports, {
		// at first: remove all aliases of ports yet mentioned in zone
		list <string> aliases_of_port = PortAliases::GetListOfServiceAliases(allow_this_port);
		list_ports_allowed = filter (string just_allowed, list_ports_allowed, {
		    return (! contains(aliases_of_port, just_allowed));
		});
		// at second: add only one
		list_ports_allowed = add(list_ports_allowed, allow_this_port);
	    });
	    allowed_ports[zone] = list_ports_allowed;

	    // save it using function
	    SetBroadcastAllowedPorts(allowed_ports);
	}
    }

    /**
     * Local function for removing (disallowing) single service/port
     * for defined protocol and zone. Functions doesn't take care of
     * port-aliases.
     *
     * @param	string service/port
     * @param	string protocol
     * @param	string zone
     * @return	boolean success
     */
    boolean RemoveServiceFromProtocolZone(string remove_service, string protocol, string zone) {
	SetModified();

	string key = "FW_SERVICES_" + zone + "_" + protocol;

	list <string> allowed = splitstring(SETTINGS[key]:"", " ");
	allowed = filter (string single_service, allowed, {
	    return single_service != "" && single_service != remove_service;
	});
	SETTINGS[key] = mergestring(toset(allowed), " ");

	return true;
    }

    /**
     * Local function removes ports and their aliases (if check_for_aliases is true), for
     * requested protocol and zone.
     *
     * @param	list <string> ports to be removed
     * @param	string protocol
     * @param	string zone
     * @param	boolean check for port-aliases
     */
    void RemoveAllowedPortsOrServices (list <string> remove_ports, string protocol, string zone, boolean check_for_aliases) {
	if (size(remove_ports)<1) {
	    y2warning("Undefined list of %1 services/ports for service", protocol);
	    return;
	}

	SetModified();

	// all allowed ports
	map <string, list <string> > allowed_services = PortRanges::DividePortsAndPortRanges (
	    GetAllowedServicesForZoneProto (zone, protocol), false
	);

	// removing all aliases of ports too, adding aliases into
	if (check_for_aliases) {
	    list <string> remove_ports_with_aliases = [];
	    foreach (string remove_port, remove_ports, {
		// skip port ranges, they cannot have any port-alias
		if (PortRanges::IsPortRange (remove_port)) {
		    remove_ports_with_aliases = add (remove_ports_with_aliases, remove_port);
		    return;
		}
		list <string> remove_these_ports = PortAliases::GetListOfServiceAliases(remove_port);
		if (remove_these_ports == nil) { remove_these_ports = [ remove_port ]; }
		remove_ports_with_aliases = (list<string>) union (remove_ports_with_aliases,
		    remove_these_ports
		);
	    });
	    remove_ports = remove_ports_with_aliases;
	    
	}
	remove_ports = toset(remove_ports);

	// Remove ports only once (because of port aliases), any => integers and strings
	list <any> already_removed = [];

	foreach (string remove_port, remove_ports, {
	    // Removing from normal ports
	    allowed_services["ports"] = filter (string allowed_port, allowed_services["ports"]:[], {
		return allowed_port != "" && allowed_port != remove_port;
	    });

	    // Removing also from port ranges
	    if (allowed_services["port_ranges"]:[] != []) {
		// Removing a real port from port ranges
		if (! PortRanges::IsPortRange (remove_port)) {
		    integer remove_port_nr = PortAliases::GetPortNumber(remove_port);
		    // Because of all port aliases
		    if (!contains(already_removed, remove_port_nr)) {
			already_removed = add (already_removed, remove_port_nr);
			allowed_services["port_ranges"] = PortRanges::RemovePortFromPortRanges (
			    remove_port_nr, allowed_services["port_ranges"]:[]
			);
		    }
		// Removing a port range from port ranges
		} else {
		    if (!contains(already_removed, remove_port)) {
			// Just filtering the exact port range
			allowed_services["port_ranges"] = filter (string one_port_range, allowed_services["port_ranges"]:[], {
			    return one_port_range != remove_port;
			});
			already_removed = add (already_removed, remove_port);
		    }
		}
	    }
	});
	
	list <string> allowed_services_all = (list <string>) union (
	    allowed_services["ports"]:[],
	    allowed_services["port_ranges"]:[]
	);
	allowed_services_all = PortRanges::FlattenServices (allowed_services_all, protocol);
	
	SetAllowedServicesForZoneProto (allowed_services_all, zone, protocol);
    }

    /**
     * Local function allows ports for requested protocol and zone.
     *
     * @param	list <string> ports to be added
     * @param	string protocol
     * @param	string zone
     */
    void AddAllowedPortsOrServices (list <string> add_ports, string protocol, string zone) {
	if (size(add_ports)<1) {
	    y2warning("Undefined list of %1 services/ports for service", protocol);
	    return;
	}

	SetModified();

	// all allowed ports
	list <string> allowed_services = GetAllowedServicesForZoneProto (zone, protocol);

	allowed_services = (list<string>) union (allowed_services, add_ports);
	allowed_services = PortRanges::FlattenServices (allowed_services, protocol);

	SetAllowedServicesForZoneProto (allowed_services, zone, protocol);
    }

    /**
     * Removes service defined by package (FATE #300687) from enabled services.
     *
     * @param string service_id
     * @param string zone
     *
     * @example
     *	RemoveServiceDefinedByPackageFromZone ("service:irc-server", "EXT");
     */
    void RemoveServiceDefinedByPackageFromZone (string service, string zone) {
	if (! IsKnownZone(zone)) {
	    return nil;
	}

	if (service == nil) {
	    y2error ("Service Id can't be nil!");
	    return nil;
	} else if (regexpmatch (service, "^service:.*")) {
	    service = regexpsub (service, "^service:(.*)", "\\1");
	}

	// services defined by package are listed without "service:" which is here
	// just to distinguish between dynamic and static definitions
	list <string> supported_services = splitstring (SETTINGS["FW_CONFIGURATIONS_" + zone]:"", " ");
	// Removing the service
	supported_services = filter (string one_service, supported_services, {
	    return one_service != service;
	});
	SETTINGS["FW_CONFIGURATIONS_" + zone] = mergestring (supported_services, " ");

	SetModified();
    }

    /**
     * Adds service defined by package (FATE #300687) into list of enabled services.
     *
     * @param string service_id
     * @param string zone
     *
     * @example
     *	AddServiceDefinedByPackageIntoZone ("service:irc-server", "EXT");
     */
    void AddServiceDefinedByPackageIntoZone (string service, string zone) {
	if (! IsKnownZone(zone)) {
	    return nil;
	}

	if (service == nil) {
	    y2error ("Service Id can't be nil!");
	    return nil;
	} else if (regexpmatch (service, "^service:.*")) {
	    service = regexpsub (service, "^service:(.*)", "\\1");
	}

	// services defined by package are listed without "service:" which is here
	// just to distinguish between dynamic and static definitions
	list <string> supported_services = splitstring (SETTINGS["FW_CONFIGURATIONS_" + zone]:"", " ");
	// Adding the service
	supported_services = toset (add (supported_services, service));
	SETTINGS["FW_CONFIGURATIONS_" + zone] = mergestring (supported_services, " ");

	SetModified();
    }

    /**
     * Local function removes well-known service's support from zone.
     * Allowed ports are removed with all of their port-aliases.
     *
     * @param	string service id
     * @param	string zone
     */
    void RemoveServiceSupportFromZone (string service, string zone) {
	map <string, list <string> > needed = SuSEFirewallServices::GetNeededPortsAndProtocols(service);
	// unknown service
	if (needed == nil) {
	    y2error("Undefined service '%1'", service);
	    return nil;
	}

	// FATE #300687: Ports for SuSEfirewall added via packages
	if (SuSEFirewallServices::ServiceDefinedByPackage (service)) {
	    if (IsServiceSupportedInZone (service,zone) == true)
		RemoveServiceDefinedByPackageFromZone (service, zone);

	    return nil;
	}

	SetModified();

	// Removing service ports (and also port aliases for TCP and UDP)
	foreach (string key, service_defined_by, {
	    list <string> needed_ports = needed[key]:[];
	    if (needed_ports == []) return;

	    if (key == "tcp_ports") {
		RemoveAllowedPortsOrServices(needed_ports, "TCP", zone, true);
	    } else if (key == "udp_ports") {
		RemoveAllowedPortsOrServices(needed_ports, "UDP", zone, true);
	    } else if (key == "rpc_ports") {
		RemoveAllowedPortsOrServices(needed_ports, "RPC", zone, false);
	    } else if (key == "ip_protocols") {
		RemoveAllowedPortsOrServices(needed_ports, "IP", zone, false);
	    } else if ("broadcast_ports" == key) {
		RemoveAllowedBroadcast(needed_ports, zone);
	    } else {
		y2error("Unknown key '%1'", key);
	    }
	});
    }

    /**
     * Local function adds well-known service's support into zone. It first of all
     * removes the current support for service with port-aliases.
     *
     * @param	string service id
     * @param	string zone
     */
    void AddServiceSupportIntoZone (string service, string zone) {
	map <string, list <string> > needed = SuSEFirewallServices::GetNeededPortsAndProtocols (service);
	// unknown service
	if (needed == nil) {
	    y2error("Undefined service '%1'", service);
	    return nil;
	}

	SetModified();

	// FATE #300687: Ports for SuSEfirewall added via packages
	if (SuSEFirewallServices::ServiceDefinedByPackage (service)) {
	    AddServiceDefinedByPackageIntoZone (service, zone);

	    return nil;
	}

	// Removing service ports first (and also port aliases for TCP and UDP)
	if (IsServiceSupportedInZone(service,zone) == true) {
	    RemoveServiceSupportFromZone(service,zone);
	}

	foreach (string key, service_defined_by, {
	    list <string> needed_ports = needed[key]:[];
	    if (needed_ports == []) return;

	    if (key == "tcp_ports") {
		AddAllowedPortsOrServices(needed_ports, "TCP", zone);
	    } else if (key == "udp_ports") {
		AddAllowedPortsOrServices(needed_ports, "UDP", zone);
	    } else if (key == "rpc_ports") {
		AddAllowedPortsOrServices(needed_ports, "RPC", zone);
	    } else if (key == "ip_protocols") {
		AddAllowedPortsOrServices(needed_ports, "IP", zone);
	    } else if ("broadcast_ports" == key) {
		AddAllowedBroadcast(needed_ports, zone);
	    } else {
		y2error("Unknown key '%1'", key);
	    }
	});
    }

    // <!-- SuSEFirewall LOCAL FUNCTIONS //-->

    // <!-- SuSEFirewall GLOBAL FUNCTIONS //-->

    // bnc #388773
    // By default needed packages are just checked, not installed
    boolean check_and_install_package = false;

    /**
     * By default SuSEfirewall2 packages are just checked whether they are installed.
     * With this function, you can change the behavior to also offer installing
     * the packages.
     *
     * @param boolean new_status, 'true' if packages should be offered for installation
     */
    global void SetInstallPackagesIfMissing (boolean new_status) {
	if (new_status == nil) {
	    y2error ("Wrong value: %1", new_status);
	    return;
	}

	check_and_install_package = new_status;

	if (check_and_install_package) {
	    y2milestone ("SuSEfirewall2 packages will installed if missing");
	} else {
	    y2milestone ("SuSEfirewall2 packages will not be installed even if missing");
	}
    }

    /* Are needed packages (SuSEfirewall2) installed? */
    boolean needed_packages_installed = nil;

    /**
     * Returns whether all needed packages are installed.
     *
     * @return boolean whether SuSEfirewall2 is installed
     */
    global boolean SuSEFirewallIsInstalled () {
	if (needed_packages_installed == nil) {
	    // In mode normal, package can be installed on request
	    // if required by the module
	    if (check_and_install_package && Mode::normal()) {
		needed_packages_installed = PackageSystem::CheckAndInstallPackages ([susefirewall_package]);
		y2milestone ("CheckAndInstallPackages -> %1", needed_packages_installed);
	    // In mode install/update network might be down
	    } else {
		needed_packages_installed = PackageSystem::Installed (susefirewall_package);
		y2milestone ("Installed -> %1", needed_packages_installed);
	    }
	} else if (needed_packages_installed == false) {
	    y2milestone ("SuSEfirewall2 is not installed, skipping...");
	}

	return needed_packages_installed;
    }

    // Configuration has been read and it's useful
    boolean fw_service_can_be_configured = false;

    /**
     * Functions returns whether any firewall's configuration was modified.
     *
     * @return	boolean if the configuration was modified
     */
    global define boolean GetModified () {
	// Changed SuSEFirewall or
	// Changed SuSEFirewallServices (needs resatrting as well)
	return (modified || SuSEFirewallServices::GetModified());
    }

    /**
     * Function resets flag which doesn't allow to read configuration from disk again.
     * So you actually can reread the configuration from disk. Currently, only the first
     * Read() call reads the configuration from disk.
     */
    global define void ResetReadFlag () {
	configuration_has_been_read = false;
    }

    /**
     * Function returns localized name of the zone identified by zone shortname.
     *
     * @param	string short name
     * @return	string zone name
     *
     * @example
     *  LANG=en_US GetZoneFullName ("EXT") -> "External Zone"
     *  LANG=cs_CZ GetZoneFullName ("EXT") -> "Externí Zóna"
     */
    global define string GetZoneFullName (string zone) {
	// TRANSLATORS: Firewall zone full-name, used as combo box item or dialog title
	return zone_names[zone]:_("Unknown Zone");
    }

    /**
     * Function sets if firewall should be protected from internal zone.
     *
     * @param	boolean set to be protected from internal zone
     */
    global define void SetProtectFromInternalZone (boolean set_protect) {
	SetModified();

	if (set_protect) {
	    SETTINGS["FW_PROTECT_FROM_INT"] = "yes";
	} else {
	    SETTINGS["FW_PROTECT_FROM_INT"] = "no";
	}
    }

    /**
     * Function returns if firewall is protected from internal zone.
     *
     * @return	boolean if protected from internal
     */
    global define boolean GetProtectFromInternalZone () {
	return (SETTINGS["FW_PROTECT_FROM_INT"]:"no" == "yes");
    }

    /**
     * Function sets if firewall should support routing.
     *
     * @param	boolean set to support route or not
     */
    global define void SetSupportRoute (boolean set_route) {
	SetModified();

	if (set_route) {
	    SETTINGS["FW_ROUTE"] = "yes";
	} else {
	    SETTINGS["FW_ROUTE"] = "no";
	}
    }

    /**
     * Function returns if firewall supports routing.
     *
     * @return	boolean if route is supported
     */
    global define boolean GetSupportRoute () {
	return (SETTINGS["FW_ROUTE"]:"no" == "yes");
    }

    /**
     * Function sets how firewall should trust successfully decrypted IPsec packets.
     * It should be the zone name (shortname) or 'no' to trust packets the same as
     * firewall trusts the zone from which IPsec packet came.
     *
     * @param	string zone or "no"
     */
    global define void SetTrustIPsecAs (string zone) {
	SetModified();

	// do not trust
	if (zone == "no") {
	    SETTINGS["FW_IPSEC_TRUST"] = "no";
	} else {
	    // trust IPsec is a known zone
	    if (IsKnownZone(zone)) {
		zone = GetZoneConfigurationString(zone);
		SETTINGS["FW_IPSEC_TRUST"] = zone;
	    // unknown zone, changing to default value
	    } else {
		string defaultv = GetDefaultValue("FW_IPSEC_TRUST");
		y2warning("Trust IPsec as '%1' (unknown zone) changed to '%2'", zone, defaultv);
		SETTINGS["FW_IPSEC_TRUST"] = defaultv;
	    }
	}
    }

    /**
     * Function returns the trust level of IPsec packets.
     * See SetTrustIPsecAs() for more information.
     *
     * @return	string zone or "no"
     */
    global define string GetTrustIPsecAs () {
	// do not trust
	if (SETTINGS["FW_IPSEC_TRUST"]:nil == "no") {
	    return "no";
	// default value for 'yes" ~= "INT"
	} else if (SETTINGS["FW_IPSEC_TRUST"]:nil == "yes") {
	    return "INT";
	} else {
	    string zone = GetConfigurationStringZone(SETTINGS["FW_IPSEC_TRUST"]:"");
	    // trust as named zone (if known)
	    if (IsKnownZone(zone)) {
		return zone;
	    // unknown zone, change to default value
	    } else {
		SetModified();
		string defaultv = GetDefaultValue("FW_IPSEC_TRUST");
		y2warning("Trust IPsec as '%1' (unknown zone) changed to '%2'", SETTINGS["FW_IPSEC_TRUST"]:"", defaultv);
		SetTrustIPsecAs(defaultv);
		return "no";
	    }
	}
    }

    /**
     * Function which returns if SuSEfirewall2 should start in Write process.
     * In fact it means that SuSEfirewall2 will at the end.
     *
     * @return	boolean if the firewall should start
     */
    global define boolean GetStartService () {
	return (boolean) SETTINGS["start_firewall"]:false;
    }

    /**
     * Function which sets if SuSEfirewall should start in Write process.
     *
     * @param	boolean start_service at Write() process
     * @see GetStartService()
     */
    global define void SetStartService (boolean start_service) {
	if (! SuSEFirewall::SuSEFirewallIsInstalled()) {
	    y2warning ("Cannot set SetStartService");
	    return nil;
	}

	if (GetStartService() != start_service) {
	    SetModified();

	    y2milestone("Setting start-firewall to %1", start_service);
	    SETTINGS["start_firewall"] = start_service;
	} else {
	    // without set modified!
	    y2milestone("start-firewall has been already set to %1", start_service);
	    SETTINGS["start_firewall"] = start_service;
	}
    }

    /**
     * Function which returns whether SuSEfirewall should be enabled in
     * /etc/init.d/ starting scripts during the Write() process
     *
     * @see Write()
     * @see EnableServices()
     *
     * @return	boolean if the firewall should start
     */
    global define boolean GetEnableService () {
	return (boolean) SETTINGS["enable_firewall"]:false;
    }

    /**
     * Function which sets if SuSEfirewall should start in Write process
     *
     * @param	boolean start_service at Write() process
     */
    global define void SetEnableService (boolean enable_service) {
	if (! SuSEFirewall::SuSEFirewallIsInstalled()) {
	    y2warning ("Cannot set SetEnableService");
	    return nil;
	}

	if (GetEnableService() != enable_service) {
	    SetModified();
	
	    y2milestone("Setting enable-firewall to %1", enable_service);
	    SETTINGS["enable_firewall"] = enable_service;
	} else {
	    // without set modified
	    y2milestone("enable-firewall has been already set to %1", enable_service);
	    SETTINGS["enable_firewall"] = enable_service;
	}
    }

    /**
     * Functions starts services needed for SuSEFirewall
     *
     * @return	boolean result
     */
    global define boolean StartServices () {
	boolean all_ok = true;
	
	if (! SuSEFirewallIsInstalled()) return false;

	// bug #215416
	// SuSEfirewall2_init doesn't need to be called, only enabled

	if (Mode::testsuite()) return true;
	string tmpdir_file = (string) SCR::Read(.target.tmpdir);
	if (tmpdir_file == nil || tmpdir_file == "") tmpdir_file = "/var/lib/YaST2";
	tmpdir_file = tmpdir_file + "/SuSEfirewall2_YaST_output";

	string command = sformat(
	    "/sbin/SuSEfirewall2 start 2>'%1'; cat '%1'; rm -rf '%1'",
	    tmpdir_file
	);
	y2milestone ("Starting firewall...");
	map cmd = (map) SCR::Execute (.target.bash_output, command);
	if (cmd["exit"]:nil != 0) {
	    y2error ("Starting firewall: >%1< returned %2", command, cmd);
	    all_ok = false;
	} else {
	    y2milestone ("Started");
	}
	
	return all_ok;
    }

    /**
     * Functions stops services needed for SuSEFirewall
     *
     * @return	boolean result
     */
    global define boolean StopServices () {
	boolean all_ok = true;
	
	if (! SuSEFirewallIsInstalled()) return false;

	// bug #215416
	// SuSEfirewall2_init doesn't need to be called, only disabled

	if (Mode::testsuite()) return true;
	string tmpdir_file = (string) SCR::Read(.target.tmpdir);
	if (tmpdir_file == nil || tmpdir_file == "") tmpdir_file = "/var/lib/YaST2";
	tmpdir_file = tmpdir_file + "/SuSEfirewall2_YaST_output";

	string command = sformat(
	    "/sbin/SuSEfirewall2 stop 2>'%1'; cat '%1'; rm -rf '%1'",
	    tmpdir_file
	);
	y2milestone ("Stopping firewall...");
	map cmd = (map) SCR::Execute (.target.bash_output, command);
	if (cmd["exit"]:nil != 0) {
	    y2error ("Stopping firewall: >%1< returned %2", command, cmd);
	    all_ok = false;
	} else {
	    y2milestone ("Stopped");
	}

	return all_ok;
    }

    /**
     * Functions enables services needed for SuSEFirewall in /etc/inet.d/
     *
     * @return	boolean result
     */
    global define boolean EnableServices () {
	boolean all_ok = true;
	
	if (! SuSEFirewallIsInstalled()) return false;

	foreach (string service, firewall_services, {
	    y2debug("Enabling service: %1", service);

	    if (! Service::Enable(service)) {
		all_ok = true;
		// TRANSLATORS: a popup error message
		Report::Error (sformat (_("Cannot enable service '%1'."), service));
	    }
	});

	return all_ok;
    }

    /**
     * Functions disables services needed for SuSEFirewall in /etc/inet.d/
     *
     * @return	boolean result
     */
    global define boolean DisableServices () {
	boolean all_ok = true;
	
	if (! SuSEFirewallIsInstalled()) return false;

	foreach (string service, firewall_services_reverse, {
	    y2debug("Disabling service: %1", service);

	    if (! Service::Disable(service)) {
		all_ok = false;
		// TRANSLATORS: a popup error message
		Report::Error (sformat (_("Cannot disable service '%1'."), service));
	    }
	});

	return all_ok;
    }


    /**
     * Function determines if all SuSEFirewall scripts are enabled in
     * init scripts /etc/init.d/ now.
     * For configuration "enabled" status use GetEnableService().
     *
     * @return	boolean if enabled
     */
    global define boolean IsEnabled () {
	boolean enabled = false;

	if (! SuSEFirewallIsInstalled()) return false;

	//if (Mode::normal() || Mode::commandline()) {
	    foreach (string service, firewall_services, {
		enabled = Service::Enabled(service);
		// All services have to be enabled
		if (!enabled) {
		    y2milestone("Firewall service %1 is not enabled", service);
		    break;
		}
	    });
	//}

	if (enabled) {
	    y2milestone("Firewall init scripts are enabled");
	}

	return enabled;
    }

    /**
     * Function determines if at least one SuSEFirewall script is started now.
     * For configuration "started" status use GetStartService().
     *
     * @return	boolean if started
     */
    global define boolean IsStarted () {
	boolean started = false;

	if (! SuSEFirewallIsInstalled()) return false;

	//if (Mode::normal() || Mode::commandline()) {
	    foreach (string service, firewall_services, {
		if (Service::Status(service) == 0) {
		    started = true;
		    break;
		}
	    });
	//}

	if (started == true) {
	    y2milestone("Firewall services are started");
	} else {
	    y2milestone("Firewall services are stopped");
	}

	return started;
    }

    /**
     * Function for getting exported SuSEFirewall configuration
     *
     * @return	map <string, any> with configuration
     */
    global define map <string, any> Export () {
	return SETTINGS;
    }

    /**
     * Function for setting SuSEFirewall configuration from input
     *
     * @param	map <string, any> with configuration
     */
    global define void Import (map <string, any> import_settings) {
	SetModified();

	SETTINGS = import_settings;
    }

    /**
     * Function returns if the interface is in zone.
     *
     * @param	string interface
     * @param	string firewall zone
     * @return	boolean is in zone
     *
     * @example IsInterfaceInZone ("eth-id-01:11:DA:9C:8A:2F", "INT") -> false
     */
    global define boolean IsInterfaceInZone(string interface, string zone) {
	list <string> interfaces = splitstring(SETTINGS[ "FW_DEV_" + zone ]:"", " ");
	return contains(interfaces, interface);
    }

    /**
     * Function returns the firewall zone of interface, nil if no zone includes
     * the interface. Error is reported when interface is found in multiple
     * firewall zones, then the first appearance is returned.
     *
     * @param	string interface
     * @return	string zone
     *
     * @example GetZoneOfInterface ("eth-id-01:11:DA:9C:8A:2F") -> "DMZ"
     */
    global define string GetZoneOfInterface (string interface) {
	list interface_zone = [];

	foreach (string zone, GetKnownFirewallZones(), {
	    if (IsInterfaceInZone(interface, zone)) interface_zone = add (interface_zone, zone);
	});

	if (IsVerbose() && size(interface_zone) > 1) {
	    // TRANSLATORS: Error message, %1 = interface name (like eth0)
	    Report::Error(sformat(_("Interface '%1' is included in multiple firewall zones.
Continuing with configuration can produce errors.

It is recommended to leave the configuration and repair it manually in
the file '/etc/sysconfig/SuSEFirewall'."), interface));
	}

	// return the first existence of interface in zones
	// if it is not presented anywhere, nil is returned
	return (string) interface_zone[0]:nil;
    }

    /**
     * Function returns list of zones of requested interfaces
     *
     * @param	list<string> interfaces
     * @return	list<string> firewall zones
     *
     * @example
     *	GetZonesOfInterfaces (["eth1","eth4"]) -> ["DMZ", "EXT"]
     */
    global define list<string> GetZonesOfInterfaces (list<string> interfaces) {
	list<string> zones = [];
	string zone = "";

	foreach (string interface, interfaces, {
	    zone = GetZoneOfInterface(interface);
	    if (zone != nil) zones = add(zones, zone);
	});
	
	return toset(zones);
    }

    global define list<string> GetInterfacesInZoneSupportingAnyFeature (string zone);

    /**
     * Function returns list of zones of requested interfaces.
     * Special string 'any' in 'EXT' zone is supported.
     *
     * @param	list<string> interfaces
     * @return	list<string> firewall zones
     *
     * @example
     *	GetZonesOfInterfaces (["eth1","eth4"]) -> ["EXT"]
     */
    global define list<string> GetZonesOfInterfacesWithAnyFeatureSupported (list<string> interfaces) {
	list<string> zones = [];
	string zone = "";
	
	// 'any' in 'EXT'
	list <string> interfaces_covered_by_any =
	    SuSEFirewall::GetInterfacesInZoneSupportingAnyFeature(special_all_interface_zone);

	foreach (string interface, interfaces, {
	    // interface is covered by 'any' in 'EXT'
	    if (contains(interfaces_covered_by_any, interface))
		zone = special_all_interface_zone;
	    // interface is explicitely mentioned in some zone
	    else
		zone = GetZoneOfInterface(interface);

	    if (zone != nil) zones = add(zones, zone);
	});
	
	return toset(zones);
    }

    /**
     * Function returns list of maps of known interfaces.
     * @struct [ $[ "id":"modem0", "name":"Askey 815C", "type":"dialup", "zone":"EXT" ], ... ]
     *
     * @return	list <map <string, string> >
     * @return list <map <string, string> > of all interfaces
     */
    global define list <map <string, string> > GetAllKnownInterfaces () {
	list <map <string, string> > known_interfaces = [];
	
	// All dial-up interfaces
	list <string> dialup_interfaces = NetworkInterfaces::List("dialup");
	if (dialup_interfaces == nil) dialup_interfaces = [];

	// bugzilla #303858 - wrong values from NetworkInterfaces
	dialup_interfaces = filter (string one_iface, dialup_interfaces, {
	    if (one_iface == nil || one_iface == "") {
		y2error ("Wrong interface definition '%1'", one_iface);
		return false;
	    }
	    return true;
	});

	dialup_interfaces = filter (string interface, dialup_interfaces, ``{
	    return interface != "" && !issubstring (interface, "lo") && !issubstring (interface, "sit");
	});

	// All non-dial-up interfaces
	list <string> non_dialup_interfaces = NetworkInterfaces::List("");
	if (non_dialup_interfaces == nil) non_dialup_interfaces = [];

	// bugzilla #303858 - wrong values from NetworkInterfaces
	non_dialup_interfaces = filter (string one_iface, non_dialup_interfaces, {
	    if (one_iface == nil || one_iface == "") {
		y2error ("Wrong interface definition '%1'", one_iface);
		return false;
	    }
	    return true;
	});

	non_dialup_interfaces = filter (string interface, non_dialup_interfaces, ``{
	    return interface != "" && !issubstring (interface, "lo") && !issubstring (interface, "sit")
		&& !contains (dialup_interfaces, interface);
	});

	foreach (string interface, dialup_interfaces, {
	    known_interfaces = add(known_interfaces, $[
		"id" : interface,
		"type" : "dialup",
		// using function to get name
		"name" : NetworkInterfaces::GetValue(interface, "NAME"),
		"zone" : GetZoneOfInterface(interface),
	    ]);
	});

	foreach (string interface, non_dialup_interfaces, {
	    known_interfaces = add(known_interfaces, $[
		"id" : interface,
		// using function to get name
		"name" : NetworkInterfaces::GetValue(interface, "NAME"),
		"zone" : GetZoneOfInterface(interface),
	    ]);
	});

	return known_interfaces;
    }

    /**
     * Function returns list of non-dial-up interfaces.
     *
     * @return list <string> of non-dial-up interface names
     * @example GetAllNonDialUpInterfaces() -> ["eth1", "eth2"]
     */
    global define list <string> GetAllNonDialUpInterfaces () {
	list <string> non_dial_up_interfaces = [];
	foreach (map<string, string> interface, SuSEFirewall::GetAllKnownInterfaces(), {
	    if (interface["type"]:nil != "dial_up")
		non_dial_up_interfaces = add (non_dial_up_interfaces, interface["id"]:"");
	});
	
	return non_dial_up_interfaces;
    }

    /**
     * Function returns list of dial-up interfaces.
     *
     * @return list <string> of dial-up interface names
     * @example GetAllDialUpInterfaces() -> ["modem0", "dsl5"]
     */
    global define list <string> GetAllDialUpInterfaces () {
	list <string> dial_up_interfaces = [];
	foreach (map <string, string> interface, SuSEFirewall::GetAllKnownInterfaces(), {
	    if (interface["type"]:nil == "dial_up")
		dial_up_interfaces = add (dial_up_interfaces, interface["id"]:"");
	});
	
	return dial_up_interfaces;
    }

    /**
     * Function returns list of all known interfaces.
     *
     * @return	list <string> of interfaces
     * @example GetListOfKnownInterfaces() -> ["eth1", "eth2", "modem0", "dsl5"]
     */
    global define list <string> GetListOfKnownInterfaces () {
	list <string> interfaces = [];

	foreach (map interface_map, GetAllKnownInterfaces(), {
	    interfaces = add (interfaces, interface_map["id"]:"");
	});

	return interfaces;
    }

    /**
     * Function removes interface from defined zone.
     *
     * @param	string interface
     * @param	string zone
     * @example RemoveInterfaceFromZone ("modem0", "EXT")
     */
    global define void RemoveInterfaceFromZone (string interface, string zone) {
	SetModified();

	y2milestone("Removing interface '%1' from '%2' zone.", interface, zone);

	list <string> interfaces_in_zone = splitstring (SETTINGS["FW_DEV_" + zone]:"", " ");
	interfaces_in_zone = filter (string single_interface, interfaces_in_zone, {
	    return single_interface != "" && single_interface != interface;
	});
	SETTINGS["FW_DEV_" + zone] = mergestring(interfaces_in_zone, " ");
    }

    /**
     * Functions adds interface into defined zone.
     * All appearances of interface in other zones are removed.
     *
     * @param	string interface
     * @param	string zone
     * @example AddInterfaceIntoZone ("eth5", "DMZ")
     */
    global define void AddInterfaceIntoZone (string interface, string zone) {
	SetModified();
	
	string current_zone = GetZoneOfInterface(interface);

	DecreaseVerbosity();
	// removing all appearances of interface in zones, excepting current_zone==new_zone
	while (current_zone != nil && current_zone != zone) {
	    // interface is in any zone already, removing it at first
	    if (current_zone != zone) {
		RemoveInterfaceFromZone(interface, current_zone);
	    }
	    current_zone = GetZoneOfInterface(interface);
	}
	IncreaseVerbosity();

	y2milestone("Adding interface '%1' into '%2' zone.", interface, zone);
	list <string> interfaces_in_zone = splitstring (SETTINGS["FW_DEV_" + zone]:"", " ");
	interfaces_in_zone = toset(add (interfaces_in_zone, interface));
	SETTINGS["FW_DEV_" + zone] = mergestring(interfaces_in_zone, " ");
    }
    
    /**
     * Function returns list of known interfaces in requested zone.
     * Special strings like 'any' or 'auto' and unknown interfaces are removed from list.
     *
     * @param	string zone
     * @return	list <string> of interfaces
     * @example GetInterfacesInZone ("DMZ") -> ["eth4", "eth5"]
     */
    global define list<string> GetInterfacesInZone (string zone) {
	list <string> interfaces_in_zone = splitstring (SETTINGS["FW_DEV_" + zone]:"", " ");

	list <string> known_interfaces_now = GetListOfKnownInterfaces();

	// filtering special strings
	interfaces_in_zone = filter(string interface, interfaces_in_zone, ``{
	    return interface != "" && contains(known_interfaces_now, interface);
	});
	
	return interfaces_in_zone;
    }

    /**
     * Function returns all interfaces already configured in firewall.
     *
     * @return	list <string> of configured interfaces
     */
    global define list<string> GetFirewallInterfaces () {
	list<string> firewall_configured_devices = [];

	foreach (string zone, GetKnownFirewallZones(), {
	    firewall_configured_devices = (list<string>) union (firewall_configured_devices, GetInterfacesInZone(zone));
	});

	return toset(firewall_configured_devices);
    }

    /**
     * Returns list of interfaces not mentioned in any zone and covered by the
     * special string 'any' in zone 'EXT' if such string exists there and the zone
     * is EXT. If the feature 'any' is not set, function returns empty list.
     *
     * @param string zone
     * @return list <string> of interfaces covered by special string 'any'
     * @see IsAnyNetworkInterfaceSupported()
     */
    global define list<string> InterfacesSupportedByAnyFeature (string zone) {
	list <string> result = [];
	
	if (zone == special_all_interface_zone && IsAnyNetworkInterfaceSupported()) {
	    list <string> known_interfaces_now  = GetListOfKnownInterfaces();
	    list <string> configured_interfaces = GetFirewallInterfaces();
	    foreach (string one_interface, known_interfaces_now, {
		if (! contains(configured_interfaces, one_interface)) {
		    y2milestone("Interface '%1' supported by special string '%2' in zone '%3'",
			one_interface, special_all_interface_string, special_all_interface_zone);
		    result = add (result, one_interface);
		}
	    });
	}
	
	return result;
    }

    /**
     * Function returns list of known interfaces in requested zone.
     * Special string 'any' in EXT zone covers all interfaces without
     * any zone assignment.
     *
     * @param	string zone
     * @return	list <string> of interfaces
     */
    global define list<string> GetInterfacesInZoneSupportingAnyFeature (string zone) {
	list <string> interfaces_in_zone = GetInterfacesInZone(zone);

	// 'any' in EXT zone, add all interfaces without zone to this one
	list <string> interfaces_covered_by_any = InterfacesSupportedByAnyFeature(zone);
	if (size(interfaces_covered_by_any)>0) {
	    interfaces_in_zone = (list <string>) union (interfaces_in_zone, interfaces_covered_by_any);
	}
	
	return interfaces_in_zone;
    }

    boolean ArePortsOrServicesAllowed (list <string> needed_ports, string protocol, string zone, boolean check_for_aliases);

    /**
     * Function returns if requested service is allowed in respective zone.
     * Function takes care for service's aliases (only for TCP and UDP).
     * Service is defined by set of parameters such as port and protocol.
     *
     * @param	string service (service name, port name, port alias or port number)
     * @param	protocol TCP, UDP, RCP or IP
     * @param	interface name (like modem0), firewall zone (like "EXT") or "any" for all zones.
     * @return	boolean if service is allowed
     *
     * @example
     *	HaveService ("ssh", "TCP", "EXT") -> true
     *	HaveService ("ssh", "TCP", "modem0") -> false
     *	HaveService ("53", "UDP", "dsl") -> false
     */
    global define boolean HaveService(string service, string protocol, string interface) {
	if (! IsSupportedProtocol(protocol)) {
	    y2error("Unknown protocol: %1", protocol);
	    return nil;
	}

	// definition of searched zones
	list<string> zones = [];

	// "any" for all zones, this is ugly
	if (interface == "any") {
	    zones = GetKnownFirewallZones();
	// string interface is the zone name
	} else if (IsKnownZone(interface)) {
	    zones = add (zones, interface);
	// interface is the interface name
	} else {
	    interface = GetZoneOfInterface(interface);
	    if (interface != nil) {
		zones = add (zones, interface);
	    }
	}
	
	// SuSEFirewall feature FW_PROTECT_FROM_INT
	// should not be protected and searched zones include also internal (or the zone IS internal, sure)
	if (! GetProtectFromInternalZone() && contains(zones, int_zone_shortname)) {
	    y2milestone("Checking for service '%1', in '%2', PROTECT_FROM_INTERNAL='no' => allowed", service, interface);
	    return true;
	}
	
	// Check and return whether the service (port) is supported anywhere
	boolean ret = false;
	foreach (string zone, zones, {
	    // This function can also handle port ranges
	    if (ArePortsOrServicesAllowed([service], protocol, zone, true)) {
		ret = true;
		break;
	    }
	});
	
	return ret;
    }

    /**
     * Function adds service into selected zone (or zone of interface) for selected protocol.
     * Function take care about port-aliases, first of all, removes all of them.
     *
     * @param	string service/port
     * @param	string protocol TCP, UDP, RPC, IP
     * @param	string zone name or interface name
     * @return	boolean success
     *
     * @example
     *	AddService ("ssh", "TCP", "EXT")
     *	AddService ("ssh", "TCP", "dsl0")
     */
    global define boolean AddService (string service, string protocol, string interface) {
	boolean success = false;
	y2milestone("Adding service %1, protocol %2 to %3", service, protocol, interface);

	if (! IsSupportedProtocol(protocol)) {
	    y2error("Unknown protocol: %1", protocol);
	    return false;
	}

	list <string> zones_affected = [];

	// "all" means for all known zones
	if (interface == "all") {
	    zones_affected = GetKnownFirewallZones();

	// zone or interface name
	} else {
	    // is probably an interface name
	    if (! IsKnownZone(interface)) {
		// interface is probably interface-name, checking for respective zone
		interface = GetZoneOfInterface(interface);
		// interface is not assigned to any zone
		if (interface == nil) {
		    // TRANSLATORS: Error message, %1 = interface name (like eth0)
		    Report::Error(sformat(_("Interface '%1' is not assigned to any firewall zone.
Run YaST2 Firewall and assign it.
"), interface)
		    );
		    y2warning("Interface '%1' is not assigned to any firewall zone", interface);
		    return false;
		}
	    }
	    zones_affected = [interface];
	}

	SetModified();

	// Adding service support into each mentioned zone
	foreach (string zone, zones_affected, {
	    // If there isn't already
	    if (!ArePortsOrServicesAllowed([service], protocol, zone, true)) {
		AddAllowedPortsOrServices([service], protocol, zone);
	    } else {
		y2milestone("Port %1 has been already allowed in %2", service, zone);
	    }
	});

	return true;
    }

    /**
     * Function removes service from selected zone (or for interface) for selected protocol.
     * Function takes care about port-aliases, removes all of them.
     *
     * @param	string service/port
     * @param	string protocol TCP, UDP, RPC, IP
     * @param	string zone name or interface name
     * @return	boolean success
     *
     * @example
     *	RemoveService ("22", "TCP", "DMZ") -> true
     *  is the same as
     *	RemoveService ("ssh", "TCP", "DMZ") -> true
     */
    global define boolean RemoveService (string service, string protocol, string interface) {
	boolean success = false;
	y2milestone("Removing service %1, protocol %2 from %3", service, protocol, interface);

	if (! IsSupportedProtocol(protocol)) {
	    y2error("Unknown protocol: %1", protocol);
	    return false;
	}

	list <string> zones_affected = [];

	// "all" means for all known zones
	if (interface == "all") {
	    zones_affected = GetKnownFirewallZones();

	// zone or interface name
	} else {
	    if (! IsKnownZone(interface)) {
		// interface is probably interface-name, checking for respective zone
		interface = GetZoneOfInterface(interface);
		// interface is not assigned to any zone
		if (interface == nil) {
		    // TRANSLATORS: Error message, %1 = interface name (like eth0)
		    Report::Error(sformat(_("Interface '%1' is not assigned to any firewall zone.
Run YaST2 Firewall and assign it.
"), interface)
		    );
		    y2warning("Interface '%1' is not assigned to any firewall zone", interface);
		    return false;
		}
	    }
	    zones_affected = [interface];
	}

	SetModified();


	// Adding service support into each mentioned zone
	foreach (string zone, zones_affected, {
	    // if the service is allowed
	    if (ArePortsOrServicesAllowed([service], protocol, zone, true)) {
		RemoveAllowedPortsOrServices([service], protocol, zone, true);
	    } else {
		y2milestone("Port %1 has been already removed from %2", service, zone);
	    }
	});

	return true;
    }

    /**
     * Function returns if needed services are all allowed (or not) in the firewall.
     * Last parameter sets if it also should check for port-aliases, what makes sense
     * for TCP and UDP ports.
     * Protocols and Zones aren't checked for existency. It's on you to do it.
     *
     * @param	list <string> needed (checked) ports for service
     * @param	string protocol TCP, UDP, RPC or IP
     * @param	zone name like EXT
     * @param	boolean check for port-aliases (true is a reasonable default)
     * @return	boolean if all ports are allowed
     * @example
     *	ArePortsOrServicesAllowed (["53", "54"], "UDP", "INT", true) -> true
     */
    boolean ArePortsOrServicesAllowed (list <string> needed_ports, string protocol, string zone, boolean check_for_aliases) {
	boolean are_allowed = true;

	if (size(needed_ports)<1) {
	    y2warning("Undefined list of %1 services/ports for service", protocol);
	    return true;
	}

	map <string, list <string> > allowed_ports = $[];
	// BTW: only TCP and UDP ports can have aliases and only TCP and UDP ports can have port ranges
	if (check_for_aliases) {
	    allowed_ports = PortRanges::DividePortsAndPortRanges (
		GetAllowedServicesForZoneProto (zone, protocol), true
	    );
	} else {
	    allowed_ports["ports"] = GetAllowedServicesForZoneProto (zone, protocol);
	}

	foreach (string needed_port, needed_ports, {
	    if (
		! contains(allowed_ports["ports"]:[], needed_port) &&
		! PortRanges::PortIsInPortranges (needed_port, allowed_ports["port_ranges"]:[])
	    ) {
		are_allowed = false;
		break;
	    }
	});

	return are_allowed;
    }

    /**
     * Returns whether a service is mentioned in FW_CONFIGURATIONS_[EXT|INT|DMZ].
     * These services are defined by random packages.
     *
     * @return boolean if service is supported in zone
     * @param string service, e.g., "service:sshd"
     * @param string zone, e.g., "EXT"
     *
     * @example
     *	IsServiceDefinedByPackageSupportedInZone ("service:sshd", "EXT") -> true
     */
    boolean IsServiceDefinedByPackageSupportedInZone (string service, string zone) {
	if (! IsKnownZone(zone)) {
	    return nil;
	}

	if (service == nil) {
	    y2error ("Service Id can't be nil!");
	    return nil;
	} else if (regexpmatch (service, "^service:.*")) {
	    service = regexpsub (service, "^service:(.*)", "\\1");
	}

	// services defined by package are listed without "service:" which is here
	// just to distinguish between dynamic and static definitions
	list <string> supported_services = splitstring (SETTINGS["FW_CONFIGURATIONS_" + zone]:"", " ");
	return contains (supported_services, service);
    }

    /**
     * Function returns if service is supported (allowed) in zone. Service must be defined
     * in the SuSEFirewallServices. Works transparently also with services defined by packages.
     * Such service starts with "service:" prefix.
     *
     * @see YCP Module SuSEFirewallServices
     * @param	string service id
     * @param	string zone
     * @return	boolean if supported
     *
     * @example
     *	// All ports defined by dns-server service in SuSEFirewallServices module
     *	// are enabled in the respective zone
     *	IsServiceSupportedInZone ("dns-server", "EXT") -> true
     *  // irc-server definition exists on the system and the irc-server
     *  // is mentioned in FW_CONFIGURATIONS_EXT variable of SuSEfirewall2
     *  IsServiceSupportedInZone ("service:irc-server", "EXT") -> true
     */
    global define boolean IsServiceSupportedInZone (string service, string zone) {
	if (! IsKnownZone(zone)) {
	    return nil;
	}

	map <string, list <string> > needed = SuSEFirewallServices::GetNeededPortsAndProtocols(service);
	// unknown service
	if (needed == nil) {
	    y2error("Undefined service '%1'", service);
	    return nil;
	}

	// SuSEFirewall feature FW_PROTECT_FROM_INT
	// should not be protected and searched zones include also internal (or the zone IS internal, sure)
	if (zone == int_zone_shortname && ! GetProtectFromInternalZone()) {
	    y2milestone("Checking for service '%1', in '%2', PROTECT_FROM_INTERNAL='no' => allowed", service, zone);
	    return true;
	}

	// FATE #300687: Ports for SuSEfirewall added via packages
	if (SuSEFirewallServices::ServiceDefinedByPackage(service)) {
	    boolean supported = IsServiceDefinedByPackageSupportedInZone (service, zone);
	    return supported;
	}

	// starting with nil value, any false means that the service is not supported
	boolean service_is_supported = nil;
	foreach (string key, service_defined_by, {
	    list <string> needed_ports = needed[key]:[];
	    if (needed_ports == []) return;

	    if (key == "tcp_ports") {
		service_is_supported = ArePortsOrServicesAllowed(needed_ports, "TCP", zone, true);
	    } else if (key == "udp_ports") {
		service_is_supported = ArePortsOrServicesAllowed(needed_ports, "UDP", zone, true);
	    } else if (key == "rpc_ports") {
		service_is_supported = ArePortsOrServicesAllowed(needed_ports, "RPC", zone, false);
	    } else if (key == "ip_protocols") {
		service_is_supported = ArePortsOrServicesAllowed(needed_ports, "IP", zone, false);
	    } else if ("broadcast_ports" == key) {
		// testing for allowed broadcast ports
		service_is_supported = IsBroadcastAllowed(needed_ports, zone);
	    } else {
		y2error("Unknown key '%1'", key);
	    }

	    // service is not supported, we don't have to do more tests
	    if (service_is_supported == false) break;
	});

	return service_is_supported;
    }

    /**
     * Function returns map of supported services all network interfaces.
     *
     * @param	list <string> of services
     * @return	map <string, map < string, boolean > >
     *
     * @struct	Returns $[service : $[ interface : supported_status ]]
     *
     * @example
     *	GetServicesInZones (["service:irc-server"]) -> $["service:irc-server":$["eth1":true]]
     *  // No such service "something"
     *	GetServicesInZones (["something"])) -> $["something":$["eth1":nil]]
     *  GetServicesInZones (["samba-server"]) -> $["samba-server":$["eth1":false]]
     */
    global define map <string, map <string, boolean> > GetServicesInZones (list<string> services) {
	// list of interfaces for each zone
	map <string, list <string> > interface_in_zone = $[];

	foreach (string interface, GetListOfKnownInterfaces(), {
	    // zone of interface
	    string zone_used = GetZoneOfInterface(interface);
	    // interface can be unassigned
	    if (zone_used == nil || zone_used == "") {
		return;
	    }
	    interface_in_zone[zone_used] = add (interface_in_zone[zone_used]:[], interface);
	});

	// $[ service : $[ network_interface : status ]]
	map <string, map <string, boolean> > services_status = $[];

	// for all services requested
	foreach (string service, services, {
	    services_status[service] = $[];
	    // for all zones in configuration
	    foreach (string zone, list <string> interfaces, interface_in_zone, {
		boolean status = IsServiceSupportedInZone(service, zone);
		// for all interfaces in zone
		foreach (string interface, interfaces, {
		    services_status[service, interface] = status;
		});
	    });
	});

	return services_status;
    }

    /**
     * Function returns map of supported services in all firewall zones.
     *
     * @param	list <string> of services
     * @return	map <string, map < string, boolean> >
     *
     * @struct	Returns $[service : $[ zone_name : supported_status]]
     *
     * @example
     *  // Firewall in not protected from internal zone, that's why
     *  // all services report that they are enabled in INT zone
     *  GetServices (["samba-server", "service:irc-server"]) -> $[
     *    "samba-server" : $["DMZ":false, "EXT":false, "INT":true],
     *    "service:irc-server" : $["DMZ":false, "EXT":true, "INT":true]
     *  ]
     */
    global define map <string, map <string, boolean> > GetServices (list<string> services) {
	// $[ service : $[ firewall_zone : status ]]
	map <string, map <string, boolean> > services_status = $[];

	// for all services requested
	foreach (string service, services, {
	    services_status[service] = $[];
	    // for all zones in configuration
	    foreach (string zone, GetKnownFirewallZones(), {
		services_status[service,zone] = IsServiceSupportedInZone(service, zone);
	    });
	});

	return services_status;
    }

    /**
     * Function sets status for several services in several firewall zones.
     *
     * @param	list <string> service ids
     * @param	list <string> firewall zones (EXT|INT|DMZ...)
     * @param	boolean new status of services
     * @return	boolean if successfull
     *
     * @example
     *	SetServicesForZones (["samba-server", "service:irc-server"], ["DMZ", "EXT"], false);
     *	SetServicesForZones (["samba-server", "service:irc-server"], ["EXT", "DMZ"], true);
     *
     * @see GetServicesInZones()
     * @see GetServices()
     */
    global define boolean SetServicesForZones (list<string> services_ids, list<string> firewall_zones, boolean new_status) {
	// no groups == all groups
	if (size(firewall_zones)==0) firewall_zones = GetKnownFirewallZones();

	// setting for each service
	foreach (string service_id, services_ids, {
	    foreach (string firewall_zone, firewall_zones, {
		// zone must be known one
		if (! IsKnownZone(firewall_zone)) {
		    y2error("Zone '%1' is unknown firewall zone, skipping...", firewall_zone);
		    return;
		}

		SetModified();

		// setting new status
		if (new_status == true) {
		    y2milestone("Adding '%1' into '%2' zone", service_id, firewall_zone);
		    AddServiceSupportIntoZone(service_id, firewall_zone);
		} else {
		    y2milestone("Removing '%1' from '%2' zone", service_id, firewall_zone);
		    RemoveServiceSupportFromZone(service_id, firewall_zone);
		}
	    });
	});
    }

    /**
     * Function sets status for several services on several network interfaces.
     *
     * @param	list <string> service ids
     * @param	list <string> network interfaces
     * @param	boolean new status of services
     * @return	boolean if successfull
     *
     * @example
     *  // Disabling services
     *	SetServices (["samba-server", "service:irc-server"], ["eth1", "modem0"], false)
     *  // Enabling services
     *  SetServices (["samba-server", "service:irc-server"], ["eth1", "modem0"], true)
     * @see SetServicesForZones()
     */
    global define boolean SetServices (list<string> services_ids, list<string> interfaces, boolean new_status) {
	list<string> firewall_zones = GetZonesOfInterfacesWithAnyFeatureSupported(interfaces);
	if (size(firewall_zones)==0) {
	    y2error("Interfaces '%1' are not in any group if interfaces", interfaces);
	    return false;
	}

	SetModified();

	return SetServicesForZones(services_ids, firewall_zones, new_status);
    }

    /**
     * Local function sets the default configuration and fills internal values.
     */
    void ReadDefaultConfiguration () {
	SETTINGS = $[];

	ResetSysconfigSuSEFirewall ( GetListOfSuSEFirewallVariables() );
    }

    /**
     * Local function reads current configuration and fills internal values.
     */
    void ReadCurrentConfiguration () {
	SETTINGS = $[];

	// is firewall enabled in /etc/init.d/ ?
    	SETTINGS["enable_firewall"] = IsEnabled();
	// is firewall started now?
	SETTINGS["start_firewall"]  = IsStarted();

	ReadSysconfigSuSEFirewall ( GetListOfSuSEFirewallVariables() );
    }

    // old internal services definitions are converted to new services defined by packages
    // but only once
    string converted_to_services_dbp_file = Directory::vardir + "/yast2-firewall-already-converted-to-sdbp";

    // services have been already converted
    boolean already_converted = false;

    global void ConvertToServicesDefinedByPackages();

    /**
     * Fills the configuration with default settings,
     * adjusts internal variables that firewall cannot be configured.
     */
    void FillUpEmptyConfig () {
	// do not call it again
	configuration_has_been_read = true;

	// Default settings, services are disabled
	SETTINGS = DEFAULT_SETTINGS;
	SETTINGS["enable_firewall"] = false;
	SETTINGS["start_firewall"]  = false;

	// Cannot be configured, packages weren't installed
	fw_service_can_be_configured = false;
    }

    /**
     * Function for reading SuSEFirewall configuration.
     * Fills internal variables only.
     *
     * @return boolean if successful
     */
    global define boolean Read () {
	// Don't fill up the logs with tones of Check-logs
	// Turn on for debugging
	NetworkInterfaces::report_every_check = false;

	// Do not read it again and again
	// to avoid rewriting changes already made
	if (configuration_has_been_read) {
	    y2milestone ("SuSEfirewall2 configuration has been read already.");
	    return fw_service_can_be_configured;
	}

	// bnc #429861
	if (Stage::initial()) {
	    y2warning ("Stage::initial -> firewall can't be configured.");
	    FillUpEmptyConfig();
	    return false;
	}

	if (! SuSEFirewallIsInstalled()) {
	    y2warning ("Package not installed, disabling SuSEfirewall2-related functions.");
	    FillUpEmptyConfig();
	    return false;
	}

	// Can be configured, packages were installed
	fw_service_can_be_configured = true;

	// Progress only for normal configuration
	boolean have_progress = (Mode::normal());

	if (have_progress) {
	    // TRANSLATORS: Dialog caption
	    string read_caption = _("Initializing Firewall Configuration");

	    Progress::New( read_caption, " ", 4,
		[
		    // TRANSLATORS: Progress step
		    _("Check for network devices"),
		    // TRANSLATORS: Progress step
		    _("Read current configuration"),
		    // TRANSLATORS: Progress step
		    _("Check possibly conflicting services"),
		    // TRANSLATORS: Progress step
		    _("Read dynamic definitions of installed services"),
		],
		[
		    // TRANSLATORS: Progress step
		    _("Checking for network devices..."),
		    // TRANSLATORS: Progress step
		    _("Reading current configuration..."),
		    // TRANSLATORS: Progress step
		    _("Checking possibly conflicting services..."),
		    // TRANSLATORS: Progress step
		    _("Reading dynamic definitions of installed services..."),
		    Message::Finished(),
		],
		""
	    );

	    Progress::NextStage();
	}

	// Always call NI::Read, bnc #396646
	NetworkInterfaces::Read();

	if (Mode::installation() || Mode::autoinst()) {
	    // Allways modified for installation, allways save the final state
	    // fixing bug #67355
	    // SetModified();
	    boolean make_parser_happy = true;
	}

	if (have_progress) Progress::NextStage();

	// get default configuration for autoinstallation
	// if (Mode::installation() || Mode::autoinst()) {
	if (Mode::autoinst()) {
	    ReadDefaultConfiguration();
	// read current configuration for another cases
	} else {
	    ReadCurrentConfiguration();
	}

	if (have_progress) Progress::NextStage();

	// checking if any possibly conficting services were turned on in configuration
	// filling internal values for later checkings
	// CheckAllPossiblyConflictingServices();
	// -- Function has been turned off as we don't support services defined by YaST itself anymore --

	y2milestone("Firewall configuration has been read: %1.", SETTINGS);
	// to read configuration only once
	configuration_has_been_read = true;

	if (have_progress) Progress::NextStage();

	SuSEFirewallServices::ReadServicesDefinedByRPMPackages();

	if (have_progress) Progress::NextStage();

	// bnc #399217
	// Converting built-in service definitions to services defined by packages
	ConvertToServicesDefinedByPackages();

	if (have_progress) Progress::Finish();

	return true;
    }
    
    /**
     * Function returns whether some RPC service is allowed in the configuration.
     * These services reallocate their ports when restarted. See details in
     * bugzilla bug #186186.
     *
     * @return boolean some_RPC_service_used
     */
    boolean AnyRPCServiceInConfiguration () {
	boolean ret = false;

	foreach (string fw_zone, GetKnownFirewallZones(), {
	    string fw_rule = sformat("FW_SERVICES_%1_RPC", fw_zone);
	    string listed_services = SETTINGS[fw_rule]:GetDefaultValue(fw_rule);
	    // easy case
	    if (listed_services == nil || listed_services == "") return;

	    // something listed but it still might be empty definition
	    list <string> services_list = splitstring (listed_services, " \n\t");
	    services_list = filter (string service, services_list, {
		return (service != "");
	    });
	    if (size (listed_services) > 0) {
		ret = true;
		break;
	    }
	});
	
	y2milestone ("Some RPC service found: %1", ret);
	return ret;
    }

    /**
     * Function which stops firewall. Then firewall is started immediately when firewall
     * is wanted to be started: SetStartService(boolean).
     *
     * @return	boolean if successful
     */
    global define boolean ActivateConfiguration () {
	// just disabled
	if (! SuSEFirewallIsInstalled()) return true;

	// Firewall should start after Write()
	if (GetStartService()) {
	    // Not started - start it
	    if (!IsStarted()) {
		y2milestone("Starting firewall services");
		return StartServices();
	    // Started - restart it
	    } else {
		// modified - restart it, or ...
		// bugzilla #186186
		// If any RPC service is configured to be allowed, always restart the firewall
		// Some of these service's ports might have been reallocated (when SuSEFirewall
		// is used from outside, e.g., yast2-nfs-server)
		if (GetModified() || AnyRPCServiceInConfiguration()) {
		    y2milestone("Stopping firewall services");
		    StopServices();
		    y2milestone("Starting firewall services");
		    return StartServices();
		// not modified - skip restart
		} else {
		    y2milestone("Configuration hasn't modified, skipping restarting services");
		    return true;
		}
	    }
	// Firewall should stop after Write()
	} else {
	    // started - stop
	    if (IsStarted()) {
		y2milestone("Stopping firewall services");
		return StopServices();
	    // stopped - skip stopping
	    } else {
		y2milestone("Firewall has been stopped already");
		return true;
	    }
	}
    }

    /**
     * Function writes configuration into /etc/sysconfig/ and enables or disables
     * firewall in /etc/init.d/ by the setting SetEnableService(boolean).
     * This is a write-only configuration, firewall is never started only enabled
     * or disabled.
     *
     * @return	boolean if successful
     */
    global define boolean WriteConfiguration () {
	// just disabled
	if (! SuSEFirewallIsInstalled()) return true;

	// Progress only for normal configuration and command line
	boolean have_progress = (Mode::normal());

	if (have_progress) {
	    // TRANSLATORS: Dialog caption
	    string write_caption = _("Writing Firewall Configuration");

	    Progress::New( write_caption, " ", 2,
		[
		    // TRANSLATORS: Progress step
		    _("Write firewall settings"),
		    // TRANSLATORS: Progress step
		    _("Adjust firewall service"),
		],
		[
		    // TRANSLATORS: Progress step
		    _("Writing firewall settings..."),
		    // TRANSLATORS: Progress step
		    _("Adjusting firewall service..."),
		    // TRANSLATORS: Progress step
		    Message::Finished(),
		],
		""
	    );

	    Progress::NextStage();
	}

	// only modified configuration is written
	if (GetModified()) {
	    y2milestone("Firewall configuration has been changed. Writing: %1.", SETTINGS);

	    if (! WriteSysconfigSuSEFirewall ( GetListOfSuSEFirewallVariables() )) {
		// TRANSLATORS: a popup error message
		Report::Error(_("Writing settings failed"));
		return false;
	    }
	} else {
	    y2milestone("Firewall settings weren't modified, skipping...");
	}

	if (have_progress) Progress::NextStage();

	// Adjusting services
	if (GetModified()) {
	    // enabling firewall in /etc/init.d/
	    if (SETTINGS["enable_firewall"]:false) {
		y2milestone("Enabling firewall services");
		if (! EnableServices()) {
		    return false;
		}
	    // disabling firewall in /etc/init.d/    
	    } else {
		y2milestone("Disabling firewall services");
		if (! DisableServices()) {
		    return false;
		}
	    }
	} else {
	    y2milestone("Firewall enable/disable wasn't modified, skipping...");
	}

	if (have_progress) Progress::NextStage();

	if (already_converted && ! FileUtils::Exists (converted_to_services_dbp_file)) {
	    y2milestone ("Writing %1: %2",
		converted_to_services_dbp_file,
		SCR::Write (.target.string, converted_to_services_dbp_file, "")
	    );
	}

	if (have_progress) Progress::Finish();

	return true;
    }

    void CheckKernelModules ();

    /**
     * Helper function for the backward compatibility.
     * See WriteConfiguration(). Remove from code ASAP.
     *
     * @return boolean if succesful
     */
    global define boolean WriteOnly() {
	return WriteConfiguration();
    }

    /**
     * Function for writing and enabling configuration it is an union of
     * WriteConfiguration() and ActivateConfiguration().
     *
     * @return	boolean if succesfull
     */
    global define boolean Write () {
	CheckKernelModules();

	// just disabled
	if (! SuSEFirewallIsInstalled()) return true;

	if (! WriteConfiguration())	return false;

	if (! ActivateConfiguration())	return false;

	return true;
    }

    /**
     * Function for saving configuration and restarting firewall.
     * Is is the same as Write() but write is allways forced.
     *
     * @return	boolean if successful
     */
    global define boolean SaveAndRestartService () {
	y2milestone("Forced save and restart");
	SetModified();

	SetStartService(true);

	if (! Write()) {
	    return false;
	}
	
	return true;
    }

    /**
     * This powerful function returns list of services/ports which are
     * not assigned to any fully-supported known-services.
     * This function doesn't check for services defined by packages.
     * They are listed by a different way.
     *
     * @return	list <string> of additional (unassigned) services
     *
     * @example
     *	GetAdditionalServices("TCP", "EXT") -> ["53", "128"]
     */
    global define list <string> GetAdditionalServices (string protocol, string zone) {
	list <string> additional_services = [];

	if (! IsSupportedProtocol(protocol)) {
	    y2error("Unknown protocol '%1'", protocol);
	    return nil;
	}
	if (! IsKnownZone(zone)) {
	    y2error("Unknown zone '%1'", zone);
	    return nil;
	}

	// all ports or services allowed in zone for protocol
	list <string> all_allowed_services = GetAllowedServicesForZoneProto(zone, protocol);

	// all ports or services used by known service
	list <string> all_used_services = [];

	// trying all possible (known) services
	foreach (string service_id, string service_name, SuSEFirewallServices::GetSupportedServices(), {
	    // only when the service is allowed in zone - remove all its needed ports
	    if (IsServiceSupportedInZone(service_id, zone) == true) {

		// all needed ports etc for service/protocol
		list <string> needed_all = [];
		if (protocol == "TCP") {
		    needed_all = SuSEFirewallServices::GetNeededTCPPorts(service_id);
		} else if (protocol == "UDP") {
		    needed_all = SuSEFirewallServices::GetNeededUDPPorts(service_id);
		} else if (protocol == "RPC") {
		    needed_all = SuSEFirewallServices::GetNeededRPCPorts(service_id);
		} else if (protocol == "IP") {
		    needed_all = SuSEFirewallServices::GetNeededIPProtocols(service_id);
		}
		foreach (string remove_port, needed_all, {
		    // all used services and their aliases
		    all_used_services = (list <string>) union (
			all_used_services, PortAliases::GetListOfServiceAliases(remove_port)
		    );
		});

	    }
	});

	// some services are used by known defined-services
	if (size(all_used_services)>0) {
	    all_used_services = toset (all_used_services);
	    // removing all used services from all allowed
	    all_allowed_services = filter (string port, all_allowed_services, {
		return (! contains(all_used_services, port));
	    });
	}

	// well, actually it returns list of services not-assigned to any well-known service
	return all_allowed_services;
    }

    /**
     * Function sets additional ports/services from taken list. Firstly, all additional services
     * are removed also with their aliases. Secondly new ports/protocols are added.
     * It uses GetAdditionalServices() function to get the current state and
     * then it removes what has been removed and adds what has been added.
     *
     * @param	string protocol
     * @param	string zone
     * @param	list <string> list of ports/protocols
     * @see	GetAdditionalServices()
     *
     * @example
     *	SetAdditionalServices ("TCP", "EXT", ["53", "128"])
     */
    global define void SetAdditionalServices (string protocol, string zone, list <string> new_list_services) {
	list <string> old_list_services = toset(GetAdditionalServices(protocol, zone));
	new_list_services = toset(new_list_services);

	if (new_list_services != old_list_services) {
	    SetModified();

	    list <string> add_services = [];
	    list <string> remove_services = [];

	    // Add these services
	    foreach (string service, new_list_services, {
		if (!contains(old_list_services, service))
		    add_services = add (add_services, service);
	    });
	    // Remove these services
	    foreach (string service, old_list_services, {
		if (!contains(new_list_services, service))
		    remove_services = add (remove_services, service);
	    });

	    if (size(remove_services)>0) {
		y2milestone("Removing additional services %1/%2 from zone %3", remove_services, protocol, zone);
		RemoveAllowedPortsOrServices (remove_services, protocol, zone, true);
	    }
	    if (size(add_services)>0) {
		y2milestone("Adding additional services %1/%2 into zone %3", add_services, protocol, zone);
		AddAllowedPortsOrServices (add_services, protocol, zone);
	    }
	}
    }

    /**
     * Function returns if any other firewall then SuSEfirewall2 is currently running on the
     * system. It uses command `iptables` to get information about just active iptables
     * rules and compares the output with current status of SuSEfirewall2.
     *
     * @return	boolean if other firewall is running
     */
    global define boolean IsOtherFirewallRunning () {
	boolean any_firewall_running = true;

	// grep must return at least blank lines, else it returns 'exit 1' instead of 'exit 0'
	string command = "iptables -L -n | grep -v \"^\\(Chain\\|target\\)\"";

	map iptables = (map) SCR::Execute(.target.bash_output, command);
	if (iptables["exit"]:0 == 0) {
	    list <string> iptables_list = splitstring(iptables["stdout"]:"", "\n");
	    iptables_list = filter (string iptable_rule, iptables_list, {
		return iptable_rule != "";
	    });

	    y2milestone("Count of active iptables now: %1", size(iptables_list));

	    // none iptables rules
	    if (size(iptables_list)>0) {
		any_firewall_running = true;
	    // any iptables rules exist
	    } else {
		any_firewall_running = false;
	    }
	// error running command
	} else {
	    y2error("Services Command: %1 (Exit %2) -> %3", command, iptables["exit"]:nil, iptables["stderr"]:nil);
	    return nil;
	}

	// any firewall is running but it is not a SuSEfirewall2
	if (any_firewall_running && !IsStarted()) {
	    y2warning("Any other firewall is running...");
	    return true;
	}
	// no firewall is running or the running firewall is SuSEfirewall2
	return false;
    }

    /**
     * Function returns map of `interfaces in zones`.
     *
     * @return	map <string, list <string> > interface in zones
     *
     * @struct	map $[zone : [list of interfaces]]
     *
     * @example
     *	GetFirewallInterfacesMap() -> $["DMZ":[], "EXT":["dsl0"], "INT":["eth1", "eth2"]]
     */
    global define map <string, list <string> > GetFirewallInterfacesMap () {
	map <string, list <string> > firewall_interfaces_now = $[];

	// list of all known interfaces
	list <string> known_interfaces = GetListOfKnownInterfaces();

	// searching each zone
	foreach (string zone, GetKnownFirewallZones(), {
	    // filtering non-existing interfaces
	    firewall_interfaces_now[zone] = filter (string interface, GetInterfacesInZone(zone), {
		return contains(known_interfaces, interface);
	    });
	});

	return firewall_interfaces_now;
    }

    /**
     * Function returns list of special strings like 'any' or 'auto' and uknown interfaces.
     *
     * @param	string zone
     * @return	list <string> special strings or unknown interfaces
     *
     * @example
     *	GetSpecialInterfacesInZone("EXT") -> ["any", "unknown-1", "wrong-3"]
     */
    global define list <string> GetSpecialInterfacesInZone (string zone) {
	list <string> interfaces_in_zone = splitstring (SETTINGS["FW_DEV_" + zone]:"", " ");

	list <string> known_interfaces_now = GetInterfacesInZone (zone);

	// filtering known interfaces and spaces
	interfaces_in_zone = filter(string interface, interfaces_in_zone, {
	    return interface != "" && !contains(known_interfaces_now, interface);
	});
	
	return interfaces_in_zone;
    }

    /**
     * Function removes special string from defined zone.
     *
     * @param	string interface
     * @param	string zone
     */
    global define void RemoveSpecialInterfaceFromZone (string interface, string zone) {
	SetModified();

	y2milestone("Removing special string '%1' from '%2' zone.", interface, zone);

	list <string> interfaces_in_zone = splitstring (SETTINGS["FW_DEV_" + zone]:"", " ");
	interfaces_in_zone = filter (string single_interface, interfaces_in_zone, {
	    return single_interface != "" && single_interface != interface;
	});
	SETTINGS["FW_DEV_" + zone] = mergestring(interfaces_in_zone, " ");
    }

    /**
     * Functions adds special string into defined zone.
     *
     * @param	string interface
     * @param	string zone
     */
    global define void AddSpecialInterfaceIntoZone (string interface, string zone) {
	SetModified();
	
	y2milestone("Adding special string '%1' into '%2' zone.", interface, zone);
	list <string> interfaces_in_zone = splitstring (SETTINGS["FW_DEV_" + zone]:"", " ");
	interfaces_in_zone = toset(add (interfaces_in_zone, interface));
	SETTINGS["FW_DEV_" + zone] = mergestring(interfaces_in_zone, " ");
    }

    /**
     * Function returns actual state of Masquerading support.
     *
     * @return	boolean if supported
     */
    global define boolean GetMasquerade () {
	return (SETTINGS["FW_MASQUERADE"]:"no" == "yes" && SETTINGS["FW_ROUTE"]:"no" == "yes");
    }

    /**
     * Function sets Masquerade support.
     *
     * @param	boolean to support or not to support it
     */
    global define void SetMasquerade (boolean enable) {
	SetModified();

	SETTINGS["FW_MASQUERADE"] = (enable ? "yes" : "no");

	// routing is needed for masquerading, but we can't swithc it off when disabling masquerading
	if (enable) SETTINGS["FW_ROUTE"] = "yes";
    }

    /**
     * Function returns list of rules of forwarding ports
     * to masqueraded IPs.
     *
     * @return  list <map <string, string> > list of rules
     *
     * @struct	list [$[ key: value ]]
     *
     * @example
     *	GetListOfForwardsIntoMasquerade() -> [
     * $[
     *   "forward_to":"172.24.233.1",
     *   "protocol":"tcp",
     *   "req_ip":"192.168.0.3",
     *   "req_port":"355",
     *   "source_net":"192.168.0.0/20",
     *   "to_port":"533"],
     *   ...
     * ]
     */
    global define list <map <string, string> > GetListOfForwardsIntoMasquerade () {
	list <map <string, string> > list_of_rules = [];

	foreach (string forward_rule, splitstring(SETTINGS["FW_FORWARD_MASQ"]:"", " "), {
	    if (forward_rule == "") return;

	    // Format: <source network>,<ip to forward to>,<protocol>,<port>[,redirect port,[destination ip]]
	    list <string> fw_rul = splitstring(forward_rule,",");

	    // first four parameters has to be defined
	    if (fw_rul[0]:"" == "" || fw_rul[1]:"" == "" || fw_rul[2]:"" == "" || fw_rul[3]:"" == "")
		y2warning("Wrong definition of redirect rule: '%1', part of '%2'",
		    forward_rule, SETTINGS["FW_FORWARD_MASQ"]:""
		);

	    list_of_rules = add (list_of_rules, $[
		"source_net" : fw_rul[0]:"",
		"forward_to" : fw_rul[1]:"",
		"protocol"   : tolower(fw_rul[2]:""),
		"req_port"   : tolower(fw_rul[3]:""),
		// to_port is req_port when undefined
		"to_port"    : tolower(fw_rul[4]:fw_rul[3]:""),
		"req_ip"     : tolower(fw_rul[5]:""),
	    ]);
	});

	return list_of_rules;
    }

    /**
     * Function removes rule for forwarding into masquerade
     * from the list of current rules returned by GetListOfForwardsIntoMasquerade().
     *
     * @params	integer item number
     *
     * @see GetListOfForwardsIntoMasquerade()
     */
    global define void RemoveForwardIntoMasqueradeRule (integer remove_item) {
	SetModified();

	list <string> forward_rules = [];

	integer row_counter = 0;
	foreach (string forward_rule, splitstring(SETTINGS["FW_FORWARD_MASQ"]:"", " "), {
	    if (forward_rule == "") return;

	    if (row_counter != remove_item) {
		forward_rules = add (forward_rules, forward_rule);
	    }
	    
	    row_counter = row_counter + 1;
	});

	SETTINGS["FW_FORWARD_MASQ"] = mergestring(forward_rules, " ");
    }

    /**
     * Adds forward into masquerade rule.
     *
     * @param string source_net
     * @param string forward_to_ip
     * @param string protocol
     * @param string req_port
     * @param string redirect_to_port
     * @param string requested_ip
     *
     * @example
     *	AddForwardIntoMasqueradeRule ("0/0", "192.168.32.1", "TCP", "80", "8080", "10.0.0.1")
     */
    global define void AddForwardIntoMasqueradeRule (
	string source_net, string forward_to_ip, string protocol, string req_port,
	string redirect_to_port, string requested_ip
    ) {
	SetModified();

	string masquerade_rules = SETTINGS["FW_FORWARD_MASQ"]:"";

	masquerade_rules = masquerade_rules + (masquerade_rules != "" ? " ":"") +
	    source_net + "," +
	    forward_to_ip + "," +
	    protocol + "," +
	    req_port;

	if (redirect_to_port != "" || requested_ip != "") {
	    if (requested_ip != "") {
		masquerade_rules = masquerade_rules + "," + redirect_to_port + "," + requested_ip;
	    // port1 -> port2 are same
	    } else if (redirect_to_port != req_port) {
		masquerade_rules = masquerade_rules + "," + redirect_to_port;
	    }
	}

	SETTINGS["FW_FORWARD_MASQ"] = masquerade_rules;
    }

    /**
     * Function returns actual state of logging for rule taken as parameter.
     *
     * @param	string rule definition 'ACCEPT' or 'DROP'
     * @return	string 'ALL', 'CRIT', or 'NONE'
     *
     * @example
     *	GetLoggingSettings("ACCEPT") -> "CRIT"
     *	GetLoggingSettings("DROP") -> "CRIT"
     */
    global define string GetLoggingSettings(string rule) {
	string ret_val = nil;

	if (rule == "ACCEPT") {
	    if (SETTINGS["FW_LOG_ACCEPT_ALL"]:"no" == "yes") {
		ret_val = "ALL";
	    } else if (SETTINGS["FW_LOG_ACCEPT_CRIT"]:"yes" == "yes") {
		ret_val = "CRIT";
	    } else {
		ret_val = "NONE";
	    }
	} else if (rule == "DROP") {
	    if (SETTINGS["FW_LOG_DROP_ALL"]:"no" == "yes") {
		ret_val = "ALL";
	    } else if (SETTINGS["FW_LOG_DROP_CRIT"]:"yes" == "yes") {
		ret_val = "CRIT";
	    } else {
		ret_val = "NONE";
	    }
	} else {
	    y2error("Possible rules are only 'ACCEPT' or 'DROP'");
	}

	return ret_val;
    }

    /**
     * Function sets state of logging for rule taken as parameter.
     *
     * @param	string rule definition 'ACCEPT' or 'DROP'
     * @param	string new logging state 'ALL', 'CRIT', or 'NONE'
     *
     * @example
     *	SetLoggingSettings ("ACCEPT", "ALL")
     *	SetLoggingSettings ("DROP", "NONE")
     */
    global define void SetLoggingSettings(string rule, string state) {
	SetModified();

	if (rule == "ACCEPT") {
	    if (state == "ALL") {
		SETTINGS["FW_LOG_ACCEPT_CRIT"] = "yes";
		SETTINGS["FW_LOG_ACCEPT_ALL"]  = "yes";
	    } else if (state == "CRIT") {
		SETTINGS["FW_LOG_ACCEPT_CRIT"] = "yes";
		SETTINGS["FW_LOG_ACCEPT_ALL"]  = "no";
	    } else {
		SETTINGS["FW_LOG_ACCEPT_CRIT"] = "no";
		SETTINGS["FW_LOG_ACCEPT_ALL"]  = "no";
	    }
	} else if (rule == "DROP") {
	    if (state == "ALL") {
		SETTINGS["FW_LOG_DROP_CRIT"] = "yes";
		SETTINGS["FW_LOG_DROP_ALL"]  = "yes";
	    } else if (state == "CRIT") {
		SETTINGS["FW_LOG_DROP_CRIT"] = "yes";
		SETTINGS["FW_LOG_DROP_ALL"]  = "no";
	    } else {
		SETTINGS["FW_LOG_DROP_CRIT"] = "no";
		SETTINGS["FW_LOG_DROP_ALL"]  = "no";
	    }
	} else {
	    y2error("Possible rules are only 'ACCEPT' or 'DROP'");
	}
    }

    /**
     * Function returns yes/no - ingoring broadcast for zone
     *
     * @param	string zone
     * @return	string "yes" or "no"
     *
     * @example
     *	// Does not logg ignored broadcast packets
     *	GetIgnoreLoggingBroadcast ("EXT") -> "yes"
     */
    global define string GetIgnoreLoggingBroadcast (string zone) {
	if (! IsKnownZone(zone)) {
	    y2error("Unknown zone '%1'", zone);
	    return nil;
	}

	return SETTINGS["FW_IGNORE_FW_BROADCAST_" + zone]:"no";
    }

    /**
     * Function sets yes/no - ingoring broadcast for zone
     *
     * @param	string zone
     * @param	string ignore 'yes' or 'no'
     *
     * @example
     *	// Do not log broadcast packetes from DMZ
     *	SetIgnoreLoggingBroadcast ("DMZ", "yes")
     */
    global define void SetIgnoreLoggingBroadcast (string zone, string bcast) {
	if (! IsKnownZone(zone)) {
	    y2error("Unknown zone '%1'", zone);
	    return nil;
	}

	SetModified();

	SETTINGS["FW_IGNORE_FW_BROADCAST_" + zone] = bcast;
    }

    /**
     * Function adds a special interface 'xenbr+' into the FW_FORWARD_ALWAYS_INOUT_DEV variable.
     *
     * @see: https://bugzilla.novell.com/show_bug.cgi?id=154133
     * @see: https://bugzilla.novell.com/show_bug.cgi?id=233934
     * @see: https://bugzilla.novell.com/show_bug.cgi?id=375482
     */
    global define void AddXenSupport () {
    /*
	// xenbr+ works for all bridges
	// bridge number is newly dependant on network card number
	// eth1 -> xenbr1, eth8 -> xenbr8
	string special_xen_interface = "xenbr+";

	SetModified();

	list <string> allways_inout_dev = splitstring (SETTINGS["FW_FORWARD_ALWAYS_INOUT_DEV"]:"", " ");
	allways_inout_dev = toset(add (allways_inout_dev, special_xen_interface));
	SETTINGS["FW_FORWARD_ALWAYS_INOUT_DEV"] = mergestring(allways_inout_dev, " ");
	
	y2milestone("FW_FORWARD_ALWAYS_INOUT_DEV -> %1", SETTINGS["FW_FORWARD_ALWAYS_INOUT_DEV"]:"");
    */
	y2milestone ("The whole functionality is currently handled by SuSEfirewall2 itself");
    }

    // Firewall Expert Rulezz

    /**
     * Returns list of rules describing protocols and ports that are allowed
     * to be accessed from listed hosts. All is returned as a single string.
     * Zone needs to be defined.
     *
     * @param string zone
     * @return string with rules
     */
    global define string GetAcceptExpertRules (string zone) {
	zone = toupper(zone);

	// Check for zone
	if (! contains(GetKnownFirewallZones(), zone)) {
	    y2error("Unknown firewall zone: %1", zone);
	    return nil;
	}

	return SETTINGS["FW_SERVICES_ACCEPT_" + zone]:"";
    }

    /**
     * Sets expert allow rules for zone.
     *
     * @param string zone
     * @param string whitespace-separated expert_rules
     * @return boolean if successful
     */
    global define boolean SetAcceptExpertRules (string zone, string expert_rules) {
	zone = toupper(zone);

	// Check for zone
	if (! contains(GetKnownFirewallZones(), zone)) {
	    y2error("Unknown firewall zone: %1", zone);
	    return false;
	}
	
	SETTINGS["FW_SERVICES_ACCEPT_" + zone] = expert_rules;
	SetModified();

	return true;
    }

    /**
     * Returns list of additional kernel modules, that are loaded by firewall on startup.
     * For instance "ip_conntrack_ftp" and "ip_nat_ftp" for FTP service.
     *
     * @return list <string> of kernel modules
     *
     * @see /etc/sysconfig/SuSEfirewall2 option nr. 32 (FW_LOAD_MODULES)
     */
    global define list <string> GetFirewallKernelModules () {
	list <string> k_modules = splitstring (SETTINGS["FW_LOAD_MODULES"]:"", " \t\n");

	k_modules = filter (string one_module, k_modules, {
	    return (one_module != "");
	});

	return toset (k_modules);
    }

    /**
     * Sets list of additional kernel modules to be loaded by firewall on startup.
     *
     * @param list <string> of kernel modules
     *
     * @see /etc/sysconfig/SuSEfirewall2 option nr. 32
     *
     * @example SuSEFirewall::SetFirewallKernelModules (["ip_conntrack_ftp","ip_nat_ftp"]);
     */
    global define void SetFirewallKernelModules (list <string> k_modules) {
	k_modules = filter (string one_module, k_modules, {
	    if (one_module == nil) {
		y2error ("List of modules %1 contains 'nil'! It will be ignored.", k_modules);
		return false;
	    } else if (one_module == "") {
		y2warning ("List of modules %1 contains an empty string, it will be ignored.", k_modules);
		return false;
	    }
	    
	    if (regexpmatch (one_module, " ") || regexpmatch (one_module, "\t")) {
		y2warning (
		    "Additional module '%1' contains spaces. They will be evaluated as two or more modules later.",
		    one_module
		);
	    }
	    
	    return true;
	});

	SETTINGS["FW_LOAD_MODULES"] = mergestring (k_modules, " ");
	SetModified();
    }

    map <string, string> protocol_translations = $[
	// protocol name
	"tcp" : _("TCP"),
	// protocol name
	"udp" : _("UDP"),
	// protocol name
	"_rpc_" : _("RPC"),
	// protocol name
	"ip"  : _("IP"),
    ];

    /**
     * Returns translated protocol name. Translation is provided from
     * SuSEfirewall2 sysconfig format to l10n format.
     *
     * @param string string from sysconfig (e.g., _rpc_)
     * @return string translated string (e.g., RPC)
     */
    global string GetProtocolTranslatedName (string protocol) {
	protocol = tolower (protocol);
	
	if (protocol == "") {
	    return "";
	} else if (protocol_translations[protocol]:nil == nil) {
	    y2error ("Unknown protocol: %1", protocol);
	    // table item, %1 stands for the buggy protocol name
	    return sformat (_("Unknown protocol (%1)"), protocol);
	} else {
	    return protocol_translations[protocol]:"";
	}
    }

    /**
     * Returns list of FW_SERVICES_ACCEPT_RELATED_*: Services to allow that are
     * considered RELATED by the connection tracking engine, e.g., SLP browsing
     * reply or Samba browsing reply.
     *
     * @param string zone
     * @return list <string> list of definitions
     *
     * @example
     * GetServicesAcceptRelated ("EXT") -> ["0/0,udp,427", "0/0,udp,137"]
     *
     * @see SetServicesAcceptRelated()
     */
    global list <string> GetServicesAcceptRelated (string zone) {
	if (! IsKnownZone (zone)) {
	    y2error ("Uknown zone '%1'", zone);
	    return [];
	}

	return splitstring (SETTINGS["FW_SERVICES_ACCEPT_RELATED_" + zone]:"", " \t\n");
    }

    /**
     * Functions sets FW_SERVICES_ACCEPT_RELATED_*: Services to allow that are
     * considered RELATED by the connection tracking engine, e.g., SLP browsing
     * reply or Samba browsing reply.
     *
     * @param string zone
     * @param list <string> list of rules
     *
     * @example
     * SetServicesAcceptRelated ("EXT", ["0/0,udp,427", "0/0,udp,137"])
     *
     * @see GetServicesAcceptRelated()
     */
    global void SetServicesAcceptRelated (string zone, list <string> ruleset) {
	if (! IsKnownZone (zone)) {
	    y2error ("Uknown zone '%1'", zone);
	    return;
	}

	ruleset = filter (string one_rule, ruleset, {
	    return (one_rule != nil);
	});

	SetModified();

	SETTINGS["FW_SERVICES_ACCEPT_RELATED_" + zone] = mergestring (ruleset, "\n");
    }

    /**
     * Checks whether any Accept-Related rules have been defined.
     * If true, required kernel modules are added.
     */
    void CheckKernelModules () {
	boolean needs_additional_module = false;

	foreach (string one_zone, GetKnownFirewallZones(), {
	    if (size (GetServicesAcceptRelated (one_zone)) >= 0) {
		y2milestone ("Some ServicesAcceptRelated are defined");
		needs_additional_module = true;
		break;
	    }
	});

	if (needs_additional_module) {
	    list <string> k_modules = splitstring (SETTINGS["FW_LOAD_MODULES"]:"", " ");

	    if (! contains (k_modules, broadcast_related_module)) {
		y2warning ("FW_LOAD_MODULES doesn't contain %1, adding", broadcast_related_module);
		k_modules = add (k_modules, broadcast_related_module);
		SETTINGS["FW_LOAD_MODULES"] = mergestring (k_modules, " ");
		SetModified();
	    }
	}
    }

    /**
     * Removes old-service definitions before they are added as services defined
     * by packages.
     */
    void RemoveOldAllowedServiceFromZone (map <string, any> old_service_def, string zone) {
	y2milestone ("Removing: %1 from zone %2", old_service_def, zone);

	if (old_service_def["tcp_ports"]:[] != []) {
	    foreach (string one_service, old_service_def["tcp_ports"]:[], {
		RemoveService (one_service, "TCP", zone);
	    });
	}

	if (old_service_def["udp_ports"]:[] != []) {
	    foreach (string one_service, old_service_def["udp_ports"]:[], {
		RemoveService (one_service, "UDP", zone);
	    });
	}

	if (old_service_def["rpc_ports"]:[] != []) {
	    foreach (string one_service, old_service_def["rpc_ports"]:[], {
		RemoveService (one_service, "RPC", zone);
	    });
	}

	if (old_service_def["ip_protocols"]:[] != []) {
	    foreach (string one_service, old_service_def["ip_protocols"]:[], {
		RemoveService (one_service, "IP", zone);
	    });
	}

	if (old_service_def["broadcast_ports"]:[] != []) {
	    map <string, list <string> > broadcast = GetBroadcastAllowedPorts();

	    broadcast[zone] = filter (string one_port, broadcast[zone]:[], {
		return (! contains (old_service_def["broadcast_ports"]:[], one_port));
	    });

	    SetBroadcastAllowedPorts (broadcast);
	}
    }

    /**
     * Converts old built-in service definitions to services defined by packages.
     *
     * @see #bnc 399217
     */
    global void ConvertToServicesDefinedByPackages () {
        if (already_converted) {
            return;
        }

        if (FileUtils::Exists (converted_to_services_dbp_file)) {
	    y2milestone ("Configuration has been already converted");
            already_converted = true;
            return;
        }

	// $[ zone : $[ protocol : [ list of ports ] ] ]
	map <string, map <string, list <string> > > current_conf = $[];

        foreach (string zone, GetKnownFirewallZones (), {
	    current_conf[zone] = $[];

	    foreach (string protocol, supported_protocols, {
		current_conf[zone, protocol] = GetAllowedServicesForZoneProto (zone, protocol);
		current_conf[zone, "broadcast"] = splitstring (GetBroadcastConfiguration (zone), " \n");
	    });
        });

	y2milestone ("Current conf: %1", current_conf);

	foreach (string zone, GetKnownFirewallZones (), {
	    foreach (string old_service_id, map <string, any> old_service_def, SuSEFirewallServices::OLD_SERVICES, {
		y2milestone ("Checking %1 in %2 zone", old_service_id, zone);

		if (old_service_def["tcp_ports"]:[] != [] && ArePortsOrServicesAllowed (old_service_def["tcp_ports"]:[], "TCP", zone, true) != true)
		    return;

		if (old_service_def["udp_ports"]:[] != [] && ArePortsOrServicesAllowed (old_service_def["udp_ports"]:[], "UDP", zone, true) != true)
		    return;

		if (old_service_def["rpc_ports"]:[] != [] && ArePortsOrServicesAllowed (old_service_def["rpc_ports"]:[], "RPC", zone, false) != true)
		    return;

		if (old_service_def["ip_protocols"]:[] != [] && ArePortsOrServicesAllowed (old_service_def["ip_protocols"]:[], "IP", zone, false) != true)
		    return;

		if (old_service_def["broadcast_ports"]:[] != [] && IsBroadcastAllowed (old_service_def["broadcast_ports"]:[], zone) != true)
		    return;

		if (old_service_def["convert_to"]:[] == []) {
		    y2milestone ("Service %1 supported, but it doesn't have any replacement", old_service_id);
		    return;
		}

		boolean replaced = false;

		foreach (string replacement, old_service_def["convert_to"]:[], {
		    if (SuSEFirewallServices::IsKnownService (replacement)) {
			y2milestone ("Old service %1 matches %2", old_service_id, replacement);
			RemoveOldAllowedServiceFromZone (old_service_def, zone);
			SetServicesForZones ([replacement], [zone], true);
			replaced = true;
			break;
		    }
		});

		if (! replaced) {
		    y2warning ("Old service %1 matches %2 but none are installed", old_service_id, old_service_def["convert_to"]:[]);
		}
	    });
	});

	y2milestone ("Converting done");
	already_converted = true;
    }

    // <!-- SuSEFirewall GLOBAL FUNCTIONS //-->

/* EOF */
}

ACC SHELL 2018