Merge pull request #606 from SvenRoederer/update_community-berlin
[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 port(val)
135 val = tonumber(val)
136 return ( val and val >= 0 and val <= 65535 )
137 end
138
139 function portrange(val)
140 local p1, p2 = val:match("^(%d+)%-(%d+)$")
141 if p1 and p2 and port(p1) and port(p2) then
142 return true
143 else
144 return port(val)
145 end
146 end
147
148 function macaddr(val)
149 if val and val:match(
150 "^[a-fA-F0-9]+:[a-fA-F0-9]+:[a-fA-F0-9]+:" ..
151 "[a-fA-F0-9]+:[a-fA-F0-9]+:[a-fA-F0-9]+$"
152 ) then
153 local parts = util.split( val, ":" )
154
155 for i = 1,6 do
156 parts[i] = tonumber( parts[i], 16 )
157 if parts[i] < 0 or parts[i] > 255 then
158 return false
159 end
160 end
161
162 return true
163 end
164
165 return false
166 end
167
168 function hostname(val)
169 if val and (#val < 254) and (
170 val:match("^[a-zA-Z_]+$") or
171 (val:match("^[a-zA-Z0-9_][a-zA-Z0-9_%-%.]*[a-zA-Z0-9]$") and
172 val:match("[^0-9%.]"))
173 ) then
174 return true
175 end
176 return false
177 end
178
179 function host(val, ipv4only)
180 return hostname(val) or ((ipv4only == 1) and ip4addr(val)) or ((not (ipv4only == 1)) and ipaddr(val))
181 end
182
183 function network(val)
184 return uciname(val) or host(val)
185 end
186
187 function hostport(val, ipv4only)
188 local h, p = val:match("^([^:]+):([^:]+)$")
189 return not not (h and p and host(h, ipv4only) and port(p))
190 end
191
192 function ip4addrport(val, bracket)
193 local h, p = val:match("^([^:]+):([^:]+)$")
194 return (h and p and ip4addr(h) and port(p))
195 end
196
197 function ip4addrport(val)
198 local h, p = val:match("^([^:]+):([^:]+)$")
199 return (h and p and ip4addr(h) and port(p))
200 end
201
202 function ipaddrport(val, bracket)
203 local h, p = val:match("^([^%[%]:]+):([^:]+)$")
204 if (h and p and ip4addr(h) and port(p)) then
205 return true
206 elseif (bracket == 1) then
207 h, p = val:match("^%[(.+)%]:([^:]+)$")
208 if (h and p and ip6addr(h) and port(p)) then
209 return true
210 end
211 end
212 h, p = val:match("^([^%[%]]+):([^:]+)$")
213 return (h and p and ip6addr(h) and port(p))
214 end
215
216 function wpakey(val)
217 if #val == 64 then
218 return (val:match("^[a-fA-F0-9]+$") ~= nil)
219 else
220 return (#val >= 8) and (#val <= 63)
221 end
222 end
223
224 function wepkey(val)
225 if val:sub(1, 2) == "s:" then
226 val = val:sub(3)
227 end
228
229 if (#val == 10) or (#val == 26) then
230 return (val:match("^[a-fA-F0-9]+$") ~= nil)
231 else
232 return (#val == 5) or (#val == 13)
233 end
234 end
235
236 function string(val)
237 return true -- Everything qualifies as valid string
238 end
239
240 function directory( val, seen )
241 local s = fs.stat(val)
242 seen = seen or { }
243
244 if s and not seen[s.ino] then
245 seen[s.ino] = true
246 if s.type == "dir" then
247 return true
248 elseif s.type == "lnk" then
249 return directory( fs.readlink(val), seen )
250 end
251 end
252
253 return false
254 end
255
256 function file( val, seen )
257 local s = fs.stat(val)
258 seen = seen or { }
259
260 if s and not seen[s.ino] then
261 seen[s.ino] = true
262 if s.type == "reg" then
263 return true
264 elseif s.type == "lnk" then
265 return file( fs.readlink(val), seen )
266 end
267 end
268
269 return false
270 end
271
272 function device( val, seen )
273 local s = fs.stat(val)
274 seen = seen or { }
275
276 if s and not seen[s.ino] then
277 seen[s.ino] = true
278 if s.type == "chr" or s.type == "blk" then
279 return true
280 elseif s.type == "lnk" then
281 return device( fs.readlink(val), seen )
282 end
283 end
284
285 return false
286 end
287
288 function uciname(val)
289 return (val:match("^[a-zA-Z0-9_]+$") ~= nil)
290 end
291
292 function range(val, min, max)
293 val = tonumber(val)
294 min = tonumber(min)
295 max = tonumber(max)
296
297 if val ~= nil and min ~= nil and max ~= nil then
298 return ((val >= min) and (val <= max))
299 end
300
301 return false
302 end
303
304 function min(val, min)
305 val = tonumber(val)
306 min = tonumber(min)
307
308 if val ~= nil and min ~= nil then
309 return (val >= min)
310 end
311
312 return false
313 end
314
315 function max(val, max)
316 val = tonumber(val)
317 max = tonumber(max)
318
319 if val ~= nil and max ~= nil then
320 return (val <= max)
321 end
322
323 return false
324 end
325
326 function rangelength(val, min, max)
327 val = tostring(val)
328 min = tonumber(min)
329 max = tonumber(max)
330
331 if val ~= nil and min ~= nil and max ~= nil then
332 return ((#val >= min) and (#val <= max))
333 end
334
335 return false
336 end
337
338 function minlength(val, min)
339 val = tostring(val)
340 min = tonumber(min)
341
342 if val ~= nil and min ~= nil then
343 return (#val >= min)
344 end
345
346 return false
347 end
348
349 function maxlength(val, max)
350 val = tostring(val)
351 max = tonumber(max)
352
353 if val ~= nil and max ~= nil then
354 return (#val <= max)
355 end
356
357 return false
358 end
359
360 function phonedigit(val)
361 return (val:match("^[0-9\*#!%.]+$") ~= nil)
362 end
363
364 function timehhmmss(val)
365 return (val:match("^[0-6][0-9]:[0-6][0-9]:[0-6][0-9]$") ~= nil)
366 end
367
368 function dateyyyymmdd(val)
369 if val ~= nil then
370 yearstr, monthstr, daystr = val:match("^(%d%d%d%d)-(%d%d)-(%d%d)$")
371 if (yearstr == nil) or (monthstr == nil) or (daystr == nil) then
372 return false;
373 end
374 year = tonumber(yearstr)
375 month = tonumber(monthstr)
376 day = tonumber(daystr)
377 if (year == nil) or (month == nil) or (day == nil) then
378 return false;
379 end
380
381 local days_in_month = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
382
383 local function is_leap_year(year)
384 return (year % 4 == 0) and ((year % 100 ~= 0) or (year % 400 == 0))
385 end
386
387 function get_days_in_month(month, year)
388 if (month == 2) and is_leap_year(year) then
389 return 29
390 else
391 return days_in_month[month]
392 end
393 end
394 if (year < 2015) then
395 return false
396 end
397 if ((month == 0) or (month > 12)) then
398 return false
399 end
400 if ((day == 0) or (day > get_days_in_month(month, year))) then
401 return false
402 end
403 return true
404 end
405 return false
406 end
407