Several escaping fixes
[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 require("luci.util")
31 require("luci.http")
32 require("luci.model.uci")
33
34 local uci = luci.model.uci
35 local class = luci.util.class
36 local instanceof = luci.util.instanceof
37
38 FORM_NODATA = 0
39 FORM_VALID = 1
40 FORM_INVALID = -1
41
42 CREATE_PREFIX = "cbi.cts."
43 REMOVE_PREFIX = "cbi.rts."
44
45 -- Loads a CBI map from given file, creating an environment and returns it
46 function load(cbimap, ...)
47 require("luci.fs")
48 require("luci.i18n")
49 require("luci.config")
50 require("luci.util")
51
52 local cbidir = luci.util.libpath() .. "/model/cbi/"
53 local func, err = loadfile(cbidir..cbimap..".lua")
54
55 if not func then
56 return nil
57 end
58
59 luci.i18n.loadc("cbi")
60
61 luci.util.resfenv(func)
62 luci.util.updfenv(func, luci.cbi)
63 luci.util.extfenv(func, "translate", luci.i18n.translate)
64 luci.util.extfenv(func, "translatef", luci.i18n.translatef)
65 luci.util.extfenv(func, "arg", {...})
66
67 local maps = {func()}
68
69 for i, map in ipairs(maps) do
70 if not instanceof(map, Node) then
71 error("CBI map returns no valid map object!")
72 return nil
73 end
74 end
75
76 return maps
77 end
78
79 -- Node pseudo abstract class
80 Node = class()
81
82 function Node.__init__(self, title, description)
83 self.children = {}
84 self.title = title or ""
85 self.description = description or ""
86 self.template = "cbi/node"
87 end
88
89 -- i18n helper
90 function Node._i18n(self, config, section, option, title, description)
91
92 -- i18n loaded?
93 if type(luci.i18n) == "table" then
94
95 local key = config and config:gsub("[^%w]+", "") or ""
96
97 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
98 if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
99
100 self.title = title or luci.i18n.translate( key, option or section or config )
101 self.description = description or luci.i18n.translate( key .. "_desc", "" )
102 end
103 end
104
105 -- Append child nodes
106 function Node.append(self, obj)
107 table.insert(self.children, obj)
108 end
109
110 -- Parse this node and its children
111 function Node.parse(self, ...)
112 for k, child in ipairs(self.children) do
113 child:parse(...)
114 end
115 end
116
117 -- Render this node
118 function Node.render(self, scope)
119 scope = scope or {}
120 scope.self = self
121
122 luci.template.render(self.template, scope)
123 end
124
125 -- Render the children
126 function Node.render_children(self, ...)
127 for k, node in ipairs(self.children) do
128 node:render(...)
129 end
130 end
131
132
133 --[[
134 A simple template element
135 ]]--
136 Template = class(Node)
137
138 function Template.__init__(self, template)
139 Node.__init__(self)
140 self.template = template
141 end
142
143 function Template.render(self)
144 luci.template.render(self.template, {self=self})
145 end
146
147
148 --[[
149 Map - A map describing a configuration file
150 ]]--
151 Map = class(Node)
152
153 function Map.__init__(self, config, ...)
154 Node.__init__(self, ...)
155 Node._i18n(self, config, nil, nil, ...)
156
157 self.config = config
158 self.parsechain = {self.config}
159 self.template = "cbi/map"
160 if not uci.load(self.config) then
161 error("Unable to read UCI data: " .. self.config)
162 end
163 end
164
165
166 -- Chain foreign config
167 function Map.chain(self, config)
168 table.insert(self.parsechain, config)
169 end
170
171 -- Use optimized UCI writing
172 function Map.parse(self, ...)
173 Node.parse(self, ...)
174 for i, config in ipairs(self.parsechain) do
175 uci.save(config)
176 end
177 if luci.http.formvalue("cbi.apply") then
178 for i, config in ipairs(self.parsechain) do
179 uci.commit(config)
180 if luci.config.uci_oncommit and luci.config.uci_oncommit[config] then
181 luci.util.exec(luci.config.uci_oncommit[config])
182 end
183
184 -- Refresh data because commit changes section names
185 uci.unload(config)
186 uci.load(config)
187 end
188
189 -- Reparse sections
190 Node.parse(self, ...)
191
192 end
193 for i, config in ipairs(self.parsechain) do
194 uci.unload(config)
195 end
196 end
197
198 -- Creates a child section
199 function Map.section(self, class, ...)
200 if instanceof(class, AbstractSection) then
201 local obj = class(self, ...)
202 self:append(obj)
203 return obj
204 else
205 error("class must be a descendent of AbstractSection")
206 end
207 end
208
209 -- UCI add
210 function Map.add(self, sectiontype)
211 return uci.add(self.config, sectiontype)
212 end
213
214 -- UCI set
215 function Map.set(self, section, option, value)
216 if option then
217 return uci.set(self.config, section, option, value)
218 else
219 return uci.set(self.config, section, value)
220 end
221 end
222
223 -- UCI del
224 function Map.del(self, section, option)
225 if option then
226 return uci.delete(self.config, section, option)
227 else
228 return uci.delete(self.config, section)
229 end
230 end
231
232 -- UCI get
233 function Map.get(self, section, option)
234 if not section then
235 return uci.get_all(self.config)
236 elseif option then
237 return uci.get(self.config, section, option)
238 else
239 return uci.get_all(self.config, section)
240 end
241 end
242
243 -- UCI stateget
244 function Map.stateget(self, section, option)
245 return uci.get_statevalue(self.config, section, option)
246 end
247
248
249 --[[
250 Page - A simple node
251 ]]--
252
253 Page = class(Node)
254 Page.__init__ = Node.__init__
255 Page.parse = function() end
256
257
258 --[[
259 SimpleForm - A Simple non-UCI form
260 ]]--
261 SimpleForm = class(Node)
262
263 function SimpleForm.__init__(self, config, title, description, data)
264 Node.__init__(self, title, description)
265 self.config = config
266 self.data = data or {}
267 self.template = "cbi/simpleform"
268 self.dorender = true
269 end
270
271 function SimpleForm.parse(self, ...)
272 if luci.http.formvalue("cbi.submit") then
273 Node.parse(self, 1, ...)
274 end
275
276 local valid = true
277 for k, j in ipairs(self.children) do
278 for i, v in ipairs(j.children) do
279 valid = valid
280 and (not v.tag_missing or not v.tag_missing[1])
281 and (not v.tag_invalid or not v.tag_invalid[1])
282 end
283 end
284
285 local state =
286 not luci.http.formvalue("cbi.submit") and 0
287 or valid and 1
288 or -1
289
290 self.dorender = self:handle(state, self.data) ~= false
291 end
292
293 function SimpleForm.render(self, ...)
294 if self.dorender then
295 Node.render(self, ...)
296 end
297 end
298
299 function SimpleForm.section(self, class, ...)
300 if instanceof(class, AbstractSection) then
301 local obj = class(self, ...)
302 self:append(obj)
303 return obj
304 else
305 error("class must be a descendent of AbstractSection")
306 end
307 end
308
309 -- Creates a child field
310 function SimpleForm.field(self, class, ...)
311 local section
312 for k, v in ipairs(self.children) do
313 if instanceof(v, SimpleSection) then
314 section = v
315 break
316 end
317 end
318 if not section then
319 section = self:section(SimpleSection)
320 end
321
322 if instanceof(class, AbstractValue) then
323 local obj = class(self, ...)
324 obj.track_missing = true
325 section:append(obj)
326 return obj
327 else
328 error("class must be a descendent of AbstractValue")
329 end
330 end
331
332 function SimpleForm.set(self, section, option, value)
333 self.data[option] = value
334 end
335
336
337 function SimpleForm.del(self, section, option)
338 self.data[option] = nil
339 end
340
341
342 function SimpleForm.get(self, section, option)
343 return self.data[option]
344 end
345
346
347
348 --[[
349 AbstractSection
350 ]]--
351 AbstractSection = class(Node)
352
353 function AbstractSection.__init__(self, map, sectiontype, ...)
354 Node.__init__(self, ...)
355 self.sectiontype = sectiontype
356 self.map = map
357 self.config = map.config
358 self.optionals = {}
359 self.defaults = {}
360
361 self.optional = true
362 self.addremove = false
363 self.dynamic = false
364 end
365
366 -- Appends a new option
367 function AbstractSection.option(self, class, option, ...)
368 if instanceof(class, AbstractValue) then
369 local obj = class(self.map, option, ...)
370
371 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
372
373 self:append(obj)
374 return obj
375 else
376 error("class must be a descendent of AbstractValue")
377 end
378 end
379
380 -- Parse optional options
381 function AbstractSection.parse_optionals(self, section)
382 if not self.optional then
383 return
384 end
385
386 self.optionals[section] = {}
387
388 local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
389 for k,v in ipairs(self.children) do
390 if v.optional and not v:cfgvalue(section) then
391 if field == v.option then
392 field = nil
393 else
394 table.insert(self.optionals[section], v)
395 end
396 end
397 end
398
399 if field and #field > 0 and self.dynamic then
400 self:add_dynamic(field)
401 end
402 end
403
404 -- Add a dynamic option
405 function AbstractSection.add_dynamic(self, field, optional)
406 local o = self:option(Value, field, field)
407 o.optional = optional
408 end
409
410 -- Parse all dynamic options
411 function AbstractSection.parse_dynamic(self, section)
412 if not self.dynamic then
413 return
414 end
415
416 local arr = luci.util.clone(self:cfgvalue(section))
417 local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
418 for k, v in pairs(form) do
419 arr[k] = v
420 end
421
422 for key,val in pairs(arr) do
423 local create = true
424
425 for i,c in ipairs(self.children) do
426 if c.option == key then
427 create = false
428 end
429 end
430
431 if create and key:sub(1, 1) ~= "." then
432 self:add_dynamic(key, true)
433 end
434 end
435 end
436
437 -- Returns the section's UCI table
438 function AbstractSection.cfgvalue(self, section)
439 return self.map:get(section)
440 end
441
442 -- Removes the section
443 function AbstractSection.remove(self, section)
444 return self.map:del(section)
445 end
446
447 -- Creates the section
448 function AbstractSection.create(self, section)
449 local stat
450
451 if section then
452 stat = self.map:set(section, nil, self.sectiontype)
453 else
454 section = self.map:add(self.sectiontype)
455 stat = section
456 end
457
458 if stat then
459 for k,v in pairs(self.children) do
460 if v.default then
461 self.map:set(section, v.option, v.default)
462 end
463 end
464
465 for k,v in pairs(self.defaults) do
466 self.map:set(section, k, v)
467 end
468 end
469
470 return stat
471 end
472
473
474 SimpleSection = class(AbstractSection)
475
476 function SimpleSection.__init__(self, form, ...)
477 AbstractSection.__init__(self, form, nil, ...)
478 self.template = "cbi/nullsection"
479 end
480
481
482 Table = class(AbstractSection)
483
484 function Table.__init__(self, form, data, ...)
485 local datasource = {}
486 datasource.config = "table"
487 self.data = data
488
489 function datasource.get(self, section, option)
490 return data[section][option]
491 end
492
493 AbstractSection.__init__(self, datasource, "table", ...)
494 self.template = "cbi/tblsection"
495 end
496
497 function Table.cfgsections(self)
498 local sections = {}
499
500 for i, v in pairs(self.data) do
501 table.insert(sections, i)
502 end
503
504 return sections
505 end
506
507
508
509 --[[
510 NamedSection - A fixed configuration section defined by its name
511 ]]--
512 NamedSection = class(AbstractSection)
513
514 function NamedSection.__init__(self, map, section, type, ...)
515 AbstractSection.__init__(self, map, type, ...)
516 Node._i18n(self, map.config, section, nil, ...)
517
518 self.template = "cbi/nsection"
519 self.section = section
520 self.addremove = false
521 end
522
523 function NamedSection.parse(self)
524 local s = self.section
525 local active = self:cfgvalue(s)
526
527
528 if self.addremove then
529 local path = self.config.."."..s
530 if active then -- Remove the section
531 if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
532 return
533 end
534 else -- Create and apply default values
535 if luci.http.formvalue("cbi.cns."..path) then
536 self:create(s)
537 return
538 end
539 end
540 end
541
542 if active then
543 AbstractSection.parse_dynamic(self, s)
544 if luci.http.formvalue("cbi.submit") then
545 Node.parse(self, s)
546 end
547 AbstractSection.parse_optionals(self, s)
548 end
549 end
550
551
552 --[[
553 TypedSection - A (set of) configuration section(s) defined by the type
554 addremove: Defines whether the user can add/remove sections of this type
555 anonymous: Allow creating anonymous sections
556 validate: a validation function returning nil if the section is invalid
557 ]]--
558 TypedSection = class(AbstractSection)
559
560 function TypedSection.__init__(self, map, type, ...)
561 AbstractSection.__init__(self, map, type, ...)
562 Node._i18n(self, map.config, type, nil, ...)
563
564 self.template = "cbi/tsection"
565 self.deps = {}
566
567 self.anonymous = false
568 end
569
570 -- Return all matching UCI sections for this TypedSection
571 function TypedSection.cfgsections(self)
572 local sections = {}
573 uci.foreach(self.map.config, self.sectiontype,
574 function (section)
575 if self:checkscope(section[".name"]) then
576 table.insert(sections, section[".name"])
577 end
578 end)
579
580 return sections
581 end
582
583 -- Limits scope to sections that have certain option => value pairs
584 function TypedSection.depends(self, option, value)
585 table.insert(self.deps, {option=option, value=value})
586 end
587
588 function TypedSection.parse(self)
589 if self.addremove then
590 -- Create
591 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
592 local name = luci.http.formvalue(crval)
593 if self.anonymous then
594 if name then
595 self:create()
596 end
597 else
598 if name then
599 -- Ignore if it already exists
600 if self:cfgvalue(name) then
601 name = nil;
602 end
603
604 name = self:checkscope(name)
605
606 if not name then
607 self.err_invalid = true
608 end
609
610 if name and name:len() > 0 then
611 self:create(name)
612 end
613 end
614 end
615
616 -- Remove
617 crval = REMOVE_PREFIX .. self.config
618 name = luci.http.formvaluetable(crval)
619 for k,v in pairs(name) do
620 if self:cfgvalue(k) and self:checkscope(k) then
621 self:remove(k)
622 end
623 end
624 end
625
626 for i, k in ipairs(self:cfgsections()) do
627 AbstractSection.parse_dynamic(self, k)
628 if luci.http.formvalue("cbi.submit") then
629 Node.parse(self, k)
630 end
631 AbstractSection.parse_optionals(self, k)
632 end
633 end
634
635 -- Verifies scope of sections
636 function TypedSection.checkscope(self, section)
637 -- Check if we are not excluded
638 if self.filter and not self:filter(section) then
639 return nil
640 end
641
642 -- Check if at least one dependency is met
643 if #self.deps > 0 and self:cfgvalue(section) then
644 local stat = false
645
646 for k, v in ipairs(self.deps) do
647 if self:cfgvalue(section)[v.option] == v.value then
648 stat = true
649 end
650 end
651
652 if not stat then
653 return nil
654 end
655 end
656
657 return self:validate(section)
658 end
659
660
661 -- Dummy validate function
662 function TypedSection.validate(self, section)
663 return section
664 end
665
666
667 --[[
668 AbstractValue - An abstract Value Type
669 null: Value can be empty
670 valid: A function returning the value if it is valid otherwise nil
671 depends: A table of option => value pairs of which one must be true
672 default: The default value
673 size: The size of the input fields
674 rmempty: Unset value if empty
675 optional: This value is optional (see AbstractSection.optionals)
676 ]]--
677 AbstractValue = class(Node)
678
679 function AbstractValue.__init__(self, map, option, ...)
680 Node.__init__(self, ...)
681 self.option = option
682 self.map = map
683 self.config = map.config
684 self.tag_invalid = {}
685 self.tag_missing = {}
686 self.deps = {}
687
688 self.track_missing = false
689 self.rmempty = false
690 self.default = nil
691 self.size = nil
692 self.optional = false
693 self.stateful = false
694 end
695
696 -- Add a dependencie to another section field
697 function AbstractValue.depends(self, field, value)
698 table.insert(self.deps, {field=field, value=value})
699 end
700
701 -- Return whether this object should be created
702 function AbstractValue.formcreated(self, section)
703 local key = "cbi.opt."..self.config.."."..section
704 return (luci.http.formvalue(key) == self.option)
705 end
706
707 -- Returns the formvalue for this object
708 function AbstractValue.formvalue(self, section)
709 local key = "cbid."..self.map.config.."."..section.."."..self.option
710 return luci.http.formvalue(key)
711 end
712
713 function AbstractValue.additional(self, value)
714 self.optional = value
715 end
716
717 function AbstractValue.mandatory(self, value)
718 self.rmempty = not value
719 end
720
721 function AbstractValue.parse(self, section)
722 local fvalue = self:formvalue(section)
723 local cvalue = self:cfgvalue(section)
724
725 if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI
726 fvalue = self:transform(self:validate(fvalue, section))
727 if not fvalue then
728 self.tag_invalid[section] = true
729 end
730 if fvalue and not (fvalue == cvalue) then
731 self:write(section, fvalue)
732 end
733 else -- Unset the UCI or error
734 if self.rmempty or self.optional then
735 self:remove(section)
736 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
737 self.tag_missing[section] = true
738 end
739 end
740 end
741
742 -- Render if this value exists or if it is mandatory
743 function AbstractValue.render(self, s, scope)
744 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
745 scope = scope or {}
746 scope.section = s
747 scope.cbid = "cbid." .. self.config ..
748 "." .. s ..
749 "." .. self.option
750
751 scope.ifattr = function(cond,key,val)
752 if cond then
753 return string.format(
754 ' %s="%s"', tostring(key),
755 luci.util.pcdata(tostring( val
756 or scope[key]
757 or (type(self[key]) ~= "function" and self[key])
758 or "" ))
759 )
760 else
761 return ''
762 end
763 end
764
765 scope.attr = function(...)
766 return scope.ifattr( true, ... )
767 end
768
769 Node.render(self, scope)
770 end
771 end
772
773 -- Return the UCI value of this object
774 function AbstractValue.cfgvalue(self, section)
775 return self.stateful
776 and self.map:stateget(section, self.option)
777 or self.map:get(section, self.option)
778 end
779
780 -- Validate the form value
781 function AbstractValue.validate(self, value)
782 return value
783 end
784
785 AbstractValue.transform = AbstractValue.validate
786
787
788 -- Write to UCI
789 function AbstractValue.write(self, section, value)
790 return self.map:set(section, self.option, value)
791 end
792
793 -- Remove from UCI
794 function AbstractValue.remove(self, section)
795 return self.map:del(section, self.option)
796 end
797
798
799
800
801 --[[
802 Value - A one-line value
803 maxlength: The maximum length
804 ]]--
805 Value = class(AbstractValue)
806
807 function Value.__init__(self, ...)
808 AbstractValue.__init__(self, ...)
809 self.template = "cbi/value"
810 self.keylist = {}
811 self.vallist = {}
812 end
813
814 function Value.value(self, key, val)
815 val = val or key
816 table.insert(self.keylist, tostring(key))
817 table.insert(self.vallist, tostring(val))
818 end
819
820
821 -- DummyValue - This does nothing except being there
822 DummyValue = class(AbstractValue)
823
824 function DummyValue.__init__(self, map, ...)
825 AbstractValue.__init__(self, map, ...)
826 self.template = "cbi/dvalue"
827 self.value = nil
828 end
829
830 function DummyValue.parse(self)
831
832 end
833
834
835 --[[
836 Flag - A flag being enabled or disabled
837 ]]--
838 Flag = class(AbstractValue)
839
840 function Flag.__init__(self, ...)
841 AbstractValue.__init__(self, ...)
842 self.template = "cbi/fvalue"
843
844 self.enabled = "1"
845 self.disabled = "0"
846 end
847
848 -- A flag can only have two states: set or unset
849 function Flag.parse(self, section)
850 local fvalue = self:formvalue(section)
851
852 if fvalue then
853 fvalue = self.enabled
854 else
855 fvalue = self.disabled
856 end
857
858 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
859 if not(fvalue == self:cfgvalue(section)) then
860 self:write(section, fvalue)
861 end
862 else
863 self:remove(section)
864 end
865 end
866
867
868
869 --[[
870 ListValue - A one-line value predefined in a list
871 widget: The widget that will be used (select, radio)
872 ]]--
873 ListValue = class(AbstractValue)
874
875 function ListValue.__init__(self, ...)
876 AbstractValue.__init__(self, ...)
877 self.template = "cbi/lvalue"
878 self.keylist = {}
879 self.vallist = {}
880
881 self.size = 1
882 self.widget = "select"
883 end
884
885 function ListValue.value(self, key, val)
886 val = val or key
887 table.insert(self.keylist, tostring(key))
888 table.insert(self.vallist, tostring(val))
889 end
890
891 function ListValue.validate(self, val)
892 if luci.util.contains(self.keylist, val) then
893 return val
894 else
895 return nil
896 end
897 end
898
899
900
901 --[[
902 MultiValue - Multiple delimited values
903 widget: The widget that will be used (select, checkbox)
904 delimiter: The delimiter that will separate the values (default: " ")
905 ]]--
906 MultiValue = class(AbstractValue)
907
908 function MultiValue.__init__(self, ...)
909 AbstractValue.__init__(self, ...)
910 self.template = "cbi/mvalue"
911 self.keylist = {}
912 self.vallist = {}
913
914 self.widget = "checkbox"
915 self.delimiter = " "
916 end
917
918 function MultiValue.render(self, ...)
919 if self.widget == "select" and not self.size then
920 self.size = #self.vallist
921 end
922
923 AbstractValue.render(self, ...)
924 end
925
926 function MultiValue.value(self, key, val)
927 val = val or key
928 table.insert(self.keylist, tostring(key))
929 table.insert(self.vallist, tostring(val))
930 end
931
932 function MultiValue.valuelist(self, section)
933 local val = self:cfgvalue(section)
934
935 if not(type(val) == "string") then
936 return {}
937 end
938
939 return luci.util.split(val, self.delimiter)
940 end
941
942 function MultiValue.validate(self, val)
943 val = (type(val) == "table") and val or {val}
944
945 local result
946
947 for i, value in ipairs(val) do
948 if luci.util.contains(self.keylist, value) then
949 result = result and (result .. self.delimiter .. value) or value
950 end
951 end
952
953 return result
954 end
955
956 --[[
957 TextValue - A multi-line value
958 rows: Rows
959 ]]--
960 TextValue = class(AbstractValue)
961
962 function TextValue.__init__(self, ...)
963 AbstractValue.__init__(self, ...)
964 self.template = "cbi/tvalue"
965 end