libs/web: relax validation for hostnames to allow names like _kerberos._udp.EXAMPLE.COM
[project/luci.git] / libs / web / luasrc / cbi / datatypes.lua
1 --[[
2
3 LuCI - Configuration Bind Interface - Datatype Tests
4 (c) 2010 Jo-Philipp Wich <xm@subsignal.org>
5
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 $Id$
13
14 ]]--
15
16 local fs = require "nixio.fs"
17 local ip = require "luci.ip"
18 local math = require "math"
19 local util = require "luci.util"
20 local tonumber, tostring, type, unpack, select = tonumber, tostring, type, unpack, select
21
22
23 module "luci.cbi.datatypes"
24
25
26 _M['or'] = function(v, ...)
27 local i
28 for i = 1, select('#', ...), 2 do
29 local f = select(i, ...)
30 local a = select(i+1, ...)
31 if type(f) ~= "function" then
32 if f == v then
33 return true
34 end
35 i = i - 1
36 elseif f(v, unpack(a)) then
37 return true
38 end
39 end
40 return false
41 end
42
43 _M['and'] = function(v, ...)
44 local i
45 for i = 1, select('#', ...), 2 do
46 local f = select(i, ...)
47 local a = select(i+1, ...)
48 if type(f) ~= "function" then
49 if f ~= v then
50 return false
51 end
52 i = i - 1
53 elseif not f(v, unpack(a)) then
54 return false
55 end
56 end
57 return true
58 end
59
60 function neg(v, ...)
61 return _M['or'](v:gsub("^%s*!%s*", ""), ...)
62 end
63
64 function list(v, subvalidator, subargs)
65 if type(subvalidator) ~= "function" then
66 return false
67 end
68 local token
69 for token in v:gmatch("%S+") do
70 if not subvalidator(token, unpack(subargs)) then
71 return false
72 end
73 end
74 return true
75 end
76
77 function bool(val)
78 if val == "1" or val == "yes" or val == "on" or val == "true" then
79 return true
80 elseif val == "0" or val == "no" or val == "off" or val == "false" then
81 return true
82 elseif val == "" or val == nil then
83 return true
84 end
85
86 return false
87 end
88
89 function uinteger(val)
90 local n = tonumber(val)
91 if n ~= nil and math.floor(n) == n and n >= 0 then
92 return true
93 end
94
95 return false
96 end
97
98 function integer(val)
99 local n = tonumber(val)
100 if n ~= nil and math.floor(n) == n then
101 return true
102 end
103
104 return false
105 end
106
107 function ufloat(val)
108 local n = tonumber(val)
109 return ( n ~= nil and n >= 0 )
110 end
111
112 function float(val)
113 return ( tonumber(val) ~= nil )
114 end
115
116 function ipaddr(val)
117 return ip4addr(val) or ip6addr(val)
118 end
119
120 function ip4addr(val)
121 if val then
122 return ip.IPv4(val) and true or false
123 end
124
125 return false
126 end
127
128 function ip4prefix(val)
129 val = tonumber(val)
130 return ( val and val >= 0 and val <= 32 )
131 end
132
133 function ip6addr(val)
134 if val then
135 return ip.IPv6(val) and true or false
136 end
137
138 return false
139 end
140
141 function ip6prefix(val)
142 val = tonumber(val)
143 return ( val and val >= 0 and val <= 128 )
144 end
145
146 function port(val)
147 val = tonumber(val)
148 return ( val and val >= 0 and val <= 65535 )
149 end
150
151 function portrange(val)
152 local p1, p2 = val:match("^(%d+)%-(%d+)$")
153 if p1 and p2 and port(p1) and port(p2) then
154 return true
155 else
156 return port(val)
157 end
158 end
159
160 function macaddr(val)
161 if val and val:match(
162 "^[a-fA-F0-9]+:[a-fA-F0-9]+:[a-fA-F0-9]+:" ..
163 "[a-fA-F0-9]+:[a-fA-F0-9]+:[a-fA-F0-9]+$"
164 ) then
165 local parts = util.split( val, ":" )
166
167 for i = 1,6 do
168 parts[i] = tonumber( parts[i], 16 )
169 if parts[i] < 0 or parts[i] > 255 then
170 return false
171 end
172 end
173
174 return true
175 end
176
177 return false
178 end
179
180 function hostname(val)
181 if val and (#val < 254) and (
182 val:match("^[a-zA-Z_]+$") or
183 (val:match("^[a-zA-Z0-9_][a-zA-Z0-9_%-%.]*[a-zA-Z0-9]$") and
184 val:match("[^0-9%.]"))
185 ) then
186 return true
187 end
188 return false
189 end
190
191 function host(val)
192 return hostname(val) or ipaddr(val)
193 end
194
195 function network(val)
196 return uciname(val) or host(val)
197 end
198
199 function wpakey(val)
200 if #val == 64 then
201 return (val:match("^[a-fA-F0-9]+$") ~= nil)
202 else
203 return (#val >= 8) and (#val <= 63)
204 end
205 end
206
207 function wepkey(val)
208 if val:sub(1, 2) == "s:" then
209 val = val:sub(3)
210 end
211
212 if (#val == 10) or (#val == 26) then
213 return (val:match("^[a-fA-F0-9]+$") ~= nil)
214 else
215 return (#val == 5) or (#val == 13)
216 end
217 end
218
219 function string(val)
220 return true -- Everything qualifies as valid string
221 end
222
223 function directory( val, seen )
224 local s = fs.stat(val)
225 seen = seen or { }
226
227 if s and not seen[s.ino] then
228 seen[s.ino] = true
229 if s.type == "dir" then
230 return true
231 elseif s.type == "lnk" then
232 return directory( fs.readlink(val), seen )
233 end
234 end
235
236 return false
237 end
238
239 function file( val, seen )
240 local s = fs.stat(val)
241 seen = seen or { }
242
243 if s and not seen[s.ino] then
244 seen[s.ino] = true
245 if s.type == "reg" then
246 return true
247 elseif s.type == "lnk" then
248 return file( fs.readlink(val), seen )
249 end
250 end
251
252 return false
253 end
254
255 function device( val, seen )
256 local s = fs.stat(val)
257 seen = seen or { }
258
259 if s and not seen[s.ino] then
260 seen[s.ino] = true
261 if s.type == "chr" or s.type == "blk" then
262 return true
263 elseif s.type == "lnk" then
264 return device( fs.readlink(val), seen )
265 end
266 end
267
268 return false
269 end
270
271 function uciname(val)
272 return (val:match("^[a-zA-Z0-9_]+$") ~= nil)
273 end
274
275 function range(val, min, max)
276 val = tonumber(val)
277 min = tonumber(min)
278 max = tonumber(max)
279
280 if val ~= nil and min ~= nil and max ~= nil then
281 return ((val >= min) and (val <= max))
282 end
283
284 return false
285 end
286
287 function min(val, min)
288 val = tonumber(val)
289 min = tonumber(min)
290
291 if val ~= nil and min ~= nil then
292 return (val >= min)
293 end
294
295 return false
296 end
297
298 function max(val, max)
299 val = tonumber(val)
300 max = tonumber(max)
301
302 if val ~= nil and max ~= nil then
303 return (val <= max)
304 end
305
306 return false
307 end
308
309 function rangelength(val, min, max)
310 val = tostring(val)
311 min = tonumber(min)
312 max = tonumber(max)
313
314 if val ~= nil and min ~= nil and max ~= nil then
315 return ((#val >= min) and (#val <= max))
316 end
317
318 return false
319 end
320
321 function minlength(val, min)
322 val = tostring(val)
323 min = tonumber(min)
324
325 if val ~= nil and min ~= nil then
326 return (#val >= min)
327 end
328
329 return false
330 end
331
332 function maxlength(val, max)
333 val = tostring(val)
334 max = tonumber(max)
335
336 if val ~= nil and max ~= nil then
337 return (#val <= max)
338 end
339
340 return false
341 end
342
343 function phonedigit(val)
344 return (val:match("^[0-9\*#]+$") ~= nil)
345 end