applications/luci-asterisk: fix copy & paste error
[project/luci.git] / applications / luci-asterisk / luasrc / asterisk.lua
1 --[[
2 LuCI - Lua Configuration Interface
3 Asterisk PBX interface library
4
5 Copyright 2009 Jo-Philipp Wich <xm@subsignal.org>
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11 http://www.apache.org/licenses/LICENSE-2.0
12
13 $Id$
14
15 ]]--
16
17 module("luci.asterisk", package.seeall)
18 require("luci.asterisk.cc_idd")
19
20 local _io = require("io")
21 local uci = require("luci.model.uci").cursor()
22 local sys = require("luci.sys")
23 local util = require("luci.util")
24
25 AST_BIN = "/usr/sbin/asterisk"
26 AST_FLAGS = "-r -x"
27
28
29 --- LuCI Asterisk - Resync uci context
30 function uci_resync()
31 uci = luci.model.uci.cursor()
32 end
33
34 --- LuCI Asterisk io interface
35 -- Handles low level io.
36 -- @type module
37 io = luci.util.class()
38
39 --- Execute command and return output
40 -- @param command String containing the command to execute
41 -- @return String containing the command output
42 function io.exec(command)
43 local fh = _io.popen( "%s %s %q" %{ AST_BIN, AST_FLAGS, command }, "r" )
44 assert(fh, "Failed to invoke asterisk")
45
46 local buffer = fh:read("*a")
47 fh:close()
48 return buffer
49 end
50
51 --- Execute command and invoke given callback for each readed line
52 -- @param command String containing the command to execute
53 -- @param callback Function to call back for each line
54 -- @return Always true
55 function io.execl(command, callback)
56 local ln
57 local fh = _io.popen( "%s %s %q" %{ AST_BIN, AST_FLAGS, command }, "r" )
58 assert(fh, "Failed to invoke asterisk")
59
60 repeat
61 ln = fh:read("*l")
62 callback(ln)
63 until not ln
64
65 fh:close()
66 return true
67 end
68
69 --- Execute command and return an iterator that returns one line per invokation
70 -- @param command String containing the command to execute
71 -- @return Iterator function
72 function io.execi(command)
73 local fh = _io.popen( "%s %s %q" %{ AST_BIN, AST_FLAGS, command }, "r" )
74 assert(fh, "Failed to invoke asterisk")
75
76 return function()
77 local ln = fh:read("*l")
78 if not ln then fh:close() end
79 return ln
80 end
81 end
82
83
84 --- LuCI Asterisk - core status
85 core = luci.util.class()
86
87 --- Retrive version string.
88 -- @return String containing the reported asterisk version
89 function core.version(self)
90 local version = io.exec("core show version")
91 return version:gsub(" *\n", "")
92 end
93
94
95 --- LuCI Asterisk - SIP information.
96 -- @type module
97 sip = luci.util.class()
98
99 --- Get a list of known SIP peers
100 -- @return Table containing each SIP peer
101 function sip.peers(self)
102 local head = false
103 local peers = { }
104
105 for line in io.execi("sip show peers") do
106 if not head then
107 head = true
108 elseif not line:match(" sip peers ") then
109 local online, delay, id, uid
110 local name, host, dyn, nat, acl, port, status =
111 line:match("(.-) +(.-) +([D ]) ([N ]) (.) (%d+) +(.+)")
112
113 if host == '(Unspecified)' then host = nil end
114 if port == '0' then port = nil else port = tonumber(port) end
115
116 dyn = ( dyn == 'D' and true or false )
117 nat = ( nat == 'N' and true or false )
118 acl = ( acl ~= ' ' and true or false )
119
120 online, delay = status:match("(OK) %((%d+) ms%)")
121
122 if online == 'OK' then
123 online = true
124 delay = tonumber(delay)
125 elseif status ~= 'Unmonitored' then
126 online = false
127 delay = 0
128 else
129 online = nil
130 delay = 0
131 end
132
133 id, uid = name:match("(.+)/(.+)")
134
135 if not ( id and uid ) then
136 id = name .. "..."
137 uid = nil
138 end
139
140 peers[#peers+1] = {
141 online = online,
142 delay = delay,
143 name = id,
144 user = uid,
145 dynamic = dyn,
146 nat = nat,
147 acl = acl,
148 host = host,
149 port = port
150 }
151 end
152 end
153
154 return peers
155 end
156
157 --- Get informations of given SIP peer
158 -- @param peer String containing the name of the SIP peer
159 function sip.peer(peer)
160 local info = { }
161 local keys = { }
162
163 for line in io.execi("sip show peer " .. peer) do
164 if #line > 0 then
165 local key, val = line:match("(.-) *: +(.*)")
166 if key and val then
167
168 key = key:gsub("^ +",""):gsub(" +$", "")
169 val = val:gsub("^ +",""):gsub(" +$", "")
170
171 if key == "* Name" then
172 key = "Name"
173 elseif key == "Addr->IP" then
174 info.address, info.port = val:match("(.+) Port (.+)")
175 info.port = tonumber(info.port)
176 elseif key == "Status" then
177 info.online, info.delay = val:match("(OK) %((%d+) ms%)")
178 if info.online == 'OK' then
179 info.online = true
180 info.delay = tonumber(info.delay)
181 elseif status ~= 'Unmonitored' then
182 info.online = false
183 info.delay = 0
184 else
185 info.online = nil
186 info.delay = 0
187 end
188 end
189
190 if val == 'Yes' or val == 'yes' or val == '<Set>' then
191 val = true
192 elseif val == 'No' or val == 'no' then
193 val = false
194 elseif val == '<Not set>' or val == '(none)' then
195 val = nil
196 end
197
198 keys[#keys+1] = key
199 info[key] = val
200 end
201 end
202 end
203
204 return info, keys
205 end
206
207
208 --- LuCI Asterisk - Internal helpers
209 -- @type module
210 tools = luci.util.class()
211
212 --- Convert given value to a list of tokens. Split by white space.
213 -- @param val String or table value
214 -- @return Table containing tokens
215 function tools.parse_list(v)
216 local tokens = { }
217
218 v = type(v) == "table" and v or { v }
219 for _, v in ipairs(v) do
220 if type(v) == "string" then
221 for v in v:gmatch("(%S+)") do
222 tokens[#tokens+1] = v
223 end
224 end
225 end
226
227 return tokens
228 end
229
230 --- Convert given list to a collection of hyperlinks
231 -- @param list Table of tokens
232 -- @param url String pattern or callback function to construct urls (optional)
233 -- @param sep String containing the seperator (optional, default is ", ")
234 -- @return String containing the html fragment
235 function tools.hyperlinks(list, url, sep)
236 local html
237
238 local function mkurl(p, t)
239 if type(p) == "string" then
240 return p:format(t)
241 elseif type(p) == "function" then
242 return p(t)
243 else
244 return '#'
245 end
246 end
247
248 list = list or { }
249 url = url or "%s"
250 sep = sep or ", "
251
252 for _, token in ipairs(list) do
253 html = ( html and html .. sep or '' ) ..
254 '<a href="%s">%s</a>' %{ mkurl(url, token), token }
255 end
256
257 return html or ''
258 end
259
260
261 --- LuCI Asterisk - International Direct Dialing Prefixes
262 -- @type module
263 idd = luci.util.class()
264
265 --- Lookup the country name for the given IDD code.
266 -- @param country String containing IDD code
267 -- @return String containing the country name
268 function idd.country(c)
269 for _, v in ipairs(cc_idd.CC_IDD) do
270 if type(v[3]) == "table" then
271 for _, v2 in ipairs(v[3]) do
272 if v2 == tostring(c) then
273 return v[1]
274 end
275 end
276 elseif v[3] == tostring(c) then
277 return v[1]
278 end
279 end
280 end
281
282 --- Lookup the country code for the given IDD code.
283 -- @param country String containing IDD code
284 -- @return Table containing the country code(s)
285 function idd.cc(c)
286 for _, v in ipairs(cc_idd.CC_IDD) do
287 if type(v[3]) == "table" then
288 for _, v2 in ipairs(v[3]) do
289 if v2 == tostring(c) then
290 return type(v[2]) == "table"
291 and v[2] or { v[2] }
292 end
293 end
294 elseif v[3] == tostring(c) then
295 return type(v[2]) == "table"
296 and v[2] or { v[2] }
297 end
298 end
299 end
300
301 --- Lookup the IDD code(s) for the given country.
302 -- @param idd String containing the country name
303 -- @return Table containing the IDD code(s)
304 function idd.idd(c)
305 for _, v in ipairs(cc_idd.CC_IDD) do
306 if v[1]:lower():match(c:lower()) then
307 return type(v[3]) == "table"
308 and v[3] or { v[3] }
309 end
310 end
311 end
312
313
314 --- LuCI Asterisk - Country Code Prefixes
315 -- @type module
316 cc = luci.util.class()
317
318 --- Lookup the country name for the given CC code.
319 -- @param country String containing CC code
320 -- @return String containing the country name
321 function cc.country(c)
322 for _, v in ipairs(cc_idd.CC_IDD) do
323 if type(v[2]) == "table" then
324 for _, v2 in ipairs(v[2]) do
325 if v2 == tostring(c) then
326 return v[1]
327 end
328 end
329 elseif v[2] == tostring(c) then
330 return v[1]
331 end
332 end
333 end
334
335 --- Lookup the international dialing code for the given CC code.
336 -- @param cc String containing CC code
337 -- @return String containing IDD code
338 function cc.idd(c)
339 for _, v in ipairs(cc_idd.CC_IDD) do
340 if type(v[2]) == "table" then
341 for _, v2 in ipairs(v[2]) do
342 if v2 == tostring(c) then
343 return type(v[3]) == "table"
344 and v[3] or { v[3] }
345 end
346 end
347 elseif v[2] == tostring(c) then
348 return type(v[3]) == "table"
349 and v[3] or { v[3] }
350 end
351 end
352 end
353
354 --- Lookup the CC code(s) for the given country.
355 -- @param country String containing the country name
356 -- @return Table containing the CC code(s)
357 function cc.cc(c)
358 for _, v in ipairs(cc_idd.CC_IDD) do
359 if v[1]:lower():match(c:lower()) then
360 return type(v[2]) == "table"
361 and v[2] or { v[2] }
362 end
363 end
364 end
365
366
367 --- LuCI Asterisk - Dialzone
368 -- @type module
369 dialzone = luci.util.class()
370
371 --- Parse a dialzone section
372 -- @param zone Table containing the zone info
373 -- @return Table with parsed information
374 function dialzone.parse(z)
375 if z['.name'] then
376 return {
377 trunks = tools.parse_list(z.uses),
378 name = z['.name'],
379 description = z.description or z['.name'],
380 addprefix = z.addprefix,
381 matches = tools.parse_list(z.match),
382 intlmatches = tools.parse_list(z.international),
383 countrycode = z.countrycode,
384 localzone = z.localzone,
385 localprefix = z.localprefix
386 }
387 end
388 end
389
390 --- Get a list of known dial zones
391 -- @return Associative table of zones and table of zone names
392 function dialzone.zones()
393 local zones = { }
394 local znames = { }
395 uci:foreach("asterisk", "dialzone",
396 function(z)
397 zones[z['.name']] = dialzone.parse(z)
398 znames[#znames+1] = z['.name']
399 end)
400 return zones, znames
401 end
402
403 --- Get a specific dial zone
404 -- @param name Name of the dial zone
405 -- @return Table containing zone information
406 function dialzone.zone(n)
407 local zone
408 uci:foreach("asterisk", "dialzone",
409 function(z)
410 if z['.name'] == n then
411 zone = dialzone.parse(z)
412 end
413 end)
414 return zone
415 end
416
417 --- Find uci section hash for given zone number
418 -- @param idx Zone number
419 -- @return String containing the uci hash pointing to the section
420 function dialzone.ucisection(i)
421 local hash
422 local index = 1
423 i = tonumber(i)
424 uci:foreach("asterisk", "dialzone",
425 function(z)
426 if not hash and index == i then
427 hash = z['.name']
428 end
429 index = index + 1
430 end)
431 return hash
432 end
433
434
435 --- LuCI Asterisk - Dialplan
436 -- @type module
437 dialplan = luci.util.class()
438
439 --- Parse a dialplan section
440 -- @param plan Table containing the plan info
441 -- @return Table with parsed information
442 function dialplan.parse(z)
443 if z['.name'] then
444 local plan = {
445 zones = { },
446 name = z['.name'],
447 description = z.description or z['.name']
448 }
449
450 for _, name in ipairs(tools.parse_list(z.include)) do
451 local zone = dialzone.zone(name)
452 if zone then
453 plan.zones[#plan.zones+1] = zone
454 end
455 end
456
457 return plan
458 end
459 end
460
461 --- Get a list of known dial plans
462 -- @return Associative table of plans and table of plan names
463 function dialplan.plans()
464 local plans = { }
465 local pnames = { }
466 uci:foreach("asterisk", "dialplan",
467 function(p)
468 plans[p['.name']] = dialplan.parse(p)
469 pnames[#pnames+1] = p['.name']
470 end)
471 return plans, pnames
472 end
473
474 --- Get a specific dial plan
475 -- @param name Name of the dial plan
476 -- @return Table containing plan information
477 function dialplan.plan(n)
478 local plan
479 uci:foreach("asterisk", "dialplan",
480 function(p)
481 if p['.name'] == n then
482 plan = dialplan.parse(p)
483 end
484 end)
485 return plan
486 end