luci/applications/luci-asterisk/luasrc/asterisk.lua

320 lines
7.3 KiB
Lua

--[[
LuCI - Lua Configuration Interface
Asterisk PBX interface library
Copyright 2009 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
module("luci.asterisk", package.seeall)
local _io = require("io")
local uci = require("luci.model.uci").cursor()
local sys = require("luci.sys")
local util = require("luci.util")
AST_BIN = "/usr/sbin/asterisk"
AST_FLAGS = "-r -x"
--- LuCI Asterisk io interface
-- Handles low level io.
-- @type module
io = luci.util.class()
--- Execute command and return output
-- @param command String containing the command to execute
-- @return String containing the command output
function io.exec(command)
local fh = _io.popen( "%s %s %q" %{ AST_BIN, AST_FLAGS, command }, "r" )
assert(fh, "Failed to invoke asterisk")
local buffer = fh:read("*a")
fh:close()
return buffer
end
--- Execute command and invoke given callback for each readed line
-- @param command String containing the command to execute
-- @param callback Function to call back for each line
-- @return Always true
function io.execl(command, callback)
local ln
local fh = _io.popen( "%s %s %q" %{ AST_BIN, AST_FLAGS, command }, "r" )
assert(fh, "Failed to invoke asterisk")
repeat
ln = fh:read("*l")
callback(ln)
until not ln
fh:close()
return true
end
--- Execute command and return an iterator that returns one line per invokation
-- @param command String containing the command to execute
-- @return Iterator function
function io.execi(command)
local fh = _io.popen( "%s %s %q" %{ AST_BIN, AST_FLAGS, command }, "r" )
assert(fh, "Failed to invoke asterisk")
return function()
local ln = fh:read("*l")
if not ln then fh:close() end
return ln
end
end
--- LuCI Asterisk - core status
core = luci.util.class()
--- Retrive version string.
-- @return String containing the reported asterisk version
function core.version(self)
local version = io.exec("core show version")
return version:gsub(" *\n", "")
end
--- LuCI Asterisk - SIP information.
-- @type module
sip = luci.util.class()
--- Get a list of known SIP peers
-- @return Table containing each SIP peer
function sip.peers(self)
local head = false
local peers = { }
for line in io.execi("sip show peers") do
if not head then
head = true
elseif not line:match(" sip peers ") then
local online, delay, id, uid
local name, host, dyn, nat, acl, port, status =
line:match("(.-) +(.-) +([D ]) ([N ]) (.) (%d+) +(.+)")
if host == '(Unspecified)' then host = nil end
if port == '0' then port = nil else port = tonumber(port) end
dyn = ( dyn == 'D' and true or false )
nat = ( nat == 'N' and true or false )
acl = ( acl ~= ' ' and true or false )
online, delay = status:match("(OK) %((%d+) ms%)")
if online == 'OK' then
online = true
delay = tonumber(delay)
elseif status ~= 'Unmonitored' then
online = false
delay = 0
else
online = nil
delay = 0
end
id, uid = name:match("(.+)/(.+)")
if not ( id and uid ) then
id = name .. "..."
uid = nil
end
peers[#peers+1] = {
online = online,
delay = delay,
name = id,
user = uid,
dynamic = dyn,
nat = nat,
acl = acl,
host = host,
port = port
}
end
end
return peers
end
--- Get informations of given SIP peer
-- @param peer String containing the name of the SIP peer
function sip.peer(peer)
local info = { }
local keys = { }
for line in io.execi("sip show peer " .. peer) do
if #line > 0 then
local key, val = line:match("(.-) *: +(.*)")
if key and val then
key = key:gsub("^ +",""):gsub(" +$", "")
val = val:gsub("^ +",""):gsub(" +$", "")
if key == "* Name" then
key = "Name"
elseif key == "Addr->IP" then
info.address, info.port = val:match("(.+) Port (.+)")
info.port = tonumber(info.port)
elseif key == "Status" then
info.online, info.delay = val:match("(OK) %((%d+) ms%)")
if info.online == 'OK' then
info.online = true
info.delay = tonumber(info.delay)
elseif status ~= 'Unmonitored' then
info.online = false
info.delay = 0
else
info.online = nil
info.delay = 0
end
end
if val == 'Yes' or val == 'yes' or val == '<Set>' then
val = true
elseif val == 'No' or val == 'no' then
val = false
elseif val == '<Not set>' or val == '(none)' then
val = nil
end
keys[#keys+1] = key
info[key] = val
end
end
end
return info, keys
end
--- LuCI Asterisk - Internal helpers
-- @type module
tools = luci.util.class()
--- Convert given value to a list of tokens. Split by white space.
-- @param val String or table value
-- @return Table containing tokens
function tools.parse_list(v)
local tokens = { }
v = type(v) == "table" and v or { v }
for _, v in ipairs(v) do
if type(v) == "string" then
for v in v:gmatch("(%S+)") do
tokens[#tokens+1] = v
end
end
end
return tokens
end
--- Convert given list to a collection of hyperlinks
-- @param list Table of tokens
-- @param url String pattern or callback function to construct urls (optional)
-- @param sep String containing the seperator (optional, default is ", ")
-- @return String containing the html fragment
function tools.hyperlinks(list, url, sep)
local html
local function mkurl(p, t)
if type(p) == "string" then
return p:format(t)
elseif type(p) == "function" then
return p(t)
else
return '#'
end
end
list = list or { }
url = url or "%s"
sep = sep or ", "
for _, token in ipairs(list) do
html = ( html and html .. sep or '' ) ..
'<a href="%s">%s</a>' %{ mkurl(url, token), token }
end
return html or ''
end
--- LuCI Asterisk - Dialzone
-- @type module
dialzone = luci.util.class()
--- Parse a dialzone section
-- @param zone Table containing the zone info
-- @return Table with parsed information
function dialzone.parse(z)
if z['.name'] then
return {
trunks = tools.parse_list(z.uses),
name = z['.name'],
description = z.description or z['.name'],
addprefix = z.addprefix,
matches = tools.parse_list(z.match),
intlmatches = tools.parse_list(z.international),
countrycode = z.countrycode,
localzone = z.localzone,
localprefix = z.localprefix
}
end
end
--- Get a list of known dial zones
-- @return Associative table of zones and table of zone names
function dialzone.zones()
local zones = { }
local znames = { }
uci:foreach("asterisk", "dialzone",
function(z)
zones[z['.name']] = dialzone.parse(z)
znames[#znames+1] = z['.name']
end)
return zones, znames
end
--- Get a specific dial zone
-- @param name Name of the dial zone
-- @return Table containing zone information
function dialzone.zone(n)
local zone
uci:foreach("asterisk", "dialzone",
function(z)
if z['.name'] == n then
zone = dialzone.parse(z)
end
end)
return zone
end
--- Find uci section hash for given zone number
-- @param idx Zone number
-- @return String containing the uci hash pointing to the section
function dialzone.ucisection(i)
local hash
local index = 1
i = tonumber(i)
uci:foreach("asterisk", "dialzone",
function(z)
if not hash and index == i then
hash = z['.name']
end
index = index + 1
end)
return hash
end