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