Many improvements to pbx-calls.lua:
[project/luci.git] / applications / luci-pbx / luasrc / model / cbi / pbx-calls.lua
1 --[[
2     Copyright 2011 Iordan Iordanov <iiordanov (AT) gmail.com>
3
4     This file is part of luci-pbx.
5
6     luci-pbx is free software: you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation, either version 3 of the License, or
9     (at your option) any later version.
10
11     luci-pbx is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with luci-pbx.  If not, see <http://www.gnu.org/licenses/>.
18 ]]--
19
20 if     nixio.fs.access("/etc/init.d/asterisk")   then
21    server = "asterisk"
22 elseif nixio.fs.access("/etc/init.d/freeswitch") then
23    server = "freeswitch"
24 else
25    server = ""
26 end
27
28 modulename       = "pbx-calls"
29 voipmodulename   = "pbx-voip"
30 googlemodulename = "pbx-google"
31 usersmodulename  = "pbx-users"
32 validoutaccounts = {}
33 allvalidusers    = {}
34
35 -- Checks whether the entered extension is valid syntactically.
36 function is_valid_extension(exten)
37    return (exten:match("[#*+0-9NXZ]+$") ~= nil)
38 end
39
40 -- Gets rid of leading and trailing whitespace.
41 function trim(s)
42   return (s:gsub("^%s*(.-)%s*$", "%1"))
43 end
44
45
46 m = Map (modulename, translate("Call Routing"),
47          translate("This is where you indicate which Google/SIP accounts are used to call what \
48                    country/area codes, which users can use which SIP/Google accounts, how incoming\
49                    calls are routed, what numbers can get into this PBX with a password, and what\
50                    numbers are blacklisted."))
51
52 -- Recreate the config, and restart services after changes are commited to the configuration.
53 function m.on_after_commit(self)
54         luci.sys.call("/etc/init.d/pbx-" .. server .. " restart 1\>/dev/null 2\>/dev/null")
55         luci.sys.call("/etc/init.d/"     .. server .. " restart 1\>/dev/null 2\>/dev/null")
56 end
57
58 ----------------------------------------------------------------------------------------------------
59 s = m:section(NamedSection, "outgoing_calls", "call_routing", translate("Outgoing Calls"),
60         translate("If you have more than one account which can make outgoing calls, you\
61                 should enter a list of phone numbers and prefixes in the following fields for each\
62                 provider listed. Invalid prefixes are removed silently, and only 0-9, X, Z, N, #, *,\
63                 and + are valid characters. The letter X matches 0-9, Z matches 1-9, and N matches 2-9.\
64                 For example to make calls to Germany through a provider, you can enter 49. To make calls\
65                 to North America, you can enter 1NXXNXXXXXX. If one of your providers can make \"local\"\
66                 calls to an area code like New York's 646, you can enter 646NXXXXXX for that\
67                 provider. You should leave one account with an empty list to make calls with\
68                 it by default, if no other provider's prefixes match. The system will automatically\
69                 replace an empty list with a message that the provider dials all numbers. Be as specific as\
70                 possible (i.e. 1NXXNXXXXXX is better than 1). Please note all international dial codes\
71                 are discarded (e.g. 00, 011, 010, 0011). Entries can be made in a\
72                 space-separated list, and/or one per line by hitting enter after every one."))
73 s.anonymous = true
74
75 m.uci:foreach(googlemodulename, "gtalk_jabber", 
76               function(s1)
77                  if s1.username ~= nil and s1.name ~= nil and
78                     s1.make_outgoing_calls == "yes" then
79                     patt = s:option(DynamicList, s1.name, s1.username)
80
81                     -- Add provider to the associative array of valid accounts.
82                     validoutaccounts[s1.name] = s1.username
83
84                     -- If the saved field is empty, we return a string
85                     -- telling the user that this account would dial any exten.
86                     function patt.cfgvalue(self, section)
87                        value = self.map:get(section, self.option)
88
89                        if value == nil then
90                           return {"Dials any number"}
91                        else
92                           return value
93                        end
94                     end
95
96                     -- Write only valid extensions into the config file.
97                     function patt.write(self, section, value)
98                        newvalue = {}
99                        nindex = 1
100                        for index, field in ipairs(value) do
101                           val = trim(value[index])
102                           if is_valid_extension(val) == true then
103                              newvalue[nindex] = val
104                              nindex = nindex + 1
105                           end
106                        end
107                        DynamicList.write(self, section, newvalue)
108                     end
109                  end
110               end)
111
112 m.uci:foreach(voipmodulename, "voip_provider", 
113               function(s1)
114                  if s1.defaultuser ~= nil and s1.host ~= nil and 
115                     s1.name ~= nil        and s1.make_outgoing_calls == "yes" then
116                     patt = s:option(DynamicList, s1.name, s1.defaultuser .. "@" .. s1.host)
117
118                     -- Add provider to the associative array of valid accounts.
119                     validoutaccounts[s1.name] = s1.defaultuser .. "@" .. s1.host
120
121                     -- If the saved field is empty, we return a string
122                     -- telling the user that this account would dial any exten.
123                     function patt.cfgvalue(self, section)
124                        value = self.map:get(section, self.option)
125
126                        if value == nil then
127                           return {"Dials any number"}
128                        else
129                           return value
130                        end
131                     end
132
133                     -- Write only valid extensions into the config file.
134                     function patt.write(self, section, value)
135                        newvalue = {}
136                        nindex = 1
137                        for index, field in ipairs(value) do
138                           val = trim(value[index])
139                           if is_valid_extension(val) == true then
140                              newvalue[nindex] = val
141                              nindex = nindex + 1
142                           end
143                        end
144                        DynamicList.write(self, section, newvalue)
145                     end
146                  end
147               end)
148
149 ----------------------------------------------------------------------------------------------------
150 s = m:section(NamedSection, "incoming_calls", "call_routing", translate("Incoming Calls"),
151         translate("For each provider that receives calls, here you can restrict which users to ring\
152                 on incoming calls. If the list is empty, the system will indicate that all users\
153                 which are enabled for incoming calls will ring. Invalid usernames will be rejected\
154                 silently. Also, entering a username here overrides the user's setting to not receive\
155                 incoming calls, so this way, you can make users ring only for select providers.\
156                 Entries can be made in a space-separated list, and/or one per\
157                 line by hitting enter after every one."))
158 s.anonymous = true
159
160 m.uci:foreach(googlemodulename, "gtalk_jabber", 
161               function(s1)
162                  if s1.username ~= nil and s1.register == "yes" then
163                     field_name=string.gsub(s1.username, "%W", "_")
164                     gtalkaccts = s:option(DynamicList, field_name, s1.username)
165                     
166                     -- If the saved field is empty, we return a string
167                     -- telling the user that this account would dial any exten.
168                     function gtalkaccts.cfgvalue(self, section)
169                        value = self.map:get(section, self.option)
170                        
171                        if value == nil then
172                           return {"Rings all users"}
173                        else
174                           return value
175                        end
176                     end
177
178                     -- Write only valid user names.
179                     function gtalkaccts.write(self, section, value)
180                        newvalue = {}
181                        nindex = 1
182                        for index, field in ipairs(value) do
183                           trimuser = trim(value[index])
184                           if allvalidusers[trimuser] == true then
185                              newvalue[nindex] = trimuser
186                              nindex = nindex + 1
187                           end
188                        end
189                        DynamicList.write(self, section, newvalue)
190                     end
191                  end
192               end)
193
194
195 m.uci:foreach(voipmodulename, "voip_provider", 
196               function(s1)
197                  if s1.defaultuser ~= nil and s1.host ~= nil and s1.register == "yes" then
198                     field_name=string.gsub(s1.defaultuser .. "_" .. s1.host, "%W", "_")
199                     voipaccts = s:option(DynamicList, field_name, s1.defaultuser .. "@" .. s1.host)
200
201                     -- If the saved field is empty, we return a string
202                     -- telling the user that this account would dial any exten.
203                     function voipaccts.cfgvalue(self, section)
204                        value = self.map:get(section, self.option)
205                        
206                        if value == nil then
207                           return {"Rings all users"}
208                        else
209                           return value
210                        end
211                     end
212
213                     -- Write only valid user names.
214                     function voipaccts.write(self, section, value)
215                        newvalue = {}
216                        nindex = 1
217                        for index, field in ipairs(value) do
218                           trimuser = trim(value[index])
219                           if allvalidusers[trimuser] == true then
220                              newvalue[nindex] = trimuser
221                              nindex = nindex + 1
222                           end
223                        end
224                        DynamicList.write(self, section, newvalue)
225                     end
226                  end
227               end)
228
229 ----------------------------------------------------------------------------------------------------
230 s = m:section(NamedSection, "providers_user_can_use", "call_routing",
231       translate("Providers Used for Outgoing Calls"),
232       translate("If you would like, you could restrict which providers users are allowed to use for outgoing\
233         calls. By default all users can use all providers. To show up in the list below the user should\
234         be allowed to make outgoing calls in the \"User Accounts\" page. Enter VoIP providers in the format\
235         username@some.host.name, as listed in \"Outgoing Calls\" above. It's easiest to copy and paste\
236         the providers from above. Invalid entries will be rejected silently. Entries can be made in a \
237         space-separated list, and/or one per line by hitting enter after every one."))
238 s.anonymous = true
239
240 m.uci:foreach(usersmodulename, "local_user",
241               function(s1)
242                  -- Add user to list of all valid users.
243                  if s1.defaultuser ~= nil then allvalidusers[s1.defaultuser] = true end
244
245                  if s1.defaultuser ~= nil and s1.can_call == "yes" then
246                     providers = s:option(DynamicList, s1.defaultuser, s1.defaultuser)
247                     
248                     -- If the saved field is empty, we return a string
249                     -- telling the user that this account would dial any exten.
250                     function providers.cfgvalue(self, section)
251                        value = self.map:get(section, self.option)
252
253                        if value == nil then
254                           return {"Uses all provider accounts"}
255                        else
256                           newvalue = {}
257                           -- Convert internal names to user@host values.
258                           for i,v in ipairs(value) do
259                              newvalue[i] = validoutaccounts[v]
260                           end
261                           return newvalue
262                        end
263                     end
264                     
265                     -- Cook the new values prior to entering them into the config file.
266                     -- Also, enter them only if they are valid.
267                     function providers.write(self, section, value)
268                        cookedvalue = {}
269                        cindex = 1
270                        for index, field in ipairs(value) do
271                           cooked = string.gsub(trim(value[index]), "%W", "_")
272                           if validoutaccounts[cooked] ~= nil then
273                              cookedvalue[cindex] = cooked
274                              cindex = cindex + 1
275                           end
276                        end
277                        DynamicList.write(self, section, cookedvalue)
278                     end
279                  end
280               end)
281
282 ----------------------------------------------------------------------------------------------------
283 s = m:section(TypedSection, "callthrough_numbers", translate("Call-through Numbers"),
284         translate("Designate numbers which will be allowed to call through this system and which user's\
285                   privileges it will have."))
286 s.anonymous = true
287 s.addremove = true
288
289 num = s:option(DynamicList, "callthrough_number_list", translate("Call-through Numbers"))
290 num.datatype = "uinteger"
291
292 p = s:option(ListValue, "enabled", translate("Enabled"))
293 p:value("yes", translate("Yes"))
294 p:value("no",  translate("No"))
295 p.default = "yes"
296
297 user = s:option(Value, "defaultuser",  translate("User Name"),
298          translate("The number(s) specified above will be able to dial out with this user's providers.\
299                    Invalid usernames are dropped silently, please verify that the entry was accepted."))
300 function user.write(self, section, value)
301    trimuser = trim(value)
302    if allvalidusers[trimuser] == true then
303       Value.write(self, section, trimuser)
304    end
305 end
306
307 pwd = s:option(Value, "pin", translate("PIN"),
308                translate("Your PIN disappears when saved for your protection. It will be changed\
309                          only when you enter a value different from the saved one. Leaving the PIN\
310                          empty is possible, but please beware of the security implications."))
311 pwd.password = true
312 pwd.rmempty = false
313
314 -- We skip reading off the saved value and return nothing.
315 function pwd.cfgvalue(self, section)
316     return "" 
317 end
318
319 -- We check the entered value against the saved one, and only write if the entered value is
320 -- something other than the empty string, and it differes from the saved value.
321 function pwd.write(self, section, value)
322     local orig_pwd = m:get(section, self.option)
323     if value and #value > 0 and orig_pwd ~= value then
324         Value.write(self, section, value)
325     end
326 end
327
328 ----------------------------------------------------------------------------------------------------
329 s = m:section(NamedSection, "blacklisting", "call_routing", translate("Blacklisted Numbers"),
330                  translate("Enter phone numbers that you want to decline calls from automatically.\
331                         You should probably omit the country code and any leading\
332                         zeroes, but please experiment to make sure you are blocking numbers from your\
333                         desired area successfully."))
334 s.anonymous = true
335
336 b = s:option(DynamicList, "blacklist1", translate("Dynamic List of Blacklisted Numbers"),
337             translate("Specify numbers individually here. Press enter to add more numbers."))
338 b.cast = "string"
339 b.datatype = "uinteger"
340
341 b = s:option(Value, "blacklist2", translate("Space-Separated List of Blacklisted Numbers"),
342             translate("Copy-paste large lists of numbers here."))
343 b.template = "cbi/tvalue"
344 b.rows = 3
345
346 return m