libs/cbi: drop UVL, introduce server side datatype validation
[project/luci.git] / libs / cbi / luasrc / cbi.lua
1 --[[
2 LuCI - Configuration Bind Interface
3
4 Description:
5 Offers an interface for binding configuration values to certain
6 data types. Supports value and range validation and basic dependencies.
7
8 FileId:
9 $Id$
10
11 License:
12 Copyright 2008 Steven Barth <steven@midlink.org>
13
14 Licensed under the Apache License, Version 2.0 (the "License");
15 you may not use this file except in compliance with the License.
16 You may obtain a copy of the License at
17
18         http://www.apache.org/licenses/LICENSE-2.0
19
20 Unless required by applicable law or agreed to in writing, software
21 distributed under the License is distributed on an "AS IS" BASIS,
22 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 See the License for the specific language governing permissions and
24 limitations under the License.
25
26 ]]--
27 module("luci.cbi", package.seeall)
28
29 require("luci.template")
30 local util = require("luci.util")
31 require("luci.http")
32
33
34 --local event      = require "luci.sys.event"
35 local fs         = require("nixio.fs")
36 local uci        = require("luci.model.uci")
37 local datatypes  = require("luci.cbi.datatypes")
38 local class      = util.class
39 local instanceof = util.instanceof
40
41 FORM_NODATA  =  0
42 FORM_PROCEED =  0
43 FORM_VALID   =  1
44 FORM_DONE        =  1
45 FORM_INVALID = -1
46 FORM_CHANGED =  2
47 FORM_SKIP    =  4
48
49 AUTO = true
50
51 CREATE_PREFIX = "cbi.cts."
52 REMOVE_PREFIX = "cbi.rts."
53
54 -- Loads a CBI map from given file, creating an environment and returns it
55 function load(cbimap, ...)
56         local fs   = require "nixio.fs"
57         local i18n = require "luci.i18n"
58         require("luci.config")
59         require("luci.util")
60
61         local upldir = "/lib/uci/upload/"
62         local cbidir = luci.util.libpath() .. "/model/cbi/"
63         local func, err
64
65         if fs.access(cbidir..cbimap..".lua") then
66                 func, err = loadfile(cbidir..cbimap..".lua")
67         elseif fs.access(cbimap) then
68                 func, err = loadfile(cbimap)
69         else
70                 func, err = nil, "Model '" .. cbimap .. "' not found!"
71         end
72
73         assert(func, err)
74
75         luci.i18n.loadc("base")
76
77         local env = {
78                 translate=i18n.translate,
79                 translatef=i18n.translatef,
80                 arg={...}
81         }
82
83         setfenv(func, setmetatable(env, {__index =
84                 function(tbl, key)
85                         return rawget(tbl, key) or _M[key] or _G[key]
86                 end}))
87
88         local maps       = { func() }
89         local uploads    = { }
90         local has_upload = false
91
92         for i, map in ipairs(maps) do
93                 if not instanceof(map, Node) then
94                         error("CBI map returns no valid map object!")
95                         return nil
96                 else
97                         map:prepare()
98                         if map.upload_fields then
99                                 has_upload = true
100                                 for _, field in ipairs(map.upload_fields) do
101                                         uploads[
102                                                 field.config .. '.' ..
103                                                 field.section.sectiontype .. '.' ..
104                                                 field.option
105                                         ] = true
106                                 end
107                         end
108                 end
109         end
110
111         if has_upload then
112                 local uci = luci.model.uci.cursor()
113                 local prm = luci.http.context.request.message.params
114                 local fd, cbid
115
116                 luci.http.setfilehandler(
117                         function( field, chunk, eof )
118                                 if not field then return end
119                                 if field.name and not cbid then
120                                         local c, s, o = field.name:gmatch(
121                                                 "cbid%.([^%.]+)%.([^%.]+)%.([^%.]+)"
122                                         )()
123
124                                         if c and s and o then
125                                                 local t = uci:get( c, s )
126                                                 if t and uploads[c.."."..t.."."..o] then
127                                                         local path = upldir .. field.name
128                                                         fd = io.open(path, "w")
129                                                         if fd then
130                                                                 cbid = field.name
131                                                                 prm[cbid] = path
132                                                         end
133                                                 end
134                                         end
135                                 end
136
137                                 if field.name == cbid and fd then
138                                         fd:write(chunk)
139                                 end
140
141                                 if eof and fd then
142                                         fd:close()
143                                         fd   = nil
144                                         cbid = nil
145                                 end
146                         end
147                 )
148         end
149
150         return maps
151 end
152
153
154 -- Node pseudo abstract class
155 Node = class()
156
157 function Node.__init__(self, title, description)
158         self.children = {}
159         self.title = title or ""
160         self.description = description or ""
161         self.template = "cbi/node"
162 end
163
164 -- hook helper
165 function Node._run_hook(self, hook)
166         if type(self[hook]) == "function" then
167                 return self[hook](self)
168         end 
169 end
170
171 function Node._run_hooks(self, ...)
172         local f
173         local r = false
174         for _, f in ipairs(arg) do
175                 if type(self[f]) == "function" then
176                         self[f](self)
177                         r = true
178                 end
179         end
180         return r
181 end
182
183 -- Prepare nodes
184 function Node.prepare(self, ...)
185         for k, child in ipairs(self.children) do
186                 child:prepare(...)
187         end
188 end
189
190 -- Append child nodes
191 function Node.append(self, obj)
192         table.insert(self.children, obj)
193 end
194
195 -- Parse this node and its children
196 function Node.parse(self, ...)
197         for k, child in ipairs(self.children) do
198                 child:parse(...)
199         end
200 end
201
202 -- Render this node
203 function Node.render(self, scope)
204         scope = scope or {}
205         scope.self = self
206
207         luci.template.render(self.template, scope)
208 end
209
210 -- Render the children
211 function Node.render_children(self, ...)
212         for k, node in ipairs(self.children) do
213                 node:render(...)
214         end
215 end
216
217
218 --[[
219 A simple template element
220 ]]--
221 Template = class(Node)
222
223 function Template.__init__(self, template)
224         Node.__init__(self)
225         self.template = template
226 end
227
228 function Template.render(self)
229         luci.template.render(self.template, {self=self})
230 end
231
232 function Template.parse(self, readinput)
233         self.readinput = (readinput ~= false)
234         return Map.formvalue(self, "cbi.submit") and FORM_DONE or FORM_NODATA
235 end
236
237
238 --[[
239 Map - A map describing a configuration file
240 ]]--
241 Map = class(Node)
242
243 function Map.__init__(self, config, ...)
244         Node.__init__(self, ...)
245
246         self.config = config
247         self.parsechain = {self.config}
248         self.template = "cbi/map"
249         self.apply_on_parse = nil
250         self.readinput = true
251         self.proceed = false
252         self.flow = {}
253
254         self.uci = uci.cursor()
255         self.save = true
256
257         self.changed = false
258
259         if not self.uci:load(self.config) then
260                 error("Unable to read UCI data: " .. self.config)
261         end
262 end
263
264 function Map.formvalue(self, key)
265         return self.readinput and luci.http.formvalue(key)
266 end
267
268 function Map.formvaluetable(self, key)
269         return self.readinput and luci.http.formvaluetable(key) or {}
270 end
271
272 function Map.get_scheme(self, sectiontype, option)
273         if not option then
274                 return self.scheme and self.scheme.sections[sectiontype]
275         else
276                 return self.scheme and self.scheme.variables[sectiontype]
277                  and self.scheme.variables[sectiontype][option]
278         end
279 end
280
281 function Map.submitstate(self)
282         return self:formvalue("cbi.submit")
283 end
284
285 -- Chain foreign config
286 function Map.chain(self, config)
287         table.insert(self.parsechain, config)
288 end
289
290 function Map.state_handler(self, state)
291         return state
292 end
293
294 -- Use optimized UCI writing
295 function Map.parse(self, readinput, ...)
296         self.readinput = (readinput ~= false)
297         self:_run_hooks("on_parse")
298
299         if self:formvalue("cbi.skip") then
300                 self.state = FORM_SKIP
301                 return self:state_handler(self.state)
302         end
303
304         Node.parse(self, ...)
305
306         if self.save then
307                 for i, config in ipairs(self.parsechain) do
308                         self.uci:save(config)
309                 end
310                 if self:submitstate() and ((not self.proceed and self.flow.autoapply) or luci.http.formvalue("cbi.apply")) then
311                         self:_run_hooks("on_before_commit")
312                         for i, config in ipairs(self.parsechain) do
313                                 self.uci:commit(config)
314
315                                 -- Refresh data because commit changes section names
316                                 self.uci:load(config)
317                         end
318                         self:_run_hooks("on_commit", "on_after_commit", "on_before_apply")
319                         if self.apply_on_parse then
320                                 self.uci:apply(self.parsechain)
321                                 self:_run_hooks("on_apply", "on_after_apply")
322                         else
323                                 self._apply = function()
324                                         local cmd = self.uci:apply(self.parsechain, true)
325                                         return io.popen(cmd)
326                                 end
327                         end
328
329                         -- Reparse sections
330                         Node.parse(self, true)
331
332                 end
333                 for i, config in ipairs(self.parsechain) do
334                         self.uci:unload(config)
335                 end
336                 if type(self.commit_handler) == "function" then
337                         self:commit_handler(self:submitstate())
338                 end
339         end
340
341         if self:submitstate() then
342                 if not self.save then
343                         self.state = FORM_INVALID
344                 elseif self.proceed then
345                         self.state = FORM_PROCEED
346                 else
347                         self.state = self.changed and FORM_CHANGED or FORM_VALID
348                 end
349         else
350                 self.state = FORM_NODATA
351         end
352
353         return self:state_handler(self.state)
354 end
355
356 function Map.render(self, ...)
357         self:_run_hooks("on_init")
358         Node.render(self, ...)
359         if self._apply then
360                 local fp = self._apply()
361                 fp:read("*a")
362                 fp:close()
363                 self:_run_hooks("on_apply")
364         end
365 end
366
367 -- Creates a child section
368 function Map.section(self, class, ...)
369         if instanceof(class, AbstractSection) then
370                 local obj  = class(self, ...)
371                 self:append(obj)
372                 return obj
373         else
374                 error("class must be a descendent of AbstractSection")
375         end
376 end
377
378 -- UCI add
379 function Map.add(self, sectiontype)
380         return self.uci:add(self.config, sectiontype)
381 end
382
383 -- UCI set
384 function Map.set(self, section, option, value)
385         if option then
386                 return self.uci:set(self.config, section, option, value)
387         else
388                 return self.uci:set(self.config, section, value)
389         end
390 end
391
392 -- UCI del
393 function Map.del(self, section, option)
394         if option then
395                 return self.uci:delete(self.config, section, option)
396         else
397                 return self.uci:delete(self.config, section)
398         end
399 end
400
401 -- UCI get
402 function Map.get(self, section, option)
403         if not section then
404                 return self.uci:get_all(self.config)
405         elseif option then
406                 return self.uci:get(self.config, section, option)
407         else
408                 return self.uci:get_all(self.config, section)
409         end
410 end
411
412 --[[
413 Compound - Container
414 ]]--
415 Compound = class(Node)
416
417 function Compound.__init__(self, ...)
418         Node.__init__(self)
419         self.template = "cbi/compound"
420         self.children = {...}
421 end
422
423 function Compound.populate_delegator(self, delegator)
424         for _, v in ipairs(self.children) do
425                 v.delegator = delegator
426         end
427 end
428
429 function Compound.parse(self, ...)
430         local cstate, state = 0
431
432         for k, child in ipairs(self.children) do
433                 cstate = child:parse(...)
434                 state = (not state or cstate < state) and cstate or state
435         end
436
437         return state
438 end
439
440
441 --[[
442 Delegator - Node controller
443 ]]--
444 Delegator = class(Node)
445 function Delegator.__init__(self, ...)
446         Node.__init__(self, ...)
447         self.nodes = {}
448         self.defaultpath = {}
449         self.pageaction = false
450         self.readinput = true
451         self.allow_reset = false
452         self.allow_cancel = false
453         self.allow_back = false
454         self.allow_finish = false
455         self.template = "cbi/delegator"
456 end
457
458 function Delegator.set(self, name, node)
459         assert(not self.nodes[name], "Duplicate entry")
460
461         self.nodes[name] = node
462 end
463
464 function Delegator.add(self, name, node)
465         node = self:set(name, node)
466         self.defaultpath[#self.defaultpath+1] = name
467 end
468
469 function Delegator.insert_after(self, name, after)
470         local n = #self.chain + 1
471         for k, v in ipairs(self.chain) do
472                 if v == after then
473                         n = k + 1
474                         break
475                 end
476         end
477         table.insert(self.chain, n, name)
478 end
479
480 function Delegator.set_route(self, ...)
481         local n, chain, route = 0, self.chain, {...}
482         for i = 1, #chain do
483                 if chain[i] == self.current then
484                         n = i
485                         break
486                 end
487         end
488         for i = 1, #route do
489                 n = n + 1
490                 chain[n] = route[i]
491         end
492         for i = n + 1, #chain do
493                 chain[i] = nil
494         end
495 end
496
497 function Delegator.get(self, name)
498         local node = self.nodes[name]
499
500         if type(node) == "string" then
501                 node = load(node, name)
502         end
503
504         if type(node) == "table" and getmetatable(node) == nil then
505                 node = Compound(unpack(node))
506         end
507
508         return node
509 end
510
511 function Delegator.parse(self, ...)
512         if self.allow_cancel and Map.formvalue(self, "cbi.cancel") then
513                 if self:_run_hooks("on_cancel") then
514                         return FORM_DONE
515                 end
516         end
517         
518         if not Map.formvalue(self, "cbi.delg.current") then
519                 self:_run_hooks("on_init")
520         end
521
522         local newcurrent
523         self.chain = self.chain or self:get_chain()
524         self.current = self.current or self:get_active()
525         self.active = self.active or self:get(self.current)
526         assert(self.active, "Invalid state")
527         
528         local stat = FORM_DONE
529         if type(self.active) ~= "function" then
530                 self.active:populate_delegator(self)
531                 stat = self.active:parse() 
532         else
533                 self:active()
534         end
535
536         if stat > FORM_PROCEED then
537                 if Map.formvalue(self, "cbi.delg.back") then
538                         newcurrent = self:get_prev(self.current)
539                 else
540                         newcurrent = self:get_next(self.current)
541                 end
542         elseif stat < FORM_PROCEED then
543                 return stat
544         end
545         
546
547         if not Map.formvalue(self, "cbi.submit") then
548                 return FORM_NODATA
549         elseif stat > FORM_PROCEED 
550         and (not newcurrent or not self:get(newcurrent)) then
551                 return self:_run_hook("on_done") or FORM_DONE
552         else
553                 self.current = newcurrent or self.current
554                 self.active = self:get(self.current)
555                 if type(self.active) ~= "function" then
556                         self.active:populate_delegator(self)
557                         local stat = self.active:parse(false)
558                         if stat == FORM_SKIP then
559                                 return self:parse(...)
560                         else
561                                 return FORM_PROCEED
562                         end
563                 else
564                         return self:parse(...)
565                 end
566         end
567 end
568
569 function Delegator.get_next(self, state)
570         for k, v in ipairs(self.chain) do
571                 if v == state then
572                         return self.chain[k+1]
573                 end
574         end
575 end
576
577 function Delegator.get_prev(self, state)
578         for k, v in ipairs(self.chain) do
579                 if v == state then
580                         return self.chain[k-1]
581                 end
582         end
583 end
584
585 function Delegator.get_chain(self)
586         local x = Map.formvalue(self, "cbi.delg.path") or self.defaultpath
587         return type(x) == "table" and x or {x}
588 end
589
590 function Delegator.get_active(self)
591         return Map.formvalue(self, "cbi.delg.current") or self.chain[1]
592 end
593
594 --[[
595 Page - A simple node
596 ]]--
597
598 Page = class(Node)
599 Page.__init__ = Node.__init__
600 Page.parse    = function() end
601
602
603 --[[
604 SimpleForm - A Simple non-UCI form
605 ]]--
606 SimpleForm = class(Node)
607
608 function SimpleForm.__init__(self, config, title, description, data)
609         Node.__init__(self, title, description)
610         self.config = config
611         self.data = data or {}
612         self.template = "cbi/simpleform"
613         self.dorender = true
614         self.pageaction = false
615         self.readinput = true
616 end
617
618 SimpleForm.formvalue = Map.formvalue
619 SimpleForm.formvaluetable = Map.formvaluetable
620
621 function SimpleForm.parse(self, readinput, ...)
622         self.readinput = (readinput ~= false)
623
624         if self:formvalue("cbi.skip") then
625                 return FORM_SKIP
626         end
627
628         if self:formvalue("cbi.cancel") and self:_run_hooks("on_cancel") then
629                 return FORM_DONE
630         end
631
632         if self:submitstate() then
633                 Node.parse(self, 1, ...)
634         end
635
636         local valid = true
637         for k, j in ipairs(self.children) do
638                 for i, v in ipairs(j.children) do
639                         valid = valid
640                          and (not v.tag_missing or not v.tag_missing[1])
641                          and (not v.tag_invalid or not v.tag_invalid[1])
642                          and (not v.error)
643                 end
644         end
645
646         local state =
647                 not self:submitstate() and FORM_NODATA
648                 or valid and FORM_VALID
649                 or FORM_INVALID
650
651         self.dorender = not self.handle
652         if self.handle then
653                 local nrender, nstate = self:handle(state, self.data)
654                 self.dorender = self.dorender or (nrender ~= false)
655                 state = nstate or state
656         end
657         return state
658 end
659
660 function SimpleForm.render(self, ...)
661         if self.dorender then
662                 Node.render(self, ...)
663         end
664 end
665
666 function SimpleForm.submitstate(self)
667         return self:formvalue("cbi.submit")
668 end
669
670 function SimpleForm.section(self, class, ...)
671         if instanceof(class, AbstractSection) then
672                 local obj  = class(self, ...)
673                 self:append(obj)
674                 return obj
675         else
676                 error("class must be a descendent of AbstractSection")
677         end
678 end
679
680 -- Creates a child field
681 function SimpleForm.field(self, class, ...)
682         local section
683         for k, v in ipairs(self.children) do
684                 if instanceof(v, SimpleSection) then
685                         section = v
686                         break
687                 end
688         end
689         if not section then
690                 section = self:section(SimpleSection)
691         end
692
693         if instanceof(class, AbstractValue) then
694                 local obj  = class(self, section, ...)
695                 obj.track_missing = true
696                 section:append(obj)
697                 return obj
698         else
699                 error("class must be a descendent of AbstractValue")
700         end
701 end
702
703 function SimpleForm.set(self, section, option, value)
704         self.data[option] = value
705 end
706
707
708 function SimpleForm.del(self, section, option)
709         self.data[option] = nil
710 end
711
712
713 function SimpleForm.get(self, section, option)
714         return self.data[option]
715 end
716
717
718 function SimpleForm.get_scheme()
719         return nil
720 end
721
722
723 Form = class(SimpleForm)
724
725 function Form.__init__(self, ...)
726         SimpleForm.__init__(self, ...)
727         self.embedded = true
728 end
729
730
731 --[[
732 AbstractSection
733 ]]--
734 AbstractSection = class(Node)
735
736 function AbstractSection.__init__(self, map, sectiontype, ...)
737         Node.__init__(self, ...)
738         self.sectiontype = sectiontype
739         self.map = map
740         self.config = map.config
741         self.optionals = {}
742         self.defaults = {}
743         self.fields = {}
744         self.tag_error = {}
745         self.tag_invalid = {}
746         self.tag_deperror = {}
747         self.changed = false
748
749         self.optional = true
750         self.addremove = false
751         self.dynamic = false
752 end
753
754 -- Define a tab for the section
755 function AbstractSection.tab(self, tab, title, desc)
756         self.tabs      = self.tabs      or { }
757         self.tab_names = self.tab_names or { }
758
759         self.tab_names[#self.tab_names+1] = tab
760         self.tabs[tab] = {
761                 title       = title,
762                 description = desc,
763                 childs      = { }
764         }
765 end
766
767 -- Appends a new option
768 function AbstractSection.option(self, class, option, ...)
769         if instanceof(class, AbstractValue) then
770                 local obj  = class(self.map, self, option, ...)
771                 self:append(obj)
772                 self.fields[option] = obj
773                 return obj
774         elseif class == true then
775                 error("No valid class was given and autodetection failed.")
776         else
777                 error("class must be a descendant of AbstractValue")
778         end
779 end
780
781 -- Appends a new tabbed option
782 function AbstractSection.taboption(self, tab, ...)
783
784         assert(tab and self.tabs and self.tabs[tab],
785                 "Cannot assign option to not existing tab %q" % tostring(tab))
786
787         local l = self.tabs[tab].childs
788         local o = AbstractSection.option(self, ...)
789
790         if o then l[#l+1] = o end
791
792         return o
793 end
794
795 -- Render a single tab
796 function AbstractSection.render_tab(self, tab, ...)
797
798         assert(tab and self.tabs and self.tabs[tab],
799                 "Cannot render not existing tab %q" % tostring(tab))
800
801         for _, node in ipairs(self.tabs[tab].childs) do
802                 node:render(...)
803         end
804 end
805
806 -- Parse optional options
807 function AbstractSection.parse_optionals(self, section)
808         if not self.optional then
809                 return
810         end
811
812         self.optionals[section] = {}
813
814         local field = self.map:formvalue("cbi.opt."..self.config.."."..section)
815         for k,v in ipairs(self.children) do
816                 if v.optional and not v:cfgvalue(section) then
817                         if field == v.option then
818                                 field = nil
819                                 self.map.proceed = true
820                         else
821                                 table.insert(self.optionals[section], v)
822                         end
823                 end
824         end
825
826         if field and #field > 0 and self.dynamic then
827                 self:add_dynamic(field)
828         end
829 end
830
831 -- Add a dynamic option
832 function AbstractSection.add_dynamic(self, field, optional)
833         local o = self:option(Value, field, field)
834         o.optional = optional
835 end
836
837 -- Parse all dynamic options
838 function AbstractSection.parse_dynamic(self, section)
839         if not self.dynamic then
840                 return
841         end
842
843         local arr  = luci.util.clone(self:cfgvalue(section))
844         local form = self.map:formvaluetable("cbid."..self.config.."."..section)
845         for k, v in pairs(form) do
846                 arr[k] = v
847         end
848
849         for key,val in pairs(arr) do
850                 local create = true
851
852                 for i,c in ipairs(self.children) do
853                         if c.option == key then
854                                 create = false
855                         end
856                 end
857
858                 if create and key:sub(1, 1) ~= "." then
859                         self.map.proceed = true
860                         self:add_dynamic(key, true)
861                 end
862         end
863 end
864
865 -- Returns the section's UCI table
866 function AbstractSection.cfgvalue(self, section)
867         return self.map:get(section)
868 end
869
870 -- Push events
871 function AbstractSection.push_events(self)
872         --luci.util.append(self.map.events, self.events)
873         self.map.changed = true
874 end
875
876 -- Removes the section
877 function AbstractSection.remove(self, section)
878         self.map.proceed = true
879         return self.map:del(section)
880 end
881
882 -- Creates the section
883 function AbstractSection.create(self, section)
884         local stat
885
886         if section then
887                 stat = section:match("^[%w_]+$") and self.map:set(section, nil, self.sectiontype)
888         else
889                 section = self.map:add(self.sectiontype)
890                 stat = section
891         end
892
893         if stat then
894                 for k,v in pairs(self.children) do
895                         if v.default then
896                                 self.map:set(section, v.option, v.default)
897                         end
898                 end
899
900                 for k,v in pairs(self.defaults) do
901                         self.map:set(section, k, v)
902                 end
903         end
904
905         self.map.proceed = true
906
907         return stat
908 end
909
910
911 SimpleSection = class(AbstractSection)
912
913 function SimpleSection.__init__(self, form, ...)
914         AbstractSection.__init__(self, form, nil, ...)
915         self.template = "cbi/nullsection"
916 end
917
918
919 Table = class(AbstractSection)
920
921 function Table.__init__(self, form, data, ...)
922         local datasource = {}
923         local tself = self
924         datasource.config = "table"
925         self.data = data or {}
926
927         datasource.formvalue = Map.formvalue
928         datasource.formvaluetable = Map.formvaluetable
929         datasource.readinput = true
930
931         function datasource.get(self, section, option)
932                 return tself.data[section] and tself.data[section][option]
933         end
934
935         function datasource.submitstate(self)
936                 return Map.formvalue(self, "cbi.submit")
937         end
938
939         function datasource.del(...)
940                 return true
941         end
942
943         function datasource.get_scheme()
944                 return nil
945         end
946
947         AbstractSection.__init__(self, datasource, "table", ...)
948         self.template = "cbi/tblsection"
949         self.rowcolors = true
950         self.anonymous = true
951 end
952
953 function Table.parse(self, readinput)
954         self.map.readinput = (readinput ~= false)
955         for i, k in ipairs(self:cfgsections()) do
956                 if self.map:submitstate() then
957                         Node.parse(self, k)
958                 end
959         end
960 end
961
962 function Table.cfgsections(self)
963         local sections = {}
964
965         for i, v in luci.util.kspairs(self.data) do
966                 table.insert(sections, i)
967         end
968
969         return sections
970 end
971
972 function Table.update(self, data)
973         self.data = data
974 end
975
976
977
978 --[[
979 NamedSection - A fixed configuration section defined by its name
980 ]]--
981 NamedSection = class(AbstractSection)
982
983 function NamedSection.__init__(self, map, section, stype, ...)
984         AbstractSection.__init__(self, map, stype, ...)
985
986         -- Defaults
987         self.addremove = false
988         self.template = "cbi/nsection"
989         self.section = section
990 end
991
992 function NamedSection.parse(self, novld)
993         local s = self.section
994         local active = self:cfgvalue(s)
995
996         if self.addremove then
997                 local path = self.config.."."..s
998                 if active then -- Remove the section
999                         if self.map:formvalue("cbi.rns."..path) and self:remove(s) then
1000                                 self:push_events()
1001                                 return
1002                         end
1003                 else           -- Create and apply default values
1004                         if self.map:formvalue("cbi.cns."..path) then
1005                                 self:create(s)
1006                                 return
1007                         end
1008                 end
1009         end
1010
1011         if active then
1012                 AbstractSection.parse_dynamic(self, s)
1013                 if self.map:submitstate() then
1014                         Node.parse(self, s)
1015                 end
1016                 AbstractSection.parse_optionals(self, s)
1017
1018                 if self.changed then
1019                         self:push_events()
1020                 end
1021         end
1022 end
1023
1024
1025 --[[
1026 TypedSection - A (set of) configuration section(s) defined by the type
1027         addremove:      Defines whether the user can add/remove sections of this type
1028         anonymous:  Allow creating anonymous sections
1029         validate:       a validation function returning nil if the section is invalid
1030 ]]--
1031 TypedSection = class(AbstractSection)
1032
1033 function TypedSection.__init__(self, map, type, ...)
1034         AbstractSection.__init__(self, map, type, ...)
1035
1036         self.template = "cbi/tsection"
1037         self.deps = {}
1038         self.anonymous = false
1039 end
1040
1041 -- Return all matching UCI sections for this TypedSection
1042 function TypedSection.cfgsections(self)
1043         local sections = {}
1044         self.map.uci:foreach(self.map.config, self.sectiontype,
1045                 function (section)
1046                         if self:checkscope(section[".name"]) then
1047                                 table.insert(sections, section[".name"])
1048                         end
1049                 end)
1050
1051         return sections
1052 end
1053
1054 -- Limits scope to sections that have certain option => value pairs
1055 function TypedSection.depends(self, option, value)
1056         table.insert(self.deps, {option=option, value=value})
1057 end
1058
1059 function TypedSection.parse(self, novld)
1060         if self.addremove then
1061                 -- Remove
1062                 local crval = REMOVE_PREFIX .. self.config
1063                 local name = self.map:formvaluetable(crval)
1064                 for k,v in pairs(name) do
1065                         if k:sub(-2) == ".x" then
1066                                 k = k:sub(1, #k - 2)
1067                         end
1068                         if self:cfgvalue(k) and self:checkscope(k) then
1069                                 self:remove(k)
1070                         end
1071                 end
1072         end
1073
1074         local co
1075         for i, k in ipairs(self:cfgsections()) do
1076                 AbstractSection.parse_dynamic(self, k)
1077                 if self.map:submitstate() then
1078                         Node.parse(self, k, novld)
1079                 end
1080                 AbstractSection.parse_optionals(self, k)
1081         end
1082
1083         if self.addremove then
1084                 -- Create
1085                 local created
1086                 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
1087                 local name  = self.map:formvalue(crval)
1088                 if self.anonymous then
1089                         if name then
1090                                 created = self:create()
1091                         end
1092                 else
1093                         if name then
1094                                 -- Ignore if it already exists
1095                                 if self:cfgvalue(name) then
1096                                         name = nil;
1097                                 end
1098
1099                                 name = self:checkscope(name)
1100
1101                                 if not name then
1102                                         self.err_invalid = true
1103                                 end
1104
1105                                 if name and #name > 0 then
1106                                         created = self:create(name) and name
1107                                         if not created then
1108                                                 self.invalid_cts = true
1109                                         end
1110                                 end
1111                         end
1112                 end
1113
1114                 if created then
1115                         AbstractSection.parse_optionals(self, created)
1116                 end
1117         end
1118
1119         if created or self.changed then
1120                 self:push_events()
1121         end
1122 end
1123
1124 -- Verifies scope of sections
1125 function TypedSection.checkscope(self, section)
1126         -- Check if we are not excluded
1127         if self.filter and not self:filter(section) then
1128                 return nil
1129         end
1130
1131         -- Check if at least one dependency is met
1132         if #self.deps > 0 and self:cfgvalue(section) then
1133                 local stat = false
1134
1135                 for k, v in ipairs(self.deps) do
1136                         if self:cfgvalue(section)[v.option] == v.value then
1137                                 stat = true
1138                         end
1139                 end
1140
1141                 if not stat then
1142                         return nil
1143                 end
1144         end
1145
1146         return self:validate(section)
1147 end
1148
1149
1150 -- Dummy validate function
1151 function TypedSection.validate(self, section)
1152         return section
1153 end
1154
1155
1156 --[[
1157 AbstractValue - An abstract Value Type
1158         null:           Value can be empty
1159         valid:          A function returning the value if it is valid otherwise nil
1160         depends:        A table of option => value pairs of which one must be true
1161         default:        The default value
1162         size:           The size of the input fields
1163         rmempty:        Unset value if empty
1164         optional:       This value is optional (see AbstractSection.optionals)
1165 ]]--
1166 AbstractValue = class(Node)
1167
1168 function AbstractValue.__init__(self, map, section, option, ...)
1169         Node.__init__(self, ...)
1170         self.section = section
1171         self.option  = option
1172         self.map     = map
1173         self.config  = map.config
1174         self.tag_invalid = {}
1175         self.tag_missing = {}
1176         self.tag_reqerror = {}
1177         self.tag_error = {}
1178         self.deps = {}
1179         self.subdeps = {}
1180         --self.cast = "string"
1181
1182         self.track_missing = false
1183         self.rmempty   = true
1184         self.default   = nil
1185         self.size      = nil
1186         self.optional  = false
1187 end
1188
1189 function AbstractValue.prepare(self)
1190         self.cast = self.cast or "string"
1191 end
1192
1193 -- Add a dependencie to another section field
1194 function AbstractValue.depends(self, field, value)
1195         local deps
1196         if type(field) == "string" then
1197                 deps = {}
1198                 deps[field] = value
1199         else
1200                 deps = field
1201         end
1202
1203         table.insert(self.deps, {deps=deps, add=""})
1204 end
1205
1206 -- Generates the unique CBID
1207 function AbstractValue.cbid(self, section)
1208         return "cbid."..self.map.config.."."..section.."."..self.option
1209 end
1210
1211 -- Return whether this object should be created
1212 function AbstractValue.formcreated(self, section)
1213         local key = "cbi.opt."..self.config.."."..section
1214         return (self.map:formvalue(key) == self.option)
1215 end
1216
1217 -- Returns the formvalue for this object
1218 function AbstractValue.formvalue(self, section)
1219         return self.map:formvalue(self:cbid(section))
1220 end
1221
1222 function AbstractValue.additional(self, value)
1223         self.optional = value
1224 end
1225
1226 function AbstractValue.mandatory(self, value)
1227         self.rmempty = not value
1228 end
1229
1230 function AbstractValue.parse(self, section, novld)
1231         local fvalue = self:formvalue(section)
1232         local cvalue = self:cfgvalue(section)
1233
1234         -- If favlue and cvalue are both tables and have the same content
1235         -- make them identical
1236         if type(fvalue) == "table" and type(cvalue) == "table" then
1237                 local equal = #fvalue == #cvalue
1238                 if equal then
1239                         for i=1, #fvalue do
1240                                 if cvalue[i] ~= fvalue[i] then
1241                                         equal = false
1242                                 end
1243                         end
1244                 end
1245                 if equal then
1246                         fvalue = cvalue
1247                 end
1248         end
1249
1250         if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1251                 fvalue = self:transform(self:validate(fvalue, section))
1252                 if not fvalue and not novld then
1253                         if self.error then
1254                                 self.error[section] = "invalid"
1255                         else
1256                                 self.error = { [section] = "invalid" }
1257                         end
1258                         if self.section.error then
1259                                 table.insert(self.section.error[section], "invalid")
1260                         else
1261                                 self.section.error = {[section] = {"invalid"}}
1262                         end 
1263                         self.map.save = false
1264                 end
1265                 if fvalue and not (fvalue == cvalue) then
1266                         if self:write(section, fvalue) then
1267                                 -- Push events
1268                                 self.section.changed = true
1269                                 --luci.util.append(self.map.events, self.events)
1270                         end
1271                 end
1272         else                                                    -- Unset the UCI or error
1273                 if self.rmempty or self.optional then
1274                         if self:remove(section) then
1275                                 -- Push events
1276                                 self.section.changed = true
1277                                 --luci.util.append(self.map.events, self.events)
1278                         end
1279                 elseif cvalue ~= fvalue and not novld then
1280                         self:write(section, fvalue or "")
1281                         if self.error then
1282                                 self.error[section] = "missing"
1283                         else
1284                                 self.error = { [section] = "missing" }
1285                         end
1286                         self.map.save = false
1287                 end
1288         end
1289 end
1290
1291 -- Render if this value exists or if it is mandatory
1292 function AbstractValue.render(self, s, scope)
1293         if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1294                 scope = scope or {}
1295                 scope.section   = s
1296                 scope.cbid      = self:cbid(s)
1297                 scope.striptags = luci.util.striptags
1298                 scope.pcdata    = luci.util.pcdata
1299
1300                 scope.ifattr = function(cond,key,val)
1301                         if cond then
1302                                 return string.format(
1303                                         ' %s="%s"', tostring(key),
1304                                         luci.util.pcdata(tostring( val
1305                                          or scope[key]
1306                                          or (type(self[key]) ~= "function" and self[key])
1307                                          or "" ))
1308                                 )
1309                         else
1310                                 return ''
1311                         end
1312                 end
1313
1314                 scope.attr = function(...)
1315                         return scope.ifattr( true, ... )
1316                 end
1317
1318                 Node.render(self, scope)
1319         end
1320 end
1321
1322 -- Return the UCI value of this object
1323 function AbstractValue.cfgvalue(self, section)
1324         local value = (self.error and self.error[section] == "invalid")
1325                 and self:formvalue(section) or self.map:get(section, self.option)
1326         if not value then
1327                 return nil
1328         elseif not self.cast or self.cast == type(value) then
1329                 return value
1330         elseif self.cast == "string" then
1331                 if type(value) == "table" then
1332                         return value[1]
1333                 end
1334         elseif self.cast == "table" then
1335                 return luci.util.split(value, "%s+", nil, true)
1336         end
1337 end
1338
1339 -- Validate the form value
1340 function AbstractValue.validate(self, value)
1341         if self.datatype and value and datatypes[self.datatype] then
1342                 if datatypes[self.datatype](value) then
1343                         return value
1344                 end
1345         else
1346                 return value
1347         end
1348 end
1349
1350 AbstractValue.transform = AbstractValue.validate
1351
1352
1353 -- Write to UCI
1354 function AbstractValue.write(self, section, value)
1355         return self.map:set(section, self.option, value)
1356 end
1357
1358 -- Remove from UCI
1359 function AbstractValue.remove(self, section)
1360         return self.map:del(section, self.option)
1361 end
1362
1363
1364
1365
1366 --[[
1367 Value - A one-line value
1368         maxlength:      The maximum length
1369 ]]--
1370 Value = class(AbstractValue)
1371
1372 function Value.__init__(self, ...)
1373         AbstractValue.__init__(self, ...)
1374         self.template  = "cbi/value"
1375         self.keylist = {}
1376         self.vallist = {}
1377 end
1378
1379 function Value.value(self, key, val)
1380         val = val or key
1381         table.insert(self.keylist, tostring(key))
1382         table.insert(self.vallist, tostring(val))
1383 end
1384
1385
1386 -- DummyValue - This does nothing except being there
1387 DummyValue = class(AbstractValue)
1388
1389 function DummyValue.__init__(self, ...)
1390         AbstractValue.__init__(self, ...)
1391         self.template = "cbi/dvalue"
1392         self.value = nil
1393 end
1394
1395 function DummyValue.cfgvalue(self, section)
1396         local value
1397         if self.value then
1398                 if type(self.value) == "function" then
1399                         value = self:value(section)
1400                 else
1401                         value = self.value
1402                 end
1403         else
1404                 value = AbstractValue.cfgvalue(self, section)
1405         end
1406         return value
1407 end
1408
1409 function DummyValue.parse(self)
1410
1411 end
1412
1413
1414 --[[
1415 Flag - A flag being enabled or disabled
1416 ]]--
1417 Flag = class(AbstractValue)
1418
1419 function Flag.__init__(self, ...)
1420         AbstractValue.__init__(self, ...)
1421         self.template  = "cbi/fvalue"
1422
1423         self.enabled = "1"
1424         self.disabled = "0"
1425 end
1426
1427 -- A flag can only have two states: set or unset
1428 function Flag.parse(self, section)
1429         local fvalue = self:formvalue(section)
1430
1431         if fvalue then
1432                 fvalue = self.enabled
1433         else
1434                 fvalue = self.disabled
1435         end
1436
1437         if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1438                 if not(fvalue == self:cfgvalue(section)) then
1439                         self:write(section, fvalue)
1440                 end
1441         else
1442                 self:remove(section)
1443         end
1444 end
1445
1446
1447
1448 --[[
1449 ListValue - A one-line value predefined in a list
1450         widget: The widget that will be used (select, radio)
1451 ]]--
1452 ListValue = class(AbstractValue)
1453
1454 function ListValue.__init__(self, ...)
1455         AbstractValue.__init__(self, ...)
1456         self.template  = "cbi/lvalue"
1457
1458         self.keylist = {}
1459         self.vallist = {}
1460         self.size   = 1
1461         self.widget = "select"
1462 end
1463
1464 function ListValue.value(self, key, val, ...)
1465         if luci.util.contains(self.keylist, key) then
1466                 return
1467         end
1468
1469         val = val or key
1470         table.insert(self.keylist, tostring(key))
1471         table.insert(self.vallist, tostring(val))
1472
1473         for i, deps in ipairs({...}) do
1474                 self.subdeps[#self.subdeps + 1] = {add = "-"..key, deps=deps}
1475         end
1476 end
1477
1478 function ListValue.validate(self, val)
1479         if luci.util.contains(self.keylist, val) then
1480                 return val
1481         else
1482                 return nil
1483         end
1484 end
1485
1486
1487
1488 --[[
1489 MultiValue - Multiple delimited values
1490         widget: The widget that will be used (select, checkbox)
1491         delimiter: The delimiter that will separate the values (default: " ")
1492 ]]--
1493 MultiValue = class(AbstractValue)
1494
1495 function MultiValue.__init__(self, ...)
1496         AbstractValue.__init__(self, ...)
1497         self.template = "cbi/mvalue"
1498
1499         self.keylist = {}
1500         self.vallist = {}
1501
1502         self.widget = "checkbox"
1503         self.delimiter = " "
1504 end
1505
1506 function MultiValue.render(self, ...)
1507         if self.widget == "select" and not self.size then
1508                 self.size = #self.vallist
1509         end
1510
1511         AbstractValue.render(self, ...)
1512 end
1513
1514 function MultiValue.value(self, key, val)
1515         if luci.util.contains(self.keylist, key) then
1516                 return
1517         end
1518
1519         val = val or key
1520         table.insert(self.keylist, tostring(key))
1521         table.insert(self.vallist, tostring(val))
1522 end
1523
1524 function MultiValue.valuelist(self, section)
1525         local val = self:cfgvalue(section)
1526
1527         if not(type(val) == "string") then
1528                 return {}
1529         end
1530
1531         return luci.util.split(val, self.delimiter)
1532 end
1533
1534 function MultiValue.validate(self, val)
1535         val = (type(val) == "table") and val or {val}
1536
1537         local result
1538
1539         for i, value in ipairs(val) do
1540                 if luci.util.contains(self.keylist, value) then
1541                         result = result and (result .. self.delimiter .. value) or value
1542                 end
1543         end
1544
1545         return result
1546 end
1547
1548
1549 StaticList = class(MultiValue)
1550
1551 function StaticList.__init__(self, ...)
1552         MultiValue.__init__(self, ...)
1553         self.cast = "table"
1554         self.valuelist = self.cfgvalue
1555
1556         if not self.override_scheme
1557          and self.map:get_scheme(self.section.sectiontype, self.option) then
1558                 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1559                 if self.value and vs.values and not self.override_values then
1560                         for k, v in pairs(vs.values) do
1561                                 self:value(k, v)
1562                         end
1563                 end
1564         end
1565 end
1566
1567 function StaticList.validate(self, value)
1568         value = (type(value) == "table") and value or {value}
1569
1570         local valid = {}
1571         for i, v in ipairs(value) do
1572                 if luci.util.contains(self.keylist, v) then
1573                         table.insert(valid, v)
1574                 end
1575         end
1576         return valid
1577 end
1578
1579
1580 DynamicList = class(AbstractValue)
1581
1582 function DynamicList.__init__(self, ...)
1583         AbstractValue.__init__(self, ...)
1584         self.template  = "cbi/dynlist"
1585         self.cast = "table"
1586         self.keylist = {}
1587         self.vallist = {}
1588 end
1589
1590 function DynamicList.value(self, key, val)
1591         val = val or key
1592         table.insert(self.keylist, tostring(key))
1593         table.insert(self.vallist, tostring(val))
1594 end
1595
1596 function DynamicList.write(self, ...)
1597         self.map.proceed = true
1598         return AbstractValue.write(self, ...)
1599 end
1600
1601 function DynamicList.formvalue(self, section)
1602         local value = AbstractValue.formvalue(self, section)
1603         value = (type(value) == "table") and value or {value}
1604
1605         local valid = {}
1606         for i, v in ipairs(value) do
1607                 if v and #v > 0
1608                  and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i)
1609                  and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1610                         table.insert(valid, v)
1611                 end
1612         end
1613
1614         return valid
1615 end
1616
1617
1618 --[[
1619 TextValue - A multi-line value
1620         rows:   Rows
1621 ]]--
1622 TextValue = class(AbstractValue)
1623
1624 function TextValue.__init__(self, ...)
1625         AbstractValue.__init__(self, ...)
1626         self.template  = "cbi/tvalue"
1627 end
1628
1629 --[[
1630 Button
1631 ]]--
1632 Button = class(AbstractValue)
1633
1634 function Button.__init__(self, ...)
1635         AbstractValue.__init__(self, ...)
1636         self.template  = "cbi/button"
1637         self.inputstyle = nil
1638         self.rmempty = true
1639 end
1640
1641
1642 FileUpload = class(AbstractValue)
1643
1644 function FileUpload.__init__(self, ...)
1645         AbstractValue.__init__(self, ...)
1646         self.template = "cbi/upload"
1647         if not self.map.upload_fields then
1648                 self.map.upload_fields = { self }
1649         else
1650                 self.map.upload_fields[#self.map.upload_fields+1] = self
1651         end
1652 end
1653
1654 function FileUpload.formcreated(self, section)
1655         return AbstractValue.formcreated(self, section) or
1656                 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1657                 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1658 end
1659
1660 function FileUpload.cfgvalue(self, section)
1661         local val = AbstractValue.cfgvalue(self, section)
1662         if val and fs.access(val) then
1663                 return val
1664         end
1665         return nil
1666 end
1667
1668 function FileUpload.formvalue(self, section)
1669         local val = AbstractValue.formvalue(self, section)
1670         if val then
1671                 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1672                    not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1673                 then
1674                         return val
1675                 end
1676                 fs.unlink(val)
1677                 self.value = nil
1678         end
1679         return nil
1680 end
1681
1682 function FileUpload.remove(self, section)
1683         local val = AbstractValue.formvalue(self, section)
1684         if val and fs.access(val) then fs.unlink(val) end
1685         return AbstractValue.remove(self, section)
1686 end
1687
1688
1689 FileBrowser = class(AbstractValue)
1690
1691 function FileBrowser.__init__(self, ...)
1692         AbstractValue.__init__(self, ...)
1693         self.template = "cbi/browser"
1694 end