#!/usr/bin/python -Es
# -*- coding: utf-8 -*-
#
# Copyright (C) 2009-2014 Red Hat, Inc.
#
# Authors:
# Thomas Woerner <twoerner@redhat.com>
# Jiri Popelka <jpopelka@redhat.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

from gi.repository import GObject
import sys
sys.modules['gobject'] = GObject

import argparse
import dbus
import os

#from firewall.config import *
from firewall.core.fw_test import Firewall_test
from firewall.client import *
from firewall.errors import *
from firewall.functions import joinArgs, splitArgs

# check for root user
if os.getuid() != 0:
    print(_("You need to be root to run %s.") % sys.argv[0])
    sys.exit(-1)

def __usage():
    print ("""
Usage: firewall-offline-cmd [OPTIONS...]

If no options are given, configuration from '/etc/conf.d/system-config-firewall' will be migrated.

General Options
  -h, --help           Prints a short help text and exists
  -V, --version        Print the version string of firewalld

Lokkit Compatibility Options
  --enabled             Enable firewall (default)
  --disabled            Disable firewall
  --addmodule=<module>  Ignored option, was used to enable an iptables module
  --removemodule=<module>
                        Ignored option, was used to disable an iptables module
  -s <service>, --service=<service>
                        Enable a service in the default zone (example: ssh)
  --remove-service=<service>
                        Disable a service in the default zone (example: ssh)
  -p <port>[-<port>]:<protocol>, --port=<port>[-<port>]:<protocol>
                        Enable a port in the default zone (example: ssh:tcp)
  -t <interface>, --trust=<interface>
                        Bind an interface to the trusted zone
  -m <interface>, --masq=<interface>
                        Enables masquerading in the default zone, interface
                        argument is ignored. This is IPv4 only.
  --custom-rules=[<type>:][<table>:]<filename>
                        Ignored option. Was used to add custom rules to the
                        firewall (Example:
                        ipv4:filter:/etc/conf.d/ipv4_filter_addon)
  --forward-port=if=<interface>:port=<port>:proto=<protocol>[:toport=<destination port>][:toaddr=<destination address>]
                        Forward the port with protocol for the interface to
                        either another local destination port (no destination
                        address given) or to an other destination address with
                        an optional destination port. This will be added to
                        the default zone. This is IPv4 only.
  --block-icmp=<icmp type>
                        Block this ICMP type in the default zone. The default
                        is to accept all ICMP types.

Zone Options
  --get-default-zone   Print default zone for connections and interfaces
  --set-default-zone=<zone>
                       Set default zone
  --get-zones          Print predefined zones
  --get-services       Print predefined services
  --get-icmptypes      Print predefined icmptypes
  --get-zone-of-interface=<interface>
                       Print name of the zone the interface is bound to
  --get-zone-of-source=<source>[/<mask>]
                       Print name of the zone the source[/mask] is bound to
  --list-all-zones     List everything added for or enabled in all zones
  --new-zone=<zone>    Add a new zone
  --delete-zone=<zone> Delete an existing zone
  --zone=<zone>        Use this zone to set or query options, else default zone
                       Usable for options marked with [Z]
  --set-description=<description>
                       Set new description to zone
  --get-description    Print description for zone
  --get-target         Get the zone target
  --set-target=<target>
                       Set the zone target
  --info-zone=<zone>   Print information about a zone

IPSet Options
  --new-ipset=<ipset> --type=<ipset type> [--option=<key>[=<value>]]..
                       Add a new ipset
  --delete-ipset=<ipset>
                       Delete an existing ipset
  --info-ipset=<ipset> Print information about an ipset
  --get-ipsets         Print predefined ipsets
  --ipset=<ipset> --set-description=<description>
                       Set new description to ipset
  --ipset=<ipset> --get-description
                       Print description for ipset
  --ipset=<ipset> --set-short=<description>
                       Set new short description to ipset
  --ipset=<ipset> --get-short
                       Print short description for ipset
  --ipset=<ipset> --add-entry=<entry>
                       Add a new entry to an ipset
  --ipset=<ipset> --remove-entry=<entry>
                       Remove an entry from an ipset
  --ipset=<ipset> --query-entry=<entry>
                       Return whether ipset has an entry
  --ipset=<ipset> --get-entries
                       List entries of an ipset

IcmpType Options
  --new-icmptype=<icmptype>
                       Add a new icmptype
  --delete-icmptype=<icmptype>
                       Delete an existing icmptype
  --info-icmptype=<icmptype>
                       Print information about an icmptype
  --icmptype=<icmptype> --set-description=<description>
                       Set new description to icmptype
  --icmptype=<icmptype> --get-description
                       Print description for icmptype
  --icmptype=<icmptype> --set-short=<description>
                       Set new short description to icmptype
  --icmptype=<icmptype> --get-short
                       Print short description for icmptype
  --icmptype=<icmptype> --add-destination=<ipv>
                       Enable destination for ipv in icmptype
  --icmptype=<icmptype> --remove-destination=<ipv>
                       Disable destination for ipv in icmptype
  --icmptype=<icmptype> --query-destination=<ipv>
                       Return whether destination ipv is enabled in icmptype
  --icmptype=<icmptype> --get-destinations
                       List destinations in icmptype

Service Options
  --new-service=<service>
                       Add a new service
  --delete-service=<service>
                       Delete an existing service
  --info-service=<service>
                       Print information about a service
  --service=<service> --set-description=<description>
                       Set new description to service
  --service=<service> --get-description
                       Print description for service
  --service=<service> --set-short=<description>
                       Set new short description to service
  --service=<service> --get-short
                       Print short description for service
  --service=<service> --add-port=<portid>[-<portid>]/<protocol>
                       Add a new port to service
  --service=<service> --remove-port=<portid>[-<portid>]/<protocol>
                       Remove an entry from service
  --service=<service> --query-port=<portid>[-<portid>]/<protocol>
                       Return whether the port has been added for service
  --service=<service> --get-ports
                       List ports of service
  --service=<service> --add-protocol=<protocol>
                       Add a new protocol to service
  --service=<service> --remove-protocol=<protocol>
                       Remove an entry from service
  --service=<service> --query-protocol=<protocol>
                       Return whether the protocol has been added for service
  --service=<service> --get-protocols
                       List protocols of service
  --service=<service> --add-module=<module>
                       Add a new module to service
  --service=<service> --remove-module=<module>
                       Remove an entry from service
  --service=<service> --query-module=<module>
                       Return whether the module has been added for service
  --service=<service> --get-modules
                       List modules of service
  --service=<service> --add-destination=<ipv>:<address>[/<mask>]
                       Set destination for ipv to address in service
  --service=<service> --remove-destination=<ipv>
                       Disable destination for ipv i service
  --service=<service> --query-destination=<ipv>:<address>[/<mask>]
                       Return whether destination ipv is set for service
  --service=<service> --get-destinations
                       List destinations in service

Options to Adapt and Query Zones
  --list-all           List everything added for or enabled in a zone [Z]
  --list-services      List services added for a zone [Z]
  --add-service=<service>
                       Add a service for a zone [Z]
  --remove-service-from-zone=<service>
                       Remove a service from a zone [Z]
  --query-service=<service>
                       Return whether service has been added for a zone [Z]
  --list-ports         List ports added for a zone [Z]
  --add-port=<portid>[-<portid>]/<protocol>
                       Add the port for a zone [Z]
  --remove-port=<portid>[-<portid>]/<protocol>
                       Remove the port from a zone [Z]
  --query-port=<portid>[-<portid>]/<protocol>
                       Return whether the port has been added for zone [Z]
  --list-protocols     List protocols added for a zone [Z]
  --add-protocol=<protocol>
                       Add the protocol for a zone [Z]
  --remove-protocol=<protocol>
                       Remove the protocol from a zone [Z]
  --query-protocol=<protocol>
                       Return whether the protocol has been added for zone [Z]
  --list-icmp-blocks   List Internet ICMP type blocks added for a zone [Z]
  --add-icmp-block=<icmptype>
                       Add an ICMP block for a zone [Z]
  --remove-icmp-block=<icmptype>
                       Remove the ICMP block from a zone [Z]
  --query-icmp-block=<icmptype>
                       Return whether an ICMP block has been added for a zone
                       [Z]
  --list-forward-ports List IPv4 forward ports added for a zone [Z]
  --add-forward-port=port=<portid>[-<portid>]:proto=<protocol>[:toport=<portid>[-<portid>]][:toaddr=<address>[/<mask>]]
                       Add the IPv4 forward port for a zone [Z]
  --remove-forward-port=port=<portid>[-<portid>]:proto=<protocol>[:toport=<portid>[-<portid>]][:toaddr=<address>[/<mask>]]
                       Remove the IPv4 forward port from a zone [Z]


  --query-forward-port=port=<portid>[-<portid>]:proto=<protocol>[:toport=<portid>[-<portid>]][:toaddr=<address>[/<mask>]]
                       Return whether the IPv4 forward port has been added for
                       a zone [Z]
  --add-masquerade     Enable IPv4 masquerade for a zone [Z]
  --remove-masquerade  Disable IPv4 masquerade for a zone [Z]
  --query-masquerade   Return whether IPv4 masquerading has been enabled for a
                       zone [Z]
  --list-rich-rules    List rich language rules added for a zone [Z]
  --add-rich-rule=<rule>
                       Add rich language rule 'rule' for a zone [Z]
  --remove-rich-rule=<rule>
                       Remove rich language rule 'rule' from a zone [Z]
  --query-rich-rule=<rule>
                       Return whether a rich language rule 'rule' has been
                       added for a zone [Z]

Options to Handle Bindings of Interfaces
  --list-interfaces    List interfaces that are bound to a zone [Z]
  --add-interface=<interface>
                       Bind the <interface> to a zone [Z]
  --change-interface=<interface>
                       Change zone the <interface> is bound to [Z]
  --query-interface=<interface>
                       Query whether <interface> is bound to a zone [Z]
  --remove-interface=<interface>
                       Remove binding of <interface> from a zone [Z]

Options to Handle Bindings of Sources
  --list-sources       List sources that are bound to a zone [Z]
  --add-source=<source>[/<mask>]
                       Bind <source>[/<mask>] to a zone [Z]
  --change-source=<source>[/<mask>]
                       Change zone the <source>[/<mask>] is bound to [Z]
  --query-source=<source>[/<mask>]
                       Query whether <source>[/<mask>] is bound to a zone
                       [Z]
  --remove-source=<source>[/<mask>]
                       Remove binding of <source>[/<mask>] from a zone [Z]

Direct Options
  --direct             First option for all direct options
  --get-all-chains
                       Get all chains
  --get-chains {ipv4|ipv6|eb} <table>
                       Get all chains added to the table
  --add-chain {ipv4|ipv6|eb} <table> <chain>
                       Add a new chain to the table
  --remove-chain {ipv4|ipv6|eb} <table> <chain>
                       Remove the chain from the table
  --query-chain {ipv4|ipv6|eb} <table> <chain>
                       Return whether the chain has been added to the table
  --get-all-rules
                       Get all rules
  --get-rules {ipv4|ipv6|eb} <table> <chain>
                       Get all rules added to chain in table
  --add-rule {ipv4|ipv6|eb} <table> <chain> <priority> <arg>...
                       Add rule to chain in table
  --remove-rule {ipv4|ipv6|eb} <table> <chain> <priority> <arg>...
                       Remove rule with priority from chain in table
  --remove-rules {ipv4|ipv6|eb} <table> <chain>
                       Remove rules from chain in table
  --query-rule {ipv4|ipv6|eb} <table> <chain> <priority> <arg>...
                       Return whether a rule with priority has been added to
                       chain in table
  --get-all-passthroughs
                       Get all passthrough rules
  --get-passthroughs {ipv4|ipv6|eb} <arg>...
                       Get passthrough rules
  --add-passthrough {ipv4|ipv6|eb} <arg>...
                       Add a new passthrough rule
  --remove-passthrough {ipv4|ipv6|eb} <arg>...
                       Remove a passthrough rule
  --query-passthrough {ipv4|ipv6|eb} <arg>...
                       Return whether the passthrough rule has been added

Lockdown Options
  --lockdown-on        Enable lockdown.
  --lockdown-off       Disable lockdown.
  --query-lockdown     Query whether lockdown is enabled

Lockdown Whitelist Options
  --list-lockdown-whitelist-commands
                       List all command lines that are on the whitelist
  --add-lockdown-whitelist-command=<command>
                       Add the command to the whitelist
  --remove-lockdown-whitelist-command=<command>
                       Remove the command from the whitelist
  --query-lockdown-whitelist-command=<command>
                       Query whether the command is on the whitelist
  --list-lockdown-whitelist-contexts
                       List all contexts that are on the whitelist
  --add-lockdown-whitelist-context=<context>
                       Add the context context to the whitelist
  --remove-lockdown-whitelist-context=<context>
                       Remove the context from the whitelist
  --query-lockdown-whitelist-context=<context>
                       Query whether the context is on the whitelist
  --list-lockdown-whitelist-uids
                       List all user ids that are on the whitelist
  --add-lockdown-whitelist-uid=<uid>
                       Add the user id uid to the whitelist
  --remove-lockdown-whitelist-uid=<uid>
                       Remove the user id uid from the whitelist
  --query-lockdown-whitelist-uid=<uid>
                       Query whether the user id uid is on the whitelist
  --list-lockdown-whitelist-users
                       List all user names that are on the whitelist
  --add-lockdown-whitelist-user=<user>
                       Add the user name user to the whitelist
  --remove-lockdown-whitelist-user=<user>
                       Remove the user name user from the whitelist
  --query-lockdown-whitelist-user=<user>
                       Query whether the user name user is on the whitelist

Polkit Options
  --policy-server      Change Polkit actions to 'server'  (more restricted)
  --policy-desktop     Change Polkit actions to 'desktop' (less restricted)

""")

