luci-base: add hostport and ipaddrport validation types
[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