Merge pull request #3458 from janh/vnstat2
[oweals/luci.git] / modules / luci-compat / luasrc / cbi / datatypes.lua
1 -- Copyright 2010 Jo-Philipp Wich <jow@openwrt.org>
2 -- Copyright 2017 Dan Luedtke <mail@danrl.com>
3 -- Licensed to the public under the Apache License 2.0.
4
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
10
11
12 module "luci.cbi.datatypes"
13
14
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
31
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
48
49 function neg(v, ...)
50         return _M['or'](v:gsub("^%s*!%s*", ""), ...)
51 end
52
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
65
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
74
75         return false
76 end
77
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
83
84         return false
85 end
86
87 function integer(val)
88         local n = tonumber(val)
89         if n ~= nil and math.floor(n) == n then
90                 return true
91         end
92
93         return false
94 end
95
96 function ufloat(val)
97         local n = tonumber(val)
98         return ( n ~= nil and n >= 0 )
99 end
100
101 function float(val)
102         return ( tonumber(val) ~= nil )
103 end
104
105 function ipaddr(val)
106         return ip4addr(val) or ip6addr(val)
107 end
108
109 function ip4addr(val)
110         if val then
111                 return ip.IPv4(val) and true or false
112         end
113
114         return false
115 end
116
117 function ip4prefix(val)
118         val = tonumber(val)
119         return ( val and val >= 0 and val <= 32 )
120 end
121
122 function ip6addr(val)
123         if val then
124                 return ip.IPv6(val) and true or false
125         end
126
127         return false
128 end
129
130 function ip6prefix(val)
131         val = tonumber(val)
132         return ( val and val >= 0 and val <= 128 )
133 end
134
135 function cidr(val)
136         return cidr4(val) or cidr6(val)
137 end
138
139 function cidr4(val)
140         local ip, mask = val:match("^([^/]+)/([^/]+)$")
141
142         return ip4addr(ip) and ip4prefix(mask)
143 end
144
145 function cidr6(val)
146         local ip, mask = val:match("^([^/]+)/([^/]+)$")
147
148         return ip6addr(ip) and ip6prefix(mask)
149 end
150
151 function ipnet4(val)
152         local ip, mask = val:match("^([^/]+)/([^/]+)$")
153
154         return ip4addr(ip) and ip4addr(mask)
155 end
156
157 function ipnet6(val)
158         local ip, mask = val:match("^([^/]+)/([^/]+)$")
159
160         return ip6addr(ip) and ip6addr(mask)
161 end
162
163 function ipmask(val)
164         return ipmask4(val) or ipmask6(val)
165 end
166
167 function ipmask4(val)
168         return cidr4(val) or ipnet4(val) or ip4addr(val)
169 end
170
171 function ipmask6(val)
172         return cidr6(val) or ipnet6(val) or ip6addr(val)
173 end
174
175 function ip6hostid(val)
176         if val == "eui64" or val == "random" then
177                 return true
178         else
179                 local addr = ip.IPv6(val)
180                 if addr and addr:prefix() == 128 and addr:lower("::1:0:0:0:0") then
181                         return true
182                 end
183         end
184
185         return false
186 end
187
188 function port(val)
189         val = tonumber(val)
190         return ( val and val >= 0 and val <= 65535 )
191 end
192
193 function portrange(val)
194         local p1, p2 = val:match("^(%d+)%-(%d+)$")
195         if p1 and p2 and port(p1) and port(p2) then
196                 return true
197         else
198                 return port(val)
199         end
200 end
201
202 function macaddr(val)
203         return ip.checkmac(val) and true or false
204 end
205
206 function hostname(val, strict)
207         if val and (#val < 254) and (
208            val:match("^[a-zA-Z_]+$") or
209            (val:match("^[a-zA-Z0-9_][a-zA-Z0-9_%-%.]*[a-zA-Z0-9]$") and
210             val:match("[^0-9%.]"))
211         ) then
212                 return (not strict or not val:match("^_"))
213         end
214         return false
215 end
216
217 function host(val, ipv4only)
218         return hostname(val) or ((ipv4only == 1) and ip4addr(val)) or ((not (ipv4only == 1)) and ipaddr(val))
219 end
220
221 function network(val)
222         return uciname(val) or host(val)
223 end
224
225 function hostport(val, ipv4only)
226         local h, p = val:match("^([^:]+):([^:]+)$")
227         return not not (h and p and host(h, ipv4only) and port(p))
228 end
229
230 function ip4addrport(val, bracket)
231         local h, p = val:match("^([^:]+):([^:]+)$")
232         return (h and p and ip4addr(h) and port(p))
233 end
234
235 function ip4addrport(val)
236         local h, p = val:match("^([^:]+):([^:]+)$")
237         return (h and p and ip4addr(h) and port(p))
238 end
239
240 function ipaddrport(val, bracket)
241         local h, p = val:match("^([^%[%]:]+):([^:]+)$")
242         if (h and p and ip4addr(h) and port(p)) then
243                 return true
244         elseif (bracket == 1) then
245                 h, p = val:match("^%[(.+)%]:([^:]+)$")
246                 if  (h and p and ip6addr(h) and port(p)) then
247                         return true
248                 end
249         end
250         h, p = val:match("^([^%[%]]+):([^:]+)$")
251         return (h and p and ip6addr(h) and port(p))
252 end
253
254 function wpakey(val)
255         if #val == 64 then
256                 return (val:match("^[a-fA-F0-9]+$") ~= nil)
257         else
258                 return (#val >= 8) and (#val <= 63)
259         end
260 end
261
262 function wepkey(val)
263         if val:sub(1, 2) == "s:" then
264                 val = val:sub(3)
265         end
266
267         if (#val == 10) or (#val == 26) then
268                 return (val:match("^[a-fA-F0-9]+$") ~= nil)
269         else
270                 return (#val == 5) or (#val == 13)
271         end
272 end
273
274 function hexstring(val)
275         if val then
276                 return (val:match("^[a-fA-F0-9]+$") ~= nil)
277         end
278         return false
279 end
280
281 function hex(val, maxbytes)
282         maxbytes = tonumber(maxbytes)
283         if val and maxbytes ~= nil then
284                 return ((val:match("^0x[a-fA-F0-9]+$") ~= nil) and (#val <= 2 + maxbytes * 2))
285         end
286         return false
287 end
288
289 function base64(val)
290         if val then
291                 return (val:match("^[a-zA-Z0-9/+]+=?=?$") ~= nil) and (math.fmod(#val, 4) == 0)
292         end
293         return false
294 end
295
296 function string(val)
297         return true             -- Everything qualifies as valid string
298 end
299
300 function directory(val, seen)
301         local s = fs.stat(val)
302         seen = seen or { }
303
304         if s and not seen[s.ino] then
305                 seen[s.ino] = true
306                 if s.type == "dir" then
307                         return true
308                 elseif s.type == "lnk" then
309                         return directory( fs.readlink(val), seen )
310                 end
311         end
312
313         return false
314 end
315
316 function file(val, seen)
317         local s = fs.stat(val)
318         seen = seen or { }
319
320         if s and not seen[s.ino] then
321                 seen[s.ino] = true
322                 if s.type == "reg" then
323                         return true
324                 elseif s.type == "lnk" then
325                         return file( fs.readlink(val), seen )
326                 end
327         end
328
329         return false
330 end
331
332 function device(val, seen)
333         local s = fs.stat(val)
334         seen = seen or { }
335
336         if s and not seen[s.ino] then
337                 seen[s.ino] = true
338                 if s.type == "chr" or s.type == "blk" then
339                         return true
340                 elseif s.type == "lnk" then
341                         return device( fs.readlink(val), seen )
342                 end
343         end
344
345         return false
346 end
347
348 function uciname(val)
349         return (val:match("^[a-zA-Z0-9_]+$") ~= nil)
350 end
351
352 function range(val, min, max)
353         val = tonumber(val)
354         min = tonumber(min)
355         max = tonumber(max)
356
357         if val ~= nil and min ~= nil and max ~= nil then
358                 return ((val >= min) and (val <= max))
359         end
360
361         return false
362 end
363
364 function min(val, min)
365         val = tonumber(val)
366         min = tonumber(min)
367
368         if val ~= nil and min ~= nil then
369                 return (val >= min)
370         end
371
372         return false
373 end
374
375 function max(val, max)
376         val = tonumber(val)
377         max = tonumber(max)
378
379         if val ~= nil and max ~= nil then
380                 return (val <= max)
381         end
382
383         return false
384 end
385
386 function rangelength(val, min, max)
387         val = tostring(val)
388         min = tonumber(min)
389         max = tonumber(max)
390
391         if val ~= nil and min ~= nil and max ~= nil then
392                 return ((#val >= min) and (#val <= max))
393         end
394
395         return false
396 end
397
398 function minlength(val, min)
399         val = tostring(val)
400         min = tonumber(min)
401
402         if val ~= nil and min ~= nil then
403                 return (#val >= min)
404         end
405
406         return false
407 end
408
409 function maxlength(val, max)
410         val = tostring(val)
411         max = tonumber(max)
412
413         if val ~= nil and max ~= nil then
414                 return (#val <= max)
415         end
416
417         return false
418 end
419
420 function phonedigit(val)
421         return (val:match("^[0-9%*#!%.]+$") ~= nil)
422 end
423
424 function timehhmmss(val)
425         return (val:match("^[0-6][0-9]:[0-6][0-9]:[0-6][0-9]$") ~= nil)
426 end
427
428 function dateyyyymmdd(val)
429         if val ~= nil then
430                 yearstr, monthstr, daystr = val:match("^(%d%d%d%d)-(%d%d)-(%d%d)$")
431                 if (yearstr == nil) or (monthstr == nil) or (daystr == nil) then
432                         return false;
433                 end
434                 year = tonumber(yearstr)
435                 month = tonumber(monthstr)
436                 day = tonumber(daystr)
437                 if (year == nil) or (month == nil) or (day == nil) then
438                         return false;
439                 end
440
441                 local days_in_month = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
442
443                 local function is_leap_year(year)
444                         return (year % 4 == 0) and ((year % 100 ~= 0) or (year % 400 == 0))
445                 end
446
447                 function get_days_in_month(month, year)
448                         if (month == 2) and is_leap_year(year) then
449                                 return 29
450                         else
451                                 return days_in_month[month]
452                         end
453                 end
454                 if (year < 2015) then
455                         return false
456                 end
457                 if ((month == 0) or (month > 12)) then
458                         return false
459                 end
460                 if ((day == 0) or (day > get_days_in_month(month, year))) then
461                         return false
462                 end
463                 return true
464         end
465         return false
466 end
467
468 function unique(val)
469         return true
470 end