036d6ff5e31f9b60e90bf03568eaf1e0403c9b73
[librecmc/librecmc.git] / package / luci / 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 ip6hostid(val)
169         if val and val:match("^[a-fA-F0-9:]+$") and (#val > 2) then
170                 return (ip6addr("2001:db8:0:0" .. val) or ip6addr("2001:db8:0:0:" .. val))
171         end
172
173         return false
174 end
175
176 function port(val)
177         val = tonumber(val)
178         return ( val and val >= 0 and val <= 65535 )
179 end
180
181 function portrange(val)
182         local p1, p2 = val:match("^(%d+)%-(%d+)$")
183         if p1 and p2 and port(p1) and port(p2) then
184                 return true
185         else
186                 return port(val)
187         end
188 end
189
190 function macaddr(val)
191         if val and val:match(
192                 "^[a-fA-F0-9]+:[a-fA-F0-9]+:[a-fA-F0-9]+:" ..
193                  "[a-fA-F0-9]+:[a-fA-F0-9]+:[a-fA-F0-9]+$"
194         ) then
195                 local parts = util.split( val, ":" )
196
197                 for i = 1,6 do
198                         parts[i] = tonumber( parts[i], 16 )
199                         if parts[i] < 0 or parts[i] > 255 then
200                                 return false
201                         end
202                 end
203
204                 return true
205         end
206
207         return false
208 end
209
210 function hostname(val)
211         if val and (#val < 254) and (
212            val:match("^[a-zA-Z_]+$") or
213            (val:match("^[a-zA-Z0-9_][a-zA-Z0-9_%-%.]*[a-zA-Z0-9]$") and
214             val:match("[^0-9%.]"))
215         ) then
216                 return true
217         end
218         return false
219 end
220
221 function host(val, ipv4only)
222         return hostname(val) or ((ipv4only == 1) and ip4addr(val)) or ((not (ipv4only == 1)) and ipaddr(val))
223 end
224
225 function network(val)
226         return uciname(val) or host(val)
227 end
228
229 function hostport(val, ipv4only)
230         local h, p = val:match("^([^:]+):([^:]+)$")
231         return not not (h and p and host(h, ipv4only) and port(p))
232 end
233
234 function ip4addrport(val, bracket)
235         local h, p = val:match("^([^:]+):([^:]+)$")
236         return (h and p and ip4addr(h) and port(p))
237 end
238
239 function ip4addrport(val)
240         local h, p = val:match("^([^:]+):([^:]+)$")
241         return (h and p and ip4addr(h) and port(p))
242 end
243
244 function ipaddrport(val, bracket)
245         local h, p = val:match("^([^%[%]:]+):([^:]+)$")
246         if (h and p and ip4addr(h) and port(p)) then
247                 return true
248         elseif (bracket == 1) then
249                 h, p = val:match("^%[(.+)%]:([^:]+)$")
250                 if  (h and p and ip6addr(h) and port(p)) then
251                         return true
252                 end
253         end
254         h, p = val:match("^([^%[%]]+):([^:]+)$")
255         return (h and p and ip6addr(h) and port(p))
256 end
257
258 function wpakey(val)
259         if #val == 64 then
260                 return (val:match("^[a-fA-F0-9]+$") ~= nil)
261         else
262                 return (#val >= 8) and (#val <= 63)
263         end
264 end
265
266 function wepkey(val)
267         if val:sub(1, 2) == "s:" then
268                 val = val:sub(3)
269         end
270
271         if (#val == 10) or (#val == 26) then
272                 return (val:match("^[a-fA-F0-9]+$") ~= nil)
273         else
274                 return (#val == 5) or (#val == 13)
275         end
276 end
277
278 function hexstring(val)
279         if val then
280                 return (val:match("^[a-fA-F0-9]+$") ~= nil)
281         end
282         return false
283 end
284
285 function hex(val, maxbytes)
286         maxbytes = tonumber(maxbytes)
287         if val and maxbytes ~= nil then
288                 return ((val:match("^0x[a-fA-F0-9]+$") ~= nil) and (#val <= 2 + maxbytes * 2))
289         end
290         return false
291 end
292
293 function base64(val)
294         if val then
295                 return (val:match("^[a-zA-Z0-9/+]+=?=?$") ~= nil) and (math.fmod(#val, 4) == 0)
296         end
297         return false
298 end
299
300 function string(val)
301         return true             -- Everything qualifies as valid string
302 end
303
304 function directory( val, seen )
305         local s = fs.stat(val)
306         seen = seen or { }
307
308         if s and not seen[s.ino] then
309                 seen[s.ino] = true
310                 if s.type == "dir" then
311                         return true
312                 elseif s.type == "lnk" then
313                         return directory( fs.readlink(val), seen )
314                 end
315         end
316
317         return false
318 end
319
320 function file( val, seen )
321         local s = fs.stat(val)
322         seen = seen or { }
323
324         if s and not seen[s.ino] then
325                 seen[s.ino] = true
326                 if s.type == "reg" then
327                         return true
328                 elseif s.type == "lnk" then
329                         return file( fs.readlink(val), seen )
330                 end
331         end
332
333         return false
334 end
335
336 function device( val, seen )
337         local s = fs.stat(val)
338         seen = seen or { }
339
340         if s and not seen[s.ino] then
341                 seen[s.ino] = true
342                 if s.type == "chr" or s.type == "blk" then
343                         return true
344                 elseif s.type == "lnk" then
345                         return device( fs.readlink(val), seen )
346                 end
347         end
348
349         return false
350 end
351
352 function uciname(val)
353         return (val:match("^[a-zA-Z0-9_]+$") ~= nil)
354 end
355
356 function range(val, min, max)
357         val = tonumber(val)
358         min = tonumber(min)
359         max = tonumber(max)
360
361         if val ~= nil and min ~= nil and max ~= nil then
362                 return ((val >= min) and (val <= max))
363         end
364
365         return false
366 end
367
368 function min(val, min)
369         val = tonumber(val)
370         min = tonumber(min)
371
372         if val ~= nil and min ~= nil then
373                 return (val >= min)
374         end
375
376         return false
377 end
378
379 function max(val, max)
380         val = tonumber(val)
381         max = tonumber(max)
382
383         if val ~= nil and max ~= nil then
384                 return (val <= max)
385         end
386
387         return false
388 end
389
390 function rangelength(val, min, max)
391         val = tostring(val)
392         min = tonumber(min)
393         max = tonumber(max)
394
395         if val ~= nil and min ~= nil and max ~= nil then
396                 return ((#val >= min) and (#val <= max))
397         end
398
399         return false
400 end
401
402 function minlength(val, min)
403         val = tostring(val)
404         min = tonumber(min)
405
406         if val ~= nil and min ~= nil then
407                 return (#val >= min)
408         end
409
410         return false
411 end
412
413 function maxlength(val, max)
414         val = tostring(val)
415         max = tonumber(max)
416
417         if val ~= nil and max ~= nil then
418                 return (#val <= max)
419         end
420
421         return false
422 end
423
424 function phonedigit(val)
425         return (val:match("^[0-9\*#!%.]+$") ~= nil)
426 end
427
428 function timehhmmss(val)
429         return (val:match("^[0-6][0-9]:[0-6][0-9]:[0-6][0-9]$") ~= nil)
430 end
431
432 function dateyyyymmdd(val)
433         if val ~= nil then
434                 yearstr, monthstr, daystr = val:match("^(%d%d%d%d)-(%d%d)-(%d%d)$")
435                 if (yearstr == nil) or (monthstr == nil) or (daystr == nil) then
436                         return false;
437                 end
438                 year = tonumber(yearstr)
439                 month = tonumber(monthstr)
440                 day = tonumber(daystr)
441                 if (year == nil) or (month == nil) or (day == nil) then
442                         return false;
443                 end
444
445                 local days_in_month = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
446
447                 local function is_leap_year(year)
448                         return (year % 4 == 0) and ((year % 100 ~= 0) or (year % 400 == 0))
449                 end
450
451                 function get_days_in_month(month, year)
452                         if (month == 2) and is_leap_year(year) then
453                                 return 29
454                         else
455                                 return days_in_month[month]
456                         end
457                 end
458                 if (year < 2015) then
459                         return false
460                 end
461                 if ((month == 0) or (month > 12)) then
462                         return false
463                 end
464                 if ((day == 0) or (day > get_days_in_month(month, year))) then
465                         return false
466                 end
467                 return true
468         end
469         return false
470 end
471