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