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: Merged
Approved by: Danielle Foré
Approved revision: 330
Merged at revision: 324
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 Approve
elementary Apps team Pending
Review via email: mp+319121@code.launchpad.net

This proposal supersedes a proposal from 2017-03-05.

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

> 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.

Revision history for this message
David Hewitt (davidmhewitt) wrote : Posted in a previous version of this proposal

> 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!

Revision history for this message
Adam Bieńkowski (donadigo) wrote :

Besides 2 places where the code is a little bit inconsistent in code style, it looks good (code).

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2016-12-01 11:56:30 +0000
+++ CMakeLists.txt 2017-03-06 18:53:07 +0000
@@ -38,9 +38,7 @@
38include (CPack)38include (CPack)
39add_custom_target (dist COMMAND ${CMAKE_MAKE_PROGRAM} package_source)39add_custom_target (dist COMMAND ${CMAKE_MAKE_PROGRAM} package_source)
4040
41configure_file (${CMAKE_SOURCE_DIR}/data/org.pantheon.security-privacy.policy.cmake ${CMAKE_BINARY_DIR}/data/org.pantheon.security-privacy.policy)41add_subdirectory (data)
42install(FILES ${CMAKE_BINARY_DIR}/data/org.pantheon.security-privacy.policy DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/polkit-1/actions/)
43install(FILES ${CMAKE_SOURCE_DIR}/data/security-privacy-plug-helper PERMISSIONS OWNER_EXECUTE OWNER_READ DESTINATION ${PKGDATADIR}/)
4442
45# Traslation stuff43# Traslation stuff
46add_subdirectory (po)44add_subdirectory (po)
4745
=== added file 'data/CMakeLists.txt'
--- data/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ data/CMakeLists.txt 2017-03-06 18:53:07 +0000
@@ -0,0 +1,6 @@
1configure_file (${CMAKE_SOURCE_DIR}/data/org.pantheon.security-privacy.policy.cmake ${CMAKE_BINARY_DIR}/data/org.pantheon.security-privacy.policy)
2install(FILES ${CMAKE_BINARY_DIR}/data/org.pantheon.security-privacy.policy DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/polkit-1/actions/)
3install(FILES ${CMAKE_SOURCE_DIR}/data/security-privacy-plug-helper PERMISSIONS OWNER_EXECUTE OWNER_READ DESTINATION ${PKGDATADIR}/)
4
5include (GSettings)
6add_schema ("org.pantheon.security-privacy.gschema.xml")
07
=== added file 'data/org.pantheon.security-privacy.gschema.xml'
--- data/org.pantheon.security-privacy.gschema.xml 1970-01-01 00:00:00 +0000
+++ data/org.pantheon.security-privacy.gschema.xml 2017-03-06 18:53:07 +0000
@@ -0,0 +1,12 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<schemalist>
3
4<schema id="org.pantheon.security-privacy" path="/org/pantheon/security-privacy/">
5 <key name="disabled-firewall-rules" type="a(ssssiiii)">
6 <default>[]</default>
7 <summary>Firewall rules that have been disabled, but should still be listed in the plug</summary>
8 <description>An array of rules, each containing action, protocol, direction and (bool v6) in that order.</description>
9 </key>
10</schema>
11
12</schemalist>
013
=== modified file 'src/UFWHelpers.vala'
--- src/UFWHelpers.vala 2017-03-01 22:38:37 +0000
+++ src/UFWHelpers.vala 2017-03-06 18:53:07 +0000
@@ -103,17 +103,54 @@
103 default:103 default:
104 rule_str = "%s in".printf (rule_str);104 rule_str = "%s in".printf (rule_str);
105 break;105 break;
106 }106 }
107107
108 switch (rule.protocol) {108 switch (rule.protocol) {
109 case Rule.Protocol.UDP:109 case Rule.Protocol.UDP:
110 rule_str = "%s %s/udp".printf (rule_str, rule.ports);110 rule_str = "%s proto udp".printf (rule_str);
111 break;
112 case Rule.Protocol.BOTH:
111 break;113 break;
112 default:114 default:
113 rule_str = "%s %s/tcp".printf (rule_str, rule.ports);115 rule_str = "%s proto tcp".printf (rule_str);
114 break;116 break;
115 }117 }
116118
119 if (rule.to != "" && !rule.to.contains ("Anywhere")) {
120 rule_str = "%s to %s".printf (rule_str, rule.to);
121 if (rule.to_ports != "") {
122 rule_str = "%s port %s".printf (rule_str, rule.to_ports);
123 }
124 } else {
125 if (rule.version == Rule.Version.BOTH) {
126 rule_str = "%s to any".printf (rule_str);
127 } else if (rule.version == Rule.Version.IPV6) {
128 rule_str = "%s to ::/0".printf (rule_str);
129 } else if (rule.version == Rule.Version.IPV4) {
130 rule_str = "%s to 0.0.0.0/0".printf (rule_str);
131 }
132 if (rule.to_ports != "") {
133 rule_str = "%s port %s".printf (rule_str, rule.to_ports);
134 }
135 }
136
137 if (rule.from != "" && !rule.from.contains ("Anywhere")) {
138 rule_str = "%s from %s".printf (rule_str, rule.from);
139 if (rule.from_ports != "") {
140 rule_str = "%s port %s".printf (rule_str, rule.from_ports);
141 }
142 } else {
143 if (rule.version == Rule.Version.BOTH) {
144 rule_str = "%s from any".printf (rule_str);
145 } else if (rule.version == Rule.Version.IPV6) {
146 rule_str = "%s from ::/0".printf (rule_str);
147 } else if (rule.version == Rule.Version.IPV4) {
148 rule_str = "%s from 0.0.0.0/0".printf (rule_str);
149 }
150 if (rule.from_ports != "") {
151 rule_str = "%s port %s".printf (rule_str, rule.from_ports);
152 }
153 }
117 Process.spawn_command_line_sync ("pkexec %s -5 \"%s\"".printf (get_helper_path (), rule_str));154 Process.spawn_command_line_sync ("pkexec %s -5 \"%s\"".printf (get_helper_path (), rule_str));
118 } catch (Error e) {155 } catch (Error e) {
119 warning (e.message);156 warning (e.message);
@@ -130,7 +167,8 @@
130167
131 public enum Protocol {168 public enum Protocol {
132 UDP,169 UDP,
133 TCP170 TCP,
171 BOTH
134 }172 }
135173
136 public enum Direction {174 public enum Direction {
@@ -138,60 +176,127 @@
138 OUT176 OUT
139 }177 }
140178
179 public enum Version {
180 IPV4,
181 IPV6,
182 BOTH
183 }
184
141 public Action action;185 public Action action;
142 public Protocol protocol;186 public Protocol protocol;
143 public Direction direction;187 public Direction direction;
144 public string ports;188 public string to_ports = "";
145 public bool is_v6 = false;189 public string from_ports = "";
190 public string to = "";
191 public string from = "";
192 public Version version = Version.BOTH;
146 public int number;193 public int number;
147194
148 public Rule () {195 public Rule () {
149 196
150 }197 }
151198
199 private void get_address_and_port (string input, ref Version version, ref string ports, ref string address) {
200 try {
201 var parts = input.split (" ");
202 if (parts.length > 1) {
203 ports = parts[1].split("/")[0];
204 address = parts[0];
205 var ip = new InetAddress.from_string (parts[0].split ("/")[0]);
206 if (ip != null) {
207 if (ip.get_family () == SocketFamily.IPV6) {
208 version = Version.IPV6;
209 } else {
210 version = Version.IPV4;
211 }
212 }
213 } else {
214 var ip_parts = parts[0].split ("/");
215 if (ip_parts.length > 1) {
216 if (ip_parts[1] == "tcp" || ip_parts[1] == "udp") {
217 ports = ip_parts[0];
218 } else {
219 address = parts[0];
220 var ip = new InetAddress.from_string (ip_parts[0]);
221 if (ip != null) {
222 if (ip.get_family () == SocketFamily.IPV6) {
223 version = Version.IPV6;
224 } else {
225 version = Version.IPV4;
226 }
227 }
228 }
229 } else {
230 var ip = new InetAddress.from_string (ip_parts[0]);
231 if (ip == null) {
232 if (ip_parts[0].contains ("Anywhere")) {
233 address = "Anywhere";
234 } else {
235 ports = ip_parts[0];
236 }
237 } else if (ip.get_family () == SocketFamily.IPV6) {
238 address = ip_parts[0];
239 version = Version.IPV6;
240 } else if (ip.get_family () == SocketFamily.IPV4) {
241 address = ip_parts[0];
242 version = Version.IPV4;
243 }
244 }
245 }
246 } catch (Error e) {
247 warning ("Error parsing to/from address: %s".printf (input));
248 warning (e.message);
249 }
250 }
251
152 public Rule.from_line (string line) {252 public Rule.from_line (string line) {
153 if (line.contains ("(v6)"))253 if (line.contains ("(v6)")) {
154 is_v6 = true;254 version = Version.IPV6;
155 var first = line.replace ("(v6)", "").split ("] ");255 } else {
156 number = int.parse (first[0].replace ("[", ""));256 version = Version.IPV4;
157 var second = first[1];257 }
158 var third = second.split ("/");258
159 ports = third[0];259 if (line.contains ("tcp")) {
160 string current = "";260 protocol = Protocol.TCP;
161 int position = 0;261 } else if (line.contains ("udp")) {
162 foreach (var car in third[1].data) {262 protocol = Protocol.UDP;
163 if (car == ' ') {263 } else {
164 if (current == "") {264 protocol = Protocol.BOTH;
165 continue;265 }
166 }266
167267 try {
168 if (position == 0) {268
169 if ("udp" in current)269 var r = new Regex ("""\[\s*(\d+)\]\s{1}([A-Za-z0-9 \(\)/\.:,]+?)\s{2,}([A-Z ]+?)\s{2,}([A-Za-z0-9 \(\)/\.:,]+?)(?:\s{2,}.*)?$""");
170 protocol = Protocol.UDP;270 MatchInfo info;
171 else if ("tcp" in current)271 r.match (line, 0, out info);
172 protocol = Protocol.TCP;272
173 } else if (position == 1) {273 number = int.parse (info.fetch (1));
174 if ("ALLOW" in current)274
175 action = Action.ALLOW;275 string to_match = info.fetch (2).replace (" (v6)", "");
176 else if ("DENY" in current)276 string from_match = info.fetch (4).replace (" (v6)", "");
177 action = Action.DENY;277
178 else if ("REJECT" in current)278 get_address_and_port (to_match, ref version, ref to_ports, ref to);
179 action = Action.REJECT;279 get_address_and_port (from_match, ref version, ref from_ports, ref from);
180 else if ("LIMIT" in current)280
181 action = Action.LIMIT;281 string type = info.fetch (3);
182 } else if (position == 2) {282
183 if ("IN" in current)283 if ("ALLOW" in type) {
184 direction = Direction.IN;284 action = Action.ALLOW;
185 else if ("OUT" in current)285 } else if ("DENY" in type) {
186 direction = Direction.OUT;286 action = Action.DENY;
187 break;287 } else if ("REJECT" in type) {
188 }288 action = Action.REJECT;
189289 } else if ("LIMIT" in type) {
190 current = "";290 action = Action.LIMIT;
191 position++;291 }
192 continue;292
193 }293 if ("IN" in type) {
194 current = "%s%c".printf (current, car);294 direction = Direction.IN;
295 } else if ("OUT" in type) {
296 direction = Direction.OUT;
297 }
298 } catch (Error e) {
299 return;
195 }300 }
196 }301 }
197 }302 }
198303
=== modified file 'src/Views/FirewallPanel.vala'
--- src/Views/FirewallPanel.vala 2017-03-02 17:18:04 +0000
+++ src/Views/FirewallPanel.vala 2017-03-06 18:53:07 +0000
@@ -27,13 +27,17 @@
27 private bool loading = false;27 private bool loading = false;
28 private Gtk.Popover add_popover;28 private Gtk.Popover add_popover;
29 private Gtk.ToolButton remove_button;29 private Gtk.ToolButton remove_button;
30 private Settings settings;
31 private Gee.HashMap<string, UFWHelpers.Rule> disabled_rules;
3032
31 private enum Columns {33 private enum Columns {
32 ACTION,34 ACTION,
33 PROTOCOL,35 PROTOCOL,
34 DIRECTION,36 DIRECTION,
35 PORTS,37 TO,
38 FROM,
36 V6,39 V6,
40 ENABLED,
37 RULE,41 RULE,
38 N_COLUMNS42 N_COLUMNS
39 }43 }
@@ -45,6 +49,10 @@
45 }49 }
4650
47 construct {51 construct {
52 settings = new Settings ("org.pantheon.security-privacy");
53 disabled_rules = new Gee.HashMap<string, UFWHelpers.Rule> ();
54 load_disabled_rules ();
55
48 status_switch.notify["active"].connect (() => {56 status_switch.notify["active"].connect (() => {
49 if (loading == false) {57 if (loading == false) {
50 view.sensitive = status_switch.active;58 view.sensitive = status_switch.active;
@@ -65,9 +73,7 @@
65 remove_button.sensitive = false;73 remove_button.sensitive = false;
66 if (status_switch.active == true) {74 if (status_switch.active == true) {
67 view.sensitive = true;75 view.sensitive = true;
68 foreach (var rule in UFWHelpers.get_rules ()) {76 show_rules ();
69 add_rule (rule);
70 }
71 } else {77 } else {
72 view.sensitive = false;78 view.sensitive = false;
73 }79 }
@@ -75,15 +81,114 @@
75 });81 });
76 }82 }
7783
84 private void load_disabled_rules () {
85 disabled_rules = new Gee.HashMap<string, UFWHelpers.Rule> ();
86 string? to = "", to_ports = "", from = "", from_ports = "";
87 int action = 0, protocol = 0, direction = 0, version = 0;
88 var rules = settings.get_value ("disabled-firewall-rules");
89 VariantIter iter = rules.iterator ();
90 while (iter.next ("(ssssiiii)", ref to, ref to_ports, ref from, ref from_ports, ref action, ref protocol, ref direction, ref version)) {
91 UFWHelpers.Rule new_rule = new UFWHelpers.Rule ();
92 new_rule.to = to;
93 new_rule.to_ports = to_ports;
94 new_rule.from = from;
95 new_rule.from_ports = from_ports;
96 new_rule.action = (UFWHelpers.Rule.Action)action;
97 new_rule.protocol = (UFWHelpers.Rule.Protocol)protocol;
98 new_rule.direction = (UFWHelpers.Rule.Direction)direction;
99 new_rule.version = (UFWHelpers.Rule.Version)version;
100 string hash = generate_hash_for_rule (new_rule);
101 disabled_rules.set (hash, new_rule);
102 }
103 }
104
105 private string generate_hash_for_rule (UFWHelpers.Rule r) {
106 return r.to +
107 r.to_ports +
108 r.from +
109 r.from_ports +
110 r.action.to_string () +
111 r.protocol.to_string () +
112 r.direction.to_string () +
113 r.version.to_string ();
114 }
115
116 private void reload_rule_numbers () {
117 foreach (var rule in UFWHelpers.get_rules ()) {
118 string ufw_hash = generate_hash_for_rule (rule);
119 Gtk.TreeModelForeachFunc update_row = (model, path, iter) => {
120 Value val;
121
122 list_store.get_value (iter, Columns.RULE, out val);
123 var tree_rule = (UFWHelpers.Rule)val;
124 string tree_hash = generate_hash_for_rule (tree_rule);
125 if (ufw_hash == tree_hash) {
126 tree_rule.number = rule.number;
127 list_store.set_value (iter, Columns.RULE, tree_rule);
128 return true;
129 }
130
131 return false;
132 };
133 list_store.foreach (update_row);
134 }
135 }
136
78 private void show_rules () {137 private void show_rules () {
79 list_store.clear ();138 list_store.clear ();
80 remove_button.sensitive = false;139 remove_button.sensitive = false;
81 foreach (var rule in UFWHelpers.get_rules ()) {140 foreach (var rule in UFWHelpers.get_rules ()) {
82 add_rule (rule);141 add_rule (rule);
83 }142 }
84 }143
85144 load_disabled_rules ();
86 public void add_rule (UFWHelpers.Rule rule) {145 foreach (var rule in disabled_rules.entries) {
146 add_rule (rule.value, false, rule.key);
147 }
148 }
149
150 private void disable_rule (UFWHelpers.Rule rule) {
151 save_disabled_rules (rule);
152 UFWHelpers.remove_rule (rule);
153 }
154
155 private void enable_rule (string hash) {
156 UFWHelpers.add_rule (disabled_rules.get (hash));
157 delete_disabled_rule (hash);
158 }
159
160 private void delete_disabled_rule (string hash) {
161 disabled_rules.unset (hash);
162 save_disabled_rules ();
163 }
164
165 private void save_disabled_rules (UFWHelpers.Rule? additional_rule = null) {
166 VariantBuilder builder = new VariantBuilder (new VariantType("a(ssssiiii)"));
167 foreach (var existing_rule in disabled_rules.values) {
168 builder.add ("(ssssiiii)", existing_rule.to,
169 existing_rule.to_ports,
170 existing_rule.from,
171 existing_rule.from_ports,
172 existing_rule.action,
173 existing_rule.protocol,
174 existing_rule.direction,
175 existing_rule.version);
176 }
177 if (additional_rule != null) {
178 builder.add ("(ssssiiii)", additional_rule.to,
179 additional_rule.to_ports,
180 additional_rule.from,
181 additional_rule.from_ports,
182 additional_rule.action,
183 additional_rule.protocol,
184 additional_rule.direction,
185 additional_rule.version);
186 }
187 settings.set_value ("disabled-firewall-rules", builder.end ());
188 load_disabled_rules ();
189 }
190
191 public void add_rule (UFWHelpers.Rule rule, bool enabled = true, string hash = "") {
87 Gtk.TreeIter iter;192 Gtk.TreeIter iter;
88 string action = _("Unknown");193 string action = _("Unknown");
89 if (rule.action == UFWHelpers.Rule.Action.ALLOW) {194 if (rule.action == UFWHelpers.Rule.Action.ALLOW) {
@@ -100,6 +205,8 @@
100 protocol = "UDP";205 protocol = "UDP";
101 } else if (rule.protocol == UFWHelpers.Rule.Protocol.TCP) {206 } else if (rule.protocol == UFWHelpers.Rule.Protocol.TCP) {
102 protocol = "TCP";207 protocol = "TCP";
208 } else if (rule.protocol == UFWHelpers.Rule.Protocol.BOTH) {
209 protocol = "TCP/UDP";
103 }210 }
104 string direction = _("Unknown");211 string direction = _("Unknown");
105 if (rule.direction == UFWHelpers.Rule.Direction.IN) {212 if (rule.direction == UFWHelpers.Rule.Direction.IN) {
@@ -107,27 +214,86 @@
107 } else if (rule.direction == UFWHelpers.Rule.Direction.OUT) {214 } else if (rule.direction == UFWHelpers.Rule.Direction.OUT) {
108 direction = _("Out");215 direction = _("Out");
109 }216 }
217 string version = _("Unknown");
218 if (rule.version == UFWHelpers.Rule.Version.IPV6) {
219 version = "IPv6";
220 } else if (rule.version == UFWHelpers.Rule.Version.IPV4) {
221 version = "IPv4";
222 }
223
224 string from = "";
225 string to = "";
226 if (rule.from_ports != "") {
227 if (rule.from_ports.contains (":") || rule.from_ports.contains (",")) {
228 from = _("%s Ports %s").printf (rule.from, rule.from_ports.replace (":", "-"));
229 } else {
230 from = _("%s Port %s").printf (rule.from, rule.from_ports.replace (":", "-"));
231 }
232 } else {
233 from = rule.from;
234 }
235
236 if (rule.to_ports != "") {
237 if (rule.to_ports.contains (":") || rule.to_ports.contains (",")) {
238 to = _("%s Ports %s").printf (rule.to, rule.to_ports.replace (":", "-"));
239 } else {
240 to = _("%s Port %s").printf (rule.to, rule.to_ports.replace (":", "-"));
241 }
242 } else {
243 to = rule.to;
244 }
245
110 list_store.append (out iter);246 list_store.append (out iter);
111 list_store.set (iter, Columns.ACTION, action, Columns.PROTOCOL, protocol,247 list_store.set (iter, Columns.ACTION, action, Columns.PROTOCOL, protocol,
112 Columns.DIRECTION, direction, Columns.PORTS, rule.ports.replace (":", "-"),248 Columns.DIRECTION, direction, Columns.V6, version, Columns.ENABLED, enabled,
113 Columns.V6, rule.is_v6, Columns.RULE, rule);249 Columns.RULE, rule, Columns.TO, to.strip (), Columns.FROM, from.strip ());
114 }250 }
115251
116 private void create_treeview () {252 private void create_treeview () {
117 list_store = new Gtk.ListStore (Columns.N_COLUMNS, typeof (string),253 list_store = new Gtk.ListStore (Columns.N_COLUMNS, typeof (string),
118 typeof (string), typeof (string), typeof (string), typeof (bool), typeof (UFWHelpers.Rule));254 typeof (string),
255 typeof (string),
256 typeof (string),
257 typeof (string),
258 typeof (string),
259 typeof (bool),
260 typeof (UFWHelpers.Rule));
119261
120 // The View:262 // The View:
121 view = new Gtk.TreeView.with_model (list_store);263 view = new Gtk.TreeView.with_model (list_store);
122 view.vexpand = true;264 view.vexpand = true;
265 view.activate_on_single_click = true;
123266
124 var celltoggle = new Gtk.CellRendererToggle ();267 var celltoggle = new Gtk.CellRendererToggle ();
125 var cell = new Gtk.CellRendererText ();268 var cell = new Gtk.CellRendererText ();
126 view.insert_column_with_attributes (-1, _("IPv6"), celltoggle, "active", Columns.V6);269 view.insert_column_with_attributes (-1, _("Enabled"), celltoggle, "active", Columns.ENABLED);
270 view.insert_column_with_attributes (-1, _("Version"), cell, "text", Columns.V6);
127 view.insert_column_with_attributes (-1, _("Action"), cell, "text", Columns.ACTION);271 view.insert_column_with_attributes (-1, _("Action"), cell, "text", Columns.ACTION);
128 view.insert_column_with_attributes (-1, _("Protocol"), cell, "text", Columns.PROTOCOL);272 view.insert_column_with_attributes (-1, _("Protocol"), cell, "text", Columns.PROTOCOL);
129 view.insert_column_with_attributes (-1, _("Direction"), cell, "text", Columns.DIRECTION);273 view.insert_column_with_attributes (-1, _("Direction"), cell, "text", Columns.DIRECTION);
130 view.insert_column_with_attributes (-1, _("Ports"), cell, "text", Columns.PORTS);274 view.insert_column_with_attributes (-1, _("To"), cell, "text", Columns.TO);
275 view.insert_column_with_attributes (-1, _("From"), cell, "text", Columns.FROM);
276
277 celltoggle.toggled.connect ((path) => {
278 Value active;
279 Gtk.TreeIter iter;
280 list_store.get_iter (out iter, new Gtk.TreePath.from_string(path));
281 list_store.get_value (iter, Columns.ENABLED, out active);
282 var is_active = !active.get_boolean ();
283 list_store.set (iter, Columns.ENABLED, is_active);
284
285 Value rule_value;
286 list_store.get_value (iter, Columns.RULE, out rule_value);
287 UFWHelpers.Rule rule = (UFWHelpers.Rule)rule_value.get_object ();
288 string gen_hash = generate_hash_for_rule (rule);
289 if (is_active == false) {
290 disable_rule (rule);
291 } else {
292 enable_rule (gen_hash);
293 }
294
295 reload_rule_numbers ();
296 });
131297
132 list_toolbar = new Gtk.Toolbar ();298 list_toolbar = new Gtk.Toolbar ();
133 list_toolbar.get_style_context ().add_class (Gtk.STYLE_CLASS_INLINE_TOOLBAR);299 list_toolbar.get_style_context ().add_class (Gtk.STYLE_CLASS_INLINE_TOOLBAR);
@@ -160,6 +326,14 @@
160 protocol_combobox.append_text ("UDP");326 protocol_combobox.append_text ("UDP");
161 protocol_combobox.active = 0;327 protocol_combobox.active = 0;
162328
329 var version_label = new Gtk.Label (_("Version:"));
330 version_label.xalign = 1;
331 var version_combobox = new Gtk.ComboBoxText ();
332 version_combobox.append_text ("IPv4");
333 version_combobox.append_text ("IPv6");
334 version_combobox.append_text (_("Both"));
335 version_combobox.active = 0;
336
163 var direction_label = new Gtk.Label (_("Direction:"));337 var direction_label = new Gtk.Label (_("Direction:"));
164 direction_label.xalign = 1;338 direction_label.xalign = 1;
165 var direction_combobox = new Gtk.ComboBoxText ();339 var direction_combobox = new Gtk.ComboBoxText ();
@@ -196,7 +370,14 @@
196 else370 else
197 rule.action = UFWHelpers.Rule.Action.LIMIT;371 rule.action = UFWHelpers.Rule.Action.LIMIT;
198372
199 rule.ports = ports_entry.text.replace ("-", ":");373 if (version_combobox.active == 0)
374 rule.version = UFWHelpers.Rule.Version.IPV4;
375 else if (version_combobox.active == 1)
376 rule.version = UFWHelpers.Rule.Version.IPV6;
377 else
378 rule.version = UFWHelpers.Rule.Version.BOTH;
379
380 rule.to_ports = ports_entry.text.replace ("-", ":");
200 UFWHelpers.add_rule (rule);381 UFWHelpers.add_rule (rule);
201 add_popover.hide ();382 add_popover.hide ();
202 show_rules ();383 show_rules ();
@@ -210,11 +391,13 @@
210 popover_grid.attach (policy_combobox, 1, 0, 1, 1);391 popover_grid.attach (policy_combobox, 1, 0, 1, 1);
211 popover_grid.attach (protocol_label, 0, 1, 1, 1);392 popover_grid.attach (protocol_label, 0, 1, 1, 1);
212 popover_grid.attach (protocol_combobox, 1, 1, 1, 1);393 popover_grid.attach (protocol_combobox, 1, 1, 1, 1);
213 popover_grid.attach (direction_label, 0, 2, 1, 1);394 popover_grid.attach (version_label, 0, 2, 1, 1);
214 popover_grid.attach (direction_combobox, 1, 2, 1, 1);395 popover_grid.attach (version_combobox, 1, 2, 1, 1);
215 popover_grid.attach (ports_label, 0, 3, 1, 1);396 popover_grid.attach (direction_label, 0, 3, 1, 1);
216 popover_grid.attach (ports_entry, 1, 3, 1, 1);397 popover_grid.attach (direction_combobox, 1, 3, 1, 1);
217 popover_grid.attach (add_button_grid, 0, 4, 2, 1);398 popover_grid.attach (ports_label, 0, 4, 1, 1);
399 popover_grid.attach (ports_entry, 1, 4, 1, 1);
400 popover_grid.attach (add_button_grid, 0, 5, 2, 1);
218401
219 add_popover.show_all ();402 add_popover.show_all ();
220 });403 });
@@ -230,7 +413,15 @@
230 list_store.get_iter (out iter, path);413 list_store.get_iter (out iter, path);
231 Value val;414 Value val;
232 list_store.get_value (iter, Columns.RULE, out val);415 list_store.get_value (iter, Columns.RULE, out val);
233 UFWHelpers.remove_rule ((UFWHelpers.Rule) val.get_object ());416 var rule = (UFWHelpers.Rule) val.get_object ();
417 string gen_hash = generate_hash_for_rule (rule);
418 Value active;
419 list_store.get_value (iter, Columns.ENABLED, out active);
420 if (active.get_boolean ()) {
421 UFWHelpers.remove_rule (rule);
422 } else {
423 delete_disabled_rule (gen_hash);
424 }
234 show_rules ();425 show_rules ();
235 });426 });
236 list_toolbar.insert (remove_button, -1);427 list_toolbar.insert (remove_button, -1);

Subscribers

People subscribed via source and target branches

to all changes: