summaryrefslogtreecommitdiffhomepage
path: root/libs/cbi/luasrc
diff options
context:
space:
mode:
authorJo-Philipp Wich <jow@openwrt.org>2010-04-26 22:27:18 +0000
committerJo-Philipp Wich <jow@openwrt.org>2010-04-26 22:27:18 +0000
commite75cb4f5ba4030bffb18eec9ca039678b5a508f4 (patch)
tree7bfedff4270e4ee48392923871553439634ac571 /libs/cbi/luasrc
parentee36ef41700af6941ed8d2ad8b0d94e68e43e9a6 (diff)
libs/cbi: drop UVL, introduce server side datatype validation
Diffstat (limited to 'libs/cbi/luasrc')
-rw-r--r--libs/cbi/luasrc/cbi.lua161
-rw-r--r--libs/cbi/luasrc/cbi/datatypes.lua186
2 files changed, 197 insertions, 150 deletions
diff --git a/libs/cbi/luasrc/cbi.lua b/libs/cbi/luasrc/cbi.lua
index e687164c2..a18d37d60 100644
--- a/libs/cbi/luasrc/cbi.lua
+++ b/libs/cbi/luasrc/cbi.lua
@@ -29,12 +29,12 @@ module("luci.cbi", package.seeall)
require("luci.template")
local util = require("luci.util")
require("luci.http")
-require("luci.uvl")
--local event = require "luci.sys.event"
local fs = require("nixio.fs")
local uci = require("luci.model.uci")
+local datatypes = require("luci.cbi.datatypes")
local class = util.class
local instanceof = util.instanceof
@@ -150,64 +150,6 @@ function load(cbimap, ...)
return maps
end
-local function _uvl_validate_section(node, name)
- local co = node.map:get()
-
- luci.uvl.STRICT_UNKNOWN_OPTIONS = false
- luci.uvl.STRICT_UNKNOWN_SECTIONS = false
-
- local function tag_fields(e)
- if e.option and node.fields[e.option] then
- if node.fields[e.option].error then
- node.fields[e.option].error[name] = e
- else
- node.fields[e.option].error = { [name] = e }
- end
- elseif e.childs then
- for _, c in ipairs(e.childs) do tag_fields(c) end
- end
- end
-
- local function tag_section(e)
- local s = { }
- for _, c in ipairs(e.childs or { e }) do
- if c.childs and not c:is('DEPENDENCY') then
- table.insert( s, c.childs[1]:string() )
- else
- table.insert( s, c:string() )
- end
- end
- if #s > 0 then
- if node.error then
- node.error[name] = s
- else
- node.error = { [name] = s }
- end
- end
- end
-
- local stat, err = node.map.validator:validate_section(node.config, name, co)
- if err then
- node.map.save = false
- tag_fields(err)
- tag_section(err)
- end
-
-end
-
-local function _uvl_strip_remote_dependencies(deps)
- local clean = {}
-
- for k, v in pairs(deps) do
- k = k:gsub("%$config%.%$section%.", "")
- if k:match("^[%w_]+$") and type(v) == "string" then
- clean[k] = v
- end
- end
-
- return clean
-end
-
-- Node pseudo abstract class
Node = class()
@@ -317,9 +259,6 @@ function Map.__init__(self, config, ...)
if not self.uci:load(self.config) then
error("Unable to read UCI data: " .. self.config)
end
-
- self.validator = luci.uvl.UVL()
- self.scheme = self.validator:get_scheme(self.config)
end
function Map.formvalue(self, key)
@@ -827,20 +766,6 @@ end
-- Appends a new option
function AbstractSection.option(self, class, option, ...)
- -- Autodetect from UVL
- if class == true and self.map:get_scheme(self.sectiontype, option) then
- local vs = self.map:get_scheme(self.sectiontype, option)
- if vs.type == "boolean" then
- class = Flag
- elseif vs.type == "list" then
- class = DynamicList
- elseif vs.type == "enum" or vs.type == "reference" then
- class = ListValue
- else
- class = Value
- end
- end
-
if instanceof(class, AbstractValue) then
local obj = class(self.map, self, option, ...)
self:append(obj)
@@ -1060,16 +985,6 @@ function NamedSection.__init__(self, map, section, stype, ...)
-- Defaults
self.addremove = false
-
- -- Use defaults from UVL
- if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
- local vs = self.map:get_scheme(self.sectiontype)
- self.addremove = not vs.unique and not vs.required
- self.dynamic = vs.dynamic
- self.title = self.title or vs.title
- self.description = self.description or vs.descr
- end
-
self.template = "cbi/nsection"
self.section = section
end
@@ -1097,10 +1012,6 @@ function NamedSection.parse(self, novld)
AbstractSection.parse_dynamic(self, s)
if self.map:submitstate() then
Node.parse(self, s)
-
- if not novld and not self.override_scheme and self.map.scheme then
- _uvl_validate_section(self, s)
- end
end
AbstractSection.parse_optionals(self, s)
@@ -1122,19 +1033,9 @@ TypedSection = class(AbstractSection)
function TypedSection.__init__(self, map, type, ...)
AbstractSection.__init__(self, map, type, ...)
- self.template = "cbi/tsection"
+ self.template = "cbi/tsection"
self.deps = {}
self.anonymous = false
-
- -- Use defaults from UVL
- if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
- local vs = self.map:get_scheme(self.sectiontype)
- self.addremove = not vs.unique and not vs.required
- self.dynamic = vs.dynamic
- self.anonymous = not vs.named
- self.title = self.title or vs.title
- self.description = self.description or vs.descr
- end
end
-- Return all matching UCI sections for this TypedSection
@@ -1175,10 +1076,6 @@ function TypedSection.parse(self, novld)
AbstractSection.parse_dynamic(self, k)
if self.map:submitstate() then
Node.parse(self, k, novld)
-
- if not novld and not self.override_scheme and self.map.scheme then
- _uvl_validate_section(self, k)
- end
end
AbstractSection.parse_optionals(self, k)
end
@@ -1290,29 +1187,6 @@ function AbstractValue.__init__(self, map, section, option, ...)
end
function AbstractValue.prepare(self)
- -- Use defaults from UVL
- if not self.override_scheme
- and self.map:get_scheme(self.section.sectiontype, self.option) then
- local vs = self.map:get_scheme(self.section.sectiontype, self.option)
- if self.cast == nil then
- self.cast = (vs.type == "list") and "list" or "string"
- end
- self.title = self.title or vs.title
- self.description = self.description or vs.descr
- if self.default == nil then
- self.default = vs.default
- end
-
- if vs.depends and not self.override_dependencies then
- for i, deps in ipairs(vs.depends) do
- deps = _uvl_strip_remote_dependencies(deps)
- if next(deps) then
- self:depends(deps)
- end
- end
- end
- end
-
self.cast = self.cast or "string"
end
@@ -1447,7 +1321,8 @@ end
-- Return the UCI value of this object
function AbstractValue.cfgvalue(self, section)
- local value = self.map:get(section, self.option)
+ local value = (self.error and self.error[section] == "invalid")
+ and self:formvalue(section) or self.map:get(section, self.option)
if not value then
return nil
elseif not self.cast or self.cast == type(value) then
@@ -1463,7 +1338,13 @@ end
-- Validate the form value
function AbstractValue.validate(self, value)
- return value
+ if self.datatype and value and datatypes[self.datatype] then
+ if datatypes[self.datatype](value) then
+ return value
+ end
+ else
+ return value
+ end
end
AbstractValue.transform = AbstractValue.validate
@@ -1580,26 +1461,6 @@ function ListValue.__init__(self, ...)
self.widget = "select"
end
-function ListValue.prepare(self, ...)
- AbstractValue.prepare(self, ...)
- if not self.override_scheme
- and self.map:get_scheme(self.section.sectiontype, self.option) then
- local vs = self.map:get_scheme(self.section.sectiontype, self.option)
- if self.value and vs.valuelist and not self.override_values then
- for k, v in ipairs(vs.valuelist) do
- local deps = {}
- if not self.override_dependencies
- and vs.enum_depends and vs.enum_depends[v.value] then
- for i, dep in ipairs(vs.enum_depends[v.value]) do
- table.insert(deps, _uvl_strip_remote_dependencies(dep))
- end
- end
- self:value(v.value, v.title or v.value, unpack(deps))
- end
- end
- end
-end
-
function ListValue.value(self, key, val, ...)
if luci.util.contains(self.keylist, key) then
return
diff --git a/libs/cbi/luasrc/cbi/datatypes.lua b/libs/cbi/luasrc/cbi/datatypes.lua
new file mode 100644
index 000000000..097f1d92b
--- /dev/null
+++ b/libs/cbi/luasrc/cbi/datatypes.lua
@@ -0,0 +1,186 @@
+--[[
+
+LuCI - Configuration Bind Interface - Datatype Tests
+(c) 2010 Jo-Philipp Wich <xm@subsignal.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+$Id$
+
+]]--
+
+local fs = require "nixio.fs"
+local ip = require "luci.ip"
+local math = require "math"
+local util = require "luci.util"
+
+local tonumber = tonumber
+
+module "luci.cbi.datatypes"
+
+
+function bool( val )
+ if val == "1" or val == "yes" or val == "on" or val == "true" then
+ return true
+ elseif val == "0" or val == "no" or val == "off" or val == "false" then
+ return true
+ elseif val == "" or val == nil then
+ return true
+ end
+
+ return false
+end
+
+function uint( val )
+ local n = tonumber(val)
+ if n ~= nil and math.floor(n) == n and n >= 0 then
+ return true
+ end
+
+ return false
+end
+
+function int( val )
+ local n = tonumber(val)
+ if n ~= nil and math.floor(n) == n then
+ return true
+ end
+
+ return false
+end
+
+function float( val )
+ return ( tonumber(val) ~= nil )
+end
+
+function ipaddr( val )
+ return ip4addr(val) or ip6addr(val)
+end
+
+function ip4addr( val )
+ if val then
+ return ip.IPv4(val) and true or false
+ end
+
+ return false
+end
+
+function ip4prefix( val )
+ val = tonumber(val)
+ return ( val and val >= 0 and val <= 32 )
+end
+
+function ip6addr( val )
+ if val then
+ return ip.IPv6(val) and true or false
+ end
+
+ return false
+end
+
+function ip6prefix( val )
+ val = tonumber(val)
+ return ( val and val >= 0 and val <= 128 )
+end
+
+function port( val )
+ val = tonumber(val)
+ return ( val and val >= 1 and val <= 65535 )
+end
+
+function portrange( val )
+ local p1, p2 = val:match("^(%d+)%-(%d+)$")
+ if p1 and p2 and port(p1) and port(p2) then
+ return true
+ else
+ return port(val)
+ end
+end
+
+function macaddr( val )
+ if val and val:match(
+ "^[a-fA-F0-9]+:[a-fA-F0-9]+:[a-fA-F0-9]+:" ..
+ "[a-fA-F0-9]+:[a-fA-F0-9]+:[a-fA-F0-9]+$"
+ ) then
+ local parts = util.split( val, ":" )
+
+ for i = 1,6 do
+ parts[i] = tonumber( parts[i], 16 )
+ if parts[i] < 0 or parts[i] > 255 then
+ return false
+ end
+ end
+
+ return true
+ end
+
+ return false
+end
+
+function hostname( val )
+ if val and val:match("[a-zA-Z0-9_][a-zA-Z0-9_%-%.]*") then
+ return true -- XXX: ToDo: need better solution
+ end
+
+ return false
+end
+
+function host( val )
+ return hostname(val) or ipaddr(val)
+end
+
+function string( val )
+ return true -- Everything qualifies as valid string
+end
+
+function directory( val, seen )
+ local s = fs.stat( val )
+ seen = seen or { }
+
+ if s and not seen[s.ino] then
+ seen[s.ino] = true
+ if s.type == "dir" then
+ return true
+ elseif s.type == "lnk" then
+ return directory( fs.readlink(val), seen )
+ end
+ end
+
+ return false
+end
+
+function file( val, seen )
+ local s = fs.stat( val )
+ seen = seen or { }
+
+ if s and not seen[s.ino] then
+ seen[s.ino] = true
+ if s.type == "reg" then
+ return true
+ elseif s.type == "lnk" then
+ return file( fs.readlink(val), seen )
+ end
+ end
+
+ return false
+end
+
+function device( val, seen )
+ local s = fs.stat( val )
+ seen = seen or { }
+
+ if s and not seen[s.ino] then
+ seen[s.ino] = true
+ if s.type == "chr" or s.type == "blk" then
+ return true
+ elseif s.type == "lnk" then
+ return device( fs.readlink(val), seen )
+ end
+ end
+
+ return false
+end