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
diff --git a/applications/luci-pbx/luasrc/model/cbi/pbx-calls.lua b/applications/luci-pbx/luasrc/model/cbi/pbx-calls.lua
new file mode 100644 (file)
index 0000000..029c5b0
--- /dev/null
@@ -0,0 +1,359 @@
+--[[
+    Copyright 2011 Iordan Iordanov <iiordanov (AT) gmail.com>
+
+    This file is part of luci-pbx.
+
+    luci-pbx is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    luci-pbx is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with luci-pbx.  If not, see <http://www.gnu.org/licenses/>.
+]]--
+
+if     nixio.fs.access("/etc/init.d/asterisk")   then
+   server = "asterisk"
+elseif nixio.fs.access("/etc/init.d/freeswitch") then
+   server = "freeswitch"
+else
+   server = ""
+end
+
+modulename       = "pbx-calls"
+voipmodulename   = "pbx-voip"
+googlemodulename = "pbx-google"
+usersmodulename  = "pbx-users"
+
+-- This function builds and returns a table with all the entries in a given "module" and "section".
+function get_existing_entries(module, section)
+   i = 1
+   existing_entries = {}
+   m.uci:foreach(module, section,
+                function(s1)
+                   existing_entries[i] = s1
+                   i = i + 1
+                end)
+   return existing_entries
+end
+
+-- This function is used to build and return a table where the name field for
+-- every element in a table entries are the indexes to the table (used to check
+-- validity of user input.
+function get_valid_names(entries)
+   validnames={}
+   for index,value in ipairs(entries) do
+      validnames[entries[index].name] = true
+   end
+   return validnames
+end
+
+-- This function is used to build and return a table where the defaultuser field for
+-- every element in a table entries are the indexes to the table (used to check
+-- validity of user input.
+function get_valid_defaultusers(entries)
+   validnames={}
+   for index,value in ipairs(entries) do
+      validnames[entries[index].defaultuser] = true
+   end
+   return validnames
+end
+
+function is_valid_extension(exten)
+   return (exten:match("[#*+0-9NXZ]+$") ~= nil)
+end
+
+m = Map (modulename, translate("Call Routing"),
+        translate("This is where you indicate which Google/SIP accounts are used to call what \
+                  country/area codes, which users can use which SIP/Google accounts, how incoming\
+                  calls are routed, what numbers can get into this PBX with a password, and what\
+                  numbers are blacklisted."))
+
+-- Recreate the config, and restart services after changes are commited to the configuration.
+function m.on_after_commit(self)
+       luci.sys.call("/etc/init.d/pbx-" .. server .. " restart 1\>/dev/null 2\>/dev/null")
+       luci.sys.call("/etc/init.d/"     .. server .. " restart 1\>/dev/null 2\>/dev/null")
+end
+
+----------------------------------------------------------------------------------------------------
+s = m:section(NamedSection, "outgoing_calls", "call_routing", translate("Outgoing Calls"),
+       translate("If you have more than one account which can make outgoing calls, you\
+               should enter a list of phone numbers and prefixes in the following fields for each\
+               provider listed. Invalid prefixes are removed silently, and only 0-9, X, Z, N, #, *,\
+               and + are valid characters. The letter X matches 0-9, Z matches 1-9, and N matches 2-9.\
+               For example to make calls to Germany through a provider, you can enter 49. To make calls\
+               to North America, you can enter 1NXXNXXXXXX. If one of your providers can make \"local\"\
+               calls to an area code like New York's 646, you can enter 646NXXXXXX for that\
+               provider. You should leave one account with an empty list to make calls with\
+               it by default, if no other provider's prefixes match. The system will automatically\
+               replace an empty list with a message that the provider dials all numbers. Be as specific as\
+               possible (i.e. 1NXXNXXXXXX is better than 1). Please note all international dial codes\
+               are discarded (e.g. 00, 011, 010, 0011). Entries can be made in a\
+               space-separated list, and/or one per line by hitting enter after every one."))
+s.anonymous = true
+
+m.uci:foreach(googlemodulename, "gtalk_jabber", 
+             function(s1)
+                if s1.username ~= nil and s1.name ~= nil and
+                   s1.make_outgoing_calls == "yes" then
+                   patt = s:option(DynamicList, s1.name, s1.username)
+
+                   -- If the saved field is empty, we return a string
+                   -- telling the user that this account would dial any exten.
+                   function patt.cfgvalue(self, section)
+                      value = self.map:get(section, self.option)
+
+                      if value == nil then
+                         return {"Dials any number"}
+                      else
+                         return value
+                      end
+                   end
+
+                   -- Write only valid extensions into the config file.
+                   function patt.write(self, section, value)
+                      newvalue = {}
+                      nindex = 1
+                      for index, field in ipairs(value) do
+                         if is_valid_extension(value[index]) == true then
+                            newvalue[nindex] = value[index]
+                            nindex = nindex + 1
+                         end
+                      end
+                      DynamicList.write(self, section, newvalue)
+                   end
+                end
+             end)
+
+m.uci:foreach(voipmodulename, "voip_provider", 
+             function(s1)
+                if s1.defaultuser ~= nil and s1.host ~= nil and 
+                   s1.name ~= nil        and s1.make_outgoing_calls == "yes" then
+                   patt = s:option(DynamicList, s1.name, s1.defaultuser .. "@" .. s1.host)
+
+                   -- If the saved field is empty, we return a string
+                   -- telling the user that this account would dial any exten.
+                   function patt.cfgvalue(self, section)
+                      value = self.map:get(section, self.option)
+
+                      if value == nil then
+                         return {"Dials any number"}
+                      else
+                         return value
+                      end
+                   end
+
+                   -- Write only valid extensions into the config file.
+                   function patt.write(self, section, value)
+                      newvalue = {}
+                      nindex = 1
+                      for index, field in ipairs(value) do
+                         if is_valid_extension(value[index]) == true then
+                            newvalue[nindex] = value[index]
+                            nindex = nindex + 1
+                         end
+                      end
+                      DynamicList.write(self, section, newvalue)
+                   end
+                end
+             end)
+
+----------------------------------------------------------------------------------------------------
+s = m:section(NamedSection, "incoming_calls", "call_routing", translate("Incoming Calls"),
+       translate("For each provider that receives calls, here you can restrict which users to ring\
+               on incoming calls. If the list is empty, the system will indicate that all users\
+               which are enabled for incoming calls will ring. Invalid usernames will be rejected\
+               silently. Also, entering a username here overrides the user's setting to not receive\
+               incoming calls, so this way, you can make users ring only for select providers.\
+               Entries can be made in a space-separated list, and/or one per\
+               line by hitting enter after every one."))
+s.anonymous = true
+
+m.uci:foreach(googlemodulename, "gtalk_jabber", 
+             function(s1)
+                if s1.username ~= nil and s1.register == "yes" then
+                   field_name=string.gsub(s1.username, "%W", "_")
+                   gtalkaccts = s:option(DynamicList, field_name, s1.username)
+                   
+                   -- If the saved field is empty, we return a string
+                   -- telling the user that this account would dial any exten.
+                   function gtalkaccts.cfgvalue(self, section)
+                      value = self.map:get(section, self.option)
+                      
+                      if value == nil then
+                         return {"Rings all users"}
+                      else
+                         return value
+                      end
+                   end
+
+                   -- Write only valid user names.
+                   function gtalkaccts.write(self, section, value)
+                      users=get_valid_defaultusers(get_existing_entries(usersmodulename, "local_user"))
+                      newvalue = {}
+                      nindex = 1
+                      for index, field in ipairs(value) do
+                         if users[value[index]] == true then
+                            newvalue[nindex] = value[index]
+                            nindex = nindex + 1
+                         end
+                      end
+                      DynamicList.write(self, section, newvalue)
+                   end
+                end
+             end)
+
+
+m.uci:foreach(voipmodulename, "voip_provider", 
+             function(s1)
+                if s1.defaultuser ~= nil and s1.host ~= nil and s1.register == "yes" then
+                   field_name=string.gsub(s1.defaultuser .. "_" .. s1.host, "%W", "_")
+                   voipaccts = s:option(DynamicList, field_name, s1.defaultuser .. "@" .. s1.host)
+
+                   -- If the saved field is empty, we return a string
+                   -- telling the user that this account would dial any exten.
+                   function voipaccts.cfgvalue(self, section)
+                      value = self.map:get(section, self.option)
+                      
+                      if value == nil then
+                         return {"Rings all users"}
+                      else
+                         return value
+                      end
+                   end
+
+                   -- Write only valid user names.
+                   function voipaccts.write(self, section, value)
+                      users=get_valid_defaultusers(get_existing_entries(usersmodulename, "local_user"))
+                      newvalue = {}
+                      nindex = 1
+                      for index, field in ipairs(value) do
+                         if users[value[index]] == true then
+                            newvalue[nindex] = value[index]
+                            nindex = nindex + 1
+                         end
+                      end
+                      DynamicList.write(self, section, newvalue)
+                   end
+                end
+             end)
+
+----------------------------------------------------------------------------------------------------
+s = m:section(NamedSection, "providers_user_can_use", "call_routing",
+      translate("Providers Used for Outgoing Calls"),
+      translate("If you would like, you could restrict which providers users are allowed to use for outgoing\
+       calls. By default all users can use all providers. To show up in the list below the user should\
+       be allowed to make outgoing calls in the \"User Accounts\" page. Enter VoIP providers in the format\
+       username@some.host.name, as listed in \"Outgoing Calls\" above. It's easiest to copy and paste\
+       the providers from above. Invalid entries will be rejected silently. Also, any entries automatically\
+       change to this PBX's internal naming scheme, with \"_\" replacing all non-alphanumeric characters.\
+       Entries can be made in a space-separated list, and/or one per line by hitting enter after every\
+       one."))
+s.anonymous = true
+
+m.uci:foreach(usersmodulename, "local_user", 
+             function(s1)
+                if s1.defaultuser ~= nil and s1.can_call == "yes" then
+                   providers = s:option(DynamicList, s1.defaultuser, s1.defaultuser)
+                   
+                   -- If the saved field is empty, we return a string
+                   -- telling the user that this account would dial any exten.
+                   function providers.cfgvalue(self, section)
+                      value = self.map:get(section, self.option)
+                      
+                      if value == nil then
+                         return {"Uses all provider accounts"}
+                      else
+                         return value
+                      end
+                   end
+                   
+                   -- Cook the new values prior to entering them into the config file.
+                   -- Also, enter them only if they are valid.
+                   function providers.write(self, section, value)
+                      validvoip=get_valid_names(get_existing_entries(voipmodulename, "voip_provider"))
+                      validgoog=get_valid_names(get_existing_entries(googlemodulename, "gtalk_jabber"))
+                      cookedvalue = {}
+                      cindex = 1
+                      for index, field in ipairs(value) do
+                         cooked = string.gsub(value[index], "%W", "_")
+                         if validvoip[cooked] == true or validgoog[cooked] == true then
+                            cookedvalue[cindex] = string.gsub(value[index], "%W", "_")
+                            cindex = cindex + 1
+                         end
+                      end
+                      DynamicList.write(self, section, cookedvalue)
+                   end
+                end
+             end)
+
+----------------------------------------------------------------------------------------------------
+s = m:section(TypedSection, "callthrough_numbers", translate("Call-through Numbers"),
+       translate("Designate numbers which will be allowed to call through this system and which user's\
+                 privileges it will have."))
+s.anonymous = true
+s.addremove = true
+
+num = s:option(DynamicList, "callthrough_number_list", translate("Call-through Numbers"))
+num.datatype = "uinteger"
+
+p = s:option(ListValue, "enabled", translate("Enabled"))
+p:value("yes", translate("Yes"))
+p:value("no",  translate("No"))
+p.default = "yes"
+
+user = s:option(Value, "defaultuser",  translate("User Name"),
+        translate("The number(s) specified above will be able to dial outwith this user's providers.\
+                  Invalid usernames are dropped silently, please verify that the entry was accepted."))
+function user.write(self, section, value)
+   users=get_valid_defaultusers(get_existing_entries(usersmodulename, "local_user"))
+   if users[value] == true then
+      Value.write(self, section, value)
+   end
+end
+
+pwd = s:option(Value, "pin", translate("PIN"),
+              translate("Your PIN disappears when saved for your protection. It will be changed\
+                        only when you enter a value different from the saved one. Leaving the PIN\
+                        empty is possible, but please beware of the security implications."))
+pwd.password = true
+pwd.rmempty = false
+
+-- We skip reading off the saved value and return nothing.
+function pwd.cfgvalue(self, section)
+    return "" 
+end
+
+-- We check the entered value against the saved one, and only write if the entered value is
+-- something other than the empty string, and it differes from the saved value.
+function pwd.write(self, section, value)
+    local orig_pwd = m:get(section, self.option)
+    if value and #value > 0 and orig_pwd ~= value then
+        Value.write(self, section, value)
+    end
+end
+
+----------------------------------------------------------------------------------------------------
+s = m:section(NamedSection, "blacklisting", "call_routing", translate("Blacklisted Numbers"),
+                translate("Enter phone numbers that you want to decline calls from automatically.\
+                       You should probably omit the country code and any leading\
+                       zeroes, but please experiment to make sure you are blocking numbers from your\
+                       desired area successfully."))
+s.anonymous = true
+
+b = s:option(DynamicList, "blacklist1", translate("Dynamic List of Blacklisted Numbers"),
+                   translate("Specify numbers individually here. Press enter to add more numbers."))
+b.cast = "string"
+b.datatype = "uinteger"
+
+b = s:option(Value, "blacklist2", translate("Space-Separated List of Blacklisted Numbers"),
+           translate("Copy-paste large lists of numbers here."))
+b.template = "cbi/tvalue"
+b.rows = 3
+
+return m