diff options
author | Jo-Philipp Wich <jow@openwrt.org> | 2011-01-02 19:26:37 +0000 |
---|---|---|
committer | Jo-Philipp Wich <jow@openwrt.org> | 2011-01-02 19:26:37 +0000 |
commit | 052f65950cc195adc1589566ca9725de0ad83c63 (patch) | |
tree | fe273278b704a7ea6b1ce707680a1d8412d6eb00 /libs/uvl/luasrc | |
parent | 800a2630497e1ec2c61199b3bc645d778c714529 (diff) |
libs: drop uvl and uvldoc
Diffstat (limited to 'libs/uvl/luasrc')
-rw-r--r-- | libs/uvl/luasrc/uvl.lua | 1213 | ||||
-rw-r--r-- | libs/uvl/luasrc/uvl/datatypes.lua | 187 | ||||
-rw-r--r-- | libs/uvl/luasrc/uvl/dependencies.lua | 196 | ||||
-rw-r--r-- | libs/uvl/luasrc/uvl/errors.lua | 188 | ||||
-rw-r--r-- | libs/uvl/luasrc/uvl/validation.lua | 72 |
5 files changed, 0 insertions, 1856 deletions
diff --git a/libs/uvl/luasrc/uvl.lua b/libs/uvl/luasrc/uvl.lua deleted file mode 100644 index d2ea4bd2c2..0000000000 --- a/libs/uvl/luasrc/uvl.lua +++ /dev/null @@ -1,1213 +0,0 @@ ---[[ - -UCI Validation Layer - Main Library -(c) 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net> -(c) 2008 Steven Barth <steven@midlink.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$ - -]]-- - - ---- UVL - UCI Validation Layer --- @class module --- @cstyle instance - -local fs = require "nixio.fs" -local uci = require "luci.model.uci" -local util = require "luci.util" -local nutil = require "nixio.util" -local table = require "table" -local string = require "string" - -local require, pcall, ipairs, pairs = require, pcall, ipairs, pairs -local type, error, tonumber, tostring = type, error, tonumber, tostring -local unpack, loadfile, collectgarbage = unpack, loadfile, collectgarbage - -module "luci.uvl" - -local ERR = require "luci.uvl.errors".error -local datatypes = require "luci.uvl.datatypes" -local validation = require "luci.uvl.validation" -local dependencies = require "luci.uvl.dependencies" - -local TYPE_SCHEME = 0x00 -local TYPE_CONFIG = 0x01 -local TYPE_SECTION = 0x02 -local TYPE_OPTION = 0x03 -local TYPE_ENUM = 0x04 - -local PAT_EXPR1 = "^%$?[%w_]+$" -local PAT_EXPR2 = "^%$?[%w_]+%.%$?[%w_]+$" -local PAT_EXPR3 = "^%$?[%w_]+%.%$?[%w_]+%.%$?[%w_]+$" - ---- Boolean; default true; --- treat sections found in config but not in scheme as error -STRICT_UNKNOWN_SECTIONS = true - ---- Boolean; default true; --- treat options found in config but not in scheme as error -STRICT_UNKNOWN_OPTIONS = true - ---- Boolean; default true; --- treat failed external validators as error -STRICT_EXTERNAL_VALIDATORS = true - ---- Boolean; default true; --- treat list values stored as options like errors -STRICT_LIST_TYPE = true - - -local default_schemedir = "/lib/uci/schema" -local default_savedir = "/tmp/.uvl" - - ---- Object constructor --- @class function --- @name UVL --- @param schemedir Path to the scheme directory (optional) --- @param configdir Override config directory (optional) --- @return Instance object -UVL = util.class() - -function UVL.__init__( self, schemedir, configdir ) - self.schemedir = schemedir or default_schemedir - self.configdir = configdir - self.packages = { } - self.beenthere = { } - self.depseen = { } - self.uci = uci - self.err = ERR - self.dep = dependencies - self.datatypes = datatypes -end - - ---- Parse given scheme and return the scheme tree. --- @param scheme Name of the scheme to parse --- @return Table containing the parsed scheme or nil on error --- @return String containing the reason for errors (if any) -function UVL.get_scheme( self, scheme ) - if not self.packages[scheme] then - local ok, err = self:read_scheme( scheme ) - if not ok then - return nil, err - end - end - return self.packages[scheme], nil -end - ---- Validate given configuration, section or option. --- @param config Name of the configuration to validate --- @param section Name of the section to validate (optional) --- @param option Name of the option to validate (optional) --- @return Boolean indicating whether the given config validates --- @return String containing the reason for errors (if any) -function UVL.validate( self, config, section, option ) - if config and section and option then - return self:validate_option( config, section, option ) - elseif config and section then - return self:validate_section( config, section ) - elseif config then - return self:validate_config( config ) - end -end - ---- Validate given configuration. --- @param cfg Name of the configuration to validate --- @return Boolean indicating whether the given config validates --- @return String containing the reason for errors (if any) -function UVL.validate_config( self, cfg, uci ) - - if not self.packages[cfg] then - local ok, err = self:read_scheme(cfg) - if not ok then - return false, err - end - end - - local co = config( self, uci or cfg, uci and cfg ) - local sc = { } - - self.beenthere = { } - self.depseen = { } - - if not co:config() then - return false, co:errors() - end - - local function _uci_foreach( type, func ) - for k, v in pairs(co:config()) do - if v['.type'] == type then - sc[type] = sc[type] + 1 - local ok, err = func( k, v ) - if not ok then co:error(err) end - end - end - end - - for k, v in pairs( self.packages[cfg].sections ) do - sc[k] = 0 - _uci_foreach( k, - function(s) - return self:_validate_section( co:section(s) ) - end - ) - end - - if STRICT_UNKNOWN_SECTIONS then - for k, v in pairs(co:config()) do - local so = co:section(k) - if not self.beenthere[so:cid()] then - co:error(ERR('SECT_UNKNOWN', so)) - end - end - end - - for _, k in ipairs(util.keys(sc)) do - local so = co:section(k) - if so:scheme('required') and sc[k] == 0 then - co:error(ERR('SECT_REQUIRED', so)) - elseif so:scheme('unique') and sc[k] > 1 then - co:error(ERR('SECT_UNIQUE', so)) - end - end - - return co:ok(), co:errors() -end - ---- Validate given config section. --- @param config Name of the configuration to validate --- @param section Name of the section to validate --- @return Boolean indicating whether the given config validates --- @return String containing the reason for errors (if any) -function UVL.validate_section( self, cfg, section, uci ) - - if not self.packages[cfg] then - local ok, err = self:read_scheme( cfg ) - if not ok then - return false, err - end - end - - local co = config( self, uci or cfg, uci and cfg ) - local so = co:section( section ) - - self.beenthere = { } - self.depseen = { } - - if not co:config() then - return false, co:errors() - end - - if so:config() then - return self:_validate_section( so ) - else - return false, ERR('SECT_NOTFOUND', so) - end -end - ---- Validate given config option. --- @param config Name of the configuration to validate --- @param section Name of the section to validate --- @param option Name of the option to validate --- @return Boolean indicating whether the given config validates --- @return String containing the reason for errors (if any) -function UVL.validate_option( self, cfg, section, option, uci ) - - if not self.packages[cfg] then - local ok, err = self:read_scheme( cfg ) - if not ok then - return false, err - end - end - - local co = config( self, uci or cfg, uci and cfg ) - local so = co:section( section ) - local oo = so:option( option ) - - if not co:config() then - return false, co:errors() - end - - if so:config() and oo:config() then - return self:_validate_option( oo ) - else - return false, ERR('OPT_NOTFOUND', oo) - end -end - - -function UVL._validate_section( self, section ) - - self.beenthere[section:cid()] = true - - if section:config() then - if section:scheme('named') == true and - section:config('.anonymous') == true - then - return false, ERR('SECT_NAMED', section) - end - - for _, v in ipairs(section:variables()) do - local ok, err = self:_validate_option( v ) - if not ok and ( - v:scheme('required') or v:scheme('type') == "enum" or ( - not err:is('DEP_NOTEQUAL') and - not err:is('DEP_NOVALUE') - ) - ) then - section:error(err) - end - end - - local ok, err = dependencies.check( self, section ) - if not ok then - section:error(err) - end - else - return false, ERR('SECT_NOTFOUND', section) - end - - if STRICT_UNKNOWN_OPTIONS and not section:scheme('dynamic') then - for k, v in pairs(section:config()) do - local oo = section:option(k) - if k:byte(1) == 46 and not self.beenthere[oo:cid()] then - section:error(ERR('OPT_UNKNOWN', oo)) - end - end - end - - return section:ok(), section:errors() -end - -function UVL._validate_option( self, option, nodeps ) - - self.beenthere[option:cid()] = true - - if not option:scheme() and not option:parent():scheme('dynamic') then - if STRICT_UNKNOWN_OPTIONS then - return false, option:error(ERR('OPT_UNKNOWN', option)) - else - return true - end - - elseif option:scheme() then - if not nodeps then - local ok, err = dependencies.check( self, option ) - if not ok then - if not err:is_all( - 'OPT_REQUIRED', - 'DEP_NOTEQUAL', - 'DEP_NOVALUE' - ) then - option:error(err) - return false, option:errors() - else - return true - end - end - end - - if option:scheme('required') and not option:value() then - return false, option:error(ERR('OPT_REQUIRED', option)) - - elseif option:value() then - local val = option:value() - - if option:scheme('type') == "reference" or - option:scheme('type') == "enum" - then - local scheme_values = option:scheme('values') or { } - local config_values = ( type(val) == "table" and val or { val } ) - for _, v in ipairs(config_values) do - if not scheme_values[v] then - return false, option:error( ERR( - 'OPT_BADVALUE', - option, { v, util.serialize_data( - util.keys(scheme_values) - ) } - ) ) - end - end - elseif option:scheme('type') == "list" then - if type(val) ~= "table" and STRICT_LIST_TYPE then - return false, option:error(ERR('OPT_NOTLIST', option)) - end - end - - if option:scheme('datatype') then - local dt = option:scheme('datatype') - - if self.datatypes[dt] then - val = ( type(val) == "table" and val or { val } ) - for i, v in ipairs(val) do - if not self.datatypes[dt]( v ) then - return false, option:error( - ERR('OPT_INVVALUE', option, { v, dt }) - ) - end - end - else - return false, option:error(ERR('OPT_DATATYPE', option, dt)) - end - end - - val = ( type(val) == "table" and val or { val } ) - for _, v in ipairs(val) do - if option:scheme('minlength') then - if #v < option:scheme('minlength') then - return false, option:error(ERR('OPT_RANGE', option)) - end - end - - if option:scheme('maxlength') then - if #v > option:scheme('maxlength') then - return false, option:error(ERR('OPT_RANGE', option)) - end - end - - local w = tonumber(v) - - if option:scheme('minimum') then - if not w or w < option:scheme('minimum') then - return false, option:error(ERR('OPT_RANGE', option)) - end - end - - if option:scheme('maximum') then - if not w or w > option:scheme('maximum') then - return false, option:error(ERR('OPT_RANGE', option)) - end - end - end - end - - local ok, err = validation.check( self, option ) - if not ok and STRICT_EXTERNAL_VALIDATORS then - return false, option:error(err) - end - end - - return option:ok(), option:errors() -end - ---- Find all parts of given scheme and construct validation tree. --- This is normally done on demand, so you don't have to call this function --- by yourself. --- @param shm Name of the scheme to parse --- @param alias Create an alias for the loaded scheme -function UVL.read_scheme( self, shm, alias ) - - local so = scheme( self, shm ) - local bc = "%s/bytecode/%s.lua" %{ self.schemedir, shm } - - if not fs.access(bc) then - local files = nutil.consume((fs.glob(self.schemedir .. '/*/' .. shm))) - - if #files > 0 then - local ok, err - for _, file in ipairs(files) do - if not fs.access(file) then - return false, so:error(ERR('SME_READ', so,file)) - end - - local uci = uci.cursor( fs.dirname(file), default_savedir ) - - local sname = fs.basename(file) - local sd, err = uci:load( sname ) - - if not sd then - return false, ERR('UCILOAD', so, err) - end - - ok, err = pcall(function() - uci:foreach(sname, "package", function(s) - self:_parse_package(so, s[".name"], s) - end) - uci:foreach(sname, "section", function(s) - self:_parse_section(so, s[".name"], s) - end) - uci:foreach(sname, "variable", function(s) - self:_parse_var(so, s[".name"], s) - end) - uci:foreach(sname, "enum", function(s) - self:_parse_enum(so, s[".name"], s) - end) - - end) - end - - if ok and alias then self.packages[alias] = self.packages[shm] end - return ok and self, err - else - return false, so:error(ERR('SME_FIND', so, self.schemedir)) - end - else - local sc = loadfile(bc) - if sc then - self.packages[shm] = sc() - return true - else - return false, so:error(ERR('SME_READ',so,bc)) - end - end -end - --- helper function to check for required fields -local function _req( t, n, c, r ) - for i, v in ipairs(r) do - if not c[v] then - local p, o = scheme:sid(), nil - - if t == TYPE_SECTION then - o = section( scheme, nil, p, n ) - elseif t == TYPE_OPTION then - o = option( scheme, nil, p, '(nil)', n ) - elseif t == TYPE_ENUM then - o = enum( scheme, nil, p, '(nil)', '(nil)', n ) - end - - return false, ERR('SME_REQFLD',o,v) - end - end - return true -end - --- helper function to validate references -local function _ref( c, t ) - local r, k, n = {} - if c == TYPE_SECTION then - k = "package" - n = 1 - elseif c == TYPE_OPTION then - k = "section" - n = 2 - elseif c == TYPE_ENUM then - k = "variable" - n = 3 - end - - for o in t[k]:gmatch("[^.]+") do - r[#r+1] = o - end - r[1] = ( #r[1] > 0 and r[1] or scheme:sid() ) - - if #r ~= n then - return false, ERR('SME_BADREF', scheme, k) - end - - return r -end - --- helper function to read bools -local function _bool( v ) - return ( v == "true" or v == "yes" or v == "on" or v == "1" ) -end - --- Step 0: get package meta information -function UVL._parse_package(self, scheme, k, v) - local sid = scheme:sid() - local pkg = self.packages[sid] or { - ["name"] = sid; - ["sections"] = { }; - ["variables"] = { }; - } - - pkg.title = v.title - pkg.description = v.description - - self.packages[sid] = pkg -end - --- Step 1: get all sections -function UVL._parse_section(self, scheme, k, v) - local ok, err = _req( TYPE_SECTION, k, v, { "name", "package" } ) - if err then error(scheme:error(err)) end - - local r, err = _ref( TYPE_SECTION, v ) - if err then error(scheme:error(err)) end - - local p = self.packages[r[1]] or { - ["name"] = r[1]; - ["sections"] = { }; - ["variables"] = { }; - } - p.sections[v.name] = p.sections[v.name] or { } - p.variables[v.name] = p.variables[v.name] or { } - self.packages[r[1]] = p - - local s = p.sections[v.name] - local so = scheme:section(v.name) - - for k, v2 in pairs(v) do - if k ~= "name" and k ~= "package" and k:byte(1) == 46 then - if k == "depends" then - s.depends = self:_read_dependency( v2, s.depends ) - if not s.depends then - return false, scheme:error( - ERR('SME_BADDEP', so, util.serialize_data(s.depends)) - ) - end - elseif k == "dynamic" or k == "unique" or - k == "required" or k == "named" - then - s[k] = _bool(v2) - else - s[k] = v2 - end - end - end - - s.dynamic = s.dynamic or false - s.unique = s.unique or false - s.required = s.required or false - s.named = s.named or false -end - --- Step 2: get all variables -function UVL._parse_var(self, scheme, k, v) - local ok, err = _req( TYPE_OPTION, k, v, { "name", "section" } ) - if err then error(scheme:error(err)) end - - local r, err = _ref( TYPE_OPTION, v ) - if err then error(scheme:error(err)) end - - local p = self.packages[r[1]] - if not p then - error(scheme:error( - ERR('SME_VBADPACK', {scheme:sid(), '', v.name}, r[1]) - )) - end - - local s = p.variables[r[2]] - if not s then - error(scheme:error( - ERR('SME_VBADSECT', {scheme:sid(), '', v.name}, r[2]) - )) - end - - s[v.name] = s[v.name] or { } - - local t = s[v.name] - local so = scheme:section(r[2]) - local to = so:option(v.name) - - for k, v2 in pairs(v) do - if k ~= "name" and k ~= "section" and k:byte(1) == 46 then - if k == "depends" then - t.depends = self:_read_dependency( v2, t.depends ) - if not t.depends then - error(scheme:error(so:error( - ERR('SME_BADDEP', to, util.serialize_data(v2)) - ))) - end - elseif k == "validator" then - t.validators = self:_read_validator( v2, t.validators ) - if not t.validators then - error(scheme:error(so:error( - ERR('SME_BADVAL', to, util.serialize_data(v2)) - ))) - end - elseif k == "valueof" then - local values, err = self:_read_reference( v2 ) - if err then - error(scheme:error(so:error( - ERR('REFERENCE', to, util.serialize_data(v2)):child(err) - ))) - end - t.type = "reference" - t.values = values - t.valueof = type(v2) == "table" and v2 or {v2} - elseif k == "required" then - t[k] = _bool(v2) - elseif k == "minlength" or k == "maxlength" or - k == "minimum" or k == "maximum" - then - t[k] = tonumber(v2) - else - t[k] = t[k] or v2 - end - end - end - - t.type = t.type or "variable" - t.datatype = t.datatype or "string" - t.required = t.required or false -end - --- Step 3: get all enums -function UVL._parse_enum(self, scheme, k, v) - local ok, err = _req( TYPE_ENUM, k, v, { "value", "variable" } ) - if err then error(scheme:error(err)) end - - local r, err = _ref( TYPE_ENUM, v ) - if err then error(scheme:error(err)) end - - local p = self.packages[r[1]] - if not p then - error(scheme:error( - ERR('SME_EBADPACK', {scheme:sid(), '', '', v.value}, r[1]) - )) - end - - local s = p.variables[r[2]] - if not s then - error(scheme:error( - ERR('SME_EBADSECT', {scheme:sid(), '', '', v.value}, r[2]) - )) - end - - local t = s[r[3]] - if not t then - error(scheme:error( - ERR('SME_EBADOPT', {scheme:sid(), '', '', v.value}, r[3]) - )) - end - - - local so = scheme:section(r[2]) - local oo = so:option(r[3]) - local eo = oo:enum(v.value) - - if t.type ~= "enum" and t.type ~= "reference" then - error(scheme:error(ERR('SME_EBADTYPE', eo))) - end - - if not t.values then - t.values = { [v.value] = v.title or v.value } - t.valuelist = { {value = v.value, title = v.title} } - else - t.values[v.value] = v.title or v.value - t.valuelist[#t.valuelist + 1] = {value = v.value, title = v.title} - end - - if not t.enum_depends then - t.enum_depends = { } - end - - if v.default then - if t.default then - error(scheme:error(ERR('SME_EBADDEF', eo))) - end - t.default = v.value - end - - if v.depends then - t.enum_depends[v.value] = self:_read_dependency( - v.depends, t.enum_depends[v.value] - ) - - if not t.enum_depends[v.value] then - error(scheme:error(so:error(oo:error( - ERR('SME_BADDEP', eo, util.serialize_data(v.depends)) - )))) - end - end -end - --- Read a dependency specification -function UVL._read_dependency( self, values, deps ) - local expr = "%$?[%w_]+" - if values then - values = ( type(values) == "table" and values or { values } ) - for _, value in ipairs(values) do - local condition = { } - for val in value:gmatch("[^,]+") do - local k, e, v = val:match("%s*([%w$_.]+)%s*(=?)%s*(.*)") - - if k and ( - k:match(PAT_EXPR1) or k:match(PAT_EXPR2) or k:match(PAT_EXPR3) - ) then - condition[k] = (e == '=') and v or true - else - return nil - end - end - - if not deps then - deps = { condition } - else - deps[#deps+1] = condition - end - end - end - - return deps -end - --- Read a validator specification -function UVL._read_validator( self, values, validators ) - if values then - values = ( type(values) == "table" and values or { values } ) - for _, value in ipairs(values) do - local validator - - if value:match("^exec:") then - validator = value:gsub("^exec:","") - elseif value:match("^lua:") then - validator = self:_resolve_function( (value:gsub("^lua:","") ) ) - elseif value:match("^regexp:") then - local pattern = value:gsub("^regexp:","") - validator = function( type, dtype, pack, sect, optn, arg1, arg2, arg3, arg4, arg5 ) - local values = { arg1, arg2, arg3, arg4, arg5 } - for _, v in ipairs(values) do - local ok, match = - pcall( string.match, v, pattern ) - - if not ok then - return false, match - elseif not match then - return false, - 'Value "%s" does not match pattern "%s"' % { - v, pattern - } - end - end - return true - end - end - - if validator then - if not validators then - validators = { validator } - else - validators[#validators+1] = validator - end - else - return nil - end - end - - return validators - end -end - --- Read a reference specification (XXX: We should validate external configs too...) -function UVL._read_reference( self, values ) - local val = { } - values = ( type(values) == "table" and values or { values } ) - - for _, value in ipairs(values) do - local ref = util.split(value, ".") - - if #ref == 2 or #ref == 3 then - local co = config( self, ref[1] ) - if not co:config() then return false, co:errors() end - - for k, v in pairs(co:config()) do - if v['.type'] == ref[2] then - if #ref == 2 then - if v['.anonymous'] == true then - return false, ERR('SME_INVREF', '', value) - end - val[k] = k -- XXX: title/description would be nice - elseif v[ref[3]] then - val[v[ref[3]]] = v[ref[3]] -- XXX: dito - end - end - end - else - return false, ERR('SME_BADREF', '', value) - end - end - - return val, nil -end - --- Resolve given path -function UVL._resolve_function( self, value ) - local path = util.split(value, ".") - - for i=1, #path-1 do - local stat, mod = pcall( - require, table.concat(path, ".", 1, i) - ) - - if stat and mod then - for j=i+1, #path-1 do - if not type(mod) == "table" then - break - end - mod = mod[path[j]] - if not mod then - break - end - end - mod = type(mod) == "table" and mod[path[#path]] or nil - if type(mod) == "function" then - return mod - end - end - end -end - - ---- Object representation of an uvl item - base class. -uvlitem = util.class() - -function uvlitem.cid(self) - if #self.cref == 1 then - return self.cref[1] - else - local r = { unpack(self.cref) } - local c = self.c - if c and c[r[2]] and c[r[2]]['.anonymous'] and c[r[2]]['.index'] then - r[2] = '@' .. c[r[2]]['.type'] .. - '[' .. tostring(c[r[2]]['.index']) .. ']' - end - return table.concat( r, '.' ) - end -end - -function uvlitem.sid(self) - return table.concat( self.sref, '.' ) -end - -function uvlitem.scheme(self, opt) - local s = self.s and self.s.packages - s = s and s[self.sref[1]] - if #self.sref == 4 or #self.sref == 3 then - s = s and s.variables - s = s and s[self.sref[2]] - s = s and s[self.sref[3]] - elseif #self.sref == 2 then - s = s and s.sections - s = s and s[self.sref[2]] - end - - if s and opt then - return s[opt] - elseif s then - return s - end -end - -function uvlitem.config(self, opt) - local c = self.c - - if #self.cref >= 2 and #self.cref <= 4 then - c = c and self.c[self.cref[2]] or nil - if #self.cref >= 3 then - c = c and c[self.cref[3]] or nil - end - end - - if c and opt then - return c[opt] - elseif c then - return c - end -end - -function uvlitem.title(self) - return self:scheme() and self:scheme('title') or - self.cref[3] or self.cref[2] or self.cref[1] -end - -function uvlitem.type(self) - if self.t == TYPE_CONFIG then - return 'config' - elseif self.t == TYPE_SECTION then - return 'section' - elseif self.t == TYPE_OPTION then - return 'option' - elseif self.t == TYPE_ENUM then - return 'enum' - end -end - -function uvlitem.error(self, arg1, arg2, arg3, arg4, arg5) - if not self.e then - local errconst = { 'CONFIG', 'SECTION', 'OPTION', 'OPTION' } - self.e = ERR( errconst[#self.cref], self ) - end - - return self.e:child( arg1, arg2, arg3, arg4, arg5 ) -end - -function uvlitem.errors(self) - return self.e -end - -function uvlitem.ok(self) - return not self:errors() -end - -function uvlitem.parent(self) - if self.p then - return self.p - elseif #self.cref == 3 or #self.cref == 4 then - return section( self.s, self.c, self.cref[1], self.cref[2] ) - elseif #self.cref == 2 then - return config( self.s, self.c, self.cref[1] ) - else - return nil - end -end - -function uvlitem._loadconf(self, co, c, configdir) - co = co or self._configcache - if not co then - local err - co, err = uci.cursor(configdir):get_all(c) - - if err then - self:error(ERR('UCILOAD', self, err)) - end - - self._configcache = co - end - return co -end - - ---- Object representation of a scheme. --- @class scheme --- @cstyle instance --- @name luci.uvl.scheme - ---- Scheme instance constructor. --- @class function --- @name scheme --- @param scheme Scheme instance --- @param co Configuration data --- @param c Configuration name --- @return Config instance -scheme = util.class(uvlitem) - -function scheme.__init__(self, scheme, co, c) - if not c then - c, co = co, nil - end - - self.cref = { c } - self.sref = { c } - self.c = self:_loadconf(co, c, scheme.configdir) - self.s = scheme - self.t = TYPE_SCHEME -end - ---- Add an error to scheme. --- @return Scheme error context -function scheme.error(self, arg1, arg2, arg3, arg4, arg5) - if not self.e then self.e = ERR( 'SCHEME', self ) end - return self.e:child( arg1, arg2, arg3, arg4, arg5 ) -end - ---- Get an associated config object. --- @return Config instance -function scheme.config(self) - local co = config( self.s, self.cref[1] ) - co.p = self - - return co -end - ---- Get all section objects associated with this scheme. --- @return Table containing all associated luci.uvl.section instances -function scheme.sections(self) - local v = { } - if self.s.packages[self.sref[1]].sections then - for o, _ in pairs( self.s.packages[self.sref[1]].sections ) do - v[#v+1] = option( - self.s, self.c, self.cref[1], self.cref[2], o - ) - end - end - return v -end - ---- Get an associated section object. --- @param s Section to select --- @return Section instance -function scheme.section(self, s) - local so = section( self.s, self.c, self.cref[1], s ) - so.p = self - - return so -end - - ---- Object representation of a config. --- @class config --- @cstyle instance --- @name luci.uvl.config - ---- Config instance constructor. --- @class function --- @name config --- @param scheme Scheme instance --- @param co Configuration data --- @param c Configuration name --- @return Config instance -config = util.class(uvlitem) - -function config.__init__(self, scheme, co, c) - if not c then - c, co = co, nil - end - self.cref = { c } - self.sref = { c } - self.c = self:_loadconf(co, c, scheme.configdir) - self.s = scheme - self.t = TYPE_CONFIG -end - ---- Get all section objects associated with this config. --- @return Table containing all associated luci.uvl.section instances -function config.sections(self) - local v = { } - if self.s.packages[self.sref[1]].sections then - for o, _ in pairs( self.s.packages[self.sref[1]].sections ) do - v[#v+1] = option( - self.s, self.c, self.cref[1], self.cref[2], o - ) - end - end - return v -end - ---- Get an associated section object. --- @param s Section to select --- @return Section instance -function config.section(self, s) - local so = section( self.s, self.c, self.cref[1], s ) - so.p = self - - return so -end - - ---- Object representation of a scheme/config section. --- @class module --- @cstyle instance --- @name luci.uvl.section - ---- Section instance constructor. --- @class function --- @name section --- @param scheme Scheme instance --- @param co Configuration data --- @param c Configuration name --- @param s Section name --- @return Section instance -section = util.class(uvlitem) - -function section.__init__(self, scheme, co, c, s) - self.cref = { c, s } - self.sref = { c, co and co[s] and co[s]['.type'] or s } - self.c = self:_loadconf(co, c, scheme.configdir) - self.s = scheme - self.t = TYPE_SECTION -end - ---- Get all option objects associated with this section. --- @return Table containing all associated luci.uvl.option instances -function section.variables(self) - local v = { } - if self.s.packages[self.sref[1]].variables[self.sref[2]] then - for o, _ in pairs( - self.s.packages[self.sref[1]].variables[self.sref[2]] - ) do - v[#v+1] = option( - self.s, self.c, self.cref[1], self.cref[2], o - ) - end - end - return v -end - ---- Get an associated option object. --- @param o Option to select --- @return Option instance -function section.option(self, o) - local oo = option( self.s, self.c, self.cref[1], self.cref[2], o ) - oo.p = self - - return oo -end - - ---- Object representation of a scheme/config option. --- @class module --- @cstyle instance --- @name luci.uvl.option - ---- Section instance constructor. --- @class function --- @name option --- @param scheme Scheme instance --- @param co Configuration data --- @param c Configuration name --- @param s Section name --- @param o Option name --- @return Option instance -option = util.class(uvlitem) - -function option.__init__(self, scheme, co, c, s, o) - self.cref = { c, s, o } - self.sref = { c, co and co[s] and co[s]['.type'] or s, o } - self.c = self:_loadconf(co, c, scheme.configdir) - self.s = scheme - self.t = TYPE_OPTION -end - ---- Get the value of this option. --- @return The associated configuration value -function option.value(self) - local v = self:config() or self:scheme('default') - if v and self:scheme('multival') then - v = util.split( v, "%s+", nil, true ) - end - return v -end - ---- Get the associated section information in scheme. --- @return Table containing the scheme properties -function option.section(self) - return self.s.packages[self.sref[1]].sections[self.sref[2]] -end - ---- Construct an enum object instance from given or default value. --- @param v Value to select --- @return Enum instance for selected value -function option.enum(self, val) - return enum( - self.s, self.c, - self.cref[1], self.cref[2], self.cref[3], - val or self:value() - ) -end - - ---- Object representation of a enum value. --- @class module --- @cstyle instance --- @name luci.uvl.enum - ---- Section instance constructor. --- @class function --- @name enum --- @param scheme Scheme instance --- @param co Configuration data --- @param c Configuration name --- @param s Section name --- @param o Enum name --- @param v Enum value --- @return Enum value instance -enum = util.class(option) - -function enum.__init__(self, scheme, co, c, s, o, v) - self.cref = { c, s, o, v } - self.sref = { c, co and co[s] and co[s]['.type'] or s, o, v } - self.c = self:_loadconf(co, c, scheme.configdir) - self.s = scheme - self.t = TYPE_ENUM -end diff --git a/libs/uvl/luasrc/uvl/datatypes.lua b/libs/uvl/luasrc/uvl/datatypes.lua deleted file mode 100644 index c6a5de3988..0000000000 --- a/libs/uvl/luasrc/uvl/datatypes.lua +++ /dev/null @@ -1,187 +0,0 @@ ---[[ - -UCI Validation Layer - Datatype Tests -(c) 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net> -(c) 2008 Steven Barth <steven@midlink.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.uvl.datatypes" - - -function boolean( 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 integer( 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 diff --git a/libs/uvl/luasrc/uvl/dependencies.lua b/libs/uvl/luasrc/uvl/dependencies.lua deleted file mode 100644 index 33f2fad9fc..0000000000 --- a/libs/uvl/luasrc/uvl/dependencies.lua +++ /dev/null @@ -1,196 +0,0 @@ ---[[ - -UCI Validation Layer - Dependency helper -(c) 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net> -(c) 2008 Steven Barth <steven@midlink.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 uvl = require "luci.uvl" -local ERR = require "luci.uvl.errors".error -local util = require "luci.util" -local table = require "table" - -local type, unpack = type, unpack -local ipairs, pairs = ipairs, pairs - -module "luci.uvl.dependencies" - - - -function _parse_reference( r, c, s, o ) - local ref = { } - local vars = { - config = c, - section = s, - option = o - } - - for v in r:gmatch("[^.]+") do - ref[#ref+1] = (v:gsub( "%$(.+)", vars )) - end - - if #ref < 2 then - table.insert(ref, 1, s or '$section') - end - if #ref < 3 then - table.insert(ref, 1, c or '$config') - end - - return ref -end - -function _serialize_dependency( dep, v ) - local str - - for k, v in util.spairs( dep, - function(a,b) - a = ( type(dep[a]) ~= "boolean" and "_" or "" ) .. a - b = ( type(dep[b]) ~= "boolean" and "_" or "" ) .. b - return a < b - end - ) do - str = ( str and str .. " and " or "" ) .. k .. - ( type(v) ~= "boolean" and "=" .. v or "" ) - end - - return str -end - -function check( self, object, nodeps ) - - local derr = ERR('DEPENDENCY', object) - - if not self.depseen[object:cid()] then - self.depseen[object:cid()] = true - else - return false, derr:child(ERR('DEP_RECURSIVE', object)) - end - - if object:scheme('depends') then - local ok = true - local valid = false - - for _, dep in ipairs(object:scheme('depends')) do - local subcondition = true - for k, v in pairs(dep) do - -- XXX: better error - local ref = _parse_reference( k, unpack(object.cref) ) - - if not ref then - return false, derr:child(ERR('SME_BADDEP',object,k)) - end - - local option = uvl.option( self, object.c, unpack(ref) ) - - valid, err = self:_validate_option( option, true ) - if valid then - if not ( - ( type(v) == "boolean" and option:value() ) or - ( ref[3] and option:value() ) == v - ) then - subcondition = false - - local depstr = _serialize_dependency( dep, v ) - derr:child( - type(v) == "boolean" - and ERR('DEP_NOVALUE', option, depstr) - or ERR('DEP_NOTEQUAL', option, {depstr, v}) - ) - - break - end - else - subcondition = false - - local depstr = _serialize_dependency( dep, v ) - derr:child(ERR('DEP_NOTVALID', option, depstr):child(err)) - - break - end - end - - if subcondition then - ok = true - break - else - ok = false - end - end - - if not ok then - return false, derr - end - else - return true - end - - if object:scheme("type") == "enum" and - object:scheme("enum_depends")[object:value()] - then - local ok = true - local valid = false - local enum = object:enum() - local eerr = ERR('DEP_BADENUM', enum) - - for _, dep in ipairs(enum:scheme('enum_depends')[object:value()]) do - local subcondition = true - for k, v in pairs(dep) do - -- XXX: better error - local ref = _parse_reference( k, unpack(object.cref) ) - - if not ref then - return false, derr:child(eerr:child(ERR('SME_BADDEP',enum,k))) - end - - local option = luci.uvl.option( self, object.c, unpack(ref) ) - - valid, err = self:_validate_option( option, true ) - if valid then - if not ( - ( type(v) == "boolean" and object.config[ref[2]][ref[3]] ) or - ( ref[3] and object:config() ) == v - ) then - subcondition = false - - local depstr = _serialize_dependency( dep, v ) - eerr:child( - type(v) == "boolean" - and ERR('DEP_NOVALUE', option, depstr) - or ERR('DEP_NOTEQUAL', option, {depstr, v}) - ) - - break - end - else - subcondition = false - - local depstr = _serialize_dependency( dep, v ) - eerr:child(ERR('DEP_NOTVALID', option, depstr):child(err)) - - break - end - end - - if subcondition then - return true - else - ok = false - end - end - - if not ok then - return false, derr:child(eerr) - end - end - - return true -end diff --git a/libs/uvl/luasrc/uvl/errors.lua b/libs/uvl/luasrc/uvl/errors.lua deleted file mode 100644 index 45f331f46f..0000000000 --- a/libs/uvl/luasrc/uvl/errors.lua +++ /dev/null @@ -1,188 +0,0 @@ ---[[ - -UCI Validation Layer - Error handling -(c) 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net> -(c) 2008 Steven Barth <steven@midlink.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 uci = require "luci.model.uci" -local uvl = require "luci.uvl" -local util = require "luci.util" -local string = require "string" - -local luci, tonumber, unpack, ipairs, type = - luci, tonumber, unpack, ipairs, type - -module "luci.uvl.errors" - -ERRCODES = { - UCILOAD = 'Unable to load config "%p": %1', - - SCHEME = 'Error in scheme "%p":\n%c', - CONFIG = 'Error in config "%p":\n%c', - SECTION = 'Error in section "%i" (%I):\n%c', - OPTION = 'Error in option "%i" (%I):\n%c', - REFERENCE = 'Option "%i" has invalid reference specification %1:\n%c', - DEPENDENCY = 'In dependency check for %t "%i":\n%c', - - SME_FIND = 'Can not find scheme "%p" in "%1"', - SME_READ = 'Can not access file "%1"', - SME_REQFLD = 'Missing required scheme field "%1" in "%i"', - SME_INVREF = 'Illegal reference "%1" to an anonymous section', - SME_BADREF = 'Malformed reference in "%1"', - SME_BADDEP = 'Malformed dependency specification "%1" in "%i"', - SME_BADVAL = 'Malformed validator specification "%1" in "%i"', - SME_ERRVAL = 'External validator "%1" failed: %2', - SME_VBADPACK = 'Variable "%o" in scheme "%p" references unknown package "%1"', - SME_VBADSECT = 'Variable "%o" in scheme "%p" references unknown section "%1"', - SME_EBADPACK = 'Enum "%v" in scheme "%p" references unknown package "%1"', - SME_EBADSECT = 'Enum "%v" in scheme "%p" references unknown section "%1"', - SME_EBADOPT = 'Enum "%v" in scheme "%p" references unknown option "%1"', - SME_EBADTYPE = 'Enum "%v" in scheme "%p" references non-enum option "%I"', - SME_EBADDEF = 'Enum "%v" in scheme "%p" redeclares the default value of "%I"', - - SECT_UNKNOWN = 'Section "%i" (%I) not found in scheme', - SECT_REQUIRED = 'Required section "%p.%S" not found in config', - SECT_UNIQUE = 'Unique section "%p.%S" occurs multiple times in config', - SECT_NAMED = 'The section of type "%p.%S" is stored anonymously in config but must be named', - SECT_NOTFOUND = 'Section "%p.%s" not found in config', - - OPT_UNKNOWN = 'Option "%i" (%I) not found in scheme', - OPT_REQUIRED = 'Required option "%i" has no value', - OPT_BADVALUE = 'Value "%1" of option "%i" is not defined in enum %2', - OPT_INVVALUE = 'Value "%1" of option "%i" does not validate as datatype "%2"', - OPT_NOTLIST = 'Option "%i" is defined as list but stored as plain value', - OPT_DATATYPE = 'Option "%i" has unknown datatype "%1"', - OPT_NOTFOUND = 'Option "%p.%s.%o" not found in config', - OPT_RANGE = 'Option "%p.%s.%o" is not within the specified range', - - DEP_NOTEQUAL = 'Dependency (%1) failed:\nOption "%i" is not eqal "%2"', - DEP_NOVALUE = 'Dependency (%1) failed:\nOption "%i" has no value', - DEP_NOTVALID = 'Dependency (%1) failed:\n%c', - DEP_RECURSIVE = 'Recursive dependency for option "%i" detected', - DEP_BADENUM = 'In dependency check for enum value "%i":\n%c' -} - -function i18n(key) - if luci.i18n then - return luci.i18n.translate(key) - else - return key - end -end - - -error = util.class() - -function error.__init__(self, code, pso, args) - - self.code = code - self.args = ( type(args) == "table" and args or { args } ) - - if util.instanceof( pso, uvl.uvlitem ) then - self.stype = pso.sref[2] - self.package, self.section, self.option, self.value = unpack(pso.cref) - self.object = pso - self.value = self.value or ( pso.value and pso:value() ) - else - pso = ( type(pso) == "table" and pso or { pso } ) - - if pso[2] then - local uci = uci.cursor() - self.stype = uci:get(pso[1], pso[2]) or pso[2] - end - - self.package, self.section, self.option, self.value = unpack(pso) - end -end - -function error.child(self, err) - if not self.childs then - self.childs = { err } - else - self.childs[#self.childs+1] = err - end - return self -end - -function error.string(self,pad) - pad = pad or " " - - local str = i18n(ERRCODES[self.code] or self.code) - :gsub("\n", "\n"..pad) - :gsub("%%i", self:cid()) - :gsub("%%I", self:sid()) - :gsub("%%p", self.package or '(nil)') - :gsub("%%s", self.section or '(nil)') - :gsub("%%S", self.stype or '(nil)') - :gsub("%%o", self.option or '(nil)') - :gsub("%%v", self.value or '(nil)') - :gsub("%%t", self.object and self.object:type() or '(nil)' ) - :gsub("%%T", self.object and self.object:title() or '(nil)' ) - :gsub("%%([1-9])", function(n) return self.args[tonumber(n)] or '(nil)' end) - :gsub("%%c", - function() - local s = "" - for _, err in ipairs(self.childs or {}) do - s = s .. err:string(pad.." ") .. "\n" .. pad - end - return s - end - ) - - return (str:gsub("%s+$","")) -end - -function error.cid(self) - return self.object and self.object:cid() or self.package .. - ( self.section and '.' .. self.section or '' ) .. - ( self.option and '.' .. self.option or '' ) .. - ( self.value and '.' .. self.value or '' ) -end - -function error.sid(self) - return self.object and self.object:sid() or self.package .. - ( self.stype and '.' .. self.stype or '' ) .. - ( self.option and '.' .. self.option or '' ) .. - ( self.value and '.' .. self.value or '' ) -end - -function error.is(self, code) - if self.code == code then - return true - elseif self.childs then - for _, c in ipairs(self.childs) do - if c:is(code) then - return true - end - end - end - return false -end - -function error.is_all(self, ...) - local codes = { ... } - - if util.contains(codes, self.code) then - return true - else - local equal = false - for _, c in ipairs(self.childs) do - if c.childs then - equal = c:is_all(...) - else - equal = util.contains(codes, c.code) - end - end - return equal - end -end diff --git a/libs/uvl/luasrc/uvl/validation.lua b/libs/uvl/luasrc/uvl/validation.lua deleted file mode 100644 index 25ac6dcaef..0000000000 --- a/libs/uvl/luasrc/uvl/validation.lua +++ /dev/null @@ -1,72 +0,0 @@ ---[[ - -UCI Validation Layer - Validation helper -(c) 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net> -(c) 2008 Steven Barth <steven@midlink.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 os = require "os" -local fs = require "nixio.fs" -local sys = require "luci.sys" -local ERR = require "luci.uvl.errors".error - -local ipairs, unpack, type, tostring = ipairs, unpack, type, tostring - -module "luci.uvl.validation" - -function _exec( bin, args ) - local cmd, output = "", nil - - for _, v in ipairs({ bin, unpack(args) }) do - cmd = cmd .. ("%q " % v):gsub("([%$`])","\\%1") - end - - local tmpfile = "/tmp/uvl" .. sys.uniqueid(8) - local retval = os.execute( cmd .. " 1>" .. tmpfile .. " 2>" .. tmpfile ) - - if fs.access(tmpfile) then - output = fs.readfile(tmpfile) - fs.unlink(tmpfile) - end - - return retval, output -end - -function check( self, object ) - if object:scheme('validators') then - for _, val in ipairs(object:scheme('validators')) do - local ok, err = false, nil - - local values = object:value() - values = type(values) == "table" and values or { values } - - local args = { - object:scheme('type'), object:scheme('datatype'), - object.cref[1], object.cref[2], object.cref[3] or '', - unpack(values) - } - - if type(val) == "function" then - ok, err = val(unpack(args)) - else - ok, err = _exec( val, args ) - ok = ( ok == 0 ) - end - - if not ok then - return false, ERR('SME_ERRVAL', object, {tostring(val), err}) - end - end - end - - return true, nil -end |