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
-
+modulename = "pbx-calls"
+voipmodulename = "pbx-voip"
+googlemodulename = "pbx-google"
+usersmodulename = "pbx-users"
+allvalidaccounts = {}
+nallvalidaccounts = 0
+validoutaccounts = {}
+nvalidoutaccounts = 0
+validinaccounts = {}
+nvalidinaccounts = 0
+allvalidusers = {}
+nallvalidusers = 0
+validoutusers = {}
+nvalidoutusers = 0
+
+
+-- Checks whether the entered extension is valid syntactically.
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."))
+ translate("This is where you indicate which Google/SIP accounts are used to call what \
+ country/area codes, which users can use what 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")
+ 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
-
+-- Add Google accounts to all valid accounts, and accounts valid for incoming and outgoing calls.
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)
-
+ function(s1)
+ -- Add this provider to list of valid accounts.
+ if s1.username ~= nil and s1.name ~= nil then
+ allvalidaccounts[s1.name] = s1.username
+ nallvalidaccounts = nallvalidaccounts + 1
+
+ if s1.make_outgoing_calls == "yes" then
+ -- Add provider to the associative array of valid outgoing accounts.
+ validoutaccounts[s1.name] = s1.username
+ nvalidoutaccounts = nvalidoutaccounts + 1
+ end
+
+ if s1.register == "yes" then
+ -- Add provider to the associative array of valid outgoing accounts.
+ validinaccounts[s1.name] = s1.username
+ nvalidinaccounts = nvalidinaccounts + 1
+ end
+ end
+ end)
+
+-- Add SIP accounts to all valid accounts, and accounts valid for incoming and outgoing calls.
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)
+ function(s1)
+ -- Add this provider to list of valid accounts.
+ if s1.defaultuser ~= nil and s1.host ~= nil and s1.name ~= nil then
+ allvalidaccounts[s1.name] = s1.defaultuser .. "@" .. s1.host
+ nallvalidaccounts = nallvalidaccounts + 1
+
+ if s1.make_outgoing_calls == "yes" then
+ -- Add provider to the associative array of valid outgoing accounts.
+ validoutaccounts[s1.name] = s1.defaultuser .. "@" .. s1.host
+ nvalidoutaccounts = nvalidoutaccounts + 1
+ end
+
+ if s1.register == "yes" then
+ -- Add provider to the associative array of valid outgoing accounts.
+ validinaccounts[s1.name] = s1.defaultuser .. "@" .. s1.host
+ nvalidinaccounts = nvalidinaccounts + 1
+ end
+ end
+ end)
+
+-- Add Local User accounts to all valid users, and users allowed to make outgoing calls.
+m.uci:foreach(usersmodulename, "local_user",
+ function(s1)
+ -- Add user to list of all valid users.
+ if s1.defaultuser ~= nil then
+ allvalidusers[s1.defaultuser] = true
+ nallvalidusers = nallvalidusers + 1
+
+ if s1.can_call == "yes" then
+ validoutusers[s1.defaultuser] = true
+ nvalidoutusers = nvalidoutusers + 1
+ 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."))
+-- If there are no accounts configured, or no accounts enabled for outgoing calls, display a warning.
+-- Otherwise, display the usual help text within the section.
+if nallvalidaccounts == 0 then
+ text = translate("NOTE: There are no Google or SIP provider accounts configured.")
+elseif nvalidoutaccounts == 0 then
+ text = translate("NOTE: There are no Google or SIP provider accounts enabled for outgoing calls.")
+else
+ text = translate("If you have more than one account that 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.")
+end
+
+
+s = m:section(NamedSection, "outgoing_calls", "call_routing", translate("Outgoing Calls"), text)
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)
+for k,v in pairs(validoutaccounts) do
+ patterns = s:option(DynamicList, k, v)
+
+ -- If the saved field is empty, we return a string
+ -- telling the user that this account would dial any exten.
+ function patterns.cfgvalue(self, section)
+ value = self.map:get(section, self.option)
+
+ if value == nil then
+ return {translate("Used for numbers unmatched elsewhere")}
+ else
+ return value
+ end
+ end
+
+ -- Write only valid extensions into the config file.
+ function patterns.write(self, section, value)
+ newvalue = {}
+ nindex = 1
+ for index, field in ipairs(value) do
+ val = luci.util.trim(value[index])
+ if is_valid_extension(val) == true then
+ newvalue[nindex] = val
+ nindex = nindex + 1
+ end
+ end
+ DynamicList.write(self, section, newvalue)
+ end
+end
+
+----------------------------------------------------------------------------------------------------
+-- If there are no accounts configured, or no accounts enabled for incoming calls, display a warning.
+-- Otherwise, display the usual help text within the section.
+if nallvalidaccounts == 0 then
+ text = translate("NOTE: There are no Google or SIP provider accounts configured.")
+elseif nvalidinaccounts == 0 then
+ text = translate("NOTE: There are no Google or SIP provider accounts enabled for incoming calls.")
+else
+ text = translate("For each provider enabled for incoming calls, here you can restrict which users to\
+ ring on incoming calls. If the list is empty, the system will indicate that all users \
+ 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. This way, you can make certain users ring only for specific providers. \
+ Entries can be made in a space-separated list, and/or one per line by hitting enter after \
+ every one.")
+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, "incoming_calls", "call_routing", translate("Incoming Calls"), text)
+s.anonymous = true
+
+for k,v in pairs(validinaccounts) do
+ users = s:option(DynamicList, k, v)
+
+ -- If the saved field is empty, we return a string
+ -- telling the user that this account would dial any exten.
+ function users.cfgvalue(self, section)
+ value = self.map:get(section, self.option)
+
+ if value == nil then
+ return {translate("All users enabled for incoming calls")}
+ else
+ return value
+ end
+ end
+
+ -- Write only valid user names.
+ function users.write(self, section, value)
+ newvalue = {}
+ nindex = 1
+ for index, field in ipairs(value) do
+ trimuser = luci.util.trim(value[index])
+ if allvalidusers[trimuser] == true then
+ newvalue[nindex] = trimuser
+ nindex = nindex + 1
+ end
+ end
+ DynamicList.write(self, section, newvalue)
+ end
+end
+
----------------------------------------------------------------------------------------------------
+-- If there are no user accounts configured, no user accounts enabled for outgoing calls,
+-- display a warning. Otherwise, display the usual help text within the section.
+if nallvalidusers == 0 then
+ text = translate("NOTE: There are no local user accounts configured.")
+elseif nvalidoutusers == 0 then
+ text = translate("NOTE: There are no local user accounts enabled for outgoing calls.")
+else
+ text = translate("For each user enabled for outgoing calls you can restrict what providers the user \
+ can 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, including providers not \
+ enabled for outgoing calls, will be rejected silently. Entries can be made in a space-separated \
+ list, and/or one per line by hitting enter after every one.")
+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."))
+ translate("Providers Used for Outgoing Calls"), text)
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)
+for k,v in pairs(validoutusers) do
+ providers = s:option(DynamicList, k, k)
+
+ -- 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 {translate("All providers enabled for outgoing calls")}
+ else
+ newvalue = {}
+ -- Convert internal names to user@host values.
+ for i,v in ipairs(value) do
+ newvalue[i] = validoutaccounts[v]
+ end
+ return newvalue
+ 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)
+ cookedvalue = {}
+ cindex = 1
+ for index, field in ipairs(value) do
+ cooked = string.gsub(luci.util.trim(value[index]), "%W", "_")
+ if validoutaccounts[cooked] ~= nil then
+ cookedvalue[cindex] = cooked
+ cindex = cindex + 1
+ end
+ end
+ DynamicList.write(self, section, cookedvalue)
+ 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."))
+ translate("Designate numbers that are allowed to call through this system and which user's \
+ privileges it will have."))
s.anonymous = true
s.addremove = true
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."))
+ translate("The number(s) specified above will be able to dial out with this user's providers. \
+ Invalid usernames, including users not enabled for outgoing calls, 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)
+ trimuser = luci.util.trim(value)
+ if allvalidusers[trimuser] == true then
+ Value.write(self, section, trimuser)
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."))
+ 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
----------------------------------------------------------------------------------------------------
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."))
+ 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."))
+ 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."))
+ translate("Copy-paste large lists of numbers here."))
b.template = "cbi/tvalue"
b.rows = 3