c1cf01f9cdac26daab1dcdc97d050a43d2a1835a
[project/luci.git] / modules / luci-base / 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