4c003be2a1bc50ad80301d84a6708f3125b2c96c
[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)
180 return hostname(val) or ipaddr(val)
181 end
182
183 function network(val)
184 return uciname(val) or host(val)
185 end
186
187 function hostport(val)
188 local h, p = val:match("^([^:]+):([^:]+)$")
189 return not not (h and p and host(h) and port(p))
190 end
191
192 function ipaddrport(val)
193 local h, p = val:match("^([^:]+):([^:]+)$")
194 return not not (h and p and ipaddr(h) and port(p))
195 end
196
197 function wpakey(val)
198 if #val == 64 then
199 return (val:match("^[a-fA-F0-9]+$") ~= nil)
200 else
201 return (#val >= 8) and (#val <= 63)
202 end
203 end
204
205 function wepkey(val)
206 if val:sub(1, 2) == "s:" then
207 val = val:sub(3)
208 end
209
210 if (#val == 10) or (#val == 26) then
211 return (val:match("^[a-fA-F0-9]+$") ~= nil)
212 else
213 return (#val == 5) or (#val == 13)
214 end
215 end
216
217 function string(val)
218 return true -- Everything qualifies as valid string
219 end
220
221 function directory( val, seen )
222 local s = fs.stat(val)
223 seen = seen or { }
224
225 if s and not seen[s.ino] then
226 seen[s.ino] = true
227 if s.type == "dir" then
228 return true
229 elseif s.type == "lnk" then
230 return directory( fs.readlink(val), seen )
231 end
232 end
233
234 return false
235 end
236
237 function file( val, seen )
238 local s = fs.stat(val)
239 seen = seen or { }
240
241 if s and not seen[s.ino] then
242 seen[s.ino] = true
243 if s.type == "reg" then
244 return true
245 elseif s.type == "lnk" then
246 return file( fs.readlink(val), seen )
247 end
248 end
249
250 return false
251 end
252
253 function device( val, seen )
254 local s = fs.stat(val)
255 seen = seen or { }
256
257 if s and not seen[s.ino] then
258 seen[s.ino] = true
259 if s.type == "chr" or s.type == "blk" then
260 return true
261 elseif s.type == "lnk" then
262 return device( fs.readlink(val), seen )
263 end
264 end
265
266 return false
267 end
268
269 function uciname(val)
270 return (val:match("^[a-zA-Z0-9_]+$") ~= nil)
271 end
272
273 function range(val, min, max)
274 val = tonumber(val)
275 min = tonumber(min)
276 max = tonumber(max)
277
278 if val ~= nil and min ~= nil and max ~= nil then
279 return ((val >= min) and (val <= max))
280 end
281
282 return false
283 end
284
285 function min(val, min)
286 val = tonumber(val)
287 min = tonumber(min)
288
289 if val ~= nil and min ~= nil then
290 return (val >= min)
291 end
292
293 return false
294 end
295
296 function max(val, max)
297 val = tonumber(val)
298 max = tonumber(max)
299
300 if val ~= nil and max ~= nil then
301 return (val <= max)
302 end
303
304 return false
305 end
306
307 function rangelength(val, min, max)
308 val = tostring(val)
309 min = tonumber(min)
310 max = tonumber(max)
311
312 if val ~= nil and min ~= nil and max ~= nil then
313 return ((#val >= min) and (#val <= max))
314 end
315
316 return false
317 end
318
319 function minlength(val, min)
320 val = tostring(val)
321 min = tonumber(min)
322
323 if val ~= nil and min ~= nil then
324 return (#val >= min)
325 end
326
327 return false
328 end
329
330 function maxlength(val, max)
331 val = tostring(val)
332 max = tonumber(max)
333
334 if val ~= nil and max ~= nil then
335 return (#val <= max)
336 end
337
338 return false
339 end
340
341 function phonedigit(val)
342 return (val:match("^[0-9\*#!%.]+$") ~= nil)
343 end
344
345 function timehhmmss(val)
346 return (val:match("^[0-6][0-9]:[0-6][0-9]:[0-6][0-9]$") ~= nil)
347 end
348
349 function dateyyyymmdd(val)
350 if val ~= nil then
351 yearstr, monthstr, daystr = val:match("^(%d%d%d%d)-(%d%d)-(%d%d)$")
352 if (yearstr == nil) or (monthstr == nil) or (daystr == nil) then
353 return false;
354 end
355 year = tonumber(yearstr)
356 month = tonumber(monthstr)
357 day = tonumber(daystr)
358 if (year == nil) or (month == nil) or (day == nil) then
359 return false;
360 end
361
362 local days_in_month = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
363
364 local function is_leap_year(year)
365 return (year % 4 == 0) and ((year % 100 ~= 0) or (year % 400 == 0))
366 end
367
368 function get_days_in_month(month, year)
369 if (month == 2) and is_leap_year(year) then
370 return 29
371 else
372 return days_in_month[month]
373 end
374 end
375 if (year < 2015) then
376 return false
377 end
378 if ((month == 0) or (month > 12)) then
379 return false
380 end
381 if ((day == 0) or (day > get_days_in_month(month, year))) then
382 return false
383 end
384 return true
385 end
386 return false
387 end
388