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