cc1d8b9d6dfe2d32d19c0bd1e949e30d1f49ce6a
[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 module( "luci.uvl", package.seeall )
23
24 require("luci.fs")
25 require("luci.util")
26 require("luci.model.uci")
27 require("luci.uvl.loghelper")
28 require("luci.uvl.datatypes")
29 require("luci.uvl.validation")
30 require("luci.uvl.dependencies")
31
32
33 TYPE_SECTION  = 0x01
34 TYPE_VARIABLE = 0x02
35 TYPE_ENUM     = 0x03
36
37 --- Boolean; default true;
38 -- treat sections found in config but not in scheme as error
39 STRICT_UNKNOWN_SECTIONS    = true
40
41 --- Boolean; default true;
42 -- treat options found in config but not in scheme as error
43 STRICT_UNKNOWN_OPTIONS     = true
44
45 --- Boolean; default true;
46 -- treat failed external validators as error
47 STRICT_EXTERNAL_VALIDATORS = true
48
49 --- Boolean; default true;
50 -- treat list values stored as options like errors
51 STRICT_LIST_TYPE           = true
52
53
54 local default_schemedir = "/lib/uci/schema"
55
56 local function _assert( condition, fmt, ... )
57         if not condition then
58                 return assert( nil, string.format( fmt, ... ) )
59         else
60                 return condition
61         end
62 end
63
64
65 --- Object constructor
66 -- @class                       function
67 -- @name                        UVL
68 -- @param schemedir     Path to the scheme directory (optional)
69 -- @return                      Instance object
70 UVL = luci.util.class()
71
72 function UVL.__init__( self, schemedir )
73         self.schemedir  = schemedir or default_schemedir
74         self.packages   = { }
75         self.beenthere  = { }
76         self.uci                = luci.model.uci
77         self.dep                = luci.uvl.dependencies
78         self.log        = luci.uvl.loghelper
79         self.datatypes  = luci.uvl.datatypes
80 end
81
82
83 --- Parse given scheme and return the scheme tree.
84 -- @param scheme        Name of the scheme to parse
85 -- @return                      Table containing the parsed scheme or nil on error
86 -- @return                      String containing the reason for errors (if any)
87 function UVL.get_scheme( self, scheme )
88         if not self.packages[scheme] then
89                 local ok, err = pcall( self.read_scheme, self, scheme )
90                 if not ok then
91                         return nil, self.log.scheme_error( scheme, err )
92                 end
93         end
94         return self.packages[scheme], nil
95 end
96
97 --- Return a table containing the dependencies of specified section or option.
98 -- @param config        Name of the configuration or parsed scheme object
99 -- @param section       Type of the section
100 -- @param option        Name of the option (optional)
101 -- @return                      Table containing the dependencies or nil on error
102 -- @return                      String containing the reason for errors (if any)
103 function UVL.get_dependencies( self, config, section, option )
104         config = ( type(config) == "string" and self:get_scheme(config) or config )
105
106         local deps = { }
107         local dt
108
109         if not config.sections[section] then return deps end
110
111         if option and config.variables[section][option] then
112                 dt = config.variables[section][option].depends
113         else
114                 dt = config.sections[section].depends
115         end
116
117         if dt then
118                 for _, d in ipairs(dt) do
119                         local sdeps = { }
120                         for k, v in pairs(d) do
121                                 local r = self.dep._parse_reference( k )
122                                 if r then
123                                         sdeps[r] = v
124                                 else
125                                         return nil,
126                                                 'Ambiguous dependency reference "%s" for object "%s" given'
127                                                         %{ k, self.log.id( config.name, section, option ) }
128                                 end
129                         end
130                         table.insert( deps, sdeps )
131                 end
132         end
133         return deps
134 end
135
136 --- Validate given configuration, section or option.
137 -- @param config        Name of the configuration to validate
138 -- @param section       Name of the section to validate (optional)
139 -- @param option        Name of the option to validate (optional)
140 -- @return                      Boolean indicating whether the given config validates
141 -- @return                      String containing the reason for errors (if any)
142 function UVL.validate( self, config, section, option )
143         if config and section and option then
144                 return self:validate_option( config, section, option )
145         elseif config and section then
146                 return self:validate_section( config, section )
147         elseif config then
148                 return self:validate_config( config )
149         end
150 end
151
152 --- Validate given configuration.
153 -- @param config        Name of the configuration to validate
154 -- @return                      Boolean indicating whether the given config validates
155 -- @return                      String containing the reason for errors (if any)
156 function UVL.validate_config( self, config )
157
158         if not self.packages[config] then
159                 local ok, err = pcall( self.read_scheme, self, config )
160                 if not ok then
161                         return false, self.log.scheme_error( config, err )
162                 end
163         end
164
165         self.uci.load_config( config )
166         self.beenthere = { }
167
168         local co = self.uci.get_all( config )
169         local sc = { }
170
171         if not co then
172                 return false, 'Unable to load configuration "%s"' % config
173         end
174
175         local function _uci_foreach( type, func )
176                 local ok, err
177                 for k, v in pairs(co) do
178                         if co[k]['.type'] == type then
179                                 sc[type] = sc[type] + 1
180                                 ok, err = func( k, v )
181                                 if not ok then
182                                         err = self.log.config_error( config, err )
183                                         break
184                                 end
185                         end
186                 end
187                 return ok, err
188         end
189
190         for k, v in pairs( self.packages[config].sections ) do
191                 sc[k] = 0
192                 local ok, err = _uci_foreach( k,
193                         function(s)
194                                 local sect = luci.uvl.section( self, co, k, config, s )
195                                 return self:_validate_section( sect )
196                         end
197                 )
198                 if not ok then return false, err end
199         end
200
201         if STRICT_UNKNOWN_SECTIONS then
202                 for k, v in pairs(co) do
203                         if not self.beenthere[config..'.'..k] then
204                                 return false, self.log.config_error( config,
205                                         'Section "%s" not found in scheme'
206                                                 % self.log.id( config, co[k]['.type'] ) )
207                         end
208                 end
209         end
210
211         for _, k in ipairs(luci.util.keys(sc)) do
212                 local s = self.packages[config].sections[k]
213
214                 if s.required and sc[k] == 0 then
215                         return false, self.log.config_error( config,
216                                 'Required section "%s" not found in config' % k )
217                 elseif s.unique and sc[k] > 1 then
218                         return false, self.log.config_error( config,
219                                 'Unique section "%s" occurs multiple times in config' % k )
220                 end
221         end
222
223         return true, nil
224 end
225
226 --- Validate given config section.
227 -- @param config        Name of the configuration to validate
228 -- @param section       Name of the section to validate
229 -- @return                      Boolean indicating whether the given config validates
230 -- @return                      String containing the reason for errors (if any)
231 function UVL.validate_section( self, config, section )
232
233         if not self.packages[config] then
234                 local ok, err = pcall( self.read_scheme, self, config )
235                 if not ok then
236                         return false, self.log.scheme_error( config, err )
237                 end
238         end
239
240         self.uci.load_config( config )
241         self.beenthere = { }
242
243         local co = self.uci.get_all( config )
244
245         if not co then
246                 return false, 'Unable to load configuration "%s"' % config
247         end
248
249         if co[section] then
250                 return self:_validate_section( luci.uvl.section(
251                         self, co, co[section]['.type'], config, section
252                 ) )
253         else
254                 return false, 'Section "%s" not found in config. Nothing to do.'
255                         % self.log.id( config, section )
256         end
257 end
258
259 --- Validate given config option.
260 -- @param config        Name of the configuration to validate
261 -- @param section       Name of the section to validate
262 -- @param option        Name of the option to validate
263 -- @return                      Boolean indicating whether the given config validates
264 -- @return                      String containing the reason for errors (if any)
265 function UVL.validate_option( self, config, section, option )
266
267         if not self.packages[config] then
268                 local ok, err = pcall( self.read_scheme, self, config )
269                 if not ok then
270                         return false, self.log.scheme_error( config, err )
271                 end
272         end
273
274         self.uci.load_config( config )
275         self.beenthere = { }
276
277         local co = self.uci.get_all( config )
278
279         if not co then
280                 return false, 'Unable to load configuration "%s"' % config
281         end
282
283         if co[section] and co[section][option] then
284                 return self:_validate_option( luci.uvl.option(
285                         self, co, co[section]['.type'], config, section, option
286                 ) )
287         else
288                 return false, 'Option "%s" not found in config. Nothing to do.'
289                         % self.log.id( config, section, option )
290         end
291 end
292
293
294 function UVL._validate_section( self, section )
295
296         if section:values() then
297                 if section:section().named == true and
298                    section:values()['.anonymous'] == true
299                 then
300                         return false, self.log.section_error( section,
301                                 'The section of type "%s" is stored anonymously in config but must be named'
302                                         % section:sid() )
303                 end
304
305                 for _, v in ipairs(section:variables()) do
306                         local ok, err = self:_validate_option( v )
307
308                         if not ok then
309                                 return ok, self.log.section_error( section, err )
310                         end
311                 end
312
313                 local ok, err = luci.uvl.dependencies.check( self, section )
314
315                 if not ok then
316                         return false, err
317                 end
318         else
319                 return false, 'Option "%s" not found in config' % section:sid()
320         end
321
322         if STRICT_UNKNOWN_OPTIONS and not section:section().dynamic then
323                 for k, v in pairs(section:values()) do
324                         if k:sub(1,1) ~= "." and not self.beenthere[
325                                 section:cid() .. '.' .. k
326                         ] then
327                                 return false, 'Option "%s" not found in scheme'
328                                         % self.log.id( section:sid(), k )
329                         end
330                 end
331         end
332
333         return true, nil
334 end
335
336 function UVL._validate_option( self, option, nodeps )
337
338         local item = option:option()
339         local val  = option:value()
340
341         if not item and not ( option:section() and option:section().dynamic ) then
342                 return false, 'Option "%s" not found in scheme' % option:cid()
343
344         elseif item then
345                 if item.required and not val then
346                         return false, 'Mandatory variable "%s" does not have a value'
347                                 % option:cid()
348                 end
349
350                 if ( item.type == "reference" or item.type == "enum" ) and val then
351                         if not item.values or not item.values[val] then
352                                 return false,
353                                         'Value "%s" of given option "%s" is not defined in %s { %s }'
354                                                 %{ val or '<nil>', option:cid(), item.type,
355                                                    table.concat( luci.util.keys(item.values), ", " ) }
356                         end
357                 elseif item.type == "list" and val then
358                         if type(val) ~= "table" and STRICT_LIST_TYPE then
359                                 return false,
360                                         'Option "%s" is defined as list but stored as plain value'
361                                                 % option:cid()
362                         end
363                 end
364
365                 if item.datatype and val then
366                         if self.datatypes[item.datatype] then
367                                 val = ( type(val) == "table" and val or { val } )
368                                 for i, v in ipairs(val) do
369                                         if not self.datatypes[item.datatype]( v ) then
370                                                 return false,
371                                                         'Value%s "%s" of given option "%s" does not validate as datatype "%s"'
372                                                                 %{ ( #val>1 and ' #' .. i or '' ), v,
373                                                                    option:cid(), item.datatype }
374                                         end
375                                 end
376                         else
377                                 return false, 'Unknown datatype "%s" encountered'
378                                         % item.datatype
379                         end
380                 end
381
382                 if not nodeps then
383                         return luci.uvl.dependencies.check( self, option )
384                 end
385
386                 local ok, err = luci.uvl.validation.check( self, option )
387                 if not ok and STRICT_EXTERNAL_VALIDATORS then
388                         return false, self.log.validator_error( option, err )
389                 end
390         end
391
392         return true, nil
393 end
394
395 --- Find all parts of given scheme and construct validation tree.
396 -- This is normally done on demand, so you don't have to call this function
397 -- by yourself.
398 -- @param scheme        Name of the scheme to parse
399 function UVL.read_scheme( self, scheme )
400         local schemes = { }
401         local files = luci.fs.glob(self.schemedir .. '/*/' .. scheme)
402
403         if files then
404                 for i, file in ipairs( files ) do
405                         _assert( luci.fs.access(file), "Can't access file '%s'", file )
406
407                         self.uci.set_confdir( luci.fs.dirname(file) )
408                         self.uci.load( luci.fs.basename(file) )
409
410                         table.insert( schemes, self.uci.get_all( luci.fs.basename(file) ) )
411                 end
412
413                 return self:_read_scheme_parts( scheme, schemes )
414         else
415                 error( 'Can not find scheme "%s" in "%s"' %{ scheme, self.schemedir } )
416         end
417 end
418
419 -- Process all given parts and construct validation tree
420 function UVL._read_scheme_parts( self, scheme, schemes )
421
422         -- helper function to construct identifiers for given elements
423         local function _id( c, t )
424                 if c == TYPE_SECTION then
425                         return string.format(
426                                 'section "%s.%s"',
427                                         scheme, t.name or '?' )
428                 elseif c == TYPE_VARIABLE then
429                         return string.format(
430                                 'variable "%s.%s.%s"',
431                                         scheme, t.section or '?.?', t.name or '?' )
432                 elseif c == TYPE_ENUM then
433                         return string.format(
434                                 'enum "%s.%s.%s"',
435                                         scheme, t.variable or '?.?.?', t.value or '?' )
436                 end
437         end
438
439         -- helper function to check for required fields
440         local function _req( c, t, r )
441                 for i, v in ipairs(r) do
442                         _assert( t[v], 'Missing required field "%s" in %s', v, _id(c, t) )
443                 end
444         end
445
446         -- helper function to validate references
447         local function _ref( c, t )
448                 local k
449                 if c == TYPE_SECTION then
450                         k = "package"
451                 elseif c == TYPE_VARIABLE then
452                         k = "section"
453                 elseif c == TYPE_ENUM then
454                         k = "variable"
455                 end
456
457                 local r = luci.util.split( t[k], "." )
458                 r[1] = ( #r[1] > 0 and r[1] or scheme )
459
460                 _assert( #r == c, 'Malformed %s reference in %s', k, _id(c, t) )
461
462                 return r
463         end
464
465         -- helper function to read bools
466         local function _bool( v )
467                 return ( v == "true" or v == "yes" or v == "on" or v == "1" )
468         end
469
470         -- Step 1: get all sections
471         for i, conf in ipairs( schemes ) do
472                 for k, v in pairs( conf ) do
473                         if v['.type'] == 'section' then
474
475                                 _req( TYPE_SECTION, v, { "name", "package" } )
476
477                                 local r = _ref( TYPE_SECTION, v )
478
479                                 self.packages[r[1]] =
480                                         self.packages[r[1]] or {
481                                                 ["name"]      = r[1];
482                                                 ["sections"]  = { };
483                                                 ["variables"] = { };
484                                         }
485
486                                 local p = self.packages[r[1]]
487                                           p.sections[v.name]  = p.sections[v.name]  or { }
488                                           p.variables[v.name] = p.variables[v.name] or { }
489
490                                 local s = p.sections[v.name]
491
492                                 for k, v2 in pairs(v) do
493                                         if k ~= "name" and k ~= "package" and k:sub(1,1) ~= "." then
494                                                 if k == "depends" then
495                                                         s["depends"] = _assert(
496                                                                 self:_read_dependency( v2, s["depends"] ),
497                                                                 'Section "%s" in scheme "%s" has malformed ' ..
498                                                                 'dependency specification in "%s"',
499                                                                 v.name or '<nil>', scheme or '<nil>', k
500                                                         )
501                                                 elseif k == "dynamic" or k == "unique" or
502                                                        k == "required" or k == "named"
503                                                 then
504                                                         s[k] = _bool(v2)
505                                                 else
506                                                         s[k] = v2
507                                                 end
508                                         end
509                                 end
510
511                                 s.dynamic  = s.dynamic  or false
512                                 s.unique   = s.unique   or false
513                                 s.required = s.required or false
514                                 s.named    = s.named    or false
515                         end
516                 end
517         end
518
519         -- Step 2: get all variables
520         for i, conf in ipairs( schemes ) do
521                 for k, v in pairs( conf ) do
522                         if v['.type'] == "variable" then
523
524                                 _req( TYPE_VARIABLE, v, { "name", "section" } )
525
526                                 local r = _ref( TYPE_VARIABLE, v )
527
528                                 local p = _assert( self.packages[r[1]],
529                                         'Variable "%s" in scheme "%s" references unknown package "%s"',
530                                         v.name, scheme, r[1] )
531
532                                 local s = _assert( p.variables[r[2]],
533                                         'Variable "%s" in scheme "%s" references unknown section "%s"',
534                                         v.name, scheme, r[2] )
535
536                                 s[v.name] = s[v.name] or { }
537
538                                 local t = s[v.name]
539
540                                 for k, v2 in pairs(v) do
541                                         if k ~= "name" and k ~= "section" and k:sub(1,1) ~= "." then
542                                                 if k == "depends" then
543                                                         t["depends"] = _assert(
544                                                                 self:_read_dependency( v2, t["depends"] ),
545                                                                 'Invalid reference "%s" in "%s.%s.%s"',
546                                                                 v2, v.name, scheme, k
547                                                         )
548                                                 elseif k == "validator" then
549                                                         t["validators"] = _assert(
550                                                                 self:_read_validator( v2, t["validators"] ),
551                                                                 'Variable "%s" in scheme "%s" has malformed ' ..
552                                                                 'validator specification in "%s"',
553                                                                 v.name, scheme, k
554                                                         )
555                                                 elseif k == "valueof" then
556                                                         local values, err = self:_read_reference( v2 )
557
558                                                         _assert( values,
559                                                                 'Variable "%s" in scheme "%s" has invalid ' ..
560                                                                 'reference specification:\n%s',
561                                                                         v.name, scheme, err )
562
563                                                         t.type   = "reference"
564                                                         t.values = values
565                                                 elseif k == "required" then
566                                                         t[k] = _bool(v2)
567                                                 else
568                                                         t[k] = t[k] or v2
569                                                 end
570                                         end
571                                 end
572
573                                 t.type     = t.type     or "variable"
574                                 t.datatype = t.datatype or "string"
575                                 t.required = t.required or false
576                         end
577                 end
578         end
579
580         -- Step 3: get all enums
581         for i, conf in ipairs( schemes ) do
582                 for k, v in pairs( conf ) do
583                         if v['.type'] == "enum" then
584
585                                 _req( TYPE_ENUM, v, { "value", "variable" } )
586
587                                 local r = _ref( TYPE_ENUM, v )
588                                 local p = _assert( self.packages[r[1]],
589                                         'Enum "%s" in scheme "%s" references unknown package "%s"',
590                                         v.value, scheme, r[1] )
591
592                                 local s = _assert( p.variables[r[2]],
593                                         'Enum "%s" in scheme "%s" references unknown section "%s"',
594                                         v.value, scheme, r[2] )
595
596                                 local t = _assert( s[r[3]],
597                                         'Enum "%s" in scheme "%s", section "%s" references ' ..
598                                         'unknown variable "%s"',
599                                         v.value, scheme, r[2], r[3] )
600
601                                 _assert( t.type == "enum",
602                                         'Enum "%s" in scheme "%s", section "%s" references ' ..
603                                         'variable "%s" with non enum type "%s"',
604                                         v.value, scheme, r[2], r[3], t.type )
605
606                                 if not t.values then
607                                         t.values = { [v.value] = v.title or v.value }
608                                 else
609                                         t.values[v.value] = v.title or v.value
610                                 end
611
612                                 if v.default then
613                                         _assert( not t.default,
614                                                 'Enum "%s" in scheme "%s", section "%s" redeclares ' ..
615                                                 'the default value of variable "%s"',
616                                                 v.value, scheme, r[2], v.variable )
617
618                                         t.default = v.value
619                                 end
620                         end
621                 end
622         end
623
624         return self
625 end
626
627 -- Read a dependency specification
628 function UVL._read_dependency( self, values, deps )
629         local expr = "%$?[a-zA-Z0-9_]+"
630         if values then
631                 values = ( type(values) == "table" and values or { values } )
632                 for _, value in ipairs(values) do
633                         local parts     = luci.util.split( value, "%s*,%s*", nil, true )
634                         local condition = { }
635                         for i, val in ipairs(parts) do
636                                 local k, v = unpack(luci.util.split(val, "%s*=%s*", nil, true))
637
638                                 if k and (
639                                         k:match("^"..expr.."%."..expr.."%."..expr.."$") or
640                                         k:match("^"..expr.."%."..expr.."$") or
641                                         k:match("^"..expr.."$")
642                                 ) then
643                                         condition[k] = v or true
644                                 else
645                                         return nil
646                                 end
647                         end
648
649                         if not deps then
650                                 deps = { condition }
651                         else
652                                 table.insert( deps, condition )
653                         end
654                 end
655         end
656
657         return deps
658 end
659
660 -- Read a validator specification
661 function UVL._read_validator( self, values, validators )
662         if values then
663                 values = ( type(values) == "table" and values or { values } )
664                 for _, value in ipairs(values) do
665                         local validator
666
667                         if value:match("^exec:") then
668                                 validator = value:gsub("^exec:","")
669                         elseif value:match("^lua:") then
670                                 validator = self:_resolve_function( (value:gsub("^lua:","") ) )
671                         end
672
673                         if validator then
674                                 if not validators then
675                                         validators = { validator }
676                                 else
677                                         table.insert( validators, validator )
678                                 end
679                         else
680                                 return nil
681                         end
682                 end
683
684                 return validators
685         end
686 end
687
688 -- Read a reference specification (XXX: We should validate external configs too...)
689 function UVL._read_reference( self, values )
690         local val = { }
691         values = ( type(values) == "table" and values or { values } )
692
693         for _, value in ipairs(values) do
694                 local ref = luci.util.split(value, ".")
695
696                 if #ref == 2 or #ref == 3 then
697                         self.uci.load_config(ref[1])
698                         local co = self.uci.get_all(ref[1])
699
700                         if not co then
701                                 return nil, 'Can not load config "%s" for reference "%s"'
702                                         %{ ref[1], value }
703                         end
704
705                         for k, v in pairs(co) do
706                                 if v['.type'] == ref[2] then
707                                         if #ref == 2 then
708                                                 if v['.anonymous'] == true then
709                                                         return nil, 'Illegal reference "%s" to an anonymous section'
710                                                                 % value
711                                                 end
712                                                 table.insert( val, k )
713                                         elseif v[ref[3]] then
714                                                 table.insert( val, v[ref[3]] )
715                                         end
716                                 end
717                         end
718                 else
719                         return nil, 'Malformed reference "%s"' % value
720                 end
721         end
722
723         return val, nil
724 end
725
726 -- Resolve given path
727 function UVL._resolve_function( self, value )
728         local path = luci.util.split(value, ".")
729
730         for i=1, #path-1 do
731                 local stat, mod = pcall(require, table.concat(path, ".", 1, i))
732                 if stat and mod then
733                         for j=i+1, #path-1 do
734                                 if not type(mod) == "table" then
735                                         break
736                                 end
737                                 mod = mod[path[j]]
738                                 if not mod then
739                                         break
740                                 end
741                         end
742                         mod = type(mod) == "table" and mod[path[#path]] or nil
743                         if type(mod) == "function" then
744                                 return mod
745                         end
746                 end
747         end
748 end
749
750
751 --- Object representation of a scheme/config section.
752 -- @class       module
753 -- @cstyle      instance
754 -- @name        luci.uvl.section
755
756 --- Section instance constructor.
757 -- @class                       function
758 -- @name                        section
759 -- @param scheme        Scheme instance
760 -- @param co            Configuration data
761 -- @param st            Section type
762 -- @param c                     Configuration name
763 -- @param s                     Section name
764 -- @return                      Section instance
765 section = luci.util.class()
766
767 function section.__init__(self, scheme, co, st, c, s)
768         self.csection = co[s]
769         self.ssection = scheme.packages[c].sections[st]
770         self.cref     = { c, s }
771         self.sref     = { c, st }
772         self.scheme   = scheme
773         self.config   = co
774         self.type     = luci.uvl.TYPE_SECTION
775 end
776
777 --- Get the config path of this section.
778 -- @return      String containing the identifier
779 function section.cid(self)
780         return ( self.cref[1] or '?' ) .. '.' .. ( self.cref[2] or '?' )
781 end
782
783 --- Get the scheme path of this section.
784 -- @return      String containing the identifier
785 function section.sid(self)
786         return ( self.sref[1] or '?' ) .. '.' .. ( self.sref[2] or '?' )
787 end
788
789 --- Get all configuration values within this section.
790 -- @return      Table containing the values
791 function section.values(self)
792         return self.csection
793 end
794
795 --- Get the associated section information in scheme.
796 -- @return      Table containing the scheme properties
797 function section.section(self)
798         return self.ssection
799 end
800
801 --- Get all option objects associated with this section.
802 -- @return      Table containing all associated luci.uvl.option instances
803 function section.variables(self)
804         local v = { }
805         if self.scheme.packages[self.sref[1]].variables[self.sref[2]] then
806                 for o, _ in pairs(
807                         self.scheme.packages[self.sref[1]].variables[self.sref[2]]
808                 ) do
809                         table.insert( v, luci.uvl.option(
810                                 self.scheme, self.config, self.sref[2],
811                                 self.cref[1], self.cref[2], o
812                         ) )
813                 end
814         end
815         return v
816 end
817
818
819 --- Object representation of a scheme/config option.
820 -- @class       module
821 -- @cstyle      instance
822 -- @name        luci.uvl.option
823
824 --- Section instance constructor.
825 -- @class                       function
826 -- @name                        option
827 -- @param scheme        Scheme instance
828 -- @param co            Configuration data
829 -- @param st            Section type
830 -- @param c                     Configuration name
831 -- @param s                     Section name
832 -- @param o                     Option name
833 -- @return                      Option instance
834 option = luci.util.class()
835
836 function option.__init__(self, scheme, co, st, c, s, o)
837         self.coption = co[s] and co[s][o] or nil
838         self.soption = scheme.packages[c].variables[st][o]
839         self.cref    = { c, s, o }
840         self.sref    = { c, st, o }
841         self.scheme  = scheme
842         self.config  = co
843         self.type    = luci.uvl.TYPE_OPTION
844 end
845
846 --- Get the config path of this option.
847 -- @return      String containing the identifier
848 function option.cid(self)
849         return ( self.cref[1] or '?' ) .. '.' ..
850                    ( self.cref[2] or '?' ) .. '.' ..
851                    ( self.cref[3] or '?' )
852 end
853
854 --- Get the scheme path of this option.
855 -- @return      String containing the identifier
856 function option.sid(self)
857         return ( self.sref[1] or '?' ) .. '.' ..
858                    ( self.sref[2] or '?' ) .. '.' ..
859                    ( self.sref[3] or '?' )
860 end
861
862 --- Get the value of this option.
863 -- @return      The associated configuration value
864 function option.value(self)
865         return self.coption
866 end
867
868 --- Get the associated option information in scheme.
869 -- @return      Table containing the scheme properties
870 function option.option(self)
871         return self.soption
872 end
873
874 --- Get the associated section information in scheme.
875 -- @return      Table containing the scheme properties
876 function option.section(self)
877         return self.scheme.packages[self.sref[1]].sections[self.sref[2]]
878 end