Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

List Wireguard connection as possible VPN #244

Merged
merged 3 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Widgets/DisplayWidget.vala
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ public class Network.Widgets.DisplayWidget : Gtk.Grid {
case Network.State.DISCONNECTED:
image.icon_name = "panel-network-wireless-offline-symbolic";
break;
case Network.State.FAILED:
case Network.State.WIRED_UNPLUGGED:
image.icon_name = "panel-network-wired-offline-symbolic";
break;
Expand Down
28 changes: 18 additions & 10 deletions src/Widgets/PopoverWidget.vala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

public class Network.Widgets.PopoverWidget : Gtk.Grid {
public NM.Client nm_client { get; construct; }
private NM.VpnConnection? active_vpn_connection = null;

public GLib.List<WidgetNMInterface>? network_interface { get; private owned set; }

Expand Down Expand Up @@ -340,16 +339,25 @@ public class Network.Widgets.PopoverWidget : Gtk.Grid {
}

private void update_vpn_connection () {
active_vpn_connection = null;

/* Stupid heuristic for now: at least one connection must be secure to
* display the secure badge. */
// Reset the current status
secure = false;
nm_client.get_active_connections ().foreach ((ac) => {
if (active_vpn_connection == null && ac.vpn) {
active_vpn_connection = (NM.VpnConnection) ac;

secure = active_vpn_connection.get_vpn_state () == NM.VpnConnectionState.ACTIVATED;
active_vpn_connection.vpn_state_changed.connect (() => {
secure = active_vpn_connection.get_vpn_state () == NM.VpnConnectionState.ACTIVATED;
});
unowned string connection_type = ac.get_connection_type ();
if (connection_type == NM.SettingVpn.SETTING_NAME) {
/* We cannot rely on the sole state_changed signal, as it will
* silently ignore sub-vpn specific states, like tun/tap
* interface connection etc. That's why we keep a separate
* implementation for the signal handlers. */
var _connection = (NM.VpnConnection) ac;
secure = secure || (_connection.get_vpn_state () == NM.VpnConnectionState.ACTIVATED);
_connection.vpn_state_changed.disconnect (update_vpn_connection);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these disconnects helping? I feel like I am missing something here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question… I’m not sure how signaling really works internally. Are callbacks stored in a simple list or a set (i.e. if we connnect again and again the same callback, do we get a full list of duplicated handlers, or does the "connect" method already take care to discard existing handlers when registering? I cannot found anything about this in the documentation, that’s why I did the disconnection myself to ensure we keep only one callback at a time.

Actually, when I did my tests, I discover that without disconnecting previous handlers, I ended with a lot of parallel callback calls after some time (easily testable with a simple log output), that’s why I added this disconnect call before to avoid duplicate. But maybe I missed something.

If one has a good documentation entry about maintaining signal handlers, I’d appreciate!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think @Marukesu has made good suggestions for documentation before on the Slack. Maybe he has insight with this?

_connection.vpn_state_changed.connect (update_vpn_connection);
} else if (connection_type == NM.SettingWireGuard.SETTING_NAME) {
secure = secure || (ac.get_state () == NM.ActiveConnectionState.ACTIVATED);
ac.state_changed.disconnect (update_vpn_connection);
ac.state_changed.connect (update_vpn_connection);
}
});
}
Expand Down
14 changes: 8 additions & 6 deletions src/Widgets/VpnInterface.vala
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,14 @@ public class Network.VpnInterface : Network.WidgetNMInterface {
}

private void active_connected_added_cb (NM.ActiveConnection active_connection) {
if (!active_connection.vpn) {
unowned string connection_type = active_connection.get_connection_type ();
if (connection_type != NM.SettingVpn.SETTING_NAME && connection_type != NM.SettingWireGuard.SETTING_NAME) {
return;
}

var menu_item = get_item_for_active_connection (active_connection);
if (menu_item != null) {
menu_item.vpn_connection = (NM.VpnConnection) active_connection;
menu_item.vpn_connection = active_connection;
}
}

Expand All @@ -80,13 +81,13 @@ public class Network.VpnInterface : Network.WidgetNMInterface {

item.cancellable = new Cancellable ();

if (item.vpn_connection != null && item.vpn_connection.get_vpn_state () == NM.VpnConnectionState.ACTIVATED) {
if (item.vpn_connection != null && item.vpn_connection.get_state () == NM.ActiveConnectionState.ACTIVATED) {
nm_client.deactivate_connection_async.begin (item.vpn_connection, item.cancellable, (obj, res) => {
try {
((NM.Client) obj).deactivate_connection_async.end (res);
item.cancellable = null;
} catch (Error e) {
critical ("Unable to deactivate VPN: %s", e.message);
critical ("Unable to deactivate VPN or Wireguard: %s", e.message);
}
});
} else {
Expand All @@ -95,15 +96,16 @@ public class Network.VpnInterface : Network.WidgetNMInterface {
((NM.Client) obj).activate_connection_async.end (res);
item.cancellable = null;
} catch (Error e) {
critical ("Unable to activate VPN: %s", e.message);
critical ("Unable to activate VPN or Wireguard: %s", e.message);
}
});
}
}

private void vpn_added_cb (Object obj) {
var remote_connection = (NM.RemoteConnection) obj;
if (remote_connection.get_connection_type () == NM.SettingVpn.SETTING_NAME) {
unowned string connection_type = remote_connection.get_connection_type ();
if (connection_type == NM.SettingVpn.SETTING_NAME || connection_type == NM.SettingWireGuard.SETTING_NAME) {
var item = new VpnMenuItem (remote_connection);
vpn_list.add (item);
check_vpn_availability ();
Expand Down
96 changes: 68 additions & 28 deletions src/Widgets/VpnMenuItem.vala
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,37 @@ public class Network.VpnMenuItem : Gtk.FlowBoxChild {
public NM.RemoteConnection remote_connection { get; construct; }
public Cancellable? cancellable = null;

private NM.VpnConnection? _vpn_connection = null;
public NM.VpnConnection? vpn_connection {
private NM.ActiveConnection? _vpn_connection = null;
public NM.ActiveConnection? vpn_connection {
get {
return _vpn_connection;
}
set {
if (value != null) {
_vpn_connection = value;
_vpn_connection.vpn_state_changed.connect (update_state);
/* We cannot rely on the sole state_changed signal, as it will
* silently ignore sub-vpn specific states, like tun/tap
* interface connection etc. That's why we keep a separate
* implementation for the signal handlers. */
if (value.get_vpn ()) {
((NM.VpnConnection)_vpn_connection).vpn_state_changed.connect (update_state);
} else {
_vpn_connection.state_changed.connect (update_state);
}
update_state ();
} else {
_vpn_connection.vpn_state_changed.disconnect (update_state);
return;
}
if (_vpn_connection != null) {
if (_vpn_connection.get_vpn ()) {
((NM.VpnConnection)_vpn_connection).vpn_state_changed.disconnect (update_state);
} else {
_vpn_connection.state_changed.disconnect (update_state);
}
_vpn_connection = null;

((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-disconnected-symbolic";
toggle_button.active = false;
}

((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-disconnected-symbolic";
toggle_button.active = false;
}
}

Expand Down Expand Up @@ -76,26 +89,53 @@ public class Network.VpnMenuItem : Gtk.FlowBoxChild {
}

private void update_state () {
switch (vpn_connection.vpn_state) {
case NM.VpnConnectionState.CONNECT:
case NM.VpnConnectionState.IP_CONFIG_GET:
case NM.VpnConnectionState.NEED_AUTH:
case NM.VpnConnectionState.PREPARE:
((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-acquiring-symbolic";
break;
case NM.VpnConnectionState.ACTIVATED:
((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-connected-symbolic";
toggle_button.active = true;
break;
case NM.VpnConnectionState.DISCONNECTED:
((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-disconnected-symbolic";
toggle_button.active = false;
break;
case NM.VpnConnectionState.FAILED:
case NM.VpnConnectionState.UNKNOWN:
((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-error-symbolic";
toggle_button.active = false;
break;
if (_vpn_connection == null) {
((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-disconnected-symbolic";
toggle_button.active = false;
return;
}
unowned string connection_type = _vpn_connection.get_connection_type ();
if (connection_type == NM.SettingVpn.SETTING_NAME) {
switch (((NM.VpnConnection)_vpn_connection).vpn_state) {
case NM.VpnConnectionState.CONNECT:
case NM.VpnConnectionState.IP_CONFIG_GET:
case NM.VpnConnectionState.NEED_AUTH:
case NM.VpnConnectionState.PREPARE:
((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-acquiring-symbolic";
break;
case NM.VpnConnectionState.ACTIVATED:
((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-connected-symbolic";
toggle_button.active = true;
break;
case NM.VpnConnectionState.DISCONNECTED:
((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-disconnected-symbolic";
toggle_button.active = false;
break;
case NM.VpnConnectionState.FAILED:
case NM.VpnConnectionState.UNKNOWN:
((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-error-symbolic";
toggle_button.active = false;
break;
}
} else if (connection_type == NM.SettingWireGuard.SETTING_NAME) {
switch (_vpn_connection.get_state ()) {
case NM.ActiveConnectionState.UNKNOWN:
((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-error-symbolic";
toggle_button.active = false;
break;
case NM.ActiveConnectionState.DEACTIVATED:
case NM.ActiveConnectionState.DEACTIVATING:
((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-disconnected-symbolic";
toggle_button.active = false;
break;
case NM.ActiveConnectionState.ACTIVATING:
((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-acquiring-symbolic";
break;
case NM.ActiveConnectionState.ACTIVATED:
((Gtk.Image) toggle_button.image).icon_name = "panel-network-vpn-connected-symbolic";
toggle_button.active = true;
break;
}
}
}
}
2 changes: 2 additions & 0 deletions src/Widgets/WifiMenuItem.vala
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ public class Network.WifiMenuItem : Gtk.ListBoxRow {
critical ("An access point is being connected but not active.");
}
break;
default:
break;
}
}

Expand Down