libs/web: implement new phone digit datatype, useful for VoIP related config screens
[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, type, unpack, select = tonumber, 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 ponedigit(val)
310         return (val:match("^[0-9\*#]+$") ~= nil)
311 end