Merge lp:~davidmhewitt/switchboard-plug-security-privacy/fix-1312461-control-rule into lp:~elementary-apps/switchboard-plug-security-privacy/trunk

Proposed by David Hewitt
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
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.

To post a comment you must log in.
Revision history for this message
Adam Bieńkowski (donadigo) 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.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.

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?

review: Needs Fixing (code)
Revision history for this message
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.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.
>
> 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.from_string an invalid IP Address, it throws an assertion instead of an exception meaning it terminates the program. Considering I'd be trying to use it to validate if something is an IP address or not, it's no help.

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

Revision history for this message
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.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.
>
> 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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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);

Subscribers

People subscribed via source and target branches

to all changes: