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