applications/luci-asterisk: add initial code for the asterisk interface library
[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
19 local _io  = require("io")
20 local sys  = require("luci.sys")
21 local util = require("luci.util")
22
23 AST_BIN   = "/usr/sbin/asterisk"
24 AST_FLAGS = "-r -x"
25
26
27 --- LuCI Asterisk io interface
28 -- Handles low level io.
29 -- @type        module
30 io = luci.util.class()
31
32 --- Execute command and return output
33 -- @param command       String containing the command to execute
34 -- @return                      String containing the command output
35 function io.exec(command)
36         local fh = _io.popen( "%s %s %q" %{ AST_BIN, AST_FLAGS, command }, "r" )
37         assert(fh, "Failed to invoke asterisk")
38
39         local buffer = fh:read("*a")
40         fh:close()
41         return buffer
42 end
43
44 --- Execute command and invoke given callback for each readed line
45 -- @param command       String containing the command to execute
46 -- @param callback      Function to call back for each line
47 -- @return                      Always true
48 function io.execl(command, callback)
49         local ln
50         local fh = _io.popen( "%s %s %q" %{ AST_BIN, AST_FLAGS, command }, "r" )
51         assert(fh, "Failed to invoke asterisk")
52
53         repeat
54                 ln = fh:read("*l")
55                 callback(ln)
56         until not ln
57
58         fh:close()
59         return true
60 end
61
62 --- Execute command and return an iterator that returns one line per invokation
63 -- @param command       String containing the command to execute
64 -- @return                      Iterator function
65 function io.execi(command)
66         local fh = _io.popen( "%s %s %q" %{ AST_BIN, AST_FLAGS, command }, "r" )
67         assert(fh, "Failed to invoke asterisk")
68
69         return function()
70                 local ln = fh:read("*l")
71                 if not ln then fh:close() end
72                 return ln
73         end
74 end
75
76
77 --- LuCI Asterisk - core status
78 core = luci.util.class()
79
80 --- Retrive version string.
81 -- @return      String containing the reported asterisk version
82 function core.version(self)
83         local version = io.exec("core show version")
84         return version:gsub(" *\n", "")
85 end
86
87
88 --- LuCI Asterisk - SIP information.
89 -- @type module
90 sip = luci.util.class()
91
92 --- Get a list of known SIP peers
93 -- @return              Table containing each SIP peer
94 function sip.peers(self)
95         local head  = false
96         local peers = { }
97
98         for line in io.execi("sip show peers") do
99                 if not head then
100                         head = true
101                 elseif not line:match(" sip peers ") then
102                         local online, delay, id, uid
103                         local name, host, dyn, nat, acl, port, status =
104                                 line:match("(.-) +(.-) +([D ])   ([N ])   (.)  (%d+) +(.+)")
105
106                         if host == '(Unspecified)' then host = nil end
107                         if port == '0' then port = nil else port = tonumber(port) end
108
109                         dyn = ( dyn == 'D' and true or false )
110                         nat = ( nat == 'N' and true or false )
111                         acl = ( acl ~= ' ' and true or false )
112
113                         online, delay = status:match("(OK) %((%d+) ms%)")
114
115                         if online == 'OK' then
116                                 online = true
117                                 delay  = tonumber(delay)
118                         elseif status ~= 'Unmonitored' then
119                                 online = false
120                                 delay  = 0
121                         else
122                                 online = nil
123                                 delay  = 0
124                         end
125
126                         id, uid = name:match("(.+)/(.+)")
127
128                         if not ( id and uid ) then
129                                 id  = name .. "..."
130                                 uid = nil
131                         end
132
133                         peers[#peers+1] = {
134                                 online  = online,
135                                 delay   = delay,
136                                 name    = id,
137                                 user    = uid,
138                                 dynamic = dyn,
139                                 nat     = nat,
140                                 acl     = acl,
141                                 host    = host,
142                                 port    = port
143                         }
144                 end
145         end
146
147         return peers
148 end
149
150 --- Get informations of given SIP peer
151 -- @param peer  String containing the name of the SIP peer
152 function sip.peer(peer)
153         local info = { }
154         local keys = { }
155
156         for line in io.execi("sip show peer " .. peer) do
157                 if #line > 0 then
158                         local key, val = line:match("(.-) *: +(.*)")
159                         if key and val then
160
161                                 key = key:gsub("^ +",""):gsub(" +$", "")
162                                 val = val:gsub("^ +",""):gsub(" +$", "")
163
164                                 if key == "* Name" then
165                                         key = "Name"
166                                 elseif key == "Addr->IP" then
167                                         info.address, info.port = val:match("(.+) Port (.+)")
168                                         info.port = tonumber(info.port)
169                                 elseif key == "Status" then
170                                         info.online, info.delay = val:match("(OK) %((%d+) ms%)")
171                                         if info.online == 'OK' then
172                                                 info.online = true
173                                                 info.delay  = tonumber(info.delay)
174                                         elseif status ~= 'Unmonitored' then
175                                                 info.online = false
176                                                 info.delay  = 0
177                                         else
178                                                 info.online = nil
179                                                 info.delay  = 0
180                                         end
181                                 end
182
183                                 if val == 'Yes' or val == 'yes' or val == '<Set>' then
184                                         val = true
185                                 elseif val == 'No' or val == 'no' then
186                                         val = false
187                                 elseif val == '<Not set>' or val == '(none)' then
188                                         val = nil
189                                 end
190
191                                 keys[#keys+1] = key
192                                 info[key] = val
193                         end
194                 end
195         end
196
197         return info, keys
198 end