#!/usr/bin/env lua

local json = require "luci.jsonc"
local fs   = require "nixio.fs"
local sys = require "luci.sys"
local ut = require "luci.util"

local methods = {
	addArp = {
		args = { ip = "none", macId = "none", interface = "none" },
		call = function(args)
			local arp_cmd = "/sbin/arp -i " .. tostring(args.interface) .. " -s " .. tostring(args.ip) .. " " .. tostring(args.macId)
			local handle = io.popen(arp_cmd .. " 2>&1")
			local res = handle:read("*a")
			handle:close()

			if res == "" then
				return { result = 'OK' }
			else
				return { result = res }
			end
		end

	},
	getArpTable = {
		call = function()
			local res = {}
			local pipe = io.popen("/sbin/arp -a")
			for line in pipe:lines() do
				local data = {}
				local host, ip, mac, type, interface = line:match("([^ ]+) ([^ ]+) at ([^ ]+) ([^ ]+)  on ([^ ]+)")
				if(not host) then
					host, ip, mac, ign, type, interface = line:match("([^ ]+) ([^ ]+) at ([^ ]+) ([^ ]+) ([^ ]+) on ([^ ]+)")
				end

				if(host) then
					data["ip"] = ip:gsub('%(',''):gsub('%)','')
					data["host"] = host
					data["mac"] = mac
					data["interface"] = interface
					data["type"] = (type == "PERM" and "Static" or "Dynamic")
					table.insert(res,data)
				end
			end
			pipe:close()

			return { result = res }
		end
	},
	deleteArp = {
		args = { ip = "none", interface = "none" },
		call = function(args)
			if(args.ip == "ALL") then
				local cmd = tostring("arp -a | awk '{print \" -i \" $NF \" -d \" $2;}' |sed 's/[)(]//g'| while read line ; do echo $line ;arp $line; done")
				luci.sys.exec(cmd)
			else
				luci.sys.exec("/sbin/arp -i " .. tostring(args.interface) .. " -d " .. tostring(args.ip))
			end

			return { result = 'OK'}
		end
	},
	runCommand = {
		args = { cmd = "none", cmd_args = "none" },
		call = function(args)
			local exec_cmd = tostring(args.cmd) .. " " .. tostring(args.cmd_args)
			local handle = io.popen(exec_cmd .. " 2>&1")
			local res = {}
			res = handle:read("*a")
			handle:close()

			return { result = res}
		end
	},
	ping = {
		args = { url = "none", iptype = "ipv4" },
		call = function(args)
			local exec_cmd = "ping " .. tostring(args.iptype == "ipv6" and " -6 " or " -4 ") .. " -c 5 -W 1 " .. tostring(args.url) .. " 2>&1"
			local handle = io.popen(exec_cmd)
			local res = {}
			res = handle:read("*a")
			handle:close()

			return { result = res }
		end
	},
	traceroute = {
		args = { url = "none", iptype = "ipv4" },
		call = function(args)
			local exec_cmd = "traceroute " .. tostring(args.iptype == "ipv6" and "-6" or "-4") .. " -q 1 -w 1 -n " .. tostring(args.url) .. " 2>&1"
			local handle = io.popen(exec_cmd)
			local res = {}
			res = handle:read("*a")
			handle:close()

			return { result = res }
		end
	},
	nslookup = {
		args = { url = "none", iptype = "ipv4" },
		call = function(args)
			local exec_cmd = "nslookup " .. tostring(args.url) .. " 2>&1"
			local handle = io.popen(exec_cmd)
			local res = {}
			res = handle:read("*a")
			handle:close()

			return { result = res }
		end
	}
}

local function parseInput()
        local parse = json.new()
        local done, err

        while true do
                local chunk = io.read(4096)
                if not chunk then
                        break
                elseif not done and not err then
                        done, err = parse:parse(chunk)
                end
        end

        if not done then
                print(json.stringify({ error = err or "Incomplete input" }))
                os.exit(1)
        end

        return parse:get()
end

local function validateArgs(func, uargs)
        local method = methods[func]
        if not method then
                print(json.stringify({ error = "Method not found" }))
                os.exit(1)
        end

        if type(uargs) ~= "table" then
                print(json.stringify({ error = "Invalid arguments" }))
                os.exit(1)
        end

        uargs.ubus_rpc_session = nil

        local k, v
        local margs = method.args or {}
        for k, v in pairs(uargs) do
                if margs[k] == nil or
                   (v ~= nil and type(v) ~= type(margs[k]))
                then
                        print(json.stringify({ error = "Invalid arguments" }))
                        os.exit(1)
                end
        end

        return method
end

if arg[1] == "list" then
        local _, method, rv = nil, nil, {}
        for _, method in pairs(methods) do rv[_] = method.args or {} end
        print((json.stringify(rv):gsub(":%[%]", ":{}")))
elseif arg[1] == "call" then
        local args = parseInput()
        local method = validateArgs(arg[2], args)
        local result, code = method.call(args)
        print((json.stringify(result):gsub("^%[%]$", "{}")))
        os.exit(code or 0)
end