def __print(msg=None):
    if msg:
        print(msg)

def __print_and_exit(msg=None, exit_code=0):
    FAIL = '\033[91m'
    OK =   '\033[92m'
    END =  '\033[00m'
    if exit_code != 0:
        __print(msg)
        #__print(FAIL + msg + END)
    else:
        __print(msg)
        #__print(OK + msg + END)
    sys.exit(exit_code)

def __fail(msg=None):
    __print_and_exit(msg, 2)

def __parse_port(value):
    try:
        (port, proto) = value.split("/")
    except Exception as e:
        __fail("bad port (most likely missing protocol), correct syntax is portid[-portid]/protocol")
    return (port, proto)

def __parse_port_lokkit(value):
    try:
        (port, proto) = value.split(":")
    except Exception as e:
        __fail("bad port (most likely missing protocol), correct syntax is portid[-portid]:protocol")
    return (port, proto)

def __parse_forward_port(value):
    port = None
    protocol = None
    toport = None
    toaddr = None
    args = value.split(":")
    for arg in args:
        try:
            (opt,val) = arg.split("=")
            if opt == "port":
                port = val
            elif opt == "proto":
                protocol = val
            elif opt == "toport":
                toport = val
            elif opt == "toaddr":
                toaddr = val
        except:
            __fail("invalid forward port arg '%s'" % (arg))
    if not port:
        __fail("missing port")
    if not protocol:
        __fail("missing protocol")
    if not (toport or toaddr):
        __fail("missing destination")
    return (port, protocol, toport, toaddr)

def __parse_ipset_option(value):
    args = value.split("=")
    if len(args) == 1:
        return (args[0], "")
    elif len(args) == 2:
        return args
    else:
        __fail("invalid ipset option '%s'" % (value))

def _check_destination_ipv(value):
    if value not in [ "ipv4", "ipv6" ]:
        __fail("invalid argument: %s (choose from 'ipv4', 'ipv6')" % value)
    return value

def __parse_service_destination(value):
    try:
        (ipv, destination) = value.split(":", 1)
    except Exception as e:
        __fail("bad destination, correct syntax is ipv:address[/mask]")
    return (_check_destination_ipv(ipv), destination)

def _check_ipv(value):
    if value != "ipv4" and value != "ipv6" and value != "eb":
        __fail("invalid argument: %s (choose from 'ipv4', 'ipv6', 'eb')" % value)
    return value

def __print_zone_info(zone, settings, verbose = False):
    interfaces = settings.getInterfaces()
    sources = settings.getSources()
    services = settings.getServices()
    ports = settings.getPorts()
    protocols = settings.getProtocols()
    masquerade = settings.getMasquerade()
    forward_ports = settings.getForwardPorts()
    icmp_blocks = settings.getIcmpBlocks()
    rules = settings.getRichRules()
    description = settings.getDescription()
    short_description = settings.getShort()
    __print(zone)
    if verbose:
        __print("  summary: " + short_description)
        __print("  description: " + description)
    __print("  interfaces: " + " ".join(interfaces))
    __print("  sources: " + " ".join(sources))
    __print("  services: " + " ".join(services))
    __print("  ports: " + " ".join(["%s/%s" % (port[0], port[1]) for port in ports]))
    __print("  protocols: " + " ".join(protocols))
    __print("  masquerade: %s" % ("yes" if masquerade else "no"))
    __print("  forward-ports: " + "\n\t".join(["port=%s:proto=%s:toport=%s:toaddr=%s" % (port, protocol, toport, toaddr) for (port, protocol, toport, toaddr) in forward_ports]))
    __print("  icmp-blocks: " + " ".join(icmp_blocks))
    __print("  rich rules: \n\t" + "\n\t".join(rules))

def __print_service_info(service, settings):
    ports = settings.getPorts()
    protocols = settings.getProtocols()
    modules = settings.getModules()
    description = settings.getDescription()
    destinations = settings.getDestinations()
    short_description = settings.getShort()
    __print(service)
    __print("  summary: " + short_description)
    __print("  ports: " + " ".join(["%s/%s" % (port[0], port[1]) for port in ports]))
    __print("  protocols: " + " ".join(protocols))
    __print("  modules: " + " ".join(modules))
    __print("  destination: " + " ".join(["%s:%s" % (k,v)
                                          for k,v in destinations.items()]))
    __print("  description: " + description)

def __print_icmptype_info(icmptype, settings):
    destinations = settings.getDestinations()
    description = settings.getDescription()
    short_description = settings.getShort()
    if len(destinations) == 0:
        destinations = [ "ipv4", "ipv6" ]
    __print(icmptype)
    __print("  summary: " + short_description)
    __print("  destination: " + " ".join(destinations))
    __print("  description: " + description)

def __print_ipset_info(ipset, settings):
    ipset_type = settings.getType()
    options = settings.getOptions()
    entries = settings.getEntries()
    description = settings.getDescription()
    short_description = settings.getShort()
    __print(ipset)
    __print("  summary: " + short_description)
    __print("  type: " + ipset_type)
    __print("  options: " + " ".join(["%s=%s" % (k,v) if v else k
                                      for k,v in options.items()]))
    __print("  entries: " + " ".join(entries))
    __print("  description: " + description)

def __print_query_result(value):
    if value:
        __print_and_exit("yes")
    else:
        __print_and_exit("no", 1)

def __pk_symlink(product='server'):
    _PK_DIR='/usr/share/polkit-1/actions/'
    _PK_NAME='org.fedoraproject.FirewallD1.'
    os.chdir(_PK_DIR)
    if os.path.isfile(_PK_NAME+product+'.policy'):
        if os.path.isfile(_PK_NAME+'policy'):
            os.remove(_PK_NAME+'policy')
        os.symlink(_PK_NAME+product+'.policy', _PK_NAME+'policy')
        __print_and_exit('symlink '+_PK_DIR+_PK_NAME+product+'.policy -> '+_PK_NAME+'policy')
    else:
        __fail('no such file '+_PK_DIR+_PK_NAME+product+'.policy')

# system-config-firewall: fw_sysconfig
CONF = '/etc/conf.d/system-config-firewall'
def read_sysconfig_args():
    filename = None
    if os.path.exists(CONF) and os.path.isfile(CONF):
        filename = CONF
    try:
        f = open(filename, 'r')
    except:
        return None
    argv = [ ]
    for line in f:
        if not line:
            break
        line = line.strip()
        if len(line) < 1 or line[0] == '#':
            continue
        argv.append(line)
    f.close()
    return argv

##############################################################################

parser = argparse.ArgumentParser(usage="see firewall-offline-cmd man page",
                                 add_help=False)

parser_group_lokkit = parser.add_argument_group()
parser_group_lokkit.add_argument("--enabled", action="store_true")
parser_group_lokkit.add_argument("--disabled", action="store_true")
parser_group_lokkit.add_argument("--addmodule", metavar="<module>", action='append')
parser_group_lokkit.add_argument("--removemodule", metavar="<module>", action='append')
parser_group_lokkit.add_argument("--service", "-s", metavar="<service>", action='append')
parser_group_lokkit.add_argument("--remove-service", metavar="<service>", action='append')
parser_group_lokkit.add_argument("--port", "-p", metavar="<port>", action='append')
parser_group_lokkit.add_argument("--trust", "-t", metavar="<iface>", action='append')
parser_group_lokkit.add_argument("--masq", "-m", metavar="<iface>", action='append')
parser_group_lokkit.add_argument("--custom-rules", metavar="<filename>", action='append')
parser_group_lokkit.add_argument("--forward-port", metavar="<port>", action='append')
parser_group_lokkit.add_argument("--block-icmp", metavar="<icmptype>", action='append')

parser_group_standalone = parser.add_mutually_exclusive_group()
parser_group_standalone.add_argument("-h", "--help",
                                     action="store_true")
parser_group_standalone.add_argument("-V", "--version", action="store_true")
parser_group_standalone.add_argument("--policy-server", action="store_true")
parser_group_standalone.add_argument("--policy-desktop", action="store_true")
parser_group_standalone.add_argument("--lockdown-on", action="store_true")
parser_group_standalone.add_argument("--lockdown-off", action="store_true")
parser_group_standalone.add_argument("--query-lockdown", action="store_true")

parser_group_standalone.add_argument("--get-default-zone", action="store_true")
parser_group_standalone.add_argument("--set-default-zone", metavar="<zone>")
parser_group_standalone.add_argument("--get-zones", action="store_true")
parser_group_standalone.add_argument("--get-services", action="store_true")
parser_group_standalone.add_argument("--get-icmptypes", action="store_true")
parser_group_standalone.add_argument("--get-zone-of-interface", metavar="<iface>")
parser_group_standalone.add_argument("--get-zone-of-source", metavar="<source>")
parser_group_standalone.add_argument("--list-all-zones", action="store_true")

parser_group_standalone.add_argument("--info-zone", metavar="<zone>")
parser_group_standalone.add_argument("--info-service", metavar="<service>")
parser_group_standalone.add_argument("--info-icmptype", metavar="<icmptype>")
parser_group_standalone.add_argument("--info-ipset", metavar="<ipset>")

parser_group_config = parser.add_mutually_exclusive_group()
parser_group_config.add_argument("--new-icmptype", metavar="<icmptype>")
parser_group_config.add_argument("--delete-icmptype", metavar="<icmptype>")
parser_group_config.add_argument("--new-service", metavar="<service>")
parser_group_config.add_argument("--delete-service", metavar="<service>")
parser_group_config.add_argument("--new-zone", metavar="<zone>")
parser_group_config.add_argument("--delete-zone", metavar="<zone>")
parser_group_config.add_argument("--new-ipset", metavar="<ipset>")
parser_group_config.add_argument("--delete-ipset", metavar="<ipset>")

parser_group_lockdown_whitelist = parser.add_mutually_exclusive_group()
parser_group_lockdown_whitelist.add_argument("--list-lockdown-whitelist-commands", action="store_true")
parser_group_lockdown_whitelist.add_argument("--add-lockdown-whitelist-command", metavar="<command>")
parser_group_lockdown_whitelist.add_argument("--remove-lockdown-whitelist-command", metavar="<command>")
parser_group_lockdown_whitelist.add_argument("--query-lockdown-whitelist-command", metavar="<command>")

parser_group_lockdown_whitelist.add_argument("--list-lockdown-whitelist-contexts", action="store_true")
parser_group_lockdown_whitelist.add_argument("--add-lockdown-whitelist-context", metavar="<context>")
parser_group_lockdown_whitelist.add_argument("--remove-lockdown-whitelist-context", metavar="<context>")
parser_group_lockdown_whitelist.add_argument("--query-lockdown-whitelist-context", metavar="<context>")

parser_group_lockdown_whitelist.add_argument("--list-lockdown-whitelist-uids", action="store_true")
parser_group_lockdown_whitelist.add_argument("--add-lockdown-whitelist-uid", metavar="<uid>", type=int)
parser_group_lockdown_whitelist.add_argument("--remove-lockdown-whitelist-uid", metavar="<uid>", type=int)
parser_group_lockdown_whitelist.add_argument("--query-lockdown-whitelist-uid", metavar="<uid>", type=int)

parser_group_lockdown_whitelist.add_argument("--list-lockdown-whitelist-users", action="store_true")
parser_group_lockdown_whitelist.add_argument("--add-lockdown-whitelist-user", metavar="<user>")
parser_group_lockdown_whitelist.add_argument("--remove-lockdown-whitelist-user", metavar="<user>")
parser_group_lockdown_whitelist.add_argument("--query-lockdown-whitelist-user", metavar="<user>")

parser.add_argument("--zone", default="", metavar="<zone>")

parser_group_zone = parser.add_mutually_exclusive_group()
parser_group_zone.add_argument("--add-interface", metavar="<iface>")
parser_group_zone.add_argument("--remove-interface", metavar="<iface>")
parser_group_zone.add_argument("--query-interface", metavar="<iface>")
parser_group_zone.add_argument("--change-interface", "--change-zone", metavar="<iface>")
parser_group_zone.add_argument("--list-interfaces", action="store_true")
parser_group_zone.add_argument("--add-source", metavar="<source>")
parser_group_zone.add_argument("--remove-source", metavar="<source>")
parser_group_zone.add_argument("--query-source", metavar="<source>")
parser_group_zone.add_argument("--change-source", metavar="<source>")
parser_group_zone.add_argument("--list-sources", action="store_true")
parser_group_zone.add_argument("--add-rich-rule", metavar="<rule>", action='append')
parser_group_zone.add_argument("--remove-rich-rule", metavar="<rule>", action='append')
parser_group_zone.add_argument("--query-rich-rule", metavar="<rule>")
parser_group_zone.add_argument("--add-service", metavar="<service>", action='append')
parser_group_zone.add_argument("--remove-service-from-zone", metavar="<zone>", action='append')
parser_group_zone.add_argument("--query-service", metavar="<zone>")
parser_group_zone.add_argument("--add-port", metavar="<port>", action='append')
parser_group_zone.add_argument("--remove-port", metavar="<port>", action='append')
parser_group_zone.add_argument("--query-port", metavar="<port>")
parser_group_zone.add_argument("--add-protocol", metavar="<protocol>", action='append')
parser_group_zone.add_argument("--remove-protocol", metavar="<protocol>", action='append')
parser_group_zone.add_argument("--query-protocol", metavar="<protocol>")
parser_group_zone.add_argument("--add-masquerade", action="store_true")
parser_group_zone.add_argument("--remove-masquerade", action="store_true")
parser_group_zone.add_argument("--query-masquerade", action="store_true")
parser_group_zone.add_argument("--add-icmp-block", metavar="<icmptype>", action='append')
parser_group_zone.add_argument("--remove-icmp-block", metavar="<icmptype>", action='append')
parser_group_zone.add_argument("--query-icmp-block", metavar="<icmptype>")
parser_group_zone.add_argument("--add-forward-port", metavar="<port>", action='append')
parser_group_zone.add_argument("--remove-forward-port", metavar="<port>", action='append')
parser_group_zone.add_argument("--query-forward-port", metavar="<port>")
parser_group_zone.add_argument("--list-rich-rules", action="store_true")
parser_group_zone.add_argument("--list-services", action="store_true")
parser_group_zone.add_argument("--list-ports", action="store_true")
parser_group_zone.add_argument("--list-protocols", action="store_true")
parser_group_zone.add_argument("--list-icmp-blocks", action="store_true")
parser_group_zone.add_argument("--list-forward-ports", action="store_true")
parser_group_zone.add_argument("--list-all", action="store_true")
parser_group_zone.add_argument("--get-target", action="store_true")
parser_group_zone.add_argument("--set-target", metavar="<target>")

parser.add_argument("--option", metavar="<key>[=<value>]", action='append')
parser.add_argument("--type", metavar="<ipsettype>")
parser.add_argument("--ipset", metavar="<ipset>")

parser_ipset = parser.add_mutually_exclusive_group()
#parser_ipset.add_argument("--add-option", metavar="<key>[=<value>]")
#parser_ipset.add_argument("--remove-option", metavar="<key>[=<value>]")
#parser_ipset.add_argument("--query-option", metavar="<key>[=<value>]")
#parser_ipset.add_argument("--get-options", action="store_true")

parser_ipset.add_argument("--get-ipsets", action="store_true")
parser_ipset.add_argument("--add-entry", metavar="<entry>")
parser_ipset.add_argument("--remove-entry", metavar="<entry>")
parser_ipset.add_argument("--query-entry", metavar="<entry>")
parser_ipset.add_argument("--get-entries", action="store_true")

parser.add_argument("--icmptype", metavar="<icmptype>")

parser_icmptype = parser.add_mutually_exclusive_group()
parser_icmptype.add_argument("--add-destination", metavar="<ipv>", action='append')
parser_icmptype.add_argument("--remove-destination", metavar="<ipv>", action='append')
parser_icmptype.add_argument("--query-destination", metavar="<ipv>")
parser_icmptype.add_argument("--get-destinations", action="store_true")

#parser.add_argument("--service", metavar="<service>")

parser_service = parser.add_mutually_exclusive_group()
parser_service.add_argument("--get-ports", action="store_true")
parser_service.add_argument("--get-protocols", action="store_true")

parser_service.add_argument("--add-module", metavar="<module>", action='append')
parser_service.add_argument("--remove-module", metavar="<module>", action='append')
parser_service.add_argument("--query-module", metavar="<module>")
parser_service.add_argument("--get-modules", action="store_true")

parser_service.add_argument("--set-destination", metavar="<destination>")
parser_service.add_argument("--get-destination", action="store_true")

parser_service.add_argument("--set-description", metavar="<description>")
parser_service.add_argument("--get-description", action="store_true")

parser_service.add_argument("--set-short", metavar="<description>")
parser_service.add_argument("--get-short", action="store_true")

parser.add_argument("--direct", action="store_true")

parser_direct = parser.add_mutually_exclusive_group()
parser_direct.add_argument("--add-passthrough", nargs=argparse.REMAINDER,
                    metavar=("{ ipv4 | ipv6 | eb }", "<args>"))
parser_direct.add_argument("--remove-passthrough", nargs=argparse.REMAINDER,
                    metavar=("{ ipv4 | ipv6 | eb }", "<args>"))
parser_direct.add_argument("--query-passthrough", nargs=argparse.REMAINDER,
                    metavar=("{ ipv4 | ipv6 | eb }", "<args>"))
parser_direct.add_argument("--get-passthroughs", nargs=1,
                    metavar=("{ ipv4 | ipv6 | eb }"))
parser_direct.add_argument("--get-all-passthroughs", action="store_true")
parser_direct.add_argument("--add-chain", nargs=3,
                    metavar=("{ ipv4 | ipv6 | eb }", "<table>", "<chain>"))
parser_direct.add_argument("--remove-chain", nargs=3,
                        metavar=("{ ipv4 | ipv6 | eb }", "<table>", "<chain>"))
parser_direct.add_argument("--query-chain", nargs=3,
                        metavar=("{ ipv4 | ipv6 | eb }", "<table>", "<chain>"))
parser_direct.add_argument("--get-all-chains", action="store_true")
parser_direct.add_argument("--get-chains", nargs=2,
                        metavar=("{ ipv4 | ipv6 | eb }", "<table>"))
parser_direct.add_argument("--add-rule", nargs=argparse.REMAINDER,
                        metavar=("{ ipv4 | ipv6 | eb }", "<table> <chain> <priority> <args>"))
parser_direct.add_argument("--remove-rule", nargs=argparse.REMAINDER,
                        metavar=("{ ipv4 | ipv6 | eb }", "<table> <chain> <priority> <args>"))
parser_direct.add_argument("--remove-rules", nargs=3,
                        metavar=("{ ipv4 | ipv6 | eb }", "<table> <chain>"))
parser_direct.add_argument("--query-rule", nargs=argparse.REMAINDER,
                        metavar=("{ ipv4 | ipv6 | eb }", "<table> <chain> <priority> <args>"))
parser_direct.add_argument("--get-rules", nargs=3,
                        metavar=("{ ipv4 | ipv6 | eb }", "<table>", "<chain>"))
parser_direct.add_argument("--get-all-rules", action="store_true")

##############################################################################

if len(sys.argv) > 1:
    i = -1
    args = sys.argv[1:]
    if '--add-passthrough' in args:
        i = args.index('--add-passthrough') + 1
    elif '--remove-passthrough' in args:
        i = args.index('--remove-passthrough') + 1
    elif '--query-passthrough' in args:
        i = args.index('--query-passthrough') + 1
    elif '--add-rule' in args:
        i = args.index('--add-rule') + 4
    elif '--remove-rule' in args:
        i = args.index('--remove-rule') + 4
    elif '--query-rule' in args:
        i = args.index('--query-rule') + 4
    # join <args> into one argument to prevent parser from parsing each iptables
    # option, because they can conflict with firewall-cmd options
    # # e.g. --delete (iptables) and --delete-* (firewall-cmd)
    if (i > -1) and (i < len(args) - 1):
        aux_args = args[:]
        args = aux_args[:i+1] # all but not <args>
        args.append(joinArgs(aux_args[i+1:])) # add <args> as one arg
    a = parser.parse_args(args)
else:
    # migrate configuration from /etc/conf.d/system-config-firewall
    args = read_sysconfig_args()
    if args:
        a = parser.parse_args(args)
    else:
        __fail("Opening of '%s' failed, exiting." % CONF)

options_lokkit = a.enabled or a.disabled or a.addmodule or a.removemodule or \
                 a.trust or a.masq or a.custom_rules or \
                 a.service or a.remove_service or a.port or \
                 a.trust or a.masq or a.forward_port or a.block_icmp

options_desc_xml_file = a.set_description or a.get_description or \
                        a.set_short or a.get_short

options_standalone = a.help or a.version or \
    a.policy_server or a.policy_desktop or \
    a.lockdown_on or a.lockdown_off or a.query_lockdown or \
    a.get_default_zone or a.set_default_zone

options_lockdown_whitelist = \
    a.list_lockdown_whitelist_commands or a.add_lockdown_whitelist_command or \
    a.remove_lockdown_whitelist_command or \
    a.query_lockdown_whitelist_command or \
    a.list_lockdown_whitelist_contexts or a.add_lockdown_whitelist_context or \
    a.remove_lockdown_whitelist_context or \
    a.query_lockdown_whitelist_context or \
    a.list_lockdown_whitelist_uids or a.add_lockdown_whitelist_uid is not None or \
    a.remove_lockdown_whitelist_uid is not None or \
    a.query_lockdown_whitelist_uid is not None or \
    a.list_lockdown_whitelist_users or a.add_lockdown_whitelist_user or \
    a.remove_lockdown_whitelist_user or \
    a.query_lockdown_whitelist_user

options_config = a.get_zones or a.get_services or a.get_icmptypes or \
                 options_lockdown_whitelist or a.list_all_zones or \
                 a.get_zone_of_interface or a.get_zone_of_source or \
                 a.info_zone or a.info_icmptype or a.info_service or \
                 a.info_ipset or a.get_ipsets

options_zone_action_action = \
    a.add_service or a.remove_service_from_zone or a.query_service or \
    a.add_port or a.remove_port or a.query_port or \
    a.add_protocol or a.remove_protocol or a.query_protocol or \
    a.add_icmp_block or a.remove_icmp_block or a.query_icmp_block or \
    a.add_forward_port or a.remove_forward_port or a.query_forward_port

options_zone_interfaces_sources = \
    a.list_interfaces or a.change_interface or \
    a.add_interface or a.remove_interface or a.query_interface or \
    a.list_sources or a.change_source or \
    a.add_source or a.remove_source or a.query_source

options_zone_adapt_query = \
    a.add_rich_rule or a.remove_rich_rule or a.query_rich_rule or \
    a.add_masquerade or a.remove_masquerade or a.query_masquerade or \
    a.list_services or a.list_ports or a.list_protocols or \
    a.list_icmp_blocks or a.list_forward_ports or a.list_rich_rules or \
    a.list_all or a.get_target or a.set_target

options_zone_ops = options_zone_interfaces_sources or \
               options_zone_action_action or options_zone_adapt_query

options_zone = a.zone or options_zone_ops or options_desc_xml_file

options_ipset = a.add_entry or a.remove_entry or a.query_entry or \
                a.get_entries or options_desc_xml_file

options_icmptype = a.add_destination or a.remove_destination or \
                   a.query_destination or a.get_destinations or \
                   options_desc_xml_file

options_service = a.add_port or a.remove_port or a.query_port or \
                  a.get_ports or \
                  a.add_protocol or a.remove_protocol or a.query_protocol or \
                  a.get_protocols or \
                  a.add_module or a.remove_module or a.query_module or \
                  a.get_modules or \
                  a.set_destination or a.remove_destination or \
                  a.query_destination or a.get_destinations or \
                  options_desc_xml_file

options_permanent = options_config or options_zone or \
                    a.new_icmptype or a.delete_icmptype or \
                    a.new_service or a.delete_service or \
                    a.new_zone or a.delete_zone or \
                    a.new_ipset or a.delete_ipset or \
                    a.ipset or options_ipset or \
                    (a.icmptype and options_icmptype) or \
                    (a.service and options_service)

options_direct = \
           a.add_chain or a.remove_chain or a.query_chain or \
           a.get_chains or a.get_all_chains or \
           a.add_rule or a.remove_rule or a.remove_rules or a.query_rule or \
           a.get_rules or a.get_all_rules or \
           a.add_passthrough or a.remove_passthrough or a.query_passthrough or \
           a.get_passthroughs or a.get_all_passthroughs

# these are supposed to only write out some output
options_list_get = a.help or a.version or a.list_all or a.list_all_zones or \
 a.list_lockdown_whitelist_commands or a.list_lockdown_whitelist_contexts or \
 a.list_lockdown_whitelist_uids or a.list_lockdown_whitelist_users or \
 a.list_services or a.list_ports or a.list_protocols or a.list_icmp_blocks or \
 a.list_forward_ports or a.list_rich_rules or a.list_interfaces or \
 a.list_sources or \
 a.get_default_zone or a.get_zone_of_interface or \
 a.get_zone_of_source or a.get_zones or a.get_services or a.get_icmptypes or \
 a.get_target or a.set_target or \
 a.info_zone or a.info_icmptype or a.info_service or \
 a.info_ipset or a.get_ipsets or a.get_entries or \
 a.get_destinations or a.get_description

###############################################################################

# Check various impossible combinations of options

if not (options_lokkit or options_standalone or options_ipset or \
        options_icmptype or options_service or \
        options_permanent or options_direct or options_desc_xml_file):
    __fail(parser.format_usage() + "No option specified.")

if options_lokkit and (options_standalone or \
                       options_permanent or options_direct) and \
                       not (options_service and a.service):
    __fail(parser.format_usage() +
           "Can't use lokkit options with other options.")

if options_standalone and (options_permanent or \
                           options_direct or options_ipset):
    __fail(parser.format_usage() +
           "Can't use stand-alone options with other options.")

if (options_ipset and not options_desc_xml_file and not a.ipset):
    __fail(parser.format_usage() + "No ipset specified.")

if (options_icmptype and not a.icmptype) and \
   not (options_service and a.service) and not options_desc_xml_file:
    __fail(parser.format_usage() + "No icmptype specified.")

if (options_service and a.service and len(a.service) > 0):
    if len(a.service) > 1:
        __fail(parser.format_usage() + "More than one service specified.")
    # use the first entry in the array only
    a.service = a.service[0]

if options_direct and options_zone:
    __fail(parser.format_usage() +
           "Can't use 'direct' options with other options.")

if (a.direct and not options_direct) or (options_direct and not a.direct):
    __fail(parser.format_usage() +
           "Wrong usage of 'direct' options.")

if options_config and options_zone:
    __fail(parser.format_usage() +
           "Wrong usage of --get-zones | --get-services | --get-icmptypes.")

if a.help:
    __usage()
    sys.exit(0)

zone = a.zone
fw = Firewall_test()
fw.start()

try:
    if a.version:
        __print_and_exit(config.VERSION)

    if a.policy_server:
        __pk_symlink('server')
    if a.policy_desktop:
        __pk_symlink('desktop')

    # Lokkit Compatibility Options
    if options_lokkit and not (options_service and a.service):
        trusted_zone = "trusted"
        default_zone = fw.get_default_zone()
        fw_zone = fw.config.get_zone(default_zone)
        fw_settings = FirewallClientZoneSettings(
            list(fw.config.get_zone_config(fw_zone)))

        if a.enabled:
            # Enable firewall (default)
            os.system("systemctl enable firewalld.service")
        if a.disabled:
            # Disable firewall
            os.system("systemctl disable firewalld.service")
        if a.addmodule:
            for m in a.addmodule:
                __print("Ignoring addmodule '%s'" % m)
        if a.removemodule:
            for m in a.removemodule:
                __print("Ignoring removemodule '%s'" % m)
        if a.custom_rules:
            for c in a.custom_rules:
                __print("Ignoring custom-rule '%s'" % c)
        if a.service:
            for s in a.service:
                __print("Adding service '%s' to default zone." % s)
                if not fw_settings.queryService(s):
                    fw_settings.addService(s)
                else:
                    __print("ALREADY_ENABLED: %s" % s)
        if a.remove_service:
            for s in a.remove_service:
                __print("Removing service '%s' from default zone." % s)
                if fw_settings.queryService(s):
                    fw_settings.removeService(s)
                else:
                    __print("NOT_ENABLED: %s" % s)
        if a.port:
            for port_proto in a.port:
                (port, proto) = __parse_port_lokkit(port_proto)
                __print("Adding port '%s/%s' to default zone." % (port, proto))
                if not fw_settings.queryPort(port, proto):
                    fw_settings.addPort(port, proto)
                else:
                    __print("ALREADY_ENABLED: %s" % port_proto)
        if a.trust:
            if default_zone != trusted_zone:
                fw_trusted = fw.config.get_zone("trusted")
                fw_trusted_settings = FirewallClientZoneSettings(
                                 list(fw.config.get_zone_config(fw_trusted)))
                # Bind an interface to the trusted zone
                for i in a.trust:
                    __print("Interface '%s' will be bound to zone '%s'." % \
                            (i, trusted_zone))
                    if not fw_trusted_settings.queryInterface(i):
                        fw_trusted_settings.addInterface(i)
                    else:
                        __print("ALREADY_ENABLED: %s" % i)
                fw.config.set_zone_config(fw_trusted, fw_trusted_settings.settings)
            else:
                for i in a.trust:
                    __print("Interface '%s' will be bound to zone '%s'." % \
                            (i, trusted_zone))
                    if not fw_settings.queryInterface(i):
                        fw_settings.addInterface(i)
                    else:
                        __print("ALREADY_ENABLED: %s" % i)
        if a.masq:
            # Enables masquerading in the default zone, interface argument is ignored
            __print("Enabling masquerade for the default zone.")
            fw_settings.setMasquerade(True)
        if a.forward_port:
            for fp in a.forward_port:
                (port, protocol, toport, toaddr) = __parse_forward_port(fp)
                __print("Adding forward port %s:%s:%s:%s to default zone." % \
                      (port, protocol, toport, toaddr))
                if not fw_settings.queryForwardPort(port, protocol, toport,
                                                    toaddr):
                    fw_settings.addForwardPort(port, protocol, toport, toaddr)
                else:
                    __print("ALREADY_ENABLED: %s" % fp)
        if a.block_icmp:
            for ib in a.block_icmp:
                __print("Adding icmpblock '%s' to default zone." % ib)
                if not fw_settings.queryIcmpBlock(ib):
                    fw_settings.addIcmpBlock(ib)
                else:
                    __print("ALREADY_ENABLED: %s" % ib)

        fw.config.set_zone_config(fw_zone, fw_settings.settings)

    # options from firewall-cmd
    elif a.get_default_zone:
        __print_and_exit(fw.get_default_zone())
    elif a.set_default_zone:
        fw.set_default_zone(a.set_default_zone)

    # lockdown
    elif a.lockdown_on:
        fw.enable_lockdown()
    elif a.lockdown_off:
        fw.disable_lockdown()
    elif a.query_lockdown:
        __print_query_result(fw.policies.query_lockdown())

    # zones
    elif a.get_zones:
        zones = fw.config.get_zones()
        __print_and_exit(" ".join(zones))

    elif a.new_zone:
        fw.config.new_zone(a.new_zone, FirewallClientZoneSettings().settings)

    elif a.delete_zone:
        obj = fw.config.get_zone(a.delete_zone)
        fw.config.remove_zone(obj)

    elif a.info_zone:
        zone = fw.config.get_zone(a.info_zone)
        settings = FirewallClientZoneSettings(
            list(fw.config.get_zone_config(zone)))
        __print_zone_info(a.info_zone, settings, True)
        sys.exit(0)

    # services
    elif a.get_services:
        services = fw.config.get_services()
        __print_and_exit(" ".join(services))

    elif a.new_service:
        fw.config.new_service(a.new_service,
                              FirewallClientServiceSettings().settings)

    elif a.delete_service:
        obj = fw.config.get_service(a.delete_service)
        fw.config.remove_service(obj)

        # remove service from all zones
        zones = fw.config.get_zones()
        for zone in zones:
            _zone = fw.config.get_zone(zone)
            _settings = FirewallClientZoneSettings(
                list(fw.config.get_zone_config(_zone)))
            if _settings.queryService(a.delete_service):
                _settings.removeService(a.delete_service)
                fw.config.set_zone_config(_zone, _settings.settings)

    elif a.info_service:
        service = fw.config.get_service(a.info_service)
        settings = FirewallClientServiceSettings(
            list(fw.config.get_service_config(service)))
        __print_service_info(a.info_service, settings)
        sys.exit(0)

    # icmptypes
    elif a.get_icmptypes:
        icmptypes = fw.config.get_icmptypes()
        __print_and_exit(" ".join(icmptypes))

    elif a.new_icmptype:
        fw.config.new_icmptype(a.new_icmptype,
                               FirewallClientIcmpTypeSettings().settings)

    elif a.delete_icmptype:
        obj = fw.config.get_icmptype(a.delete_icmptype)
        fw.config.remove_icmptype(obj)

        # remove icmpyte from all zones
        zones = fw.config.get_zones()
        for zone in zones:
            _zone = fw.config.get_zone(zone)
            _settings = FirewallClientZoneSettings(
                list(fw.config.get_zone_config(_zone)))
            if _settings.queryIcmpBlock(a.delete_icmptype):
                _settings.removeIcmpBlock(a.delete_icmptype)
                fw.config.set_zone_config(_zone, _settings.settings)

    elif a.info_icmptype:
        icmptype = fw.config.get_icmptype(a.info_icmptype)
        settings = FirewallClientIcmpTypeSettings(
            list(fw.config.get_icmptype_config(icmptype)))

        __print_icmptype_info(a.info_icmptype, settings)
        sys.exit(0)

    elif a.icmptype and options_icmptype:
        icmptype = fw.config.get_icmptype(a.icmptype)
        settings = FirewallClientIcmpTypeSettings(
            list(fw.config.get_icmptype_config(icmptype)))

        if a.add_destination:
            for ipv in a.add_destination:
                settings.addDestination(_check_destination_ipv(ipv))
            icmptype.update(settings)
            fw.config.set_icmptype_config(icmptype, settings.settings)

        elif a.remove_destination:
            for ipv in a.remove_destination:
                settings.removeDestination(_check_destination_ipv(ipv))
            fw.config.set_icmptype_config(icmptype, settings.settings)

        elif a.query_destination:
            r = a.query_destination in settings.getDestinations()
            __print_query_result(r)

        elif a.get_destinations:
            l = settings.getDestinations()
            if len(l) == 0:
                l = [ "ipv4", "ipv6" ]
            __print_and_exit("\n".join(l))

        elif a.set_description:
            settings.setDescription(a.set_description)
            fw.config.set_icmptype_config(icmptype, settings.settings)

        elif a.get_description:
            __print_and_exit(settings.getDescription())

        elif a.set_short:
            settings.setShort(a.set_short)
            fw.config.set_icmptype_config(icmptype, settings.settings)

        elif a.get_short:
            __print_and_exit(settings.getShort())

        else:
            __fail(parser.format_usage() + "Unknown option")

    elif a.service and options_service:
        service = fw.config.get_service(a.service)
        settings = FirewallClientServiceSettings(
            list(fw.config.get_service_config(service)))

        if a.add_port:
            for port_proto in a.add_port:
                (port, proto) = __parse_port(port_proto)
                settings.addPort(port, proto)
            fw.config.set_service_config(service, settings.settings)

        elif a.remove_port:
            for port_proto in a.remove_port:
                (port, proto) = __parse_port(port_proto)
                settings.removePort(port, proto)
            fw.config.set_service_config(service, settings.settings)

        elif a.query_port:
            (port, proto) = __parse_port(a.query_port)
            __print_query_result(settings.queryPort(port, proto))

        elif a.get_ports:
            l = settings.getPorts()
            __print_and_exit(" ".join(["%s/%s" % (port[0], port[1]) for port in l]))

        elif a.add_protocol:
            for protocol in a.add_protocol:
                settings.addProtocol(protocol)
            fw.config.set_service_config(service, settings.settings)

        elif a.remove_protocol:
            for protocol in a.remove_protocol:
                settings.removeProtocol(protocol)
            fw.config.set_service_config(service, settings.settings)

        elif a.query_protocol:
            __print_query_result(settings.queryProtocol(a.query_protocol))

        elif a.get_protocols:
            l = settings.getProtocols()
            __print_and_exit(" ".join(["%s" % protocol for protocol in l]))

        elif a.add_module:
            for module in a.add_module:
                settings.addModule(module)
            fw.config.set_service_config(service, settings.settings)

        elif a.remove_module:
            for module in a.remove_module:
                settings.removeModule(module)
            fw.config.set_service_config(service, settings.settings)

        elif a.query_module:
            __print_query_result(settings.queryModule(a.query_module))

        elif a.get_modules:
            l = settings.getModules()
            __print_and_exit(" ".join(["%s" % module for module in l]))

        elif a.set_destination:
            (ipv, dest) = __parse_service_destination(a.set_destination)
            settings.setDestination(ipv, dest)
            fw.config.set_service_config(service, settings.settings)

        elif a.remove_destination:
            for dest in a.remove_destination:
                settings.removeDestination(
                    _check_destination_ipv(dest))
            fw.config.set_service_config(service, settings.settings)

        elif a.query_destination:
            (ipv, dest) = __parse_service_destination(a.query_destination)
            __print_query_result(settings.queryDestination(ipv, dest))

        elif a.get_destinations:
            l = settings.getDestinations()
            __print_and_exit(" ".join(["%s:%s" % (dest[0], dest[1]) for dest in l.items()]))

        elif a.set_description:
            settings.setDescription(a.set_description)
            fw.config.set_service_config(service, settings.settings)

        elif a.get_description:
            __print_and_exit(settings.getDescription())

        elif a.set_short:
            settings.setShort(a.set_short)
            fw.config.set_service_config(service, settings.settings)

        elif a.get_short:
            __print_and_exit(settings.getShort())

        else:
            __fail(parser.format_usage() + "Unknown option")

    # ipsets
    if a.get_ipsets:
        ipsets = fw.config.get_ipsets()
        __print_and_exit(" ".join(sorted(ipsets)))

    elif a.new_ipset:
        if not a.type:
            __fail(parser.format_usage() + "No type specified.")

        settings = FirewallClientIPSetSettings()
        settings.setType(a.type)
        if a.option:
            for opt in a.option:
                settings.addOption(*__parse_ipset_option(opt))
        fw.config.new_ipset(a.new_ipset, settings.settings)

    elif a.delete_ipset:
        ipset = fw.config.get_ipset(a.delete_ipset)
        fw.config.remove_ipset(ipset)

    elif a.info_ipset:
        ipset = fw.config.get_ipset(a.info_ipset)
        settings = FirewallClientIPSetSettings(
            list(fw.config.get_ipset_config(ipset)))
        __print_ipset_info(a.info_ipset, settings)
        sys.exit(0)

    elif a.ipset:
        if a.add_entry:
            ipset = fw.config.get_ipset(a.ipset)
            settings = FirewallClientIPSetSettings(
            list(fw.config.get_ipset_config(ipset)))
            settings.addEntry(a.add_entry)
            fw.config.set_ipset_config(ipset, settings.settings)

        elif a.remove_entry:
            ipset = fw.config.get_ipset(a.ipset)
            settings = FirewallClientIPSetSettings(
                list(fw.config.get_ipset_config(ipset)))
            settings.removeEntry(a.remove_entry)
            fw.config.set_ipset_config(ipset, settings.settings)

        elif a.query_entry:
            ipset = fw.config.get_ipset(a.ipset)
            settings = FirewallClientIPSetSettings(
                list(fw.config.get_ipset_config(ipset)))
            r = settings.queryEntry(a.query_entry)
            __print_query_result(r)

        elif a.get_entries:
            ipset = fw.config.get_ipset(a.ipset)
            settings = FirewallClientIPSetSettings(
                list(fw.config.get_ipset_config(ipset)))
            l = settings.getEntries()
            __print_and_exit("\n".join(l))

        elif a.set_description:
             ipset = fw.config.get_ipset(a.ipset)
             settings = FirewallClientIPSetSettings(
                list(fw.config.get_ipset_config(ipset)))
             settings.setDescription(a.set_description)

             fw.config.set_ipset_config(ipset, settings.settings)

        elif a.get_description:
            ipset = fw.config.get_ipset(a.ipset)
            settings = FirewallClientIPSetSettings(
                list(fw.config.get_ipset_config(ipset)))

            __print_and_exit(settings.getDescription())

        elif a.set_short:
             ipset = fw.config.get_ipset(a.ipset)
             settings = FirewallClientIPSetSettings(
                list(fw.config.get_ipset_config(ipset)))
             settings.setShort(a.set_short)

             fw.config.set_ipset_config(ipset, settings.settings)

        elif a.get_short:
            ipset = fw.config.get_ipset(a.ipset)
            settings = FirewallClientIPSetSettings(
                list(fw.config.get_ipset_config(ipset)))

            __print_and_exit(settings.getShort())

        else:
            __fail(parser.format_usage() + "Unknown option")

    # lockdown whitelist

    elif options_lockdown_whitelist:
        whitelist = fw.config.get_policies().lockdown_whitelist

        # commands
        if a.list_lockdown_whitelist_commands:
            l = whitelist.get_commands()
            __print_and_exit("\n".join(l))
        elif a.add_lockdown_whitelist_command:
            whitelist.add_command(a.add_lockdown_whitelist_command)
        elif a.remove_lockdown_whitelist_command:
            whitelist.remove_command(a.remove_lockdown_whitelist_command)
        elif a.query_lockdown_whitelist_command:
            __print_query_result(a.query_lockdown_whitelist_command in 
                                 whitelist.get_commands())

        # contexts
        elif a.list_lockdown_whitelist_contexts:
            l = whitelist.get_contexts()
            __print_and_exit("\n".join(l))
        elif a.add_lockdown_whitelist_context:
            whitelist.add_context(a.add_lockdown_whitelist_context)
        elif a.remove_lockdown_whitelist_context:
            whitelist.remove_context(a.remove_lockdown_whitelist_context)
        elif a.query_lockdown_whitelist_context:
            __print_query_result(a.query_lockdown_whitelist_context in 
                                 whitelist.get_contexts())

        # uids
        elif a.list_lockdown_whitelist_uids:
            l = whitelist.get_uids()
            __print_and_exit(" ".join(map(str, l)))
        elif a.add_lockdown_whitelist_uid is not None:
            whitelist.add_uid(a.add_lockdown_whitelist_uid)
        elif a.remove_lockdown_whitelist_uid is not None:
            whitelist.remove_uid(a.remove_lockdown_whitelist_uid)
        elif a.query_lockdown_whitelist_uid is not None:
            __print_query_result(a.query_lockdown_whitelist_uid in
                                 whitelist.get_uids())

        # users
        elif a.list_lockdown_whitelist_users:
            l = whitelist.get_users()
            __print_and_exit("\n".join(l))
        elif a.add_lockdown_whitelist_user:
            whitelist.add_user(a.add_lockdown_whitelist_user)
        elif a.remove_lockdown_whitelist_user:
            whitelist.remove_user(a.remove_lockdown_whitelist_user)
        elif a.query_lockdown_whitelist_user:
            __print_query_result(a.query_lockdown_whitelist_user in
                                 whitelist.get_users())

        # apply whitelist changes
        whitelist.write()

    elif options_direct:
        settings = fw.config.get_direct()

        if a.add_passthrough:
            if len (a.add_passthrough) < 2:
                __fail("usage: --direct --add-passthrough { ipv4 | ipv6 | eb } <args>")
            __print(settings.add_passthrough(_check_ipv(a.add_passthrough[0]),
                                             splitArgs(a.add_passthrough[1])))

        elif a.remove_passthrough:
            if len (a.remove_passthrough) < 2:
                __fail("usage: --direct --remove-passthrough { ipv4 | ipv6 | eb } <args>")
            settings.remove_passthrough(_check_ipv(a.remove_passthrough[0]),
                                        splitArgs(a.remove_passthrough[1]))
        elif a.query_passthrough:
            if len (a.query_passthrough) < 2:
                __fail("usage: --direct --query-passthrough { ipv4 | ipv6 | eb } <args>")
            __print_query_result(
                settings.query_passthrough(_check_ipv(a.query_passthrough[0]),
                                           splitArgs(a.query_passthrough[1])))
            sys.exit(0)
        elif a.get_passthroughs:
            rules = settings.get_passthroughs(_check_ipv(a.get_passthroughs[0]))
            for rule in rules:
                __print(joinArgs(rule))
            sys.exit(0)
        elif a.get_all_passthroughs:
            pt = settings.get_all_passthroughs()
            for ipv in pt:
                for rule in pt[ipv]:
                    __print("%s %s" % (ipv, joinArgs(rule)))
            sys.exit(0)

        elif a.add_chain:
            settings.add_chain(_check_ipv(a.add_chain[0]),
                               a.add_chain[1], a.add_chain[2])
        elif a.remove_chain:
            settings.remove_chain(_check_ipv(a.remove_chain[0]),
                                  a.remove_chain[1], a.remove_chain[2])
        elif a.query_chain:
            __print_query_result(
                settings.query_chain(_check_ipv(a.query_chain[0]),
                                     a.query_chain[1], a.query_chain[2]))
            sys.exit(0)
        elif a.get_chains:
            __print_and_exit(
                    " ".join(settings.get_chains(_check_ipv(a.get_chains[0]),
                                                 a.get_chains[1])))
            sys.exit(0)
        elif a.get_all_chains:
            chains = settings.get_all_chains()
            for (ipv, table) in chains:
                for chain in chains[(ipv,table)]:
                    __print("%s %s %s" % (ipv, table, chain))
            sys.exit(0)

        elif a.add_rule:
            if len (a.add_rule) < 5:
                __fail("usage: --direct --add-rule { ipv4 | ipv6 | eb } <table> <chain> <priority> <args>")
            try:
                priority = int(a.add_rule[3])
            except ValueError:
                __fail("wrong priority\nusage: --direct --add-rule { ipv4 | ipv6 | eb } <table> <chain> <priority> <args>")
            settings.add_rule(_check_ipv(a.add_rule[0]), a.add_rule[1],
                              a.add_rule[2], priority, splitArgs(a.add_rule[4]))
        elif a.remove_rule:
            if len (a.remove_rule) < 5:
                __fail("usage: --direct --remove-rule { ipv4 | ipv6 | eb } <table> <chain> <priority> <args>")
            try:
                priority = int(a.remove_rule[3])
            except ValueError:
                __fail("usage: --direct --remove-rule { ipv4 | ipv6 | eb } <table> <chain> <priority> <args>")
            settings.remove_rule(_check_ipv(a.remove_rule[0]), a.remove_rule[1],
                                a.remove_rule[2], priority, splitArgs(a.remove_rule[4]))
        elif a.remove_rules:
            if len (a.remove_rules) < 3:
                __fail("usage: --direct --remove-rules { ipv4 | ipv6 | eb } <table> <chain>")
            settings.remove_rules(_check_ipv(a.remove_rules[0]),
                                  a.remove_rules[1], a.remove_rules[2])
        elif a.query_rule:
            if len (a.query_rule) < 5:
                __fail("usage: --direct --query-rule { ipv4 | ipv6 | eb } <table> <chain> <priority> <args>")
            try:
                priority = int(a.query_rule[3])
            except ValueError:
                __fail("usage: --direct --query-rule { ipv4 | ipv6 | eb } <table> <chain> <priority> <args>")
            __print_query_result(
                    settings.query_rule(_check_ipv(a.query_rule[0]),
                                        a.query_rule[1], a.query_rule[2],
                                        priority, splitArgs(a.query_rule[4])))
            sys.exit(0)
        elif a.get_rules:
            rules = settings.get_rules(_check_ipv(a.get_rules[0]),
                                       a.get_rules[1], a.get_rules[2])
            for (priority, rule) in rules:
                __print("%d %s" % (priority, joinArgs(rule)))
            sys.exit(0)
        elif a.get_all_rules:
            rules = settings.get_all_rules()
            for (ipv, table, chain) in rules:
                for (priority, rule) in rules[(ipv, table, chain)]:
                    __print("%s %s %s %d %s" % (ipv, table, chain, priority,
                                                joinArgs(rule)))
            sys.exit(0)

        settings.write()

    else:
        if zone == "":
            zone = fw.get_default_zone()
        fw_zone = fw.config.get_zone(zone)
        fw_settings = FirewallClientZoneSettings(
            list(fw.config.get_zone_config(fw_zone))) # convert to list, for setMasquerade

        # interface
        if a.list_interfaces:
            l = fw_settings.getInterfaces()
            __print_and_exit(" ".join(l))
        elif a.get_zone_of_interface:
            ret = []
            for zone in fw.config.get_zones():
                obj = fw.config.get_zone(zone)
                if a.get_zone_of_interface in obj.interfaces:
                    ret.append(obj.name)
            if len(ret) > 1:
                # Even it shouldn't happen, it's actually possible that
                # the same interface is in several zone XML files
                __print_and_exit(" ".join(ret) + "  (ERROR: interface '%s' is in %s zone XML files, can be only in one)" % (a.get_zone_of_interface, len(ret)))
            if len(ret) == 1:
                __print_and_exit(ret[0])
            else:
                __print_and_exit("no zone", 2)
        elif a.change_interface:
            ret = []
            for old_zone in fw.config.get_zones():
                old_zone_obj = fw.config.get_zone(old_zone)
                if a.change_interface in old_zone_obj.interfaces:
                    if old_zone_obj.name != zone:
                        old_zone_settings = FirewallClientZoneSettings(
                            fw.config.get_zone_config(old_zone_obj))

                        old_zone_settings.removeInterface(a.change_interface) # remove from old
                        fw.config.set_zone_config(old_zone_obj, old_zone_settings.settings)
            fw_settings.addInterface(a.change_interface)              # add to new
        elif a.add_interface:
            fw_settings.addInterface(a.add_interface)
        elif a.remove_interface:
            fw_settings.removeInterface(a.remove_interface)
        elif a.query_interface:
            __print_query_result(fw_settings.queryInterface(a.query_interface))

        # source
        if a.list_sources:
            sources = fw_settings.getSources()
            __print_and_exit(" ".join(sources))
        elif a.get_zone_of_source:
            ret = []
            for zone in fw.config.get_zones():
                obj = fw.config.get_zone(zone)
                if a.get_zone_of_source in obj.sources:
                    ret.append(obj.name)
            if len(ret) > 1:
                # Even it shouldn't happen, it's actually possible that
                # the same source is in several zone XML files
                __print_and_exit(" ".join(ret) + "  (ERROR: source '%s' is in %s zone XML files, can be only in one)" % (a.get_zone_of_source, len(ret)))
            if len(ret) == 1:
                __print_and_exit(ret[0])
            else:
                __print_and_exit("no zone", 2)
        elif a.change_source:
            ret = []
            for old_zone in fw.config.get_zones():
                old_zone_obj = fw.config.get_zone(old_zone)
                if a.change_source in old_zone_obj.sources:
                    if old_zone_obj.name != zone:
                        old_zone_settings = FirewallClientZoneSettings(
                            fw.config.get_zone_config(old_zone_obj))

                        old_zone_settings.removeSource(a.change_source) # remove from old
                        fw.config.set_zone_config(old_zone_obj, old_zone_settings.settings)
            fw_settings.addSource(a.change_source)              # add to new
        elif a.add_source:
            fw_settings.addSource(a.add_source)
        elif a.remove_source:
            fw_settings.removeSource(a.remove_source)
        elif a.query_source:
            __print_query_result(fw_settings.querySource(a.query_source))

        # rich rules
        if a.list_rich_rules:
            l = fw_settings.getRichRules()
            __print_and_exit("\n".join(l))
        elif a.add_rich_rule:
            for s in a.add_rich_rule:
                fw_settings.addRichRule(s)
        elif a.remove_rich_rule:
            for s in a.remove_rich_rule:
                fw_settings.removeRichRule(s)
        elif a.query_rich_rule:
            __print_query_result(fw_settings.queryRichRule(a.query_rich_rule))

        # service
        if a.list_services:
            l = fw_settings.getServices()
            __print_and_exit(" ".join(l))
        elif a.add_service:
            for s in a.add_service:
                fw_settings.addService(s)
        elif a.remove_service_from_zone:
            for s in a.remove_service_from_zone:
                fw_settings.removeService(s)
        elif a.query_service:
            __print_query_result(fw_settings.queryService(a.query_service))

        # port
        elif a.list_ports:
            l = fw_settings.getPorts()
            __print_and_exit(" ".join(["%s/%s" % (port[0], port[1]) for port in l]))
        elif a.add_port:
            for port_proto in a.add_port:
                (port, proto) = __parse_port(port_proto)
                fw_settings.addPort(port, proto)
        elif a.remove_port:
            for port_proto in a.remove_port:
                (port, proto) = __parse_port(port_proto)
                fw_settings.removePort(port, proto)
        elif a.query_port:
            (port, proto) = __parse_port(a.query_port)
            __print_query_result(fw_settings.queryPort(port, proto))

        # protocol
        elif a.list_protocols:
            l = fw_settings.getProtocols()
            __print_and_exit(" ".join(l))
        elif a.add_protocol:
            for proto in a.add_protocol:
                fw_settings.addProtocol(proto)
        elif a.remove_protocol:
            for proto in a.remove_protocol:
                fw_settings.removeProtocol(proto)
        elif a.query_protocol:
            __print_query_result(fw_settings.queryProtocol(a.query_protocol))

        # masquerade
        elif a.add_masquerade:
            fw_settings.setMasquerade(True)
        elif a.remove_masquerade:
            fw_settings.setMasquerade(False)
        elif a.query_masquerade:
            __print_query_result(fw_settings.getMasquerade())

        # forward port
        elif a.list_forward_ports:
            l = fw_settings.getForwardPorts()
            __print_and_exit("\n".join(["port=%s:proto=%s:toport=%s:toaddr=%s" % (port, protocol, toport, toaddr) for (port, protocol, toport, toaddr) in l]))
        elif a.add_forward_port:
            for fp in a.add_forward_port:
                (port, protocol, toport, toaddr) = __parse_forward_port(fp)
                fw_settings.addForwardPort(port, protocol, toport, toaddr)
        elif a.remove_forward_port:
            for fp in a.remove_forward_port:
                (port, protocol, toport, toaddr) = __parse_forward_port(fp)
                fw_settings.removeForwardPort(port, protocol, toport, toaddr)
        elif a.query_forward_port:
            (port, protocol, toport, toaddr) = __parse_forward_port(a.query_forward_port)
            __print_query_result(fw_settings.queryForwardPort(port, protocol, toport, toaddr))

        # block icmp
        elif a.list_icmp_blocks:
            l = fw_settings.getIcmpBlocks()
            __print_and_exit(" ".join(l))
        elif a.add_icmp_block:
            for ib in a.add_icmp_block:
                fw_settings.addIcmpBlock(ib)
        elif a.remove_icmp_block:
            for ib in a.remove_icmp_block:
                fw_settings.removeIcmpBlock(ib)
        elif a.query_icmp_block:
            __print_query_result(fw_settings.queryIcmpBlock(a.query_icmp_block))

        # zone target
        elif a.get_target:
            __print_and_exit(fw_settings.getTarget())
        elif a.set_target:
            fw_settings.setTarget(a.set_target)

        # list all zone settings
        elif a.list_all:
            __print_zone_info(zone if zone else fw.get_default_zone(),
                              fw_settings)
            sys.exit(0)

        # list everything
        elif a.list_all_zones:
            zones = fw.config.get_zones()
            for zone in zones:
                fw_zone = fw.config.get_zone(zone)
                fw_settings = FirewallClientZoneSettings(list(fw.config.get_zone_config(fw_zone)))
                __print_zone_info(zone, fw_settings)
                __print("")
            sys.exit(0)

        elif a.set_description:
            fw_settings.setDescription(a.set_description)

        elif a.get_description:
            __print_and_exit(fw_settings.getDescription())

        elif a.set_short:
            fw_settings.setShort(a.set_short)

        elif a.get_short:
            __print_and_exit(fw_settings.getShort())

        fw.config.set_zone_config(fw_zone, fw_settings.settings)

except Exception as msg:
    __fail("%s" % msg)
else:
    __print_and_exit("success")
