luci-base: sync translations
[project/luci.git] / modules / luci-base / luasrc / cbi / datatypes.lua
1 -- Copyright 2010 Jo-Philipp Wich <jow@openwrt.org>
2 -- Licensed to the public under the Apache License 2.0.
3
4 local fs = require "nixio.fs"
5 local ip = require "luci.ip"
6 local math = require "math"
7 local util = require "luci.util"
8 local tonumber, tostring, type, unpack, select = tonumber, tostring, type, unpack, select
9
10
11 module "luci.cbi.datatypes"
12
13
14 _M['or'] = function(v, ...)
15         local i
16         for i = 1, select('#', ...), 2 do
17                 local f = select(i, ...)
18                 local a = select(i+1, ...)
19                 if type(f) ~= "function" then
20                         if f == v then
21                                 return true
22                         end
23                         i = i - 1
24                 elseif f(v, unpack(a)) then
25                         return true
26                 end
27         end
28         return false
29 end
30
31 _M['and'] = function(v, ...)
32         local i
33         for i = 1, select('#', ...), 2 do
34                 local f = select(i, ...)
35                 local a = select(i+1, ...)
36                 if type(f) ~= "function" then
37                         if f ~= v then
38                                 return false
39                         end
40                         i = i - 1
41                 elseif not f(v, unpack(a)) then
42                         return false
43                 end
44         end
45         return true
46 end
47
48 function neg(v, ...)
49         return _M['or'](v:gsub("^%s*!%s*", ""), ...)
50 end
51
52 function list(v, subvalidator, subargs)
53         if type(subvalidator) ~= "function" then
54                 return false
55         end
56         local token
57         for token in v:gmatch("%S+") do
58                 if not subvalidator(token, unpack(subargs)) then
59                         return false
60                 end
61         end
62         return true
63 end
64
65 function bool(val)
66         if val == "1" or val == "yes" or val == "on" or val == "true" then
67                 return true
68         elseif val == "0" or val == "no" or val == "off" or val == "false" then
69                 return true
70         elseif val == "" or val == nil then
71                 return true
72         end
73
74         return false
75 end
76
77 function uinteger(val)
78         local n = tonumber(val)
79         if n ~= nil and math.floor(n) == n and n >= 0 then
80                 return true
81         end
82
83         return false
84 end
85
86 function integer(val)
87         local n = tonumber(val)
88         if n ~= nil and math.floor(n) == n then
89                 return true
90         end
91
92         return false
93 end
94
95 function ufloat(val)
96         local n = tonumber(val)
97         return ( n ~= nil and n >= 0 )
98 end
99
100 function float(val)
101         return ( tonumber(val) ~= nil )
102 end
103
104 function ipaddr(val)
105         return ip4addr(val) or ip6addr(val)
106 end
107
108 function ip4addr(val)
109         if val then
110                 return ip.IPv4(val) and true or false
111         end
112
113         return false
114 end
115
116 function ip4prefix(val)
117         val = tonumber(val)
118         return ( val and val >= 0 and val <= 32 )
119 end
120
121 function ip6addr(val)
122         if val then
123                 return ip.IPv6(val) and true or false
124         end
125
126         return false
127 end
128
129 function ip6prefix(val)
130         val = tonumber(val)
131         return ( val and val >= 0 and val <= 128 )
132 end
133
134 function ipmask(val)
135         return ipmask4(val) or ipmask6(val)
136 end
137
138 function ipmask4(val)
139         local ip, mask = val:match("^([^/]+)/([^/]+)$")
140         local bits = tonumber(mask)
141
142         if bits and (bits < 0 or bits > 32) then
143                 return false
144         end
145
146         if not bits and mask and not ip4addr(mask) then
147                 return false
148         end
149
150         return ip4addr(ip or val)
151 end
152
153 function ipmask6(val)
154         local ip, mask = val:match("^([^/]+)/([^/]+)$")
155         local bits = tonumber(mask)
156
157         if bits and (bits < 0 or bits > 128) then
158                 return false
159         end
160
161         if not bits and mask and not ip6addr(mask) then
162                 return false
163         end
164
165         return ip6addr(ip or val)
166 end
167
168 function port(val)
169         val = tonumber(val)
170         return ( val and val >= 0 and val <= 65535 )
171 end
172
173 function portrange(val)
174         local p1, p2 = val:match("^(%d+)%-(%d+)$")
175         if p1 and p2 and port(p1) and port(p2) then
176                 return true
177         else
178                 return port(val)
179         end
180 end
181
182 function macaddr(val)
183         if val and val:match(
184                 "^[a-fA-F0-9]+:[a-fA-F0-9]+:[a-fA-F0-9]+:" ..
185                  "[a-fA-F0-9]+:[a-fA-F0-9]+:[a-fA-F0-9]+$"
186         ) then
187                 local parts = util.split( val, ":" )
188
189                 for i = 1,6 do
190                         parts[i] = tonumber( parts[i], 16 )
191                         if parts[i] < 0 or parts[i] > 255 then
192                                 return false
193                         end
194                 end
195
196                 return true
197         end
198
199         return false
200 end
201
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
212
213 function host(val, ipv4only)
214         return hostname(val) or ((ipv4only == 1) and ip4addr(val)) or ((not (ipv4only == 1)) and ipaddr(val))
215 end
216
217 function network(val)
218         return uciname(val) or host(val)
219 end
220
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
225
226 function ip4addrport(val, bracket)
227         local h, p = val:match("^([^:]+):([^:]+)$")
228         return (h and p and ip4addr(h) and port(p))
229 end
230
231 function ip4addrport(val)
232         local h, p = val:match("^([^:]+):([^:]+)$")
233         return (h and p and ip4addr(h) and port(p))
234 end
235
236 function ipaddrport(val, bracket)
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
249
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
257
258 function wepkey(val)
259         if val:sub(1, 2) == "s:" then
260                 val = val:sub(3)
261         end
262
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
269
270 function hexstring(val)
271         if val then
272                 return (val:match("^[a-fA-F0-9]+$") ~= nil)
273         end
274         return false
275 end
276
277 function string(val)
278         return true             -- Everything qualifies as valid string
279 end
280
281 function directory( val, seen )
282         local s = fs.stat(val)
283         seen = seen or { }
284
285         if s and not seen[s.ino] then
286                 seen[s.ino] = true
287                 if s.type == "dir" then
288                         return true
289                 elseif s.type == "lnk" then
290                         return directory( fs.readlink(val), seen )
291                 end
292         end
293
294         return false
295 end
296
297 function file( val, seen )
298         local s = fs.stat(val)
299         seen = seen or { }
300
301         if s and not seen[s.ino] then
302                 seen[s.ino] = true
303                 if s.type == "reg" then
304                         return true
305                 elseif s.type == "lnk" then
306                         return file( fs.readlink(val), seen )
307                 end
308         end
309
310         return false
311 end
312
313 function device( val, seen )
314         local s = fs.stat(val)
315         seen = seen or { }
316
317         if s and not seen[s.ino] then
318                 seen[s.ino] = true
319                 if s.type == "chr" or s.type == "blk" then
320                         return true
321                 elseif s.type == "lnk" then
322                         return device( fs.readlink(val), seen )
323                 end
324         end
325
326         return false
327 end
328
329 function uciname(val)
330         return (val:match("^[a-zA-Z0-9_]+$") ~= nil)
331 end
332
333 function range(val, min, max)
334         val = tonumber(val)
335         min = tonumber(min)
336         max = tonumber(max)
337
338         if val ~= nil and min ~= nil and max ~= nil then
339                 return ((val >= min) and (val <= max))
340         end
341
342         return false
343 end
344
345 function min(val, min)
346         val = tonumber(val)
347         min = tonumber(min)
348
349         if val ~= nil and min ~= nil then
350                 return (val >= min)
351         end
352
353         return false
354 end
355
356 function max(val, max)
357         val = tonumber(val)
358         max = tonumber(max)
359
360         if val ~= nil and max ~= nil then
361                 return (val <= max)
362         end
363
364         return false
365 end
366
367 function rangelength(val, min, max)
368         val = tostring(val)
369         min = tonumber(min)
370         max = tonumber(max)
371
372         if val ~= nil and min ~= nil and max ~= nil then
373                 return ((#val >= min) and (#val <= max))
374         end
375
376         return false
377 end
378
379 function minlength(val, min)
380         val = tostring(val)
381         min = tonumber(min)
382
383         if val ~= nil and min ~= nil then
384                 return (#val >= min)
385         end
386
387         return false
388 end
389
390 function maxlength(val, max)
391         val = tostring(val)
392         max = tonumber(max)
393
394         if val ~= nil and max ~= nil then
395                 return (#val <= max)
396         end
397
398         return false
399 end
400
401 function phonedigit(val)
402         return (val:match("^[0-9\*#!%.]+$") ~= nil)
403 end
404
405 function timehhmmss(val)
406         return (val:match("^[0-6][0-9]:[0-6][0-9]:[0-6][0-9]$") ~= nil)
407 end
408
409 function dateyyyymmdd(val)
410         if val ~= nil then
411                 yearstr, monthstr, daystr = val:match("^(%d%d%d%d)-(%d%d)-(%d%d)$")
412                 if (yearstr == nil) or (monthstr == nil) or (daystr == nil) then
413                         return false;
414                 end
415                 year = tonumber(yearstr)
416                 month = tonumber(monthstr)
417                 day = tonumber(daystr)
418                 if (year == nil) or (month == nil) or (day == nil) then
419                         return false;
420                 end
421
422                 local days_in_month = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
423
424                 local function is_leap_year(year)
425                         return (year % 4 == 0) and ((year % 100 ~= 0) or (year % 400 == 0))
426                 end
427
428                 function get_days_in_month(month, year)
429                         if (month == 2) and is_leap_year(year) then
430                                 return 29
431                         else
432                                 return days_in_month[month]
433                         end
434                 end
435                 if (year < 2015) then
436                         return false
437                 end
438                 if ((month == 0) or (month > 12)) then
439                         return false
440                 end
441                 if ((day == 0) or (day > get_days_in_month(month, year))) then
442                         return false
443                 end
444                 return true
445         end
446         return false
447 end
448