Merge lp:~davidmhewitt/switchboard-plug-security-privacy/fix-1312461-control-rule into lp:~elementary-apps/switchboard-plug-security-privacy/trunk
- fix-1312461-control-rule
- Merge into trunk
Status: | Superseded | ||||||||
---|---|---|---|---|---|---|---|---|---|
Proposed branch: | lp:~davidmhewitt/switchboard-plug-security-privacy/fix-1312461-control-rule | ||||||||
Merge into: | lp:~elementary-apps/switchboard-plug-security-privacy/trunk | ||||||||
Diff against target: |
617 lines (+382/-70) 5 files modified
CMakeLists.txt (+1/-3) data/CMakeLists.txt (+6/-0) data/org.pantheon.security-privacy.gschema.xml (+12/-0) src/UFWHelpers.vala (+153/-48) src/Views/FirewallPanel.vala (+210/-19) |
||||||||
To merge this branch: | bzr merge lp:~davidmhewitt/switchboard-plug-security-privacy/fix-1312461-control-rule | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Adam Bieńkowski (community) | code | Needs Fixing | |
Review via email: mp+319009@code.launchpad.net |
This proposal has been superseded by a proposal from 2017-03-06.
Commit message
Firewall:
* UFW rule creation now respects the IPv4/IPv6 flag set in the Rule structure.
* New dropdown in rule creation popover to specify IPv4/IPv6/Both rule.
* New rules are created as IPv4 by default.
* Use regular expressions to parse UFW command output
* Add a checkbox to enable/disable rules
Description of the change
The IPv6 checkbox was slightly confusing in that it looked like a control that you could modify. This branch turns the IPv6 status into a text field and adds a checkbox in the same place that allows you to enable/disable a rule.
To allow this functionality, the parsing and displaying of UFW rules has been improved as rules that had been set using the UFW command line were often incorrectly displayed and interpreted by the plug. This made the enable/disable functionality on those rules really buggy.
Disabled rules are stored in GSettings as UFW has no concept of a disabled rule. When a rule is disabled by the plug, it is deleted from UFW and stored in GSettings.
David Hewitt (davidmhewitt) wrote : | # |
> I really don't like the validation of IP addresses with the regex. It makes
> the code look like clutter.
> Instead you can use much simpler solution with GLib.InetAddress: by using
> "InetAddress.
> to make sure it's valid and you can also call "get_family" on it to retrive if
> it's IPv4 or IPv6 address.
>
> Also, about this line:
> while (iter.next ("(ssssiiii)", &to, &to_ports, &from, &from_ports, &action,
> &protocol, &direction, &version)) {
>
> do you really need to use those C like arguments in order to retrieve needed
> variables? Does "out" not compile?
Thanks for the review, Adam. I'll try and fix the C like arguments, you're probably right that the out keyword will work.
As for using the InetAddress stuff, I tried that first and it wasn't suitable. Unfortunately, when you pass InetAddress.
However, if you have any better solutions to check if a string is an IPv4 address, IPv6 address or something else, I'd be open to suggestions.
- 329. By David Hewitt
-
Changed C-style syntax to be more Vala like
- 330. By David Hewitt
-
Switched to using InetAddress to parse IPs instead of horrible regexes
David Hewitt (davidmhewitt) wrote : | # |
> I really don't like the validation of IP addresses with the regex. It makes
> the code look like clutter.
> Instead you can use much simpler solution with GLib.InetAddress: by using
> "InetAddress.
> to make sure it's valid and you can also call "get_family" on it to retrive if
> it's IPv4 or IPv6 address.
>
> Also, about this line:
> while (iter.next ("(ssssiiii)", &to, &to_ports, &from, &from_ports, &action,
> &protocol, &direction, &version)) {
>
> do you really need to use those C like arguments in order to retrieve needed
> variables? Does "out" not compile?
My mistake, you can totally use InetAddress instead of the regular expressions, don't know what made me think it didn't work the first time. Thanks for the suggestion, that's saved a bunch of horrible code :)
I've implemented both suggestions!
Unmerged revisions
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2016-12-01 11:56:30 +0000 |
3 | +++ CMakeLists.txt 2017-03-06 18:49:38 +0000 |
4 | @@ -38,9 +38,7 @@ |
5 | include (CPack) |
6 | add_custom_target (dist COMMAND ${CMAKE_MAKE_PROGRAM} package_source) |
7 | |
8 | -configure_file (${CMAKE_SOURCE_DIR}/data/org.pantheon.security-privacy.policy.cmake ${CMAKE_BINARY_DIR}/data/org.pantheon.security-privacy.policy) |
9 | -install(FILES ${CMAKE_BINARY_DIR}/data/org.pantheon.security-privacy.policy DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/polkit-1/actions/) |
10 | -install(FILES ${CMAKE_SOURCE_DIR}/data/security-privacy-plug-helper PERMISSIONS OWNER_EXECUTE OWNER_READ DESTINATION ${PKGDATADIR}/) |
11 | +add_subdirectory (data) |
12 | |
13 | # Traslation stuff |
14 | add_subdirectory (po) |
15 | |
16 | === added file 'data/CMakeLists.txt' |
17 | --- data/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
18 | +++ data/CMakeLists.txt 2017-03-06 18:49:38 +0000 |
19 | @@ -0,0 +1,6 @@ |
20 | +configure_file (${CMAKE_SOURCE_DIR}/data/org.pantheon.security-privacy.policy.cmake ${CMAKE_BINARY_DIR}/data/org.pantheon.security-privacy.policy) |
21 | +install(FILES ${CMAKE_BINARY_DIR}/data/org.pantheon.security-privacy.policy DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/polkit-1/actions/) |
22 | +install(FILES ${CMAKE_SOURCE_DIR}/data/security-privacy-plug-helper PERMISSIONS OWNER_EXECUTE OWNER_READ DESTINATION ${PKGDATADIR}/) |
23 | + |
24 | +include (GSettings) |
25 | +add_schema ("org.pantheon.security-privacy.gschema.xml") |
26 | |
27 | === added file 'data/org.pantheon.security-privacy.gschema.xml' |
28 | --- data/org.pantheon.security-privacy.gschema.xml 1970-01-01 00:00:00 +0000 |
29 | +++ data/org.pantheon.security-privacy.gschema.xml 2017-03-06 18:49:38 +0000 |
30 | @@ -0,0 +1,12 @@ |
31 | +<?xml version="1.0" encoding="UTF-8"?> |
32 | +<schemalist> |
33 | + |
34 | +<schema id="org.pantheon.security-privacy" path="/org/pantheon/security-privacy/"> |
35 | + <key name="disabled-firewall-rules" type="a(ssssiiii)"> |
36 | + <default>[]</default> |
37 | + <summary>Firewall rules that have been disabled, but should still be listed in the plug</summary> |
38 | + <description>An array of rules, each containing action, protocol, direction and (bool v6) in that order.</description> |
39 | + </key> |
40 | +</schema> |
41 | + |
42 | +</schemalist> |
43 | |
44 | === modified file 'src/UFWHelpers.vala' |
45 | --- src/UFWHelpers.vala 2017-03-01 22:38:37 +0000 |
46 | +++ src/UFWHelpers.vala 2017-03-06 18:49:38 +0000 |
47 | @@ -103,17 +103,54 @@ |
48 | default: |
49 | rule_str = "%s in".printf (rule_str); |
50 | break; |
51 | - } |
52 | + } |
53 | |
54 | switch (rule.protocol) { |
55 | case Rule.Protocol.UDP: |
56 | - rule_str = "%s %s/udp".printf (rule_str, rule.ports); |
57 | + rule_str = "%s proto udp".printf (rule_str); |
58 | + break; |
59 | + case Rule.Protocol.BOTH: |
60 | break; |
61 | default: |
62 | - rule_str = "%s %s/tcp".printf (rule_str, rule.ports); |
63 | + rule_str = "%s proto tcp".printf (rule_str); |
64 | break; |
65 | } |
66 | |
67 | + if (rule.to != "" && !rule.to.contains ("Anywhere")) { |
68 | + rule_str = "%s to %s".printf (rule_str, rule.to); |
69 | + if (rule.to_ports != "") { |
70 | + rule_str = "%s port %s".printf (rule_str, rule.to_ports); |
71 | + } |
72 | + } else { |
73 | + if (rule.version == Rule.Version.BOTH) { |
74 | + rule_str = "%s to any".printf (rule_str); |
75 | + } else if (rule.version == Rule.Version.IPV6) { |
76 | + rule_str = "%s to ::/0".printf (rule_str); |
77 | + } else if (rule.version == Rule.Version.IPV4) { |
78 | + rule_str = "%s to 0.0.0.0/0".printf (rule_str); |
79 | + } |
80 | + if (rule.to_ports != "") { |
81 | + rule_str = "%s port %s".printf (rule_str, rule.to_ports); |
82 | + } |
83 | + } |
84 | + |
85 | + if (rule.from != "" && !rule.from.contains ("Anywhere")) { |
86 | + rule_str = "%s from %s".printf (rule_str, rule.from); |
87 | + if (rule.from_ports != "") { |
88 | + rule_str = "%s port %s".printf (rule_str, rule.from_ports); |
89 | + } |
90 | + } else { |
91 | + if (rule.version == Rule.Version.BOTH) { |
92 | + rule_str = "%s from any".printf (rule_str); |
93 | + } else if (rule.version == Rule.Version.IPV6) { |
94 | + rule_str = "%s from ::/0".printf (rule_str); |
95 | + } else if (rule.version == Rule.Version.IPV4) { |
96 | + rule_str = "%s from 0.0.0.0/0".printf (rule_str); |
97 | + } |
98 | + if (rule.from_ports != "") { |
99 | + rule_str = "%s port %s".printf (rule_str, rule.from_ports); |
100 | + } |
101 | + } |
102 | Process.spawn_command_line_sync ("pkexec %s -5 \"%s\"".printf (get_helper_path (), rule_str)); |
103 | } catch (Error e) { |
104 | warning (e.message); |
105 | @@ -130,7 +167,8 @@ |
106 | |
107 | public enum Protocol { |
108 | UDP, |
109 | - TCP |
110 | + TCP, |
111 | + BOTH |
112 | } |
113 | |
114 | public enum Direction { |
115 | @@ -138,60 +176,127 @@ |
116 | OUT |
117 | } |
118 | |
119 | + public enum Version { |
120 | + IPV4, |
121 | + IPV6, |
122 | + BOTH |
123 | + } |
124 | + |
125 | public Action action; |
126 | public Protocol protocol; |
127 | public Direction direction; |
128 | - public string ports; |
129 | - public bool is_v6 = false; |
130 | + public string to_ports = ""; |
131 | + public string from_ports = ""; |
132 | + public string to = ""; |
133 | + public string from = ""; |
134 | + public Version version = Version.BOTH; |
135 | public int number; |
136 | |
137 | public Rule () { |
138 | |
139 | } |
140 | |
141 | + private void get_address_and_port (string input, ref Version version, ref string ports, ref string address) { |
142 | + try { |
143 | + var parts = input.split (" "); |
144 | + if (parts.length > 1) { |
145 | + ports = parts[1].split("/")[0]; |
146 | + address = parts[0]; |
147 | + var ip = new InetAddress.from_string (parts[0].split ("/")[0]); |
148 | + if (ip != null) { |
149 | + if (ip.get_family () == SocketFamily.IPV6) { |
150 | + version = Version.IPV6; |
151 | + } else { |
152 | + version = Version.IPV4; |
153 | + } |
154 | + } |
155 | + } else { |
156 | + var ip_parts = parts[0].split ("/"); |
157 | + if (ip_parts.length > 1) { |
158 | + if (ip_parts[1] == "tcp" || ip_parts[1] == "udp") { |
159 | + ports = ip_parts[0]; |
160 | + } else { |
161 | + address = parts[0]; |
162 | + var ip = new InetAddress.from_string (ip_parts[0]); |
163 | + if (ip != null) { |
164 | + if (ip.get_family () == SocketFamily.IPV6) { |
165 | + version = Version.IPV6; |
166 | + } else { |
167 | + version = Version.IPV4; |
168 | + } |
169 | + } |
170 | + } |
171 | + } else { |
172 | + var ip = new InetAddress.from_string (ip_parts[0]); |
173 | + if (ip == null) { |
174 | + if (ip_parts[0].contains ("Anywhere")) { |
175 | + address = "Anywhere"; |
176 | + } else { |
177 | + ports = ip_parts[0]; |
178 | + } |
179 | + } else if (ip.get_family () == SocketFamily.IPV6) { |
180 | + address = ip_parts[0]; |
181 | + version = Version.IPV6; |
182 | + } else if (ip.get_family () == SocketFamily.IPV4) { |
183 | + address = ip_parts[0]; |
184 | + version = Version.IPV4; |
185 | + } |
186 | + } |
187 | + } |
188 | + } catch (Error e) { |
189 | + warning ("Error parsing to/from address: %s".printf (input)); |
190 | + warning (e.message); |
191 | + } |
192 | + } |
193 | + |
194 | public Rule.from_line (string line) { |
195 | - if (line.contains ("(v6)")) |
196 | - is_v6 = true; |
197 | - var first = line.replace ("(v6)", "").split ("] "); |
198 | - number = int.parse (first[0].replace ("[", "")); |
199 | - var second = first[1]; |
200 | - var third = second.split ("/"); |
201 | - ports = third[0]; |
202 | - string current = ""; |
203 | - int position = 0; |
204 | - foreach (var car in third[1].data) { |
205 | - if (car == ' ') { |
206 | - if (current == "") { |
207 | - continue; |
208 | - } |
209 | - |
210 | - if (position == 0) { |
211 | - if ("udp" in current) |
212 | - protocol = Protocol.UDP; |
213 | - else if ("tcp" in current) |
214 | - protocol = Protocol.TCP; |
215 | - } else if (position == 1) { |
216 | - if ("ALLOW" in current) |
217 | - action = Action.ALLOW; |
218 | - else if ("DENY" in current) |
219 | - action = Action.DENY; |
220 | - else if ("REJECT" in current) |
221 | - action = Action.REJECT; |
222 | - else if ("LIMIT" in current) |
223 | - action = Action.LIMIT; |
224 | - } else if (position == 2) { |
225 | - if ("IN" in current) |
226 | - direction = Direction.IN; |
227 | - else if ("OUT" in current) |
228 | - direction = Direction.OUT; |
229 | - break; |
230 | - } |
231 | - |
232 | - current = ""; |
233 | - position++; |
234 | - continue; |
235 | - } |
236 | - current = "%s%c".printf (current, car); |
237 | + if (line.contains ("(v6)")) { |
238 | + version = Version.IPV6; |
239 | + } else { |
240 | + version = Version.IPV4; |
241 | + } |
242 | + |
243 | + if (line.contains ("tcp")) { |
244 | + protocol = Protocol.TCP; |
245 | + } else if (line.contains ("udp")) { |
246 | + protocol = Protocol.UDP; |
247 | + } else { |
248 | + protocol = Protocol.BOTH; |
249 | + } |
250 | + |
251 | + try { |
252 | + |
253 | + var r = new Regex ("""\[\s*(\d+)\]\s{1}([A-Za-z0-9 \(\)/\.:,]+?)\s{2,}([A-Z ]+?)\s{2,}([A-Za-z0-9 \(\)/\.:,]+?)(?:\s{2,}.*)?$"""); |
254 | + MatchInfo info; |
255 | + r.match (line, 0, out info); |
256 | + |
257 | + number = int.parse (info.fetch (1)); |
258 | + |
259 | + string to_match = info.fetch (2).replace (" (v6)", ""); |
260 | + string from_match = info.fetch (4).replace (" (v6)", ""); |
261 | + |
262 | + get_address_and_port (to_match, ref version, ref to_ports, ref to); |
263 | + get_address_and_port (from_match, ref version, ref from_ports, ref from); |
264 | + |
265 | + string type = info.fetch (3); |
266 | + |
267 | + if ("ALLOW" in type) { |
268 | + action = Action.ALLOW; |
269 | + } else if ("DENY" in type) { |
270 | + action = Action.DENY; |
271 | + } else if ("REJECT" in type) { |
272 | + action = Action.REJECT; |
273 | + } else if ("LIMIT" in type) { |
274 | + action = Action.LIMIT; |
275 | + } |
276 | + |
277 | + if ("IN" in type) { |
278 | + direction = Direction.IN; |
279 | + } else if ("OUT" in type) { |
280 | + direction = Direction.OUT; |
281 | + } |
282 | + } catch (Error e) { |
283 | + return; |
284 | } |
285 | } |
286 | } |
287 | |
288 | === modified file 'src/Views/FirewallPanel.vala' |
289 | --- src/Views/FirewallPanel.vala 2017-03-02 17:18:04 +0000 |
290 | +++ src/Views/FirewallPanel.vala 2017-03-06 18:49:38 +0000 |
291 | @@ -27,13 +27,17 @@ |
292 | private bool loading = false; |
293 | private Gtk.Popover add_popover; |
294 | private Gtk.ToolButton remove_button; |
295 | + private Settings settings; |
296 | + private Gee.HashMap<string, UFWHelpers.Rule> disabled_rules; |
297 | |
298 | private enum Columns { |
299 | ACTION, |
300 | PROTOCOL, |
301 | DIRECTION, |
302 | - PORTS, |
303 | + TO, |
304 | + FROM, |
305 | V6, |
306 | + ENABLED, |
307 | RULE, |
308 | N_COLUMNS |
309 | } |
310 | @@ -45,6 +49,10 @@ |
311 | } |
312 | |
313 | construct { |
314 | + settings = new Settings ("org.pantheon.security-privacy"); |
315 | + disabled_rules = new Gee.HashMap<string, UFWHelpers.Rule> (); |
316 | + load_disabled_rules (); |
317 | + |
318 | status_switch.notify["active"].connect (() => { |
319 | if (loading == false) { |
320 | view.sensitive = status_switch.active; |
321 | @@ -65,9 +73,7 @@ |
322 | remove_button.sensitive = false; |
323 | if (status_switch.active == true) { |
324 | view.sensitive = true; |
325 | - foreach (var rule in UFWHelpers.get_rules ()) { |
326 | - add_rule (rule); |
327 | - } |
328 | + show_rules (); |
329 | } else { |
330 | view.sensitive = false; |
331 | } |
332 | @@ -75,15 +81,114 @@ |
333 | }); |
334 | } |
335 | |
336 | + private void load_disabled_rules () { |
337 | + disabled_rules = new Gee.HashMap<string, UFWHelpers.Rule> (); |
338 | + string? to = "", to_ports = "", from = "", from_ports = ""; |
339 | + int action = 0, protocol = 0, direction = 0, version = 0; |
340 | + var rules = settings.get_value ("disabled-firewall-rules"); |
341 | + VariantIter iter = rules.iterator (); |
342 | + while (iter.next ("(ssssiiii)", ref to, ref to_ports, ref from, ref from_ports, ref action, ref protocol, ref direction, ref version)) { |
343 | + UFWHelpers.Rule new_rule = new UFWHelpers.Rule (); |
344 | + new_rule.to = to; |
345 | + new_rule.to_ports = to_ports; |
346 | + new_rule.from = from; |
347 | + new_rule.from_ports = from_ports; |
348 | + new_rule.action = (UFWHelpers.Rule.Action)action; |
349 | + new_rule.protocol = (UFWHelpers.Rule.Protocol)protocol; |
350 | + new_rule.direction = (UFWHelpers.Rule.Direction)direction; |
351 | + new_rule.version = (UFWHelpers.Rule.Version)version; |
352 | + string hash = generate_hash_for_rule (new_rule); |
353 | + disabled_rules.set (hash, new_rule); |
354 | + } |
355 | + } |
356 | + |
357 | + private string generate_hash_for_rule (UFWHelpers.Rule r) { |
358 | + return r.to + |
359 | + r.to_ports + |
360 | + r.from + |
361 | + r.from_ports + |
362 | + r.action.to_string () + |
363 | + r.protocol.to_string () + |
364 | + r.direction.to_string () + |
365 | + r.version.to_string (); |
366 | + } |
367 | + |
368 | + private void reload_rule_numbers () { |
369 | + foreach (var rule in UFWHelpers.get_rules ()) { |
370 | + string ufw_hash = generate_hash_for_rule (rule); |
371 | + Gtk.TreeModelForeachFunc update_row = (model, path, iter) => { |
372 | + Value val; |
373 | + |
374 | + list_store.get_value (iter, Columns.RULE, out val); |
375 | + var tree_rule = (UFWHelpers.Rule)val; |
376 | + string tree_hash = generate_hash_for_rule (tree_rule); |
377 | + if (ufw_hash == tree_hash) { |
378 | + tree_rule.number = rule.number; |
379 | + list_store.set_value (iter, Columns.RULE, tree_rule); |
380 | + return true; |
381 | + } |
382 | + |
383 | + return false; |
384 | + }; |
385 | + list_store.foreach (update_row); |
386 | + } |
387 | + } |
388 | + |
389 | private void show_rules () { |
390 | list_store.clear (); |
391 | remove_button.sensitive = false; |
392 | foreach (var rule in UFWHelpers.get_rules ()) { |
393 | add_rule (rule); |
394 | } |
395 | - } |
396 | - |
397 | - public void add_rule (UFWHelpers.Rule rule) { |
398 | + |
399 | + load_disabled_rules (); |
400 | + foreach (var rule in disabled_rules.entries) { |
401 | + add_rule (rule.value, false, rule.key); |
402 | + } |
403 | + } |
404 | + |
405 | + private void disable_rule (UFWHelpers.Rule rule) { |
406 | + save_disabled_rules (rule); |
407 | + UFWHelpers.remove_rule (rule); |
408 | + } |
409 | + |
410 | + private void enable_rule (string hash) { |
411 | + UFWHelpers.add_rule (disabled_rules.get (hash)); |
412 | + delete_disabled_rule (hash); |
413 | + } |
414 | + |
415 | + private void delete_disabled_rule (string hash) { |
416 | + disabled_rules.unset (hash); |
417 | + save_disabled_rules (); |
418 | + } |
419 | + |
420 | + private void save_disabled_rules (UFWHelpers.Rule? additional_rule = null) { |
421 | + VariantBuilder builder = new VariantBuilder (new VariantType("a(ssssiiii)")); |
422 | + foreach (var existing_rule in disabled_rules.values) { |
423 | + builder.add ("(ssssiiii)", existing_rule.to, |
424 | + existing_rule.to_ports, |
425 | + existing_rule.from, |
426 | + existing_rule.from_ports, |
427 | + existing_rule.action, |
428 | + existing_rule.protocol, |
429 | + existing_rule.direction, |
430 | + existing_rule.version); |
431 | + } |
432 | + if (additional_rule != null) { |
433 | + builder.add ("(ssssiiii)", additional_rule.to, |
434 | + additional_rule.to_ports, |
435 | + additional_rule.from, |
436 | + additional_rule.from_ports, |
437 | + additional_rule.action, |
438 | + additional_rule.protocol, |
439 | + additional_rule.direction, |
440 | + additional_rule.version); |
441 | + } |
442 | + settings.set_value ("disabled-firewall-rules", builder.end ()); |
443 | + load_disabled_rules (); |
444 | + } |
445 | + |
446 | + public void add_rule (UFWHelpers.Rule rule, bool enabled = true, string hash = "") { |
447 | Gtk.TreeIter iter; |
448 | string action = _("Unknown"); |
449 | if (rule.action == UFWHelpers.Rule.Action.ALLOW) { |
450 | @@ -100,6 +205,8 @@ |
451 | protocol = "UDP"; |
452 | } else if (rule.protocol == UFWHelpers.Rule.Protocol.TCP) { |
453 | protocol = "TCP"; |
454 | + } else if (rule.protocol == UFWHelpers.Rule.Protocol.BOTH) { |
455 | + protocol = "TCP/UDP"; |
456 | } |
457 | string direction = _("Unknown"); |
458 | if (rule.direction == UFWHelpers.Rule.Direction.IN) { |
459 | @@ -107,27 +214,86 @@ |
460 | } else if (rule.direction == UFWHelpers.Rule.Direction.OUT) { |
461 | direction = _("Out"); |
462 | } |
463 | + string version = _("Unknown"); |
464 | + if (rule.version == UFWHelpers.Rule.Version.IPV6) { |
465 | + version = "IPv6"; |
466 | + } else if (rule.version == UFWHelpers.Rule.Version.IPV4) { |
467 | + version = "IPv4"; |
468 | + } |
469 | + |
470 | + string from = ""; |
471 | + string to = ""; |
472 | + if (rule.from_ports != "") { |
473 | + if (rule.from_ports.contains (":") || rule.from_ports.contains (",")) { |
474 | + from = _("%s Ports %s").printf (rule.from, rule.from_ports.replace (":", "-")); |
475 | + } else { |
476 | + from = _("%s Port %s").printf (rule.from, rule.from_ports.replace (":", "-")); |
477 | + } |
478 | + } else { |
479 | + from = rule.from; |
480 | + } |
481 | + |
482 | + if (rule.to_ports != "") { |
483 | + if (rule.to_ports.contains (":") || rule.to_ports.contains (",")) { |
484 | + to = _("%s Ports %s").printf (rule.to, rule.to_ports.replace (":", "-")); |
485 | + } else { |
486 | + to = _("%s Port %s").printf (rule.to, rule.to_ports.replace (":", "-")); |
487 | + } |
488 | + } else { |
489 | + to = rule.to; |
490 | + } |
491 | + |
492 | list_store.append (out iter); |
493 | list_store.set (iter, Columns.ACTION, action, Columns.PROTOCOL, protocol, |
494 | - Columns.DIRECTION, direction, Columns.PORTS, rule.ports.replace (":", "-"), |
495 | - Columns.V6, rule.is_v6, Columns.RULE, rule); |
496 | + Columns.DIRECTION, direction, Columns.V6, version, Columns.ENABLED, enabled, |
497 | + Columns.RULE, rule, Columns.TO, to.strip (), Columns.FROM, from.strip ()); |
498 | } |
499 | |
500 | private void create_treeview () { |
501 | list_store = new Gtk.ListStore (Columns.N_COLUMNS, typeof (string), |
502 | - typeof (string), typeof (string), typeof (string), typeof (bool), typeof (UFWHelpers.Rule)); |
503 | + typeof (string), |
504 | + typeof (string), |
505 | + typeof (string), |
506 | + typeof (string), |
507 | + typeof (string), |
508 | + typeof (bool), |
509 | + typeof (UFWHelpers.Rule)); |
510 | |
511 | // The View: |
512 | view = new Gtk.TreeView.with_model (list_store); |
513 | view.vexpand = true; |
514 | + view.activate_on_single_click = true; |
515 | |
516 | var celltoggle = new Gtk.CellRendererToggle (); |
517 | var cell = new Gtk.CellRendererText (); |
518 | - view.insert_column_with_attributes (-1, _("IPv6"), celltoggle, "active", Columns.V6); |
519 | + view.insert_column_with_attributes (-1, _("Enabled"), celltoggle, "active", Columns.ENABLED); |
520 | + view.insert_column_with_attributes (-1, _("Version"), cell, "text", Columns.V6); |
521 | view.insert_column_with_attributes (-1, _("Action"), cell, "text", Columns.ACTION); |
522 | view.insert_column_with_attributes (-1, _("Protocol"), cell, "text", Columns.PROTOCOL); |
523 | view.insert_column_with_attributes (-1, _("Direction"), cell, "text", Columns.DIRECTION); |
524 | - view.insert_column_with_attributes (-1, _("Ports"), cell, "text", Columns.PORTS); |
525 | + view.insert_column_with_attributes (-1, _("To"), cell, "text", Columns.TO); |
526 | + view.insert_column_with_attributes (-1, _("From"), cell, "text", Columns.FROM); |
527 | + |
528 | + celltoggle.toggled.connect ((path) => { |
529 | + Value active; |
530 | + Gtk.TreeIter iter; |
531 | + list_store.get_iter (out iter, new Gtk.TreePath.from_string(path)); |
532 | + list_store.get_value (iter, Columns.ENABLED, out active); |
533 | + var is_active = !active.get_boolean (); |
534 | + list_store.set (iter, Columns.ENABLED, is_active); |
535 | + |
536 | + Value rule_value; |
537 | + list_store.get_value (iter, Columns.RULE, out rule_value); |
538 | + UFWHelpers.Rule rule = (UFWHelpers.Rule)rule_value.get_object (); |
539 | + string gen_hash = generate_hash_for_rule (rule); |
540 | + if (is_active == false) { |
541 | + disable_rule (rule); |
542 | + } else { |
543 | + enable_rule (gen_hash); |
544 | + } |
545 | + |
546 | + reload_rule_numbers (); |
547 | + }); |
548 | |
549 | list_toolbar = new Gtk.Toolbar (); |
550 | list_toolbar.get_style_context ().add_class (Gtk.STYLE_CLASS_INLINE_TOOLBAR); |
551 | @@ -160,6 +326,14 @@ |
552 | protocol_combobox.append_text ("UDP"); |
553 | protocol_combobox.active = 0; |
554 | |
555 | + var version_label = new Gtk.Label (_("Version:")); |
556 | + version_label.xalign = 1; |
557 | + var version_combobox = new Gtk.ComboBoxText (); |
558 | + version_combobox.append_text ("IPv4"); |
559 | + version_combobox.append_text ("IPv6"); |
560 | + version_combobox.append_text (_("Both")); |
561 | + version_combobox.active = 0; |
562 | + |
563 | var direction_label = new Gtk.Label (_("Direction:")); |
564 | direction_label.xalign = 1; |
565 | var direction_combobox = new Gtk.ComboBoxText (); |
566 | @@ -196,7 +370,14 @@ |
567 | else |
568 | rule.action = UFWHelpers.Rule.Action.LIMIT; |
569 | |
570 | - rule.ports = ports_entry.text.replace ("-", ":"); |
571 | + if (version_combobox.active == 0) |
572 | + rule.version = UFWHelpers.Rule.Version.IPV4; |
573 | + else if (version_combobox.active == 1) |
574 | + rule.version = UFWHelpers.Rule.Version.IPV6; |
575 | + else |
576 | + rule.version = UFWHelpers.Rule.Version.BOTH; |
577 | + |
578 | + rule.to_ports = ports_entry.text.replace ("-", ":"); |
579 | UFWHelpers.add_rule (rule); |
580 | add_popover.hide (); |
581 | show_rules (); |
582 | @@ -210,11 +391,13 @@ |
583 | popover_grid.attach (policy_combobox, 1, 0, 1, 1); |
584 | popover_grid.attach (protocol_label, 0, 1, 1, 1); |
585 | popover_grid.attach (protocol_combobox, 1, 1, 1, 1); |
586 | - popover_grid.attach (direction_label, 0, 2, 1, 1); |
587 | - popover_grid.attach (direction_combobox, 1, 2, 1, 1); |
588 | - popover_grid.attach (ports_label, 0, 3, 1, 1); |
589 | - popover_grid.attach (ports_entry, 1, 3, 1, 1); |
590 | - popover_grid.attach (add_button_grid, 0, 4, 2, 1); |
591 | + popover_grid.attach (version_label, 0, 2, 1, 1); |
592 | + popover_grid.attach (version_combobox, 1, 2, 1, 1); |
593 | + popover_grid.attach (direction_label, 0, 3, 1, 1); |
594 | + popover_grid.attach (direction_combobox, 1, 3, 1, 1); |
595 | + popover_grid.attach (ports_label, 0, 4, 1, 1); |
596 | + popover_grid.attach (ports_entry, 1, 4, 1, 1); |
597 | + popover_grid.attach (add_button_grid, 0, 5, 2, 1); |
598 | |
599 | add_popover.show_all (); |
600 | }); |
601 | @@ -230,7 +413,15 @@ |
602 | list_store.get_iter (out iter, path); |
603 | Value val; |
604 | list_store.get_value (iter, Columns.RULE, out val); |
605 | - UFWHelpers.remove_rule ((UFWHelpers.Rule) val.get_object ()); |
606 | + var rule = (UFWHelpers.Rule) val.get_object (); |
607 | + string gen_hash = generate_hash_for_rule (rule); |
608 | + Value active; |
609 | + list_store.get_value (iter, Columns.ENABLED, out active); |
610 | + if (active.get_boolean ()) { |
611 | + UFWHelpers.remove_rule (rule); |
612 | + } else { |
613 | + delete_disabled_rule (gen_hash); |
614 | + } |
615 | show_rules (); |
616 | }); |
617 | list_toolbar.insert (remove_button, -1); |
I really don't like the validation of IP addresses with the regex. It makes the code look like clutter. from_string" to parse the IP address you can check if it's null to make sure it's valid and you can also call "get_family" on it to retrive if it's IPv4 or IPv6 address.
Instead you can use much simpler solution with GLib.InetAddress: by using "InetAddress.
Also, about this line:
while (iter.next ("(ssssiiii)", &to, &to_ports, &from, &from_ports, &action, &protocol, &direction, &version)) {
do you really need to use those C like arguments in order to retrieve needed variables? Does "out" not compile?