Merge pull request #1571 from user7887/luci-ru
[project/luci.git] / applications / luci-app-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 provider 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 telling the user that
204    -- this provider would ring all users configured for incoming calls.
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 telling the user
257    -- that this user uses all providers enavled for outgoing calls.
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 they will have."))
293 s.anonymous = true
294 s.addremove = true
295
296 num = s:option(DynamicList, "callthrough_number_list", translate("Call-through Numbers"),
297         translate("Specify numbers individually here. Press enter to add more numbers. \
298         You will have to experiment with what country and area codes you need to add \
299         to the number."))
300 num.datatype = "uinteger"
301
302 p = s:option(ListValue, "enabled", translate("Enabled"))
303 p:value("yes", translate("Yes"))
304 p:value("no",  translate("No"))
305 p.default = "yes"
306
307 user = s:option(Value, "defaultuser",  translate("User Name"),
308          translate("The number(s) specified above will be able to dial out with this user's providers. \
309                    Invalid usernames, including users not enabled for outgoing calls, are dropped silently. \
310                    Please verify that the entry was accepted."))
311 function user.write(self, section, value)
312    trimuser = luci.util.trim(value)
313    if allvalidusers[trimuser] == true then
314       Value.write(self, section, trimuser)
315    end
316 end
317
318 pwd = s:option(Value, "pin", translate("PIN"),
319                translate("Your PIN disappears when saved for your protection. It will be changed \
320                          only when you enter a value different from the saved one. Leaving the PIN \
321                          empty is possible, but please beware of the security implications."))
322 pwd.password = true
323 pwd.rmempty = false
324
325 -- We skip reading off the saved value and return nothing.
326 function pwd.cfgvalue(self, section)
327     return "" 
328 end
329
330 -- We check the entered value against the saved one, and only write if the entered value is
331 -- something other than the empty string, and it differes from the saved value.
332 function pwd.write(self, section, value)
333     local orig_pwd = m:get(section, self.option)
334     if value and #value > 0 and orig_pwd ~= value then
335         Value.write(self, section, value)
336     end
337 end
338
339 ----------------------------------------------------------------------------------------------------
340 s = m:section(TypedSection, "callback_numbers", translate("Call-back Numbers"),
341         translate("Designate numbers to whom the system will hang up and call back, which provider will \
342                    be used to call them, and which user's privileges will be granted to them."))
343 s.anonymous = true
344 s.addremove = true
345
346 num = s:option(DynamicList, "callback_number_list", translate("Call-back Numbers"),
347         translate("Specify numbers individually here. Press enter to add more numbers. \
348         You will have to experiment with what country and area codes you need to add \
349         to the number."))
350 num.datatype = "uinteger"
351
352 p = s:option(ListValue, "enabled", translate("Enabled"))
353 p:value("yes", translate("Yes"))
354 p:value("no",  translate("No"))
355 p.default = "yes"
356
357 delay = s:option(Value, "callback_hangup_delay", translate("Hang-up Delay"),
358             translate("How long to wait before hanging up. If the provider you use to dial automatically forwards \
359             to voicemail, you can set this value to a delay that will allow you to hang up before your call gets \
360             forwarded and you get billed for it."))
361 delay.datatype = "uinteger"
362 delay.default = 0
363
364 user = s:option(Value, "defaultuser",  translate("User Name"),
365          translate("The number(s) specified above will be able to dial out with this user's providers. \
366                    Invalid usernames, including users not enabled for outgoing calls, are dropped silently. \
367                    Please verify that the entry was accepted."))
368 function user.write(self, section, value)
369    trimuser = luci.util.trim(value)
370    if allvalidusers[trimuser] == true then
371       Value.write(self, section, trimuser)
372    end
373 end
374
375 pwd = s:option(Value, "pin", translate("PIN"),
376                translate("Your PIN disappears when saved for your protection. It will be changed \
377                          only when you enter a value different from the saved one. Leaving the PIN \
378                          empty is possible, but please beware of the security implications."))
379 pwd.password = true
380 pwd.rmempty = false
381
382 -- We skip reading off the saved value and return nothing.
383 function pwd.cfgvalue(self, section)
384     return "" 
385 end
386
387 -- We check the entered value against the saved one, and only write if the entered value is
388 -- something other than the empty string, and it differes from the saved value.
389 function pwd.write(self, section, value)
390     local orig_pwd = m:get(section, self.option)
391     if value and #value > 0 and orig_pwd ~= value then
392         Value.write(self, section, value)
393     end
394 end
395
396 provider = s:option(Value, "callback_provider",  translate("Call-back Provider"),
397          translate("Enter a VoIP provider to use for call-back in the format username@some.host.name, as listed in \
398          \"Outgoing Calls\" above. It's easiest to copy and paste the providers from above. Invalid entries, including \
399          providers not enabled for outgoing calls, will be rejected silently."))
400 function provider.write(self, section, value)
401     cooked = string.gsub(luci.util.trim(value), "%W", "_")
402     if validoutaccounts[cooked] ~= nil then
403         Value.write(self, section, value)
404     end
405 end
406
407 ----------------------------------------------------------------------------------------------------
408 s = m:section(NamedSection, "blacklisting", "call_routing", translate("Blacklisted Numbers"),
409               translate("Enter phone numbers that you want to decline calls from automatically. \
410               You should probably omit the country code and any leading zeroes, but please \
411               experiment to make sure you are blocking numbers from your desired area successfully."))
412 s.anonymous = true
413
414 b = s:option(DynamicList, "blacklist1", translate("Dynamic List of Blacklisted Numbers"),
415             translate("Specify numbers individually here. Press enter to add more numbers."))
416 b.cast = "string"
417 b.datatype = "uinteger"
418
419 b = s:option(Value, "blacklist2", translate("Space-Separated List of Blacklisted Numbers"),
420             translate("Copy-paste large lists of numbers here."))
421 b.template = "cbi/tvalue"
422 b.rows = 3
423
424 return m