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