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() {