0
0
mirror of https://github.com/Doodle3D/doodle3d-firmware.git synced 2024-11-16 10:47:55 +01:00

Many changes, mostly a new configuration interface:

* change API access control to use function name suffixes
* implement settings interface with baseline configuration
* make AP properties configurable
* AP SSID can now contain partial MAC address
* update more quotation style and documentation
* remove captive directory change from network mode switcher.
This commit is contained in:
Wouter R 2013-07-17 22:55:27 +02:00
parent d3e4812cbf
commit f988c13dfa
13 changed files with 213 additions and 103 deletions

View File

@ -11,7 +11,6 @@ M.DEBUG_PCALLS = true
M.API_INCLUDE_ENDPOINT_INFO = false M.API_INCLUDE_ENDPOINT_INFO = false
-- was: M.DEFAULT_AP_SSID = "d3d-ap-%MAC_ADDR_TAIL%"
M.apSsid = { M.apSsid = {
default = 'd3d-ap-%%MAC_ADDR_TAIL%%', default = 'd3d-ap-%%MAC_ADDR_TAIL%%',
type = 'string', type = 'string',
@ -20,7 +19,6 @@ M.apSsid = {
max = 32 max = 32
} }
-- was: M.DEFAULT_AP_ADDRESS = "192.168.10.1"
M.apAddress = { M.apAddress = {
default = '192.168.10.1', default = '192.168.10.1',
type = 'string', type = 'string',
@ -28,7 +26,6 @@ M.apAddress = {
regex = '%d+\.%d+\.%d+\.%d+' regex = '%d+\.%d+\.%d+\.%d+'
} }
-- was: M.DEFAULT_AP_NETMASK = "255.255.255.0"
M.apNetmask = { M.apNetmask = {
default = '255.255.255.0', default = '255.255.255.0',
type = 'string', type = 'string',

View File

@ -1,6 +1,6 @@
package.path = package.path .. ';/usr/share/lua/wifibox/?.lua' package.path = package.path .. ';/usr/share/lua/wifibox/?.lua'
local config = require('config') local confDefaults = require('conf_defaults')
local u = require('util.utils') local u = require('util.utils')
local l = require('util.logger') local l = require('util.logger')
local wifi = require('network.wlanconfig') local wifi = require('network.wlanconfig')
@ -19,7 +19,7 @@ local function init()
l:init(l.LEVEL.debug) l:init(l.LEVEL.debug)
l:setStream(io.stderr) l:setStream(io.stderr)
if config.DEBUG_PCALLS then l:info("Wifibox CGI handler started (pcall debugging enabled)") if confDefaults.DEBUG_PCALLS then l:info("Wifibox CGI handler started (pcall debugging enabled)")
else l:info("Wifibox CGI handler started") else l:info("Wifibox CGI handler started")
end end
@ -39,7 +39,7 @@ local function init()
end end
local function main() local function main()
local rq = RequestClass.new(postData, config.DEBUG_PCALLS) local rq = RequestClass.new(postData, confDefaults.DEBUG_PCALLS)
l:info("received request of type " .. rq:getRequestMethod() .. " for " .. (rq:getRequestedApiModule() or "<unknown>") l:info("received request of type " .. rq:getRequestMethod() .. " for " .. (rq:getRequestedApiModule() or "<unknown>")
.. "/" .. (rq:getRealApiFunctionName() or "<unknown>") .. " with arguments: " .. u.dump(rq:getAll())) .. "/" .. (rq:getRealApiFunctionName() or "<unknown>") .. " with arguments: " .. u.dump(rq:getAll()))
@ -48,7 +48,7 @@ end
l:debug("user agent: " .. rq:getUserAgent()) l:debug("user agent: " .. rq:getUserAgent())
end end
if (not config.DEBUG_PCALLS and rq:getRequestMethod() == 'CMDLINE') then if (not confDefaults.DEBUG_PCALLS and rq:getRequestMethod() == 'CMDLINE') then
if rq:get('autowifi') ~= nil then if rq:get('autowifi') ~= nil then
setupAutoWifiMode() setupAutoWifiMode()
else else

View File

@ -1,6 +1,7 @@
local config = require("config")
local u = require("util.utils") local u = require("util.utils")
local l = require("util.logger") local l = require("util.logger")
local s = require("util.settings")
local wifi = require("network.wlanconfig")
local uci = require("uci").cursor() local uci = require("uci").cursor()
local M = {} local M = {}
@ -95,15 +96,16 @@ end
--[[ Add/remove access point network ]] --[[ Add/remove access point network ]]
function reconf.apnet_add_noreload(dirtyList) reconf.apnet_add(dirtyList, true) end function reconf.apnet_add_noreload(dirtyList) reconf.apnet_add(dirtyList, true) end
function reconf.apnet_add(dirtyList, noReload) function reconf.apnet_add(dirtyList, noReload)
local ourSsid = wifi.getSubstitutedSsid(s.get('apSsid'))
local sname = nil local sname = nil
uci:foreach("wireless", "wifi-iface", function(s) uci:foreach("wireless", "wifi-iface", function(s)
if s.ssid == config.DEFAULT_AP_SSID then sname = s[".name"]; return false end if s.ssid == ourSsid then sname = s[".name"]; return false end
end) end)
if sname == nil then sname = uci:add("wireless", "wifi-iface") end if sname == nil then sname = uci:add("wireless", "wifi-iface") end
M.uciTableSet("wireless", sname, { M.uciTableSet("wireless", sname, {
network = wifi.NET, network = wifi.NET,
ssid = config.DEFAULT_AP_SSID, ssid = ourSsid,
encryption = "none", encryption = "none",
device = "radio0", device = "radio0",
mode = "ap", mode = "ap",
@ -115,7 +117,7 @@ end
function reconf.apnet_rm(dirtyList) function reconf.apnet_rm(dirtyList)
local sname = nil local sname = nil
uci:foreach("wireless", "wifi-iface", function(s) uci:foreach("wireless", "wifi-iface", function(s)
if s.ssid == config.DEFAULT_AP_SSID then sname = s[".name"]; return false end if s.ssid == wifi.getSubstitutedSsid(s.get(apSsid)) then sname = s[".name"]; return false end
end) end)
if sname == nil then return l:info("AP network configuration does not exist, nothing to remove") end if sname == nil then return l:info("AP network configuration does not exist, nothing to remove") end
uci:delete("wireless", sname) uci:delete("wireless", sname)
@ -130,8 +132,8 @@ function reconf.staticaddr_add(dirtyList)
--NOTE: 'type = "bridge"' should -not- be added as this prevents defining a separate dhcp pool (http://wiki.openwrt.org/doc/recipes/routedap) --NOTE: 'type = "bridge"' should -not- be added as this prevents defining a separate dhcp pool (http://wiki.openwrt.org/doc/recipes/routedap)
M.uciTableSet("network", wifi.NET, { M.uciTableSet("network", wifi.NET, {
proto = "static", proto = "static",
ipaddr = config.DEFAULT_AP_ADDRESS, ipaddr = s.get('apAddress'),
netmask = config.DEFAULT_AP_NETMASK netmask = s.get('apNetmask')
}) })
bothBits(dirtyList, "network") bothBits(dirtyList, "network")
end end
@ -178,7 +180,7 @@ end
--[[ Add/remove redirecton of all DNS requests to self ]] --[[ Add/remove redirecton of all DNS requests to self ]]
function reconf.dnsredir_add(dirtyList) function reconf.dnsredir_add(dirtyList)
local redirText = "/#/" .. config.DEFAULT_AP_ADDRESS local redirText = "/#/" .. s.get('apAddress')
local sname = u.getUciSectionName("dhcp", "dnsmasq") local sname = u.getUciSectionName("dhcp", "dnsmasq")
if sname == nil then return l:error("dhcp config does not contain a dnsmasq section") end if sname == nil then return l:error("dhcp config does not contain a dnsmasq section") end
if uci:get("dhcp", sname, "address") ~= nil then return l:debug("DNS address redirection already in place, not re-adding", false) end if uci:get("dhcp", sname, "address") ~= nil then return l:debug("DNS address redirection already in place, not re-adding", false) end
@ -226,7 +228,7 @@ function reconf.natreflect_add(dirtyList)
proto = "tcp", proto = "tcp",
src_dport = "80", src_dport = "80",
dest_port = "80", dest_port = "80",
dest_ip = config.DEFAULT_AP_ADDRESS, dest_ip = s.get('apAddress'),
target = "DNAT" target = "DNAT"
}) })
bothBits(dirtyList, "firewall") bothBits(dirtyList, "firewall")

View File

@ -12,6 +12,20 @@ M.NET = "wlan"
local dev, dev_api local dev, dev_api
-- if a substitution of baseApSsid is requested, cachedApSsid is returned if not nil
local cachedApSsid, baseApSsid = nil, nil
function M.getSubstitutedSsid(unformattedSsid)
if unformattedSsid == baseApSsid and cachedApSsid ~= nil then return cachedApSsid end
local macTail = M.getMacAddress():sub(7)
baseApSsid = unformattedSsid
cachedApSsid = unformattedSsid:gsub('%%%%MAC_ADDR_TAIL%%%%', macTail)
return cachedApSsid
end
--- Map device mode as reported by iwinfo to device mode as required by UCI --- Map device mode as reported by iwinfo to device mode as required by UCI
-- Note that this function is quite naive. -- Note that this function is quite naive.

View File

@ -0,0 +1,33 @@
local log = require('util.logger')
local settings = require('util.settings')
local M = {
isApi = true
}
function M._global_GET(request, response)
response:setSuccess()
--TODO: we need a function to list all configuration keys
for k,v in pairs(request:getAll()) do
local r,m = settings.get(k)
if r then response:addData(k, r)
else response:addData(k, "could not read key ('" .. m .. "')")
end
end
end
function M._global_POST(request, response)
response:setSuccess()
for k,v in pairs(request:getAll()) do
local r,m = settings.set(k, v)
if r then response:addData(k, "ok")
else response:addData(k, "could not set key ('" .. m .. "')")
end
end
end
return M

View File

@ -1,13 +1,14 @@
local config = require("config") local s = require("util.settings")
local u = require("util.utils") local u = require("util.utils")
local l = require("util.logger") local l = require("util.logger")
local netconf = require("network.netconfig") local netconf = require("network.netconfig")
local wifi = require("network.wlanconfig") local wifi = require("network.wlanconfig")
local ResponseClass = require("rest.response") local ResponseClass = require("rest.response")
local M = {} local M = {
isApi = true
}
M.isApi = true
function M._global(request, response) function M._global(request, response)
response:setError("not implemented") response:setError("not implemented")
@ -25,7 +26,7 @@ function M.available(request, response)
response:setSuccess("") response:setSuccess("")
local netInfoList = {} local netInfoList = {}
for _, se in ipairs(sr) do for _, se in ipairs(sr) do
if noFilter or se.mode ~= "ap" and se.ssid ~= config.DEFAULT_AP_SSID then if noFilter or se.mode ~= "ap" and se.ssid ~= wifi.getSubstitutedSsid(s.get('apSsid')) then
local netInfo = {} local netInfo = {}
netInfo["ssid"] = se.ssid netInfo["ssid"] = se.ssid
@ -123,7 +124,8 @@ function M.assoc(request, response)
end end
wifi.activateConfig(argSsid) wifi.activateConfig(argSsid)
netconf.switchConfiguration{ wifiiface="add", apnet="rm", staticaddr="rm", dhcppool="rm", wwwredir="rm", dnsredir="rm", wwwcaptive="rm", wireless="reload" } --netconf.switchConfiguration{ wifiiface="add", apnet="rm", staticaddr="rm", dhcppool="rm", wwwredir="rm", dnsredir="rm", wwwcaptive="rm", wireless="reload" }
netconf.switchConfiguration{ wifiiface="add", apnet="rm", staticaddr="rm", dhcppool="rm", wwwredir="rm", dnsredir="rm", wireless="reload" }
response:setSuccess("wlan associated") response:setSuccess("wlan associated")
response:addData("ssid", argSsid) response:addData("ssid", argSsid)
end end
@ -139,11 +141,13 @@ end
--UNTESTED --UNTESTED
function M.openap(request, response) function M.openap(request, response)
--add AP net, activate it, deactivate all others, reload network/wireless config, add all dhcp and captive settings and reload as needed --add AP net, activate it, deactivate all others, reload network/wireless config, add all dhcp and captive settings and reload as needed
local ssid = wifi.getSubstitutedSsid(s.get('apSsid'))
netconf.switchConfiguration{apnet="add_noreload"} netconf.switchConfiguration{apnet="add_noreload"}
wifi.activateConfig(config.DEFAULT_AP_SSID) wifi.activateConfig(ssid)
netconf.switchConfiguration{ wifiiface="add", network="reload", staticaddr="add", dhcppool="add", wwwredir="add", dnsredir="add", wwwcaptive="add" } --netconf.switchConfiguration{ wifiiface="add", network="reload", staticaddr="add", dhcppool="add", wwwredir="add", dnsredir="add", wwwcaptive="add" }
netconf.switchConfiguration{ wifiiface="add", network="reload", staticaddr="add", dhcppool="add", wwwredir="add", dnsredir="add" }
response:setSuccess("switched to Access Point mode") response:setSuccess("switched to Access Point mode")
response:addData("ssid", config.DEFAULT_AP_SSID) response:addData("ssid", ssid)
end end
--UNTESTED --UNTESTED

View File

@ -1,16 +1,8 @@
local l = require("util.logger") local l = require("util.logger")
local ResponseClass = require("rest.response") local ResponseClass = require("rest.response")
local M = {} local M = {
isApi = true
M.isApi = true
--empty or nil is equivalent to 'ANY', otherwise restrict to specified letters (command-line is always allowed)
M._access = {
_global = "GET",
success = "GET", fail = "GET", error = "GET",
read = "GET", write = "POST", readwrite = "ANY", readwrite2 = "",
echo = "GET"
} }
@ -38,9 +30,9 @@ end
function M.read(request, response) response:setSuccess("this endpoint can only be accessed through GET request") end function M.read(request, response) response:setSuccess("this endpoint can only be accessed through GET request") end
function M.write(request, response) response:setSuccess("this endpoint can only be accessed through POST request") end function M.write_POST(request, response) response:setSuccess("this endpoint can only be accessed through POST request") end
function M.readwrite(request, response) response:setSuccess("this endpoint can only be accessed through POST request") end function M.readwrite(request, response) response:setSuccess("this endpoint can be accessed both through GET and POST request") end
function M.readwrite2(request, response) response:setSuccess("this endpoint can only be accessed through POST request") end function M.readwrite2(request, response) response:setSuccess("this endpoint can be accessed both through GET and POST request") end
function M.echo(request, response) function M.echo(request, response)

View File

@ -1,12 +1,13 @@
local util = require("util.utils") --required for string:split() local util = require('util.utils') -- required for string:split()
local urlcode = require("util.urlcode") local urlcode = require('util.urlcode')
local config = require("config") local confDefaults = require('conf_defaults')
local ResponseClass = require("rest.response") local s = require('util.settings')
local ResponseClass = require('rest.response')
local M = {} local M = {}
M.__index = M M.__index = M
local GLOBAL_API_FUNCTION_NAME = "_global" local GLOBAL_API_FUNCTION_NAME = '_global'
--NOTE: requestedApi* contain what was extracted from the request data --NOTE: requestedApi* contain what was extracted from the request data
@ -17,7 +18,6 @@ M.requestedApiFunction = nil
M.resolvedApiFunction = nil --will contain function address, or nil M.resolvedApiFunction = nil --will contain function address, or nil
M.realApiFunctionName = nil --will contain requested name, or global name, or nil M.realApiFunctionName = nil --will contain requested name, or global name, or nil
M.resolutionError = nil --non-nil means function could not be resolved M.resolutionError = nil --non-nil means function could not be resolved
M.moduleAccessTable = nil
local function kvTableFromUrlEncodedString(encodedText) local function kvTableFromUrlEncodedString(encodedText)
@ -46,26 +46,30 @@ local function kvTableFromArray(argArray)
return args return args
end end
--NOTE: this function ignores empty tokens (e.g. '/a//b/' yields { [1] = a, [2] = b }) --- Create an array from the given '/'-separated path.
-- Empty path elements are not ignored (e.g. '/a//b' yields { [1] = '', [2] = 'a', [3] = '', [4] = 'b' }).
-- @param pathText The path to split.
-- @return An array with the path elements.
local function arrayFromPath(pathText) local function arrayFromPath(pathText)
return pathText and pathText:split("/") or {} return pathText and pathText:split('/') or {}
end end
--returns true if acceptable is nil or empty or 'ANY' or if it contains requested --- Resolve the given module name.
local function matchRequestMethod(acceptable, requested) -- Modules are searched for in the 'rest.api' path, with their name prefixed by 'api_'.
return acceptable == nil or acceptable == '' or acceptable == 'ANY' or string.find(acceptable, requested) -- e.g. if modname is 'test', then the generated 'require()' path will be 'rest.api.api_test'.
end -- Furthermore, the module must have the table key 'isApi' set to true.
-- @param modname The basename of the module to resolve.
-- @return Either a module object, or nil on error
--returns either a module object, or nil+errmsg -- @return An message on error, or nil otherwise
-- @see resolveApiFunction
local function resolveApiModule(modname) local function resolveApiModule(modname)
if modname == nil then return nil, "missing module name" end if modname == nil then return nil, "missing module name" end
if string.find(modname, "_") == 1 then return nil, "module names starting with '_' are preserved for internal use" end if string.find(modname, '_') == 1 then return nil, "module names starting with '_' are preserved for internal use" end
local reqModName = "rest.api.api_" .. modname local reqModName = 'rest.api.api_' .. modname
local ok, modObj local ok, modObj
if config.DEBUG_PCALLS then ok, modObj = true, require(reqModName) if confDefaults.DEBUG_PCALLS then ok, modObj = true, require(reqModName)
else ok, modObj = pcall(require, reqModName) else ok, modObj = pcall(require, reqModName)
end end
@ -76,9 +80,17 @@ local function resolveApiModule(modname)
return modObj return modObj
end end
--returns resultData+nil (usual), or nil+errmsg (unresolvable or inaccessible) --- Resolves a module/function name pair with appropiate access for the given request method.
--resultData contains 'func', 'accessTable' and if found, also 'blankArg' -- First, the function name suffixed with the request method, if not found the plain
local function resolveApiFunction(modname, funcname) -- function name is looked up.
-- Returned result data contains a 'func' key and if found, also 'blankArg'.
-- @param modname Basename of the module to resolve funcname in.
-- @param funcname Basename of the function to resolve.
-- @param requestMethod Method by which the request was received.
-- @return A table with resultData or nil on error.
-- @return A message on error (unresolvable or inaccessible).
-- @see resolveApiModule
local function resolveApiFunction(modname, funcname, requestMethod)
local resultData = {} local resultData = {}
if funcname and string.find(funcname, "_") == 1 then return nil, "function names starting with '_' are preserved for internal use" end if funcname and string.find(funcname, "_") == 1 then return nil, "function names starting with '_' are preserved for internal use" end
@ -90,15 +102,18 @@ local function resolveApiFunction(modname, funcname)
end end
if (funcname == nil or funcname == '') then funcname = GLOBAL_API_FUNCTION_NAME end --treat empty function name as nil if (funcname == nil or funcname == '') then funcname = GLOBAL_API_FUNCTION_NAME end --treat empty function name as nil
local f = mod[funcname] local rqType = requestMethod == 'POST' and 'POST' or 'GET'
local fGeneric = mod[funcname]
local fWithMethod = mod[funcname .. '_' .. rqType]
local funcNumber = tonumber(funcname) local funcNumber = tonumber(funcname)
if (type(f) == "function") then if (type(fWithMethod) == 'function') then
resultData.func = f resultData.func = fWithMethod
resultData.accessTable = mod._access elseif (type(fGeneric) == 'function') then
resultData.func = fGeneric
elseif funcNumber ~= nil then elseif funcNumber ~= nil then
resultData.func = mod[GLOBAL_API_FUNCTION_NAME] resultData.func = mod[GLOBAL_API_FUNCTION_NAME .. '_' .. rqType]
resultData.accessTable = mod._access if not resultData.func then resultData.func = mod[GLOBAL_API_FUNCTION_NAME] end
resultData.blankArg = funcNumber resultData.blankArg = funcNumber
else else
return nil, ("function '" .. funcname .. "' does not exist in API module '" .. modname .. "'") return nil, ("function '" .. funcname .. "' does not exist in API module '" .. modname .. "'")
@ -116,27 +131,37 @@ setmetatable(M, {
--This function initializes itself using various environment variables, the arg array and the given postData --This function initializes itself using various environment variables, the arg array and the given postData
--NOTE: if debugging is enabled, commandline arguments 'm' and 'f' override requested module and function --NOTE: if debugging is enabled, commandline arguments 'm' and 'f' override requested module and function
function M.new(postData, debug) function M.new(postData, debugEnabled)
local self = setmetatable({}, M) local self = setmetatable({}, M)
--NOTE: is it correct to assume that absence of REQUEST_METHOD indicates command line invocation? --NOTE: is it correct to assume that absence of REQUEST_METHOD indicates command line invocation?
self.requestMethod = os.getenv("REQUEST_METHOD") self.requestMethod = os.getenv('REQUEST_METHOD')
if self.requestMethod ~= nil then if self.requestMethod ~= nil then
self.remoteHost = os.getenv("REMOTE_HOST") self.remoteHost = os.getenv('REMOTE_HOST')
self.remotePort = os.getenv("REMOTE_PORT") self.remotePort = os.getenv('REMOTE_PORT')
self.userAgent = os.getenv("HTTP_USER_AGENT") self.userAgent = os.getenv('HTTP_USER_AGENT')
else else
self.requestMethod = "CMDLINE" self.requestMethod = 'CMDLINE'
end end
self.cmdLineArgs = kvTableFromArray(arg) self.cmdLineArgs = kvTableFromArray(arg)
self.getArgs = kvTableFromUrlEncodedString(os.getenv("QUERY_STRING")) self.getArgs = kvTableFromUrlEncodedString(os.getenv('QUERY_STRING'))
self.postArgs = kvTableFromUrlEncodedString(postData) self.postArgs = kvTableFromUrlEncodedString(postData)
self.pathArgs = arrayFromPath(os.getenv("PATH_INFO")) self.pathArgs = arrayFromPath(os.getenv('PATH_INFO'))
--override path arguments with command line parameter if debugging is enabled -- override path arguments with command line parameter and allow to emulate GET/POST if debugging is enabled
if debug and self.requestMethod == "CMDLINE" then if debugEnabled and self.requestMethod == 'CMDLINE' then
self.pathArgs = arrayFromPath(self.cmdLineArgs["p"]) self.pathArgs = arrayFromPath(self.cmdLineArgs['p'])
if self.cmdLineArgs['r'] == 'GET' then
self.requestMethod = 'GET'
self.getArgs = self.cmdLineArgs
self.getArgs.p, self.getArgs.r = nil, nil
elseif self.cmdLineArgs['r'] == 'POST' then
self.requestMethod = 'POST'
self.postArgs = self.cmdLineArgs
self.postArgs.p, self.postArgs.r = nil, nil
end
end end
table.remove(self.pathArgs, 1) --drop the first 'empty' field caused by the opening slash of the query string table.remove(self.pathArgs, 1) --drop the first 'empty' field caused by the opening slash of the query string
@ -144,16 +169,15 @@ function M.new(postData, debug)
if #self.pathArgs >= 1 then self.requestedApiModule = self.pathArgs[1] end if #self.pathArgs >= 1 then self.requestedApiModule = self.pathArgs[1] end
if #self.pathArgs >= 2 then self.requestedApiFunction = self.pathArgs[2] end if #self.pathArgs >= 2 then self.requestedApiFunction = self.pathArgs[2] end
if self.requestedApiModule == "" then self.requestedApiModule = nil end if self.requestedApiModule == '' then self.requestedApiModule = nil end
if self.requestedApiFunction == "" then self.requestedApiFunction = nil end if self.requestedApiFunction == '' then self.requestedApiFunction = nil end
-- Perform module/function resolution -- Perform module/function resolution
local rData, errMsg = resolveApiFunction(self:getRequestedApiModule(), self:getRequestedApiFunction()) local rData, errMsg = resolveApiFunction(self:getRequestedApiModule(), self:getRequestedApiFunction(), self.requestMethod)
if rData ~= nil and rData.func ~= nil then --function (possibly the global one) could be resolved if rData ~= nil and rData.func ~= nil then --function (possibly the global one) could be resolved
self.resolvedApiFunction = rData.func self.resolvedApiFunction = rData.func
self.moduleAccessTable = rData.accessTable
if rData.blankArg ~= nil then --apparently it was the global one, and we received a 'blank argument' if rData.blankArg ~= nil then --apparently it was the global one, and we received a 'blank argument'
self:setBlankArgument(rData.blankArg) self:setBlankArgument(rData.blankArg)
self.realApiFunctionName = GLOBAL_API_FUNCTION_NAME self.realApiFunctionName = GLOBAL_API_FUNCTION_NAME
@ -185,11 +209,11 @@ function M:getRemotePort() return self.remotePort or 0 end
function M:getUserAgent() return self.userAgent or "" end function M:getUserAgent() return self.userAgent or "" end
function M:get(key) function M:get(key)
if self.requestMethod == "GET" then if self.requestMethod == 'GET' then
return self.getArgs[key] return self.getArgs[key]
elseif self.requestMethod == "POST" then elseif self.requestMethod == 'POST' then
return self.postArgs[key] return self.postArgs[key]
elseif self.requestMethod == "CMDLINE" then elseif self.requestMethod == 'CMDLINE' then
return self.cmdLineArgs[key] return self.cmdLineArgs[key]
else else
return nil return nil
@ -197,11 +221,11 @@ function M:get(key)
end end
function M:getAll() function M:getAll()
if self.requestMethod == "GET" then if self.requestMethod == 'GET' then
return self.getArgs return self.getArgs
elseif self.requestMethod == "POST" then elseif self.requestMethod == 'POST' then
return self.postArgs return self.postArgs
elseif self.requestMethod == "CMDLINE" then elseif self.requestMethod == 'CMDLINE' then
return self.cmdLineArgs return self.cmdLineArgs
else else
return nil return nil
@ -218,16 +242,9 @@ function M:handle()
local resp = ResponseClass.new(self) local resp = ResponseClass.new(self)
if (self.resolvedApiFunction ~= nil) then --we found a function (possible the global function) if (self.resolvedApiFunction ~= nil) then --we found a function (possible the global function)
--check access type
local accessText = self.moduleAccessTable[self.realApiFunctionName]
if not matchRequestMethod(accessText, self.requestMethod) then
resp:setError("function '" .. modname .. "/" .. self.realApiFunctionName .. "' requires different request method ('" .. accessText .. "')")
return resp, "incorrect access method (" .. accessText .. " != " .. self.requestMethod .. ")"
end
--invoke the function --invoke the function
local ok, r local ok, r
if config.DEBUG_PCALLS then ok, r = true, self.resolvedApiFunction(self, resp) if confDefaults.DEBUG_PCALLS then ok, r = true, self.resolvedApiFunction(self, resp)
else ok, r = pcall(self.resolvedApiFunction, self, resp) else ok, r = pcall(self.resolvedApiFunction, self, resp)
end end

View File

@ -1,5 +1,5 @@
local JSON = require("util/JSON") local JSON = require("util/JSON")
local config = require("config") local s = require("util.settings")
local M = {} local M = {}
M.__index = M M.__index = M
@ -26,7 +26,7 @@ function M.new(requestObject)
local rqId = requestObject:get(REQUEST_ID_ARGUMENT) local rqId = requestObject:get(REQUEST_ID_ARGUMENT)
if rqId ~= nil then self.body[REQUEST_ID_ARGUMENT] = rqId end if rqId ~= nil then self.body[REQUEST_ID_ARGUMENT] = rqId end
if config.API_INCLUDE_ENDPOINT_INFO == true then if s.API_INCLUDE_ENDPOINT_INFO == true then
self.body["module"] = requestObject:getRequestedApiModule() self.body["module"] = requestObject:getRequestedApiModule()
self.body["function"] = requestObject:getRealApiFunctionName() or "" self.body["function"] = requestObject:getRealApiFunctionName() or ""
end end

View File

@ -35,8 +35,8 @@ function M:test_get()
end end
function M:test_set() function M:test_set()
local key = 'apAddress' local key, intKey = 'apAddress', 'temperature'
local goodValue, badValue1, badValue2 = '10.0.0.1', '10.00.1', '10.0.0d.1' local intValue, goodValue, badValue1, badValue2 = 340, '10.0.0.1', '10.00.1', '10.0.0d.1'
assert(s.get(key) == defaults.apAddress.default) assert(s.get(key) == defaults.apAddress.default)
assert(s.isDefault(key)) assert(s.isDefault(key))
@ -53,6 +53,14 @@ function M:test_set()
assert(s.set(key, nil)) assert(s.set(key, nil))
assert(s.isDefault(key)) assert(s.isDefault(key))
-- test with value of int type
assert(s.get(intKey) == defaults.temperature.default)
assert(s.isDefault(intKey))
assert(s.set(intKey, intValue))
assert(s.get(intKey) == intValue)
assert(not s.isDefault(intKey))
end end
function M:test_setNonExistent() function M:test_setNonExistent()

View File

@ -98,6 +98,7 @@ function M:test_symlink()
assert(false, 'not implemented') assert(false, 'not implemented')
end end
-- this should test for the following condition: 'Could not rename /www to /www-regular(/www: Invalid cross-device link)'
function M:test_symlinkInRoot() function M:test_symlinkInRoot()
assert(false, 'not implemented') assert(false, 'not implemented')
end end

View File

@ -0,0 +1,39 @@
local wlanconfig = require("network.wlanconfig")
local M = {
_is_test = true,
_skip = {},
_wifibox_only = {}
}
local function captureCommandOutput(cmd)
local f = assert(io.popen(cmd, 'r'))
return assert(f:read('*all'))
end
function M._setup()
wlanconfig.init()
end
function M.test_getMacAddress()
local reportedMac = wlanconfig.getMacAddress()
local output = captureCommandOutput('ifconfig wlan0')
local actualMac = output:match('HWaddr (%w%w:%w%w:%w%w:%w%w:%w%w:%w%w)'):gsub(':', ''):upper()
assert(reportedMac == actualMac)
end
function M.test_getSubstitutedSsid()
local mac = wlanconfig.getMacAddress()
local macTail = mac:sub(7)
local expected1 = 'pre' .. macTail .. 'post'
local expected2 = 'pre' .. macTail .. 'post-cache-test'
assert(wlanconfig.getSubstitutedSsid('pre%%MAC_ADDR_TAIL%%post') == expected1)
assert(wlanconfig.getSubstitutedSsid('pre%%MAC_ADDR_TAIL%%post-cache-test') == expected2)
assert(wlanconfig.getSubstitutedSsid('pre%%MAC_ADDR_TAIL%%post') == expected1)
assert(wlanconfig.getSubstitutedSsid('pre%%MAC_ADDR_TAIL%%post') == expected1)
end
return M

View File

@ -40,12 +40,15 @@ local function isValid(value, baseTable)
if type == 'bool' then if type == 'bool' then
return isboolean(value) or nil,"invalid bool value" return isboolean(value) or nil,"invalid bool value"
elseif type == 'int' or type == 'float' then elseif type == 'int' or type == 'float' then
local ok = isnumber(value) local numValue = tonumber(value)
ok = ok and (type == 'float' or math.floor(value) == value) local ok = numValue and true or false
if min then ok = ok and value >= min end ok = ok and (type == 'float' or math.floor(numValue) == numValue)
if max then ok = ok and value <= max end if min then ok = ok and numValue >= min end
return ok or nil,"invalid int/float value" if max then ok = ok and numValue <= max end
return ok or nil,"invalid int/float value or out of range"
elseif type == 'string' then elseif type == 'string' then
local ok = true local ok = true
if min then ok = ok and value:len() >= min end if min then ok = ok and value:len() >= min end
@ -69,7 +72,7 @@ function M.get(key)
if not base then return nil,ERR_NO_SUCH_KEY end if not base then return nil,ERR_NO_SUCH_KEY end
local v = base.default local v = base.default
local uciV = fromUciValue(uci:get(UCI_CONFIG_NAME, UCI_CONFIG_SECTION, key)) local uciV = fromUciValue(uci:get(UCI_CONFIG_NAME, UCI_CONFIG_SECTION, key), base.type)
return uciV or v return uciV or v
end end
@ -95,7 +98,7 @@ function M.set(key, value)
local current = uci:get(UCI_CONFIG_NAME, UCI_CONFIG_SECTION, key) local current = uci:get(UCI_CONFIG_NAME, UCI_CONFIG_SECTION, key)
if fromUciValue(current) == value then return true end if fromUciValue(current, base.type) == value then return true end
if value ~= nil then if value ~= nil then
local valid,m = isValid(value, base) local valid,m = isValid(value, base)