#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2009-2013 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.client import FirewallClient
from firewall.errors import *

def __print(msg=None):
    if msg and not a.quiet:
        print(msg)

def __print_and_exit(msg=None, exit_code=0):
    __print(msg)
    sys.exit(exit_code)

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

def __print_if_verbose(msg=None):
    if msg and a.verbose:
        print(msg)

def __parse_port(value):
    try:
        (port, proto) = value.split("/")
    except Exception as e:
        __fail("bad port (most likely missing 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 _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_all(zone, interfaces, sources, services, ports, masquerade, forward_ports, icmp_blocks, rules):
    attributes = []
    if zone == fw.getDefaultZone():
        attributes.append("default")
    if interfaces:
        attributes.append("active")
    if attributes:
        zone = zone + " (%s)" % ", ".join(attributes)
    __print(zone)
    __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("  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 __list_all(fw, zone):
    interfaces = fw.getInterfaces(zone)
    sources = fw.getSources(zone)
    services = fw.getServices(zone)
    ports = fw.getPorts(zone)
    masquerade = fw.queryMasquerade(zone)
    forward_ports = fw.getForwardPorts(zone)
    icmp_blocks = fw.getIcmpBlocks(zone)
    rules = fw.getRichRules(zone)
    __print_all(zone, interfaces, sources, services, ports, masquerade, forward_ports, icmp_blocks, rules)

def __list_all_permanent(fw_settings, zone):
    interfaces = fw_settings.getInterfaces()
    sources = fw_settings.getSources()
    services = fw_settings.getServices()
    ports = fw_settings.getPorts()
    masquerade = fw_settings.getMasquerade()
    forward_ports = fw_settings.getForwardPorts()
    icmp_blocks = fw_settings.getIcmpBlocks()
    rules = fw_settings.getRichRules()
    __print_all(zone, interfaces, sources, services, ports, masquerade, forward_ports, icmp_blocks, rules)

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

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

parser_group_output = parser.add_mutually_exclusive_group()
parser_group_output.add_argument("-v", "--verbose", action="store_true")
parser_group_output.add_argument("-q", "--quiet", action="store_true")

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("--state", action="store_true")
parser_group_standalone.add_argument("--reload", action="store_true")
parser_group_standalone.add_argument("--complete-reload", action="store_true")
parser_group_standalone.add_argument("--panic-on", action="store_true")
parser_group_standalone.add_argument("--panic-off", action="store_true")
parser_group_standalone.add_argument("--query-panic", 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-active-zones", 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_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("--permanent", action="store_true")
parser.add_argument("--zone", default="", metavar="<zone>")
parser.add_argument("--timeout", default=0, type=int, metavar="<seconds>")

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", 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-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-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.add_argument("--direct", action="store_true")

parser_direct = parser.add_mutually_exclusive_group()
parser_direct.add_argument("--passthrough", nargs=argparse.REMAINDER,
                    metavar=("{ ipv4 | ipv6 | eb }", "<args>"))
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-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> <args>"))
parser_direct.add_argument("--query-rule", nargs=argparse.REMAINDER,
                        metavar=("{ ipv4 | ipv6 | eb }", "<table> <chain> <args>"))
parser_direct.add_argument("--get-rules", nargs=3,
                        metavar=("{ ipv4 | ipv6 | eb }", "<table>", "<chain>"))

a = parser.parse_args()

options_standalone = a.help or a.version or \
    a.state or a.reload or a.complete_reload or \
    a.panic_on or a.panic_off or a.query_panic or \
    a.lockdown_on or a.lockdown_off or a.query_lockdown or \
    a.get_default_zone or a.set_default_zone or \
    a.get_active_zones

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 or \
    a.remove_lockdown_whitelist_uid or \
    a.query_lockdown_whitelist_uid 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

options_zone_action_action = \
    a.add_service or a.remove_service or a.query_service or \
    a.add_port or a.remove_port or a.query_port 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_icmp_blocks or \
    a.list_forward_ports or a.list_rich_rules or a.list_all

options_zone_ops = options_zone_interfaces_sources or \
               options_zone_action_action or options_zone_adapt_query

options_zone = a.zone or a.timeout or options_zone_ops

options_permanent = a.permanent or options_config or a.zone or options_zone_ops

options_direct = a.passthrough or \
           a.add_chain or a.remove_chain or a.query_chain or a.get_chains or \
                 a.add_rule or a.remove_rule or a.query_rule or a.get_rules

# 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_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_active_zones or a.get_zone_of_interface or \
 a.get_zone_of_source or a.get_zones or a.get_services or a.get_icmptypes

# Check various impossible combinations of options

if not (options_standalone or options_zone or \
        options_permanent or options_direct):
    __fail(parser.format_usage() + "No option specified.")

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

if options_direct and (options_zone or options_permanent):
    __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.timeout and not (a.add_service or a.add_port or a.add_icmp_block or \
                          a.add_forward_port or a.add_masquerade or \
                          a.add_rich_rule):
    __fail(parser.format_usage() + "Wrong --timeout usage")

if a.permanent:
    if a.timeout != 0:
        __fail(parser.format_usage() +
               "Can't specify timeout for permanent action.")
    if options_config and not a.zone:
        pass
    elif options_permanent:
        pass
    else:
        __fail(parser.format_usage() + "Wrong --permanent usage.")

if a.quiet and options_list_get:
    # it makes no sense to use --quiet with these options
    a.quiet = False
    __fail("-q/--quiet can't be used with this option(s)")

if a.help:
    os.system("man firewall-cmd")
    sys.exit(0)

zone = a.zone
try:
    fw = FirewallClient()
    if fw.connected == False:
        if a.state:
            __print_and_exit ("not running", NOT_RUNNING)
        else:
            __print_and_exit ("FirewallD is not running", NOT_RUNNING)

    if options_zone_ops and not zone:
        default = fw.getDefaultZone()
        __print_if_verbose("No zone specified, using default zone, i.e. '%s'" % default)
        active = list(fw.getActiveZones().keys())
        if active and default not in active:
            __print ("""You're performing an operation over default zone ('%s'),
but your connections/interfaces are in zone '%s' (see --get-active-zones)
You most likely need to use --zone=%s option.\n""" % (default, ",".join(active), active[0]))

    if a.permanent:
        if a.get_zones:
            zones = fw.config().listZones()
            l = [fw.config().getZone(z).get_property("name") for z in zones]
            __print_and_exit(" ".join(l))
        elif a.get_services:
            services = fw.config().listServices()
            l = [fw.config().getService(s).get_property("name") for s in services]
            __print_and_exit(" ".join(l))
        elif a.get_icmptypes:
            icmptypes = fw.config().listIcmpTypes()
            l = [fw.config().getIcmpType(i).get_property("name") for i in icmptypes]
            __print_and_exit(" ".join(l))

        # lockdown whitelist

        elif options_lockdown_whitelist:
            whitelist = fw.config().policies().getLockdownWhitelist()
                
            # commands
            if a.list_lockdown_whitelist_commands:
                l = whitelist.getCommands()
                __print_and_exit("\n".join(l))
            elif a.add_lockdown_whitelist_command:
                whitelist.addCommand(a.add_lockdown_whitelist_command)
            elif a.remove_lockdown_whitelist_command:
                whitelist.removeCommand(a.remove_lockdown_whitelist_command)
            elif a.query_lockdown_whitelist_command:
                __print_query_result(whitelist.queryCommand( \
                                     a.query_lockdown_whitelist_command))

            # contexts
            elif a.list_lockdown_whitelist_contexts:
                l = whitelist.getContexts()
                __print_and_exit("\n".join(l))
            elif a.add_lockdown_whitelist_context:
                whitelist.addContext(a.add_lockdown_whitelist_context) 
            elif a.remove_lockdown_whitelist_context:
                whitelist.removeContext(a.remove_lockdown_whitelist_context)
            elif a.query_lockdown_whitelist_context:
                __print_query_result(whitelist.queryContext( \
                                     a.query_lockdown_whitelist_context))

            # uids
            elif a.list_lockdown_whitelist_uids:
                l = whitelist.getUids()
                __print_and_exit(" ".join(map(str, l)))
            elif a.add_lockdown_whitelist_uid:
                whitelist.addUid(a.add_lockdown_whitelist_uid)
            elif a.remove_lockdown_whitelist_uid:
                whitelist.removeUid(a.remove_lockdown_whitelist_uid)
            elif a.query_lockdown_whitelist_uid:
                __print_query_result(whitelist.queryUid( \
                                     a.query_lockdown_whitelist_uid))

            # users
            elif a.list_lockdown_whitelist_users:
                l = whitelist.getUsers()
                __print_and_exit("\n".join(l))
            elif a.add_lockdown_whitelist_user:
                whitelist.addUser(a.add_lockdown_whitelist_user) 
            elif a.remove_lockdown_whitelist_user:
                whitelist.removeUser(a.remove_lockdown_whitelist_user)
            elif a.query_lockdown_whitelist_user:
                __print_query_result(whitelist.queryUser( \
                                     a.query_lockdown_whitelist_user))

            # apply whitelist changes
            fw.config().policies().setLockdownWhitelist(whitelist)

        else:
            if zone == "":
                zone = fw.getDefaultZone()
            fw_zone = fw.config().getZoneByName(zone)
            fw_settings = fw_zone.getSettings()

            # interface
            if a.list_interfaces:
                l = fw_settings.getInterfaces()
                __print_and_exit(" ".join(l))
            elif a.get_zone_of_interface:
                zone = fw.config().getZoneOfInterface(a.get_zone_of_interface)
                if zone:
                    __print_and_exit(zone)
                else:
                    __print_and_exit("no zone", 2)
            elif a.change_interface:
                old_zone_name = fw.config().getZoneOfInterface(a.change_interface)
                old_zone_obj = fw.config().getZoneByName(old_zone_name)
                old_zone_settings = old_zone_obj.getSettings()
                old_zone_settings.removeInterface(a.change_interface)  # remove from old
                old_zone_obj.update(old_zone_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:
                zone = fw.config().getZoneOfSource(a.get_zone_of_source)
                if zone:
                    __print_and_exit(zone)
                else:
                    __print_and_exit("no zone", 2)
            elif a.change_source:
                old_zone_name = fw.config().getZoneOfSource(a.change_source)
                old_zone_obj = fw.config().getZoneByName(old_zone_name)
                old_zone_settings = old_zone_obj.getSettings()
                old_zone_settings.removeSource(a.change_source)  # remove from old
                old_zone_obj.update(old_zone_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:
                for s in a.remove_service:
                    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))

            # 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))

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

            # list everything
            elif a.list_all_zones:
                zones = fw.config().listZones()
                names = [fw.config().getZone(z).get_property("name") for z in zones]
                for zone in names:
                    fw_zone = fw.config().getZoneByName(zone)
                    fw_settings = fw_zone.getSettings()
                    __list_all_permanent(fw_settings, zone)
                    __print("")
                sys.exit(0)

            fw_zone.update(fw_settings)

    elif a.version:
        __print_and_exit(fw.get_property("version"))
    elif a.state:
        state = fw.get_property("state")
        if state == "RUNNING":
            __print_and_exit ("running")
        else:
            __print_and_exit ("not running", NOT_RUNNING)
    elif a.reload:
        fw.reload()
    elif a.complete_reload:
        fw.complete_reload()
    elif a.direct:
        if a.passthrough:
            if len (a.passthrough) < 2:
                __fail("usage: --direct --passthrough { ipv4 | ipv6 | eb } <args>")
            __print(fw.passthrough(_check_ipv(a.passthrough[0]),
                                 a.passthrough[1:]))
        elif a.add_chain:
            fw.addChain(_check_ipv(a.add_chain[0]),
                        a.add_chain[1], a.add_chain[2])
        elif a.remove_chain:
            fw.removeChain(_check_ipv(a.remove_chain[0]),
                           a.remove_chain[1], a.remove_chain[2])
        elif a.query_chain:
            __print_query_result(fw.queryChain(_check_ipv(a.query_chain[0]),
                                       a.query_chain[1], a.query_chain[2]))
        elif a.get_chains:
            __print_and_exit(" ".join(fw.getChains(_check_ipv(a.get_chains[0]),
                                        a.get_chains[1])))
        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>")
            fw.addRule(_check_ipv(a.add_rule[0]), a.add_rule[1], a.add_rule[2],
                       priority, 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>")
            fw.removeRule(_check_ipv(a.remove_rule[0]),
                          a.remove_rule[1], a.remove_rule[2], priority, a.remove_rule[4:])
        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(fw.queryRule(_check_ipv(a.query_rule[0]),
                                      a.query_rule[1], a.query_rule[2], priority, a.query_rule[4:]))
        elif a.get_rules:
            rules = fw.getRules(_check_ipv(a.get_rules[0]),
                                a.get_rules[1], a.get_rules[2])
            for (priority, rule) in rules:
                __print("%d %s" % (priority, " ".join(rule)))
            sys.exit(0)
    elif a.get_default_zone:
        __print_and_exit(fw.getDefaultZone())
    elif a.set_default_zone:
        fw.setDefaultZone(a.set_default_zone)
    elif a.get_zones:
        __print_and_exit(" ".join(fw.getZones()))
    elif a.get_active_zones:
        zones = fw.getActiveZones()
        for zone in zones:
            __print("%s" % (zone))
            for x in [ "interfaces", "sources" ]:
                if x in zones[zone]:
                    __print("  %s: %s" % (x, " ".join(zones[zone][x])))
        sys.exit(0)
    elif a.get_services:
        l = fw.listServices()
        __print_and_exit(" ".join(l))
    elif a.get_icmptypes:
        l = fw.listIcmpTypes()
        __print_and_exit(" ".join(l))

    # panic
    elif a.panic_on:
        fw.enablePanicMode()
    elif a.panic_off:
        fw.disablePanicMode()
    elif a.query_panic:
        __print_query_result(fw.queryPanicMode())

    # lockdown
    elif a.lockdown_on:
        fw.enableLockdown()                           # runtime
        fw.config().set_property("Lockdown", "yes")   # permanent
    elif a.lockdown_off:
        fw.disableLockdown()                          # runtime
        fw.config().set_property("Lockdown", "no")    # permanent
    elif a.query_lockdown:
        __print_query_result(fw.queryLockdown())      # runtime
        #lockdown = fw.config().get_property("Lockdown")
        #__print_query_result(lockdown.lower() in [ "yes", "true" ])

    # lockdown whitelist

    # commands
    elif a.list_lockdown_whitelist_commands:
        l = fw.getLockdownWhitelistCommands()
        __print_and_exit("\n".join(l))
    elif a.add_lockdown_whitelist_command:
        fw.addLockdownWhitelistCommand(a.add_lockdown_whitelist_command)
    elif a.remove_lockdown_whitelist_command:
        fw.removeLockdownWhitelistCommand(a.remove_lockdown_whitelist_command)
    elif a.query_lockdown_whitelist_command:
        __print_query_result(fw.queryLockdownWhitelistCommand( \
                             a.query_lockdown_whitelist_command))

    # contexts
    elif a.list_lockdown_whitelist_contexts:
        l = fw.getLockdownWhitelistContexts()
        __print_and_exit("\n".join(l))
    elif a.add_lockdown_whitelist_context:
        fw.addLockdownWhitelistContext(a.add_lockdown_whitelist_context)
    elif a.remove_lockdown_whitelist_context:
        fw.removeLockdownWhitelistContext(a.remove_lockdown_whitelist_context)
    elif a.query_lockdown_whitelist_context:
        __print_query_result(fw.queryLockdownWhitelistContext( \
                             a.query_lockdown_whitelist_context))

    # uids
    elif a.list_lockdown_whitelist_uids:
        l = fw.getLockdownWhitelistUids()
        __print_and_exit(" ".join(map(str, l)))
    elif a.add_lockdown_whitelist_uid:
        fw.addLockdownWhitelistUid(a.add_lockdown_whitelist_uid)
    elif a.remove_lockdown_whitelist_uid:
        fw.removeLockdownWhitelistUid(a.remove_lockdown_whitelist_uid)
    elif a.query_lockdown_whitelist_uid:
        __print_query_result(fw.queryLockdownWhitelistUid( \
                             a.query_lockdown_whitelist_uid))

    # users
    elif a.list_lockdown_whitelist_users:
        l = fw.getLockdownWhitelistUsers()
        __print_and_exit(" ".join(l))
    elif a.add_lockdown_whitelist_user:
        fw.addLockdownWhitelistUser(a.add_lockdown_whitelist_user)
    elif a.remove_lockdown_whitelist_user:
        fw.removeLockdownWhitelistUser(a.remove_lockdown_whitelist_user)
    elif a.query_lockdown_whitelist_user:
        __print_query_result(fw.queryLockdownWhitelistUser( \
                             a.query_lockdown_whitelist_user))

    # interface
    elif a.list_interfaces:
        l = fw.getInterfaces(zone)
        __print_and_exit(" ".join(l))
    elif a.get_zone_of_interface:
        zone = fw.getZoneOfInterface(a.get_zone_of_interface)
        if zone:
            __print_and_exit(zone)
        else:
            __print_and_exit("no zone", 2)
    elif a.add_interface:
        fw.addInterface(zone, a.add_interface)
    elif a.change_interface:
        fw.changeZoneOfInterface(zone, a.change_interface)
    elif a.remove_interface:
        fw.removeInterface(zone, a.remove_interface)
    elif a.query_interface:
        __print_query_result(fw.queryInterface(zone, a.query_interface))

    # source
    elif a.list_sources:
        sources = fw.getSources(zone)
        __print_and_exit(" ".join(sources))
    elif a.get_zone_of_source:
        zone = fw.getZoneOfSource(a.get_zone_of_source)
        if zone:
            __print_and_exit(zone)
        else:
            __print_and_exit("no zone", 2)
    elif a.add_source:
        fw.addSource(zone, a.add_source)
    elif a.change_source:
        fw.changeZoneOfSource(zone, a.change_source)
    elif a.remove_source:
        fw.removeSource(zone, a.remove_source)
    elif a.query_source:
        __print_query_result(fw.querySource(zone, a.query_source))

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

    # service
    elif a.list_services:
        l = fw.getServices(zone)
        __print_and_exit(" ".join(l))
    elif a.add_service:
        for s in a.add_service:
            fw.addService(zone, s, a.timeout)
    elif a.remove_service:
        for s in a.remove_service:
            fw.removeService(zone, s)
    elif a.query_service:
        __print_query_result(fw.queryService(zone, a.query_service))

    # port
    elif a.list_ports:
        l = fw.getPorts(zone)
        __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.addPort(zone, port, proto, a.timeout)
    elif a.remove_port:
        for port_proto in a.remove_port:
            (port, proto) = __parse_port(port_proto)
            fw.removePort(zone, port, proto)
    elif a.query_port:
        (port, proto) = __parse_port(a.query_port)
        __print_query_result(fw.queryPort(zone, port, proto))

    # masquerade
    elif a.add_masquerade:
        fw.addMasquerade(zone, a.timeout)
    elif a.remove_masquerade:
        fw.removeMasquerade(zone)
    elif a.query_masquerade:
        __print_query_result(fw.queryMasquerade(zone))

    # forward port
    elif a.list_forward_ports:
        l = fw.getForwardPorts(zone)
        __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.addForwardPort(zone, port, protocol, toport, toaddr, a.timeout)
    elif a.remove_forward_port:
        for fp in a.remove_forward_port:
            (port, protocol, toport, toaddr) = __parse_forward_port(fp)
            fw.removeForwardPort(zone, port, protocol, toport, toaddr)
    elif a.query_forward_port:
        (port, protocol, toport, toaddr) = __parse_forward_port(a.query_forward_port)
        __print_query_result(fw.queryForwardPort(zone, port, protocol,
                                         toport, toaddr))

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

    # list all
    elif a.list_all:
        __list_all(fw, zone if zone else fw.getDefaultZone())
        sys.exit(0)

    # list everything
    elif a.list_all_zones:
        for zone in fw.getZones():
            __list_all(fw, zone)
            __print("")
        sys.exit(0)


except dbus.exceptions.DBusException as e:
    dbus_message = e.get_dbus_message()
    dbus_name = e.get_dbus_name()
    if "NotAuthorizedException" in dbus_name:
        __print ("Authorization failed.")
        __print ("Make sure polkit agent is running or run firewall-cmd as superuser.")
        sys.exit(NOT_AUTHORIZED)
    else:
        code = FirewallError.get_code(dbus_message)
        if code in [ ALREADY_ENABLED, NOT_ENABLED, ZONE_ALREADY_SET ]:
            __print_and_exit("Warning: %s" % dbus_message)
        else:
            __print_and_exit("Error: %s" % dbus_message, code)

__print_and_exit("success")
