[gnome-shell] NetworkManager: show portal logins when required



commit 47c9243271619ece99e041f9fa767fb94c8853b6
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Mon Feb 17 18:06:35 2014 +0100

    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 files changed, 121 insertions(+), 0 deletions(-)
---
diff --git a/js/ui/status/network.js b/js/ui/status/network.js
index f3e568d..049406d 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 = '<node> \
+<interface name="org.gnome.Shell.PortalHelper"> \
+<method name="Authenticate"> \
+    <arg type="o" direction="in" name="connection" /> \
+    <arg type="s" direction="in" name="url" /> \
+    <arg type="u" direction="in" name="timestamp" /> \
+</method> \
+<method name="Close"> \
+    <arg type="o" direction="in" name="connection" /> \
+</method> \
+<method name="Refresh"> \
+    <arg type="o" direction="in" name="connection" /> \
+</method> \
+<signal name="Done"> \
+    <arg type="o" name="connection" /> \
+    <arg type="u" name="result" /> \
+</signal> \
+</interface> \
+</node>';
+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() {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]