applications/luci-firewall: fix crash in rule formatting helper when a rule with...
[project/luci.git] / applications / luci-firewall / luasrc / tools / firewall.lua
1 --[[
2 LuCI - Lua Configuration Interface
3
4 Copyright 2011 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 module("luci.tools.firewall", package.seeall)
16
17 local ut = require "luci.util"
18 local ip = require "luci.ip"
19 local nx = require "nixio"
20
21 local translate, translatef = luci.i18n.translate, luci.i18n.translatef
22
23 function fmt_neg(x)
24         if type(x) == "string" then
25                 local v, neg = x:gsub("^ *! *", "")
26                 if neg > 0 then
27                         return v, "%s " % translate("not")
28                 else
29                         return x, ""
30                 end
31         end
32         return x, ""
33 end
34
35 function fmt_mac(x)
36         if x and #x > 0 then
37                 local m, n
38                 local l = { translate("MAC"), " " }
39                 for m in ut.imatch(x) do
40                         m, n = fmt_neg(m)
41                         l[#l+1] = "<var>%s%s</var>" %{ n, m }
42                         l[#l+1] = ", "
43                 end
44                 if #l > 1 then
45                         l[#l] = nil
46                         if #l > 3 then
47                                 l[1] = translate("MACs")
48                         end
49                         return table.concat(l, "")
50                 end
51         end
52 end
53
54 function fmt_port(x, d)
55         if x and #x > 0 then
56                 local p, n
57                 local l = { translate("port"), " " }
58                 for p in ut.imatch(x) do
59                         p, n = fmt_neg(p)
60                         local a, b = p:match("(%d+)%D+(%d+)")
61                         if a and b then
62                                 l[1] = translate("ports")
63                                 l[#l+1] = "<var>%s%d-%d</var>" %{ n, a, b }
64                         else
65                                 l[#l+1] = "<var>%s%d</var>" %{ n, p }
66                         end
67                         l[#l+1] = ", "
68                 end
69                 if #l > 1 then
70                         l[#l] = nil
71                         if #l > 3 then
72                                 l[1] = translate("ports")
73                         end
74                         return table.concat(l, "")
75                 end
76         end
77         return d and "<var>%s</var>" % d
78 end
79
80 function fmt_ip(x, d)
81         if x and #x > 0 then
82                 local l = { translate("IP"), " " }
83                 local v, a, n
84                 for v in ut.imatch(x) do
85                         v, n = fmt_neg(v)
86                         a, m = v:match("(%S+)/(%d+%.%S+)")
87                         a = a or v
88                         a = a:match(":") and ip.IPv6(a, m) or ip.IPv4(a, m)
89                         if a and (a:is6() and a:prefix() < 128 or a:prefix() < 32) then
90                                 l[1] = translate("IP range")
91                                 l[#l+1] = "<var title='%s - %s'>%s%s</var>" %{
92                                         a:minhost():string(),
93                                         a:maxhost():string(),
94                                         n, a:string()
95                                 }
96                         else
97                                 l[#l+1] = "<var>%s%s</var>" %{
98                                         n,
99                                         a and a:string() or v
100                                 }
101                         end
102                         l[#l+1] = ", "
103                 end
104                 if #l > 1 then
105                         l[#l] = nil
106                         if #l > 3 then
107                                 l[1] = translate("IPs")
108                         end
109                         return table.concat(l, "")
110                 end
111         end
112         return d and "<var>%s</var>" % d
113 end
114
115 function fmt_zone(x, d)
116         if x == "*" then
117                 return "<var>%s</var>" % translate("any zone")
118         elseif x and #x > 0 then
119                 return "<var>%s</var>" % x
120         elseif d then
121                 return "<var>%s</var>" % d
122         end
123 end
124
125 function fmt_icmp_type(x)
126         if x and #x > 0 then
127                 local t, v, n
128                 local l = { translate("type"), " " }
129                 for v in ut.imatch(x) do
130                         v, n = fmt_neg(v)
131                         l[#l+1] = "<var>%s%s</var>" %{ n, v }
132                         l[#l+1] = ", "
133                 end
134                 if #l > 1 then
135                         l[#l] = nil
136                         if #l > 3 then
137                                 l[1] = translate("types")
138                         end
139                         return table.concat(l, "")
140                 end
141         end
142 end
143
144 function fmt_proto(x, icmp_types)
145         if x and #x > 0 then
146                 local v, n
147                 local l = { }
148                 local t = fmt_icmp_type(icmp_types)
149                 for v in ut.imatch(x) do
150                         v, n = fmt_neg(v)
151                         if v == "tcpudp" then
152                                 l[#l+1] = "TCP"
153                                 l[#l+1] = "UDP"
154                                 l[#l+1] = ", "
155                         elseif v ~= "all" then
156                                 local p = nx.getproto(v)
157                                 if p then
158                                         -- ICMP
159                                         if (p.proto == 1 or p.proto == 58) and t then
160                                                 l[#l+1] = translatef(
161                                                         "%s%s with %s",
162                                                         n, p.aliases[1] or p.name, t
163                                                 )
164                                         else
165                                                 l[#l+1] = "%s%s" %{
166                                                         n,
167                                                         p.aliases[1] or p.name
168                                                 }
169                                         end
170                                         l[#l+1] = ", "
171                                 end
172                         end
173                 end
174                 if #l > 0 then
175                         l[#l] = nil
176                         return table.concat(l, "")
177                 end
178         end
179 end
180
181 function fmt_limit(limit, burst)
182         burst = tonumber(burst)
183         if limit and #limit > 0 then
184                 local l, u = limit:match("(%d+)/(%w+)")
185                 l = tonumber(l or limit)
186                 u = u or "second"
187                 if l then
188                         if u:match("^s") then
189                                 u = translate("second")
190                         elseif u:match("^m") then
191                                 u = translate("minute")
192                         elseif u:match("^h") then
193                                 u = translate("hour")
194                         elseif u:match("^d") then
195                                 u = translate("day")
196                         end
197                         if burst and burst > 0 then
198                                 return translatef("<var>%d</var> pkts. per <var>%s</var>, \
199                                     burst <var>%d</var> pkts.", l, u, burst)
200                         else
201                                 return translatef("<var>%d</var> pkts. per <var>%s</var>", l, u)
202                         end
203                 end
204         end
205 end
206
207 function fmt_target(x, dest)
208         if dest and #dest > 0 then
209                 if x == "ACCEPT" then
210                         return translate("Accept forward")
211                 elseif x == "REJECT" then
212                         return translate("Refuse forward")
213                 elseif x == "NOTRACK" then
214                         return translate("Do not track forward")
215                 else --if x == "DROP" then
216                         return translate("Discard forward")
217                 end
218         else
219                 if x == "ACCEPT" then
220                         return translate("Accept input")
221                 elseif x == "REJECT" then
222                         return translate("Refuse input")
223                 elseif x == "NOTRACK" then
224                         return translate("Do not track input")
225                 else --if x == "DROP" then
226                         return translate("Discard input")
227                 end
228         end
229 end
230
231
232 function opt_enabled(s, t, ...)
233         if t == luci.cbi.Button then
234                 local o = s:option(t, "__enabled")
235                 function o.render(self, section)
236                         if self.map:get(section, "enabled") ~= "0" then
237                                 self.title      = translate("Rule is enabled")
238                                 self.inputtitle = translate("Disable")
239                                 self.inputstyle = "reset"
240                         else
241                                 self.title      = translate("Rule is disabled")
242                                 self.inputtitle = translate("Enable")
243                                 self.inputstyle = "apply"
244                         end
245                         t.render(self, section)
246                 end
247                 function o.write(self, section, value)
248                         if self.map:get(section, "enabled") ~= "0" then
249                                 self.map:set(section, "enabled", "0")
250                         else
251                                 self.map:del(section, "enabled")
252                         end
253                 end
254                 return o
255         else
256                 local o = s:option(t, "enabled", ...)
257                       o.enabled = ""
258                           o.disabled = "0"
259                       o.default = o.enabled
260                 return o
261         end
262 end
263
264 function opt_name(s, t, ...)
265         local o = s:option(t, "name", ...)
266
267         function o.cfgvalue(self, section)
268                 return self.map:get(section, "name") or
269                         self.map:get(section, "_name") or "-"
270         end
271
272         function o.write(self, section, value)
273                 if value ~= "-" then
274                         self.map:set(section, "name", value)
275                         self.map:del(section, "_name")
276                 else
277                         self:remove(section)
278                 end
279         end
280
281         function o.remove(self, section)
282                 self.map:del(section, "name")
283                 self.map:del(section, "_name")
284         end
285
286         return o
287 end