d4f21cdcc3433f05561018ff003d668b9a0d89d9
[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 what 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 that can make outgoing calls, you \
131    should enter a list of phone numbers and/or 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 not matched by another \
140    provider's prefixes. Be as specific as possible (i.e. 1NXXNXXXXXX is better than 1). Please note \
141    all international dial codes are discarded (e.g. 00, 011, 010, 0011). Entries can be made in a \
142    space-separated 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 numbers unmatched elsewhere")}
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 enabled for incoming calls, here you can restrict which users to\
188                 ring on incoming calls. If the list is empty, the system will indicate that all users \
189                 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 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("For each user enabled for outgoing calls you can restrict what providers the user \
240         can use for outgoing calls. By default all users can use all providers. To show up in the list \
241         below the user should be allowed to make outgoing calls in the \"User Accounts\" page. Enter VoIP \
242         providers in the format username@some.host.name, as listed in \"Outgoing Calls\" above. It's \
243         easiest to copy and paste the providers from above. Invalid entries, including providers not \
244         enabled for outgoing calls, will be rejected silently. Entries can be made in a space-separated \
245         list, and/or one per 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 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 that are 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 zeroes, but please \
340               experiment to make sure you are blocking numbers from your desired area successfully."))
341 s.anonymous = true
342
343 b = s:option(DynamicList, "blacklist1", translate("Dynamic List of Blacklisted Numbers"),
344             translate("Specify numbers individually here. Press enter to add more numbers."))
345 b.cast = "string"
346 b.datatype = "uinteger"
347
348 b = s:option(Value, "blacklist2", translate("Space-Separated List of Blacklisted Numbers"),
349             translate("Copy-paste large lists of numbers here."))
350 b.template = "cbi/tvalue"
351 b.rows = 3
352
353 return m