55cdf8a74b93073935e5994ab8ce18e77ff06968
1 -- Copyright 2010 Jo-Philipp Wich <jow@openwrt.org>
2 -- Copyright 2017 Dan Luedtke <mail@danrl.com>
5 local fs = require "nixio.fs"
6 local ip = require "luci.ip"
7 local math = require "math"
8 local util = require "luci.util"
9 local tonumber, tostring, type, unpack, select = tonumber, tostring, type, unpack, select
12 module "luci.cbi.datatypes"
15 _M['or'] = function(v, ...)
16         local i
17         for i = 1, select('#', ...), 2 do
18                 local f = select(i, ...)
19                 local a = select(i+1, ...)
20                 if type(f) ~= "function" then
21                         if f == v then
22                                 return true
23                         end
24                         i = i - 1
25                 elseif f(v, unpack(a)) then
26                         return true
27                 end
28         end
29         return false
30 end
32 _M['and'] = function(v, ...)
33         local i
34         for i = 1, select('#', ...), 2 do
35                 local f = select(i, ...)
36                 local a = select(i+1, ...)
37                 if type(f) ~= "function" then
38                         if f ~= v then
39                                 return false
40                         end
41                         i = i - 1
42                 elseif not f(v, unpack(a)) then
43                         return false
44                 end
45         end
46         return true
47 end
49 function neg(v, ...)
50         return _M['or'](v:gsub("^%s*!%s*", ""), ...)
51 end
53 function list(v, subvalidator, subargs)
54         if type(subvalidator) ~= "function" then
55                 return false
56         end
57         local token
58         for token in v:gmatch("%S+") do
59                 if not subvalidator(token, unpack(subargs)) then
60                         return false
61                 end
62         end
63         return true
64 end
66 function bool(val)
67         if val == "1" or val == "yes" or val == "on" or val == "true" then
68                 return true
69         elseif val == "0" or val == "no" or val == "off" or val == "false" then
70                 return true
71         elseif val == "" or val == nil then
72                 return true
73         end
75         return false
76 end
78 function uinteger(val)
79         local n = tonumber(val)
80         if n ~= nil and math.floor(n) == n and n >= 0 then
81                 return true
82         end
84         return false
85 end
87 function integer(val)
88         local n = tonumber(val)
89         if n ~= nil and math.floor(n) == n then
90                 return true
91         end
93         return false
94 end
96 function ufloat(val)
97         local n = tonumber(val)
98         return ( n ~= nil and n >= 0 )
99 end
101 function float(val)
102         return ( tonumber(val) ~= nil )
103 end
107 end
110         if val then
111                 return ip.IPv4(val) and true or false
112         end
114         return false
115 end
117 function ip4prefix(val)
118         val = tonumber(val)
119         return ( val and val >= 0 and val <= 32 )
120 end
123         if val then
124                 return ip.IPv6(val) and true or false
125         end
127         return false
128 end
130 function ip6prefix(val)
131         val = tonumber(val)
132         return ( val and val >= 0 and val <= 128 )
133 end
135 function cidr4(val)
136         local ip, mask = val:match("^([^/]+)/([^/]+)\$")
139 end
141 function cidr6(val)
142         local ip, mask = val:match("^([^/]+)/([^/]+)\$")
145 end
147 function ipnet4(val)
148         local ip, mask = val:match("^([^/]+)/([^/]+)\$")
151 end
153 function ipnet6(val)
154         local ip, mask = val:match("^([^/]+)/([^/]+)\$")
157 end
161 end
164         return cidr4(val) or ipnet4(val) or ip4addr(val)
165 end
168         return cidr6(val) or ipnet6(val) or ip6addr(val)
169 end
171 function ip6hostid(val)
172         if val == "eui64" or val == "random" then
173                 return true
174         else
177                         return true
178                 end
179         end
181         return false
182 end
184 function port(val)
185         val = tonumber(val)
186         return ( val and val >= 0 and val <= 65535 )
187 end
189 function portrange(val)
190         local p1, p2 = val:match("^(%d+)%-(%d+)\$")
191         if p1 and p2 and port(p1) and port(p2) then
192                 return true
193         else
194                 return port(val)
195         end
196 end
199         return ip.checkmac(val) and true or false
200 end
202 function hostname(val)
203         if val and (#val < 254) and (
204            val:match("^[a-zA-Z_]+\$") or
205            (val:match("^[a-zA-Z0-9_][a-zA-Z0-9_%-%.]*[a-zA-Z0-9]\$") and
206             val:match("[^0-9%.]"))
207         ) then
208                 return true
209         end
210         return false
211 end
213 function host(val, ipv4only)
214         return hostname(val) or ((ipv4only == 1) and ip4addr(val)) or ((not (ipv4only == 1)) and ipaddr(val))
215 end
217 function network(val)
218         return uciname(val) or host(val)
219 end
221 function hostport(val, ipv4only)
222         local h, p = val:match("^([^:]+):([^:]+)\$")
223         return not not (h and p and host(h, ipv4only) and port(p))
224 end
227         local h, p = val:match("^([^:]+):([^:]+)\$")
228         return (h and p and ip4addr(h) and port(p))
229 end
232         local h, p = val:match("^([^:]+):([^:]+)\$")
233         return (h and p and ip4addr(h) and port(p))
234 end
237         local h, p = val:match("^([^%[%]:]+):([^:]+)\$")
238         if (h and p and ip4addr(h) and port(p)) then
239                 return true
240         elseif (bracket == 1) then
241                 h, p = val:match("^%[(.+)%]:([^:]+)\$")
242                 if  (h and p and ip6addr(h) and port(p)) then
243                         return true
244                 end
245         end
246         h, p = val:match("^([^%[%]]+):([^:]+)\$")
247         return (h and p and ip6addr(h) and port(p))
248 end
250 function wpakey(val)
251         if #val == 64 then
252                 return (val:match("^[a-fA-F0-9]+\$") ~= nil)
253         else
254                 return (#val >= 8) and (#val <= 63)
255         end
256 end
258 function wepkey(val)
259         if val:sub(1, 2) == "s:" then
260                 val = val:sub(3)
261         end
263         if (#val == 10) or (#val == 26) then
264                 return (val:match("^[a-fA-F0-9]+\$") ~= nil)
265         else
266                 return (#val == 5) or (#val == 13)
267         end
268 end
270 function hexstring(val)
271         if val then
272                 return (val:match("^[a-fA-F0-9]+\$") ~= nil)
273         end
274         return false
275 end
277 function hex(val, maxbytes)
278         maxbytes = tonumber(maxbytes)
279         if val and maxbytes ~= nil then
280                 return ((val:match("^0x[a-fA-F0-9]+\$") ~= nil) and (#val <= 2 + maxbytes * 2))
281         end
282         return false
283 end
285 function base64(val)
286         if val then
287                 return (val:match("^[a-zA-Z0-9/+]+=?=?\$") ~= nil) and (math.fmod(#val, 4) == 0)
288         end
289         return false
290 end
292 function string(val)
293         return true             -- Everything qualifies as valid string
294 end
296 function directory(val, seen)
297         local s = fs.stat(val)
298         seen = seen or { }
300         if s and not seen[s.ino] then
301                 seen[s.ino] = true
302                 if s.type == "dir" then
303                         return true
304                 elseif s.type == "lnk" then
306                 end
307         end
309         return false
310 end
312 function file(val, seen)
313         local s = fs.stat(val)
314         seen = seen or { }
316         if s and not seen[s.ino] then
317                 seen[s.ino] = true
318                 if s.type == "reg" then
319                         return true
320                 elseif s.type == "lnk" then
322                 end
323         end
325         return false
326 end
328 function device(val, seen)
329         local s = fs.stat(val)
330         seen = seen or { }
332         if s and not seen[s.ino] then
333                 seen[s.ino] = true
334                 if s.type == "chr" or s.type == "blk" then
335                         return true
336                 elseif s.type == "lnk" then
338                 end
339         end
341         return false
342 end
344 function uciname(val)
345         return (val:match("^[a-zA-Z0-9_]+\$") ~= nil)
346 end
348 function range(val, min, max)
349         val = tonumber(val)
350         min = tonumber(min)
351         max = tonumber(max)
353         if val ~= nil and min ~= nil and max ~= nil then
354                 return ((val >= min) and (val <= max))
355         end
357         return false
358 end
360 function min(val, min)
361         val = tonumber(val)
362         min = tonumber(min)
364         if val ~= nil and min ~= nil then
365                 return (val >= min)
366         end
368         return false
369 end
371 function max(val, max)
372         val = tonumber(val)
373         max = tonumber(max)
375         if val ~= nil and max ~= nil then
376                 return (val <= max)
377         end
379         return false
380 end
382 function rangelength(val, min, max)
383         val = tostring(val)
384         min = tonumber(min)
385         max = tonumber(max)
387         if val ~= nil and min ~= nil and max ~= nil then
388                 return ((#val >= min) and (#val <= max))
389         end
391         return false
392 end
394 function minlength(val, min)
395         val = tostring(val)
396         min = tonumber(min)
398         if val ~= nil and min ~= nil then
399                 return (#val >= min)
400         end
402         return false
403 end
405 function maxlength(val, max)
406         val = tostring(val)
407         max = tonumber(max)
409         if val ~= nil and max ~= nil then
410                 return (#val <= max)
411         end
413         return false
414 end
416 function phonedigit(val)
417         return (val:match("^[0-9\*#!%.]+\$") ~= nil)
418 end
420 function timehhmmss(val)
421         return (val:match("^[0-6][0-9]:[0-6][0-9]:[0-6][0-9]\$") ~= nil)
422 end
424 function dateyyyymmdd(val)
425         if val ~= nil then
426                 yearstr, monthstr, daystr = val:match("^(%d%d%d%d)-(%d%d)-(%d%d)\$")
427                 if (yearstr == nil) or (monthstr == nil) or (daystr == nil) then
428                         return false;
429                 end
430                 year = tonumber(yearstr)
431                 month = tonumber(monthstr)
432                 day = tonumber(daystr)
433                 if (year == nil) or (month == nil) or (day == nil) then
434                         return false;
435                 end
437                 local days_in_month = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
439                 local function is_leap_year(year)
440                         return (year % 4 == 0) and ((year % 100 ~= 0) or (year % 400 == 0))
441                 end
443                 function get_days_in_month(month, year)
444                         if (month == 2) and is_leap_year(year) then
445                                 return 29
446                         else
447                                 return days_in_month[month]
448                         end
449                 end
450                 if (year < 2015) then
451                         return false
452                 end
453                 if ((month == 0) or (month > 12)) then
454                         return false
455                 end
456                 if ((day == 0) or (day > get_days_in_month(month, year))) then
457                         return false
458                 end
459                 return true
460         end
461         return false
462 end