convert luci.fs users to nixio.fs api
[project/luci.git] / libs / uvl / luasrc / uvl.lua
1 --[[
2
3 UCI Validation Layer - Main Library
4 (c) 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
5 (c) 2008 Steven Barth <steven@midlink.org>
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11                 http://www.apache.org/licenses/LICENSE-2.0
12
13 $Id$
14
15 ]]--
16
17
18 --- UVL - UCI Validation Layer
19 -- @class       module
20 -- @cstyle      instance
21
22 require "nixio.util"
23
24 local fs = require "nixio.fs"
25 local uci = require "luci.model.uci"
26 local util = require "luci.util"
27 local table = require "table"
28 local string = require "string"
29
30 local require, pcall, ipairs, pairs = require, pcall, ipairs, pairs
31 local type, error, tonumber, tostring = type, error, tonumber, tostring
32 local unpack, loadfile = unpack, loadfile
33
34 module "luci.uvl"
35
36 local ERR = require "luci.uvl.errors"
37 local datatypes = require "luci.uvl.datatypes"
38 local validation = require "luci.uvl.validation"
39 local dependencies = require "luci.uvl.dependencies"
40
41 local TYPE_SCHEME   = 0x00
42 local TYPE_CONFIG   = 0x01
43 local TYPE_SECTION  = 0x02
44 local TYPE_OPTION   = 0x03
45 local TYPE_ENUM     = 0x04
46
47 --- Boolean; default true;
48 -- treat sections found in config but not in scheme as error
49 STRICT_UNKNOWN_SECTIONS    = true
50
51 --- Boolean; default true;
52 -- treat options found in config but not in scheme as error
53 STRICT_UNKNOWN_OPTIONS     = true
54
55 --- Boolean; default true;
56 -- treat failed external validators as error
57 STRICT_EXTERNAL_VALIDATORS = true
58
59 --- Boolean; default true;
60 -- treat list values stored as options like errors
61 STRICT_LIST_TYPE           = true
62
63
64 local default_schemedir = "/lib/uci/schema"
65 local default_savedir = "/tmp/.uvl"
66
67
68 --- Object constructor
69 -- @class                       function
70 -- @name                        UVL
71 -- @param schemedir     Path to the scheme directory (optional)
72 -- @param configdir     Override config directory (optional)
73 -- @return                      Instance object
74 UVL = util.class()
75
76 function UVL.__init__( self, schemedir, configdir )
77         self.schemedir  = schemedir or default_schemedir
78         self.configdir  = configdir
79         self.packages   = { }
80         self.beenthere  = { }
81         self.depseen    = { }
82         self.uci                = uci
83         self.err                = ERR
84         self.dep                = dependencies
85         self.datatypes  = datatypes
86 end
87
88
89 --- Parse given scheme and return the scheme tree.
90 -- @param scheme        Name of the scheme to parse
91 -- @return                      Table containing the parsed scheme or nil on error
92 -- @return                      String containing the reason for errors (if any)
93 function UVL.get_scheme( self, scheme )
94         if not self.packages[scheme] then
95                 local ok, err = self:read_scheme( scheme )
96                 if not ok then
97                         return nil, err
98                 end
99         end
100         return self.packages[scheme], nil
101 end
102
103 --- Validate given configuration, section or option.
104 -- @param config        Name of the configuration to validate
105 -- @param section       Name of the section to validate (optional)
106 -- @param option        Name of the option to validate (optional)
107 -- @return                      Boolean indicating whether the given config validates
108 -- @return                      String containing the reason for errors (if any)
109 function UVL.validate( self, config, section, option )
110         if config and section and option then
111                 return self:validate_option( config, section, option )
112         elseif config and section then
113                 return self:validate_section( config, section )
114         elseif config then
115                 return self:validate_config( config )
116         end
117 end
118
119 --- Validate given configuration.
120 -- @param cfg   Name of the configuration to validate
121 -- @return                      Boolean indicating whether the given config validates
122 -- @return                      String containing the reason for errors (if any)
123 function UVL.validate_config( self, cfg, uci )
124
125         if not self.packages[cfg] then
126                 local ok, err = self:read_scheme(cfg)
127                 if not ok then
128                         return false, err
129                 end
130         end
131
132         local co = config( self, uci or cfg, uci and cfg )
133         local sc = { }
134
135         self.beenthere = { }
136         self.depseen   = { }
137
138         if not co:config() then
139                 return false, co:errors()
140         end
141
142         local function _uci_foreach( type, func )
143                 for k, v in pairs(co:config()) do
144                         if v['.type'] == type then
145                                 sc[type] = sc[type] + 1
146                                 local ok, err = func( k, v )
147                                 if not ok then co:error(err) end
148                         end
149                 end
150         end
151
152         for k, v in pairs( self.packages[cfg].sections ) do
153                 sc[k] = 0
154                 _uci_foreach( k,
155                         function(s)
156                                 return self:_validate_section( co:section(s) )
157                         end
158                 )
159         end
160
161         if STRICT_UNKNOWN_SECTIONS then
162                 for k, v in pairs(co:config()) do
163                         local so = co:section(k)
164                         if not self.beenthere[so:cid()] then
165                                 co:error(ERR.SECT_UNKNOWN(so))
166                         end
167                 end
168         end
169
170         for _, k in ipairs(util.keys(sc)) do
171                 local so = co:section(k)
172                 if so:scheme('required') and sc[k] == 0 then
173                         co:error(ERR.SECT_REQUIRED(so))
174                 elseif so:scheme('unique') and sc[k] > 1 then
175                         co:error(ERR.SECT_UNIQUE(so))
176                 end
177         end
178
179         return co:ok(), co:errors()
180 end
181
182 --- Validate given config section.
183 -- @param config        Name of the configuration to validate
184 -- @param section       Name of the section to validate
185 -- @return                      Boolean indicating whether the given config validates
186 -- @return                      String containing the reason for errors (if any)
187 function UVL.validate_section( self, cfg, section, uci )
188
189         if not self.packages[cfg] then
190                 local ok, err = self:read_scheme( cfg )
191                 if not ok then
192                         return false, err
193                 end
194         end
195
196         local co = config( self, uci or cfg, uci and cfg )
197         local so = co:section( section )
198
199         self.beenthere = { }
200         self.depseen   = { }
201
202         if not co:config() then
203                 return false, co:errors()
204         end
205
206         if so:config() then
207                 return self:_validate_section( so )
208         else
209                 return false, ERR.SECT_NOTFOUND(so)
210         end
211 end
212
213 --- Validate given config option.
214 -- @param config        Name of the configuration to validate
215 -- @param section       Name of the section to validate
216 -- @param option        Name of the option to validate
217 -- @return                      Boolean indicating whether the given config validates
218 -- @return                      String containing the reason for errors (if any)
219 function UVL.validate_option( self, cfg, section, option, uci )
220
221         if not self.packages[cfg] then
222                 local ok, err = self:read_scheme( cfg )
223                 if not ok then
224                         return false, err
225                 end
226         end
227
228         local co = config( self, uci or cfg, uci and cfg )
229         local so = co:section( section )
230         local oo = so:option( option )
231
232         if not co:config() then
233                 return false, co:errors()
234         end
235
236         if so:config() and oo:config() then
237                 return self:_validate_option( oo )
238         else
239                 return false, ERR.OPT_NOTFOUND(oo)
240         end
241 end
242
243
244 function UVL._validate_section( self, section )
245
246         self.beenthere[section:cid()] = true
247
248         if section:config() then
249                 if section:scheme('named') == true and
250                    section:config('.anonymous') == true
251                 then
252                         return false, ERR.SECT_NAMED(section)
253                 end
254
255                 for _, v in ipairs(section:variables()) do
256                         local ok, err = self:_validate_option( v )
257                         if not ok and (
258                                 v:scheme('required') or v:scheme('type') == "enum" or (
259                                         not err:is(ERR.ERR_DEP_NOTEQUAL) and
260                                         not err:is(ERR.ERR_DEP_NOVALUE)
261                                 )
262                         ) then
263                                 section:error(err)
264                         end
265                 end
266
267                 local ok, err = dependencies.check( self, section )
268                 if not ok then
269                         section:error(err)
270                 end
271         else
272                 return false, ERR.SECT_NOTFOUND(section)
273         end
274
275         if STRICT_UNKNOWN_OPTIONS and not section:scheme('dynamic') then
276                 for k, v in pairs(section:config()) do
277                         local oo = section:option(k)
278                         if k:sub(1,1) ~= "." and not self.beenthere[oo:cid()] then
279                                 section:error(ERR.OPT_UNKNOWN(oo))
280                         end
281                 end
282         end
283
284         return section:ok(), section:errors()
285 end
286
287 function UVL._validate_option( self, option, nodeps )
288
289         self.beenthere[option:cid()] = true
290
291         if not option:scheme() and not option:parent():scheme('dynamic') then
292                 if STRICT_UNKNOWN_OPTIONS then
293                         return false, option:error(ERR.OPT_UNKNOWN(option))
294                 else
295                         return true
296                 end
297
298         elseif option:scheme() then
299                 if not nodeps then
300                         local ok, err = dependencies.check( self, option )
301                         if not ok then
302                                 if not err:is_all(
303                                         ERR.ERR_OPT_REQUIRED,
304                                         ERR.ERR_DEP_NOTEQUAL,
305                                         ERR.ERR_DEP_NOVALUE
306                                 ) then
307                                         option:error(err)
308                                         return false, option:errors()
309                                 else
310                                         return true
311                                 end
312                         end
313                 end
314
315                 if option:scheme('required') and not option:value() then
316                         return false, option:error(ERR.OPT_REQUIRED(option))
317
318                 elseif option:value() then
319                         local val = option:value()
320
321                         if option:scheme('type') == "reference" or
322                            option:scheme('type') == "enum"
323                         then
324                                 local scheme_values = option:scheme('values') or { }
325                                 local config_values = ( type(val) == "table" and val or { val } )
326                                 for _, v in ipairs(config_values) do
327                                         if not scheme_values[v] then
328                                                 return false, option:error( ERR.OPT_BADVALUE(
329                                                         option, { v, util.serialize_data(
330                                                                 util.keys(scheme_values)
331                                                         ) }
332                                                 ) )
333                                         end
334                                 end
335                         elseif option:scheme('type') == "list" then
336                                 if type(val) ~= "table" and STRICT_LIST_TYPE then
337                                         return false, option:error(ERR.OPT_NOTLIST(option))
338                                 end
339                         end
340
341                         if option:scheme('datatype') then
342                                 local dt = option:scheme('datatype')
343
344                                 if self.datatypes[dt] then
345                                         val = ( type(val) == "table" and val or { val } )
346                                         for i, v in ipairs(val) do
347                                                 if not self.datatypes[dt]( v ) then
348                                                         return false, option:error(
349                                                                 ERR.OPT_INVVALUE(option, { v, dt })
350                                                         )
351                                                 end
352                                         end
353                                 else
354                                         return false, option:error(ERR.OPT_DATATYPE(option, dt))
355                                 end
356                         end
357
358                         val = ( type(val) == "table" and val or { val } )
359                         for _, v in ipairs(val) do
360                                 if option:scheme('minlength') then
361                                         if #v < option:scheme('minlength') then
362                                                 return false, option:error(ERR.OPT_RANGE(option))
363                                         end
364                                 end
365
366                                 if option:scheme('maxlength') then
367                                         if #v > option:scheme('maxlength') then
368                                                 return false, option:error(ERR.OPT_RANGE(option))
369                                         end
370                                 end
371
372                                 local w = tonumber(v)
373
374                                 if option:scheme('minimum') then
375                                         if not w or w < option:scheme('minimum') then
376                                                 return false, option:error(ERR.OPT_RANGE(option))
377                                         end
378                                 end
379
380                                 if option:scheme('maximum') then
381                                         if not w or w > option:scheme('maximum') then
382                                                 return false, option:error(ERR.OPT_RANGE(option))
383                                         end
384                                 end
385                         end
386                 end
387
388                 local ok, err = validation.check( self, option )
389                 if not ok and STRICT_EXTERNAL_VALIDATORS then
390                         return false, option:error(err)
391                 end
392         end
393
394         return option:ok(), option:errors()
395 end
396
397 --- Find all parts of given scheme and construct validation tree.
398 -- This is normally done on demand, so you don't have to call this function
399 -- by yourself.
400 -- @param shm   Name of the scheme to parse
401 -- @param alias         Create an alias for the loaded scheme
402 function UVL.read_scheme( self, shm, alias )
403
404         local so = scheme( self, shm )
405         local bc = "%s/bytecode/%s.lua" %{ self.schemedir, shm }
406
407         if not fs.access(bc) then
408                 local files = nixio.util.consume(fs.glob(self.schemedir .. '/*/' .. shm))
409
410                 if #files > 0 then
411                         local ok, err
412                         for file in files do
413                                 if not fs.access(file) then
414                                         return false, so:error(ERR.SME_READ(so,file))
415                                 end
416
417                                 local uci = uci.cursor( fs.dirname(file), default_savedir )
418
419                                 local sname = fs.basename(file)
420                                 local sd, err = uci:load( sname )
421
422                                 if not sd then
423                                         return false, ERR.UCILOAD(so, err)
424                                 end
425
426                                 ok, err = pcall(function()
427                                         uci:foreach(sname, "package", function(s)
428                                                 self:_parse_package(so, s[".name"], s)
429                                         end)
430                                         uci:foreach(sname, "section", function(s)
431                                                 self:_parse_section(so, s[".name"], s)
432                                         end)
433                                         uci:foreach(sname, "variable", function(s)
434                                                 self:_parse_var(so, s[".name"], s)
435                                         end)
436                                         uci:foreach(sname, "enum", function(s)
437                                                 self:_parse_enum(so, s[".name"], s)
438                                         end)
439
440                                 end)
441                         end
442
443                         if ok and alias then self.packages[alias] = self.packages[shm] end
444                         return ok and self, err
445                 else
446                         return false, so:error(ERR.SME_FIND(so, self.schemedir))
447                 end
448         else
449                 local sc = loadfile(bc)
450                 if sc then
451                         self.packages[shm] = sc()
452                         return true
453                 else
454                         return false, so:error(ERR.SME_READ(so,bc))
455                 end
456         end
457 end
458
459 -- helper function to check for required fields
460 local function _req( t, n, c, r )
461         for i, v in ipairs(r) do
462                 if not c[v] then
463                         local p, o = scheme:sid(), nil
464
465                         if t == TYPE_SECTION then
466                                 o = section( scheme, nil, p, n )
467                         elseif t == TYPE_OPTION then
468                                 o = option( scheme, nil, p, '(nil)', n )
469                         elseif t == TYPE_ENUM then
470                                 o = enum( scheme, nil, p, '(nil)', '(nil)', n )
471                         end
472
473                         return false, ERR.SME_REQFLD(o,v)
474                 end
475         end
476         return true
477 end
478
479 -- helper function to validate references
480 local function _ref( c, t )
481         local r, k, n = {}
482         if c == TYPE_SECTION then
483                 k = "package"
484                 n = 1
485         elseif c == TYPE_OPTION then
486                 k = "section"
487                 n = 2
488         elseif c == TYPE_ENUM then
489                 k = "variable"
490                 n = 3
491         end
492
493         for o in t[k]:gmatch("[^.]+") do
494                 r[#r+1] = o
495         end
496         r[1] = ( #r[1] > 0 and r[1] or scheme:sid() )
497
498         if #r ~= n then
499                 return false, ERR.SME_BADREF(scheme, k)
500         end
501
502         return r
503 end
504
505 -- helper function to read bools
506 local function _bool( v )
507         return ( v == "true" or v == "yes" or v == "on" or v == "1" )
508 end
509
510 -- Step 0: get package meta information
511 function UVL._parse_package(self, scheme, k, v)
512         local sid = scheme:sid()
513         local pkg = self.packages[sid] or {
514                 ["name"]      = sid;
515                 ["sections"]  = { };
516                 ["variables"] = { };
517         }
518
519         pkg.title = v.title
520         pkg.description = v.description
521
522         self.packages[sid] = pkg
523 end
524
525 -- Step 1: get all sections
526 function UVL._parse_section(self, scheme, k, v)
527         local ok, err = _req( TYPE_SECTION, k, v, { "name", "package" } )
528         if err then error(scheme:error(err)) end
529
530         local r, err = _ref( TYPE_SECTION, v )
531         if err then error(scheme:error(err)) end
532
533         local p = self.packages[r[1]] or {
534                 ["name"]      = r[1];
535                 ["sections"]  = { };
536                 ["variables"] = { };
537         }
538         p.sections[v.name]  = p.sections[v.name]  or { }
539         p.variables[v.name] = p.variables[v.name] or { }
540         self.packages[r[1]] = p
541
542         local s  = p.sections[v.name]
543         local so = scheme:section(v.name)
544
545         for k, v2 in pairs(v) do
546                 if k ~= "name" and k ~= "package" and k:sub(1,1) ~= "." then
547                         if k == "depends" then
548                                 s.depends = self:_read_dependency( v2, s.depends )
549                                 if not s.depends then
550                                         return false, scheme:error(
551                                                 ERR.SME_BADDEP(so, util.serialize_data(s.depends))
552                                         )
553                                 end
554                         elseif k == "dynamic" or k == "unique" or
555                                k == "required" or k == "named"
556                         then
557                                 s[k] = _bool(v2)
558                         else
559                                 s[k] = v2
560                         end
561                 end
562         end
563
564         s.dynamic  = s.dynamic  or false
565         s.unique   = s.unique   or false
566         s.required = s.required or false
567         s.named    = s.named    or false
568 end
569
570 -- Step 2: get all variables
571 function UVL._parse_var(self, scheme, k, v)
572         local ok, err = _req( TYPE_OPTION, k, v, { "name", "section" } )
573         if err then error(scheme:error(err)) end
574
575         local r, err = _ref( TYPE_OPTION, v )
576         if err then error(scheme:error(err)) end
577
578         local p = self.packages[r[1]]
579         if not p then
580                 error(scheme:error(
581                         ERR.SME_VBADPACK({scheme:sid(), '', v.name}, r[1])
582                 ))
583         end
584
585         local s = p.variables[r[2]]
586         if not s then
587                 error(scheme:error(
588                         ERR.SME_VBADSECT({scheme:sid(), '', v.name}, r[2])
589                 ))
590         end
591
592         s[v.name] = s[v.name] or { }
593
594         local t  = s[v.name]
595         local so = scheme:section(r[2])
596         local to = so:option(v.name)
597
598         for k, v2 in pairs(v) do
599                 if k ~= "name" and k ~= "section" and k:sub(1,1) ~= "." then
600                         if k == "depends" then
601                                 t.depends = self:_read_dependency( v2, t.depends )
602                                 if not t.depends then
603                                         error(scheme:error(so:error(
604                                                 ERR.SME_BADDEP(to, util.serialize_data(v2))
605                                         )))
606                                 end
607                         elseif k == "validator" then
608                                 t.validators = self:_read_validator( v2, t.validators )
609                                 if not t.validators then
610                                         error(scheme:error(so:error(
611                                                 ERR.SME_BADVAL(to, util.serialize_data(v2))
612                                         )))
613                                 end
614                         elseif k == "valueof" then
615                                 local values, err = self:_read_reference( v2 )
616                                 if err then
617                                         error(scheme:error(so:error(
618                                                 ERR.REFERENCE(to, util.serialize_data(v2)):child(err)
619                                         )))
620                                 end
621                                 t.type   = "reference"
622                                 t.values = values
623                                 t.valueof = type(v2) == "table" and v2 or {v2}
624                         elseif k == "required" then
625                                 t[k] = _bool(v2)
626                         elseif k == "minlength" or k == "maxlength" or
627                    k == "minimum" or k == "maximum"
628             then
629                                 t[k] = tonumber(v2)
630                         else
631                                 t[k] = t[k] or v2
632                         end
633                 end
634         end
635
636         t.type     = t.type     or "variable"
637         t.datatype = t.datatype or "string"
638         t.required = t.required or false
639 end
640
641 -- Step 3: get all enums
642 function UVL._parse_enum(self, scheme, k, v)
643         local ok, err = _req( TYPE_ENUM, k, v, { "value", "variable" } )
644         if err then error(scheme:error(err)) end
645
646         local r, err = _ref( TYPE_ENUM, v )
647         if err then error(scheme:error(err)) end
648
649         local p = self.packages[r[1]]
650         if not p then
651                 error(scheme:error(
652                         ERR.SME_EBADPACK({scheme:sid(), '', '', v.value}, r[1])
653                 ))
654         end
655
656         local s = p.variables[r[2]]
657         if not s then
658                 error(scheme:error(
659                         ERR.SME_EBADSECT({scheme:sid(), '', '', v.value}, r[2])
660                 ))
661         end
662
663         local t = s[r[3]]
664         if not t then
665                 error(scheme:error(
666                         ERR.SME_EBADOPT({scheme:sid(), '', '', v.value}, r[3])
667                 ))
668         end
669
670
671         local so = scheme:section(r[2])
672         local oo = so:option(r[3])
673         local eo = oo:enum(v.value)
674
675         if t.type ~= "enum" and t.type ~= "reference" then
676                 error(scheme:error(ERR.SME_EBADTYPE(eo)))
677         end
678
679         if not t.values then
680                 t.values = { [v.value] = v.title or v.value }
681                 t.valuelist = { {value = v.value, title = v.title} }
682         else
683                 t.values[v.value] = v.title or v.value
684                 t.valuelist[#t.valuelist + 1] = {value = v.value, title = v.title}
685         end
686
687         if not t.enum_depends then
688                 t.enum_depends = { }
689         end
690
691         if v.default then
692                 if t.default then
693                         error(scheme:error(ERR.SME_EBADDEF(eo)))
694                 end
695                 t.default = v.value
696         end
697
698         if v.depends then
699                 t.enum_depends[v.value] = self:_read_dependency(
700                         v.depends, t.enum_depends[v.value]
701                 )
702
703                 if not t.enum_depends[v.value] then
704                         error(scheme:error(so:error(oo:error(
705                                 ERR.SME_BADDEP(eo, util.serialize_data(v.depends))
706                         ))))
707                 end
708         end
709 end
710
711 -- Read a dependency specification
712 function UVL._read_dependency( self, values, deps )
713         local expr = "%$?[%w_]+"
714         if values then
715                 values = ( type(values) == "table" and values or { values } )
716                 for _, value in ipairs(values) do
717                         local condition = { }
718                         for val in value:gmatch("[^,]+") do
719                                 local k, e, v = val:match("%s*([%w$_.]+)%s*(=?)%s*(.*)")
720
721                                 if k and (
722                                         k:match("^"..expr.."%."..expr.."%."..expr.."$") or
723                                         k:match("^"..expr.."%."..expr.."$") or
724                                         k:match("^"..expr.."$")
725                                 ) then
726                                         condition[k] = (e == '=') and v or true
727                                 else
728                                         return nil
729                                 end
730                         end
731
732                         if not deps then
733                                 deps = { condition }
734                         else
735                                 deps[#deps+1] = condition
736                         end
737                 end
738         end
739
740         return deps
741 end
742
743 -- Read a validator specification
744 function UVL._read_validator( self, values, validators )
745         if values then
746                 values = ( type(values) == "table" and values or { values } )
747                 for _, value in ipairs(values) do
748                         local validator
749
750                         if value:match("^exec:") then
751                                 validator = value:gsub("^exec:","")
752                         elseif value:match("^lua:") then
753                                 validator = self:_resolve_function( (value:gsub("^lua:","") ) )
754                         elseif value:match("^regexp:") then
755                                 local pattern = value:gsub("^regexp:","")
756                                 validator = function( type, dtype, pack, sect, optn, ... )
757                                         local values = { ... }
758                                         for _, v in ipairs(values) do
759                                                 local ok, match =
760                                                         pcall( string.match, v, pattern )
761
762                                                 if not ok then
763                                                         return false, match
764                                                 elseif not match then
765                                                         return false,
766                                                                 'Value "%s" does not match pattern "%s"' % {
767                                                                         v, pattern
768                                                                 }
769                                                 end
770                                         end
771                                         return true
772                                 end
773                         end
774
775                         if validator then
776                                 if not validators then
777                                         validators = { validator }
778                                 else
779                                         validators[#validators+1] = validator
780                                 end
781                         else
782                                 return nil
783                         end
784                 end
785
786                 return validators
787         end
788 end
789
790 -- Read a reference specification (XXX: We should validate external configs too...)
791 function UVL._read_reference( self, values )
792         local val = { }
793         values = ( type(values) == "table" and values or { values } )
794
795         for _, value in ipairs(values) do
796                 local ref = util.split(value, ".")
797
798                 if #ref == 2 or #ref == 3 then
799                         local co = config( self, ref[1] )
800                         if not co:config() then return false, co:errors() end
801
802                         for k, v in pairs(co:config()) do
803                                 if v['.type'] == ref[2] then
804                                         if #ref == 2 then
805                                                 if v['.anonymous'] == true then
806                                                         return false, ERR.SME_INVREF('', value)
807                                                 end
808                                                 val[k] = k      -- XXX: title/description would be nice
809                                         elseif v[ref[3]] then
810                                                 val[v[ref[3]]] = v[ref[3]]  -- XXX: dito
811                                         end
812                                 end
813                         end
814                 else
815                         return false, ERR.SME_BADREF('', value)
816                 end
817         end
818
819         return val, nil
820 end
821
822 -- Resolve given path
823 function UVL._resolve_function( self, value )
824         local path = util.split(value, ".")
825
826         for i=1, #path-1 do
827                 local stat, mod = pcall(
828                         require, table.concat(path, ".", 1, i)
829                 )
830
831                 if stat and mod then
832                         for j=i+1, #path-1 do
833                                 if not type(mod) == "table" then
834                                         break
835                                 end
836                                 mod = mod[path[j]]
837                                 if not mod then
838                                         break
839                                 end
840                         end
841                         mod = type(mod) == "table" and mod[path[#path]] or nil
842                         if type(mod) == "function" then
843                                 return mod
844                         end
845                 end
846         end
847 end
848
849
850 --- Object representation of an uvl item - base class.
851 uvlitem = util.class()
852
853 function uvlitem.cid(self)
854         if #self.cref == 1 then
855                 return self.cref[1]
856         else
857                 local r = { unpack(self.cref) }
858                 local c = self.c
859                 if c and c[r[2]] and c[r[2]]['.anonymous'] and c[r[2]]['.index'] then
860                         r[2] = '@' .. c[r[2]]['.type'] ..
861                                    '[' .. tostring(c[r[2]]['.index']) .. ']'
862                 end
863                 return table.concat( r, '.' )
864         end
865 end
866
867 function uvlitem.sid(self)
868         return table.concat( self.sref, '.' )
869 end
870
871 function uvlitem.scheme(self, opt)
872         local s = self.s and self.s.packages
873         s = s      and s[self.sref[1]]
874         if #self.sref == 4 or #self.sref == 3 then
875                 s = s      and s.variables
876                 s = s      and s[self.sref[2]]
877                 s = s      and s[self.sref[3]]
878         elseif #self.sref == 2 then
879                 s = s      and s.sections
880                 s = s      and s[self.sref[2]]
881         end
882
883         if s and opt then
884                 return s[opt]
885         elseif s then
886                 return s
887         end
888 end
889
890 function uvlitem.config(self, opt)
891         local c = self.c
892
893         if #self.cref >= 2 and #self.cref <= 4 then
894                 c = c and self.c[self.cref[2]] or nil
895                 if #self.cref >= 3 then
896                         c = c and c[self.cref[3]] or nil
897                 end
898         end
899
900         if c and opt then
901                 return c[opt]
902         elseif c then
903                 return c
904         end
905 end
906
907 function uvlitem.title(self)
908         return self:scheme() and self:scheme('title') or
909                 self.cref[3] or self.cref[2] or self.cref[1]
910 end
911
912 function uvlitem.type(self)
913         if self.t == TYPE_CONFIG then
914                 return 'config'
915         elseif self.t == TYPE_SECTION then
916                 return 'section'
917         elseif self.t == TYPE_OPTION then
918                 return 'option'
919         elseif self.t == TYPE_ENUM then
920                 return 'enum'
921         end
922 end
923
924 function uvlitem.error(self, ...)
925         if not self.e then
926                 local errconst = { ERR.CONFIG, ERR.SECTION, ERR.OPTION, ERR.OPTION }
927                 self.e = errconst[#self.cref]( self )
928         end
929
930         return self.e:child( ... )
931 end
932
933 function uvlitem.errors(self)
934         return self.e
935 end
936
937 function uvlitem.ok(self)
938         return not self:errors()
939 end
940
941 function uvlitem.parent(self)
942         if self.p then
943                 return self.p
944         elseif #self.cref == 3 or #self.cref == 4 then
945                 return section( self.s, self.c, self.cref[1], self.cref[2] )
946         elseif #self.cref == 2 then
947                 return config( self.s, self.c, self.cref[1] )
948         else
949                 return nil
950         end
951 end
952
953 function uvlitem._loadconf(self, co, c, configdir)
954         co = co or self._configcache
955         if not co then
956                 local err
957                 co, err = uci.cursor(configdir):get_all(c)
958
959                 if err then
960                         self:error(ERR.UCILOAD(self, err))
961                 end
962
963                 self._configcache = co
964         end
965         return co
966 end
967
968
969 --- Object representation of a scheme.
970 -- @class       scheme
971 -- @cstyle      instance
972 -- @name        luci.uvl.scheme
973
974 --- Scheme instance constructor.
975 -- @class                       function
976 -- @name                        scheme
977 -- @param scheme        Scheme instance
978 -- @param co            Configuration data
979 -- @param c                     Configuration name
980 -- @return                      Config instance
981 scheme = util.class(uvlitem)
982
983 function scheme.__init__(self, scheme, co, c)
984         if not c then
985                 c, co = co, nil
986         end
987
988         self.cref = { c }
989         self.sref = { c }
990         self.c    = self:_loadconf(co, c, scheme.configdir)
991         self.s    = scheme
992         self.t    = TYPE_SCHEME
993 end
994
995 --- Add an error to scheme.
996 -- @return      Scheme error context
997 function scheme.error(self, ...)
998         if not self.e then self.e = ERR.SCHEME( self ) end
999         return self.e:child( ... )
1000 end
1001
1002 --- Get an associated config object.
1003 -- @return      Config instance
1004 function scheme.config(self)
1005         local co = config( self.s, self.cref[1] )
1006               co.p = self
1007
1008         return co
1009 end
1010
1011 --- Get all section objects associated with this scheme.
1012 -- @return      Table containing all associated luci.uvl.section instances
1013 function scheme.sections(self)
1014         local v = { }
1015         if self.s.packages[self.sref[1]].sections then
1016                 for o, _ in pairs( self.s.packages[self.sref[1]].sections ) do
1017                         v[#v+1] = option(
1018                                 self.s, self.c, self.cref[1], self.cref[2], o
1019                         )
1020                 end
1021         end
1022         return v
1023 end
1024
1025 --- Get an associated section object.
1026 -- @param s     Section to select
1027 -- @return      Section instance
1028 function scheme.section(self, s)
1029         local so = section( self.s, self.c, self.cref[1], s )
1030               so.p = self
1031
1032         return so
1033 end
1034
1035
1036 --- Object representation of a config.
1037 -- @class       config
1038 -- @cstyle      instance
1039 -- @name        luci.uvl.config
1040
1041 --- Config instance constructor.
1042 -- @class                       function
1043 -- @name                        config
1044 -- @param scheme        Scheme instance
1045 -- @param co            Configuration data
1046 -- @param c                     Configuration name
1047 -- @return                      Config instance
1048 config = util.class(uvlitem)
1049
1050 function config.__init__(self, scheme, co, c)
1051         if not c then
1052                 c, co = co, nil
1053         end
1054         self.cref = { c }
1055         self.sref = { c }
1056         self.c    = self:_loadconf(co, c, scheme.configdir)
1057         self.s    = scheme
1058         self.t    = TYPE_CONFIG
1059 end
1060
1061 --- Get all section objects associated with this config.
1062 -- @return      Table containing all associated luci.uvl.section instances
1063 function config.sections(self)
1064         local v = { }
1065         if self.s.packages[self.sref[1]].sections then
1066                 for o, _ in pairs( self.s.packages[self.sref[1]].sections ) do
1067                         v[#v+1] = option(
1068                                 self.s, self.c, self.cref[1], self.cref[2], o
1069                         )
1070                 end
1071         end
1072         return v
1073 end
1074
1075 --- Get an associated section object.
1076 -- @param s     Section to select
1077 -- @return      Section instance
1078 function config.section(self, s)
1079         local so = section( self.s, self.c, self.cref[1], s )
1080               so.p = self
1081
1082         return so
1083 end
1084
1085
1086 --- Object representation of a scheme/config section.
1087 -- @class       module
1088 -- @cstyle      instance
1089 -- @name        luci.uvl.section
1090
1091 --- Section instance constructor.
1092 -- @class                       function
1093 -- @name                        section
1094 -- @param scheme        Scheme instance
1095 -- @param co            Configuration data
1096 -- @param c                     Configuration name
1097 -- @param s                     Section name
1098 -- @return                      Section instance
1099 section = util.class(uvlitem)
1100
1101 function section.__init__(self, scheme, co, c, s)
1102         self.cref = { c, s }
1103         self.sref = { c, co and co[s] and co[s]['.type'] or s }
1104         self.c    = self:_loadconf(co, c, scheme.configdir)
1105         self.s    = scheme
1106         self.t    = TYPE_SECTION
1107 end
1108
1109 --- Get all option objects associated with this section.
1110 -- @return      Table containing all associated luci.uvl.option instances
1111 function section.variables(self)
1112         local v = { }
1113         if self.s.packages[self.sref[1]].variables[self.sref[2]] then
1114                 for o, _ in pairs(
1115                         self.s.packages[self.sref[1]].variables[self.sref[2]]
1116                 ) do
1117                         v[#v+1] = option(
1118                                 self.s, self.c, self.cref[1], self.cref[2], o
1119                         )
1120                 end
1121         end
1122         return v
1123 end
1124
1125 --- Get an associated option object.
1126 -- @param o     Option to select
1127 -- @return      Option instance
1128 function section.option(self, o)
1129         local oo = option( self.s, self.c, self.cref[1], self.cref[2], o )
1130               oo.p = self
1131
1132         return oo
1133 end
1134
1135
1136 --- Object representation of a scheme/config option.
1137 -- @class       module
1138 -- @cstyle      instance
1139 -- @name        luci.uvl.option
1140
1141 --- Section instance constructor.
1142 -- @class                       function
1143 -- @name                        option
1144 -- @param scheme        Scheme instance
1145 -- @param co            Configuration data
1146 -- @param c                     Configuration name
1147 -- @param s                     Section name
1148 -- @param o                     Option name
1149 -- @return                      Option instance
1150 option = util.class(uvlitem)
1151
1152 function option.__init__(self, scheme, co, c, s, o)
1153         self.cref = { c, s, o }
1154         self.sref = { c, co and co[s] and co[s]['.type'] or s, o }
1155         self.c    = self:_loadconf(co, c, scheme.configdir)
1156         self.s    = scheme
1157         self.t    = TYPE_OPTION
1158 end
1159
1160 --- Get the value of this option.
1161 -- @return      The associated configuration value
1162 function option.value(self)
1163         local v = self:config() or self:scheme('default')
1164         if v and self:scheme('multival') then
1165                 v = util.split( v, "%s+", nil, true )
1166         end
1167         return v
1168 end
1169
1170 --- Get the associated section information in scheme.
1171 -- @return      Table containing the scheme properties
1172 function option.section(self)
1173         return self.s.packages[self.sref[1]].sections[self.sref[2]]
1174 end
1175
1176 --- Construct an enum object instance from given or default value.
1177 -- @param v     Value to select
1178 -- @return      Enum instance for selected value
1179 function option.enum(self, val)
1180         return enum(
1181                 self.s, self.c,
1182                 self.cref[1], self.cref[2], self.cref[3],
1183                 val or self:value()
1184         )
1185 end
1186
1187
1188 --- Object representation of a enum value.
1189 -- @class       module
1190 -- @cstyle      instance
1191 -- @name        luci.uvl.enum
1192
1193 --- Section instance constructor.
1194 -- @class                       function
1195 -- @name                        enum
1196 -- @param scheme        Scheme instance
1197 -- @param co            Configuration data
1198 -- @param c                     Configuration name
1199 -- @param s                     Section name
1200 -- @param o                     Enum name
1201 -- @param v                     Enum value
1202 -- @return                      Enum value instance
1203 enum = util.class(option)
1204
1205 function enum.__init__(self, scheme, co, c, s, o, v)
1206         self.cref = { c, s, o, v }
1207         self.sref = { c, co and co[s] and co[s]['.type'] or s, o, v }
1208         self.c    = self:_loadconf(co, c, scheme.configdir)
1209         self.s    = scheme
1210         self.t    = TYPE_ENUM
1211 end