From 47c9243271619ece99e041f9fa767fb94c8853b6 Mon Sep 17 00:00:00 2001 From: Giovanni Campagna Date: Mon, 17 Feb 2014 18:06:35 +0100 Subject: [PATCH] NetworkManager: show portal logins when required Listen to changes in connectivity, and ask our helper to authenticate when needed. We don't have a URL to connect to yet (we will have when the new NM API lands), so we use the default of trying www.gnome.org (which is also more reliable because we can recognize when the login is done) https://bugzilla.gnome.org/show_bug.cgi?id=704416 --- js/ui/status/network.js | 121 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/js/ui/status/network.js b/js/ui/status/network.js index f3e568dd6..049406d08 100644 --- a/js/ui/status/network.js +++ b/js/ui/status/network.js @@ -44,6 +44,33 @@ const NM80211Mode = NetworkManager['80211Mode']; const NM80211ApFlags = NetworkManager['80211ApFlags']; const NM80211ApSecurityFlags = NetworkManager['80211ApSecurityFlags']; +const PortalHelperResult = { + CANCELLED: 0, + COMPLETED: 1, + RECHECK: 2 +}; + +const PortalHelperIface = ' \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +'; +const PortalHelperProxy = Gio.DBusProxy.makeProxyWrapper(PortalHelperIface); + function ssidCompare(one, two) { if (!one || !two) return false; @@ -1581,6 +1608,7 @@ const NMApplet = new Lang.Class({ this._activeConnections = [ ]; this._connections = [ ]; + this._connectivityQueue = [ ]; this._mainConnection = null; this._mainConnectionIconChangedId = 0; @@ -1609,6 +1637,7 @@ const NMApplet = new Lang.Class({ this._client.connect('notify::primary-connection', Lang.bind(this, this._syncMainConnection)); this._client.connect('notify::activating-connection', Lang.bind(this, this._syncMainConnection)); this._client.connect('notify::active-connections', Lang.bind(this, this._syncVPNConnections)); + this._client.connect('notify::connectivity', Lang.bind(this, this._syncConnectivity)); this._client.connect('device-added', Lang.bind(this, this._deviceAdded)); this._client.connect('device-removed', Lang.bind(this, this._deviceRemoved)); this._settings.connect('new-connection', Lang.bind(this, this._newConnection)); @@ -1777,6 +1806,7 @@ const NMApplet = new Lang.Class({ } this._updateIcon(); + this._syncConnectivity(); }, _syncVPNConnections: function() { @@ -1882,6 +1912,97 @@ const NMApplet = new Lang.Class({ _syncNMState: function() { this.indicators.visible = this._client.manager_running; this.menu.actor.visible = this._client.networking_enabled; + + this._syncConnectivity(); + }, + + _flushConnectivityQueue: function() { + if (this._portalHelperProxy) { + for (let item of this._connectivityQueue) + this._portalHelperProxy.CloseRemote(item); + } + + this._connectivityQueue = []; + }, + + _closeConnectivityCheck: function(path) { + let index = this._connectivityQueue.indexOf(path); + + if (index >= 0) { + if (this._portalHelperProxy) + this._portalHelperProxy.CloseRemote(path); + + this._connectivityQueue.splice(index, 1); + } + }, + + _portalHelperDone: function(proxy, emitter, parameters) { + let [path, result] = parameters; + + if (result == PortalHelperResult.CANCELLED) { + // Keep the connection in the queue, so the user is not + // spammed with more logins until we next flush the queue, + // which will happen once he chooses a better connection + // or we get to full connectivity through other means + } else if (result == PortalHelperResult.COMPLETED) { + this._closeConnectivityCheck(path); + return; + } else if (result == PortalHelperResult.RECHECK) { + this._client.check_connectivity_async(null, Lang.bind(this, function(client, result) { + try { + let state = client.check_connectivity_finish(result); + if (state >= NetworkManager.ConnectivityState.FULL) + this._closeConnectivityCheck(path); + } catch(e) { } + })); + } else { + log('Invalid result from portal helper: ' + result); + } + }, + + _syncConnectivity: function() { + if (this._mainConnection == null || + this._mainConnection.state != NetworkManager.ActiveConnectionState.ACTIVATED) { + this._flushConnectivityQueue(); + return; + } + + let isPortal = this._client.connectivity == NetworkManager.ConnectivityState.PORTAL; + // For testing, allow interpreting any value != FULL as PORTAL, because + // LIMITED (no upstream route after the default gateway) is easy to obtain + // with a tethered phone + // NONE is also possible, with a connection configured to force no default route + // (but in general we should only prompt a portal if we know there is a portal) + if (GLib.getenv('GNOME_SHELL_CONNECTIVITY_TEST') != null) + isPortal = isPortal || this._client.connectivity < NetworkManager.ConnectivityState.FULL; + if (!isPortal) + return; + + let path = this._mainConnection.get_path(); + for (let item of this._connectivityQueue) { + if (item == path) + return; + } + + let timestamp = global.get_current_time(); + if (this._portalHelperProxy) { + this._portalHelperProxy.AuthenticateRemote(path, '', timestamp); + } else { + new PortalHelperProxy(Gio.DBus.session, 'org.gnome.Shell.PortalHelper', + '/org/gnome/Shell/PortalHelper', Lang.bind(this, function (proxy, error) { + if (error) { + log('Error launching the portal helper: ' + error); + return; + } + + this._portalHelperProxy = proxy; + proxy.connectSignal('Done', Lang.bind(this, this._portalHelperDone)); + + proxy.AuthenticateRemote(path, '', timestamp); + })); + } + + this._connectivityQueue.push(path); }, _updateIcon: function() {