summaryrefslogtreecommitdiff
path: root/portal-notify.patch
diff options
context:
space:
mode:
Diffstat (limited to 'portal-notify.patch')
-rw-r--r--portal-notify.patch499
1 files changed, 499 insertions, 0 deletions
diff --git a/portal-notify.patch b/portal-notify.patch
new file mode 100644
index 0000000..0b1028c
--- /dev/null
+++ b/portal-notify.patch
@@ -0,0 +1,499 @@
+From 4a03da36817c8d22a32a63d5c115efcf49ce85f5 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
+Date: Wed, 12 Jun 2024 13:13:41 +0200
+Subject: [PATCH 1/3] network: Split out CaptivePortalHandler class
+
+The handling of captive portals is going to be extended a bit,
+so split out a proper class instead of mixing it in with the
+indicator code.
+---
+ js/ui/status/network.js | 152 ++++++++++++++++++++++------------------
+ 1 file changed, 84 insertions(+), 68 deletions(-)
+
+diff --git a/js/ui/status/network.js b/js/ui/status/network.js
+index b407d8e78d..6d070f6d88 100644
+--- a/js/ui/status/network.js
++++ b/js/ui/status/network.js
+@@ -51,7 +51,7 @@ var PortalHelperResult = {
+ };
+
+ const PortalHelperIface = loadInterfaceXML('org.gnome.Shell.PortalHelper');
+-const PortalHelperProxy = Gio.DBusProxy.makeProxyWrapper(PortalHelperIface);
++const PortalHelperInfo = Gio.DBusInterfaceInfo.new_for_xml(PortalHelperIface);
+
+ function signalToIcon(value) {
+ if (value < 20)
+@@ -1707,6 +1707,77 @@ var DeviceCategory = class extends PopupMenu.PopupMenuSection {
+ }
+ };
+
++class CaptivePortalHandler {
++ constructor(checkUri) {
++ this._checkUri = checkUri;
++ this._connectivityQueue = new Set();
++ this._portalHelperProxy = null;
++ }
++
++ addConnection(path) {
++ if (this._connectivityQueue.has(path))
++ return;
++
++ this._launchPortalHelper(path).catch(logError);
++ }
++
++ removeConnection(path) {
++ if (this._connectivityQueue.delete(path))
++ this._portalHelperProxy?.CloseRemote(path);
++ }
++
++ _portalHelperDone(parameters) {
++ const [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 they choose a better connection
++ // or we get to full connectivity through other means
++ } else if (result === PortalHelperResult.COMPLETED) {
++ this.removeConnection(path);
++ } else if (result === PortalHelperResult.RECHECK) {
++ this.emit('recheck', path);
++ } else {
++ log(`Invalid result from portal helper: ${result}`);
++ }
++ }
++
++ async _launchPortalHelper(path) {
++ const timestamp = global.get_current_time();
++ if (!this._portalHelperProxy) {
++ this._portalHelperProxy = new Gio.DBusProxy({
++ g_connection: Gio.DBus.session,
++ g_name: 'org.gnome.Shell.PortalHelper',
++ g_object_path: '/org/gnome/Shell/PortalHelper',
++ g_interface_name: PortalHelperInfo.name,
++ g_interface_info: PortalHelperInfo,
++ });
++ this._portalHelperProxy.connectSignal('Done',
++ (proxy, emitter, params) => {
++ this._portalHelperDone(params);
++ });
++
++ try {
++ await this._portalHelperProxy.init_async(
++ GLib.PRIORITY_DEFAULT, null);
++ } catch (e) {
++ console.error(`Error launching the portal helper: ${e.message}`);
++ }
++ }
++
++ this._portalHelperProxy?.AuthenticateRemote(path, this._checkUri, timestamp);
++ this._connectivityQueue.add(path);
++ }
++
++ clear() {
++ for (const item of this._connectivityQueue)
++ this._portalHelperProxy?.CloseRemote(item);
++ this._connectivityQueue.clear();
++ }
++}
++Signals.addSignalMethods(CaptivePortalHandler.prototype);
++
+ var NMApplet = GObject.registerClass(
+ class Indicator extends PanelMenu.SystemIndicator {
+ _init() {
+@@ -1763,6 +1834,16 @@ class Indicator extends PanelMenu.SystemIndicator {
+ this._vpnSection.connect('icon-changed', this._updateIcon.bind(this));
+ this.menu.addMenuItem(this._vpnSection.item);
+
++ const {connectivityCheckUri} = this._client;
++ this._portalHandler = new CaptivePortalHandler(connectivityCheckUri);
++ this._portalHandler.connect('recheck', async (o, path) => {
++ try {
++ const state = await this._client.check_connectivity_async(null);
++ if (state >= NM.ConnectivityState.FULL)
++ this._portalHandler.removeConnection(path);
++ } catch (e) { }
++ });
++
+ this._readConnections();
+ this._readDevices();
+ this._syncNMState();
+@@ -2074,51 +2155,10 @@ class Indicator extends PanelMenu.SystemIndicator {
+ this._syncConnectivity();
+ }
+
+- _flushConnectivityQueue() {
+- if (this._portalHelperProxy) {
+- for (let item of this._connectivityQueue)
+- this._portalHelperProxy.CloseRemote(item);
+- }
+-
+- this._connectivityQueue = [];
+- }
+-
+- _closeConnectivityCheck(path) {
+- let index = this._connectivityQueue.indexOf(path);
+-
+- if (index >= 0) {
+- if (this._portalHelperProxy)
+- this._portalHelperProxy.CloseRemote(path);
+-
+- this._connectivityQueue.splice(index, 1);
+- }
+- }
+-
+- async _portalHelperDone(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);
+- } else if (result == PortalHelperResult.RECHECK) {
+- try {
+- const state = await this._client.check_connectivity_async(null);
+- if (state >= NM.ConnectivityState.FULL)
+- this._closeConnectivityCheck(path);
+- } catch (e) { }
+- } else {
+- log('Invalid result from portal helper: %s'.format(result));
+- }
+- }
+-
+ _syncConnectivity() {
+ if (this._mainConnection == null ||
+ this._mainConnection.state != NM.ActiveConnectionState.ACTIVATED) {
+- this._flushConnectivityQueue();
++ this._portalHandler.clear();
+ return;
+ }
+
+@@ -2133,31 +2173,7 @@ class Indicator extends PanelMenu.SystemIndicator {
+ if (!isPortal || Main.sessionMode.isGreeter)
+ 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', (proxy, error) => {
+- if (error) {
+- log('Error launching the portal helper: %s'.format(error));
+- return;
+- }
+-
+- this._portalHelperProxy = proxy;
+- proxy.connectSignal('Done', this._portalHelperDone.bind(this));
+-
+- proxy.AuthenticateRemote(path, '', timestamp);
+- });
+- }
+-
+- this._connectivityQueue.push(path);
++ this._portalHandler.addConnection(this._mainConnection.get_path());
+ }
+
+ _updateIcon() {
+--
+2.45.2
+
+
+From 7d1a2442c957df3a31ab544da9b4e1365b8c2348 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
+Date: Wed, 12 Jun 2024 13:13:41 +0200
+Subject: [PATCH 2/3] status/network: Show notification when detecting captive
+ portal
+
+When NetworkManager detects limited connectivity, we currently
+pop up the portal helper window immediately. This can both be
+disruptive when it happens unexpectedly, and unnoticeable
+when it happens during screen lock.
+
+In any case, it seems better to not pop up a window without
+explicit user action, so instead show a notification that
+launches the portal window when activated.
+
+Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/7688
+---
+ js/ui/status/network.js | 38 ++++++++++++++++++++++++++++++++++----
+ 1 file changed, 34 insertions(+), 4 deletions(-)
+
+diff --git a/js/ui/status/network.js b/js/ui/status/network.js
+index 6d070f6d88..5913467454 100644
+--- a/js/ui/status/network.js
++++ b/js/ui/status/network.js
+@@ -1711,19 +1711,43 @@ class CaptivePortalHandler {
+ constructor(checkUri) {
+ this._checkUri = checkUri;
+ this._connectivityQueue = new Set();
++ this._notifications = new Map();
+ this._portalHelperProxy = null;
+ }
+
+- addConnection(path) {
+- if (this._connectivityQueue.has(path))
++ addConnection(name, path) {
++ if (this._connectivityQueue.has(path) || this._notifications.has(path))
+ return;
+
+- this._launchPortalHelper(path).catch(logError);
++ const source = new MessageTray.Source(
++ _('System'),
++ 'emblem-system-symbolic');
++ Main.messageTray.add(source);
++
++ const notification = new MessageTray.Notification(
++ source, _('Sign Into Wi–Fi Network'), name);
++ notification.connect('activated',
++ () => this._onNotificationActivated(path));
++ notification.connect('destroy',
++ () => this._notifications.delete(path));
++ this._notifications.set(path, notification);
++ source.showNotification(notification);
+ }
+
++
+ removeConnection(path) {
+ if (this._connectivityQueue.delete(path))
+ this._portalHelperProxy?.CloseRemote(path);
++ this._notifications.get(path)?.destroy(
++ MessageTray.NotificationDestroyedReason.SOURCE_CLOSED);
++ this._notifications.delete(path);
++ }
++
++ _onNotificationActivated(path) {
++ this._launchPortalHelper(path).catch(logError);
++
++ Main.overview.hide();
++ Main.panel.closeCalendar();
+ }
+
+ _portalHelperDone(parameters) {
+@@ -1774,6 +1798,10 @@ class CaptivePortalHandler {
+ for (const item of this._connectivityQueue)
+ this._portalHelperProxy?.CloseRemote(item);
+ this._connectivityQueue.clear();
++
++ for (const n of this._notifications.values())
++ n.destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED);
++ this._notifications.clear();
+ }
+ }
+ Signals.addSignalMethods(CaptivePortalHandler.prototype);
+@@ -2173,7 +2201,9 @@ class Indicator extends PanelMenu.SystemIndicator {
+ if (!isPortal || Main.sessionMode.isGreeter)
+ return;
+
+- this._portalHandler.addConnection(this._mainConnection.get_path());
++ this._portalHandler.addConnection(
++ this._mainConnection.get_id(),
++ this._mainConnection.get_path());
+ }
+
+ _updateIcon() {
+--
+2.45.2
+
+
+From cda2810d13bb9b5294759a8268cfbb27d83190f1 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
+Date: Wed, 12 Jun 2024 13:13:41 +0200
+Subject: [PATCH 3/3] build: Add option to disable portal-helper
+
+The portal login window uses WebKit, which is a security-sensitive
+component that not all vendors want to support.
+
+Support that case with a build option, and update the captive
+portal handler to use the user's default browser if the portal-helper
+is disabled.
+---
+ data/icons/meson.build | 10 +++++++++-
+ data/meson.build | 2 +-
+ js/meson.build | 14 ++++++++------
+ js/misc/config.js.in | 2 ++
+ js/misc/meson.build | 1 +
+ js/ui/status/network.js | 13 ++++++++++---
+ meson.build | 5 +++++
+ meson_options.txt | 6 ++++++
+ src/meson.build | 2 +-
+ 9 files changed, 43 insertions(+), 12 deletions(-)
+
+diff --git a/data/icons/meson.build b/data/icons/meson.build
+index eff6e4b530..277df017b2 100644
+--- a/data/icons/meson.build
++++ b/data/icons/meson.build
+@@ -1 +1,9 @@
+-install_subdir('hicolor', install_dir: icondir)
++excluded_icons=[]
++if not have_portal_helper
++ excluded_icons += [
++ 'scalable/apps/org.gnome.Shell.CaptivePortal.svg',
++ 'symbolic/apps/org.gnome.Shell.CaptivePortal-symbolic.svg',
++ ]
++endif
++install_subdir('hicolor',
++ install_dir: icondir, exclude_files: excluded_icons)
+diff --git a/data/meson.build b/data/meson.build
+index 4a1e16d467..01cf828310 100644
+--- a/data/meson.build
++++ b/data/meson.build
+@@ -4,7 +4,7 @@ desktop_files = [
+ ]
+ service_files = []
+
+-if have_networkmanager
++if have_portal_helper
+ desktop_files += 'org.gnome.Shell.PortalHelper.desktop'
+ service_files += 'org.gnome.Shell.PortalHelper.service'
+ endif
+diff --git a/js/meson.build b/js/meson.build
+index 4809f82b83..e594e23627 100644
+--- a/js/meson.build
++++ b/js/meson.build
+@@ -8,9 +8,11 @@ js_resources = gnome.compile_resources(
+ dependencies: [config_js]
+ )
+
+-portal_resources = gnome.compile_resources(
+- 'portal-resources', 'portal-resources.gresource.xml',
+- source_dir: ['.', meson.current_build_dir()],
+- c_name: 'portal_js_resources',
+- dependencies: [config_js]
+-)
++if have_portal_helper
++ portal_resources = gnome.compile_resources(
++ 'portal-resources', 'portal-resources.gresource.xml',
++ source_dir: ['.', meson.current_build_dir()],
++ c_name: 'portal_js_resources',
++ dependencies: [config_js]
++ )
++endif
+diff --git a/js/misc/config.js.in b/js/misc/config.js.in
+index e54e280441..0882af6d01 100644
+--- a/js/misc/config.js.in
++++ b/js/misc/config.js.in
+@@ -8,6 +8,8 @@ var PACKAGE_VERSION = '@PACKAGE_VERSION@';
+ var HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
+ /* 1 if networkmanager is available, 0 otherwise */
+ var HAVE_NETWORKMANAGER = @HAVE_NETWORKMANAGER@;
++/* 1 if portal helper is enabled, 0 otherwise */
++var HAVE_PORTAL_HELPER = @HAVE_PORTAL_HELPER@;
+ /* gettext package */
+ var GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@';
+ /* locale dir */
+diff --git a/js/misc/meson.build b/js/misc/meson.build
+index 2702c3dbc9..5f5f6c390f 100644
+--- a/js/misc/meson.build
++++ b/js/misc/meson.build
+@@ -5,6 +5,7 @@ jsconf.set('GETTEXT_PACKAGE', meson.project_name())
+ jsconf.set('LIBMUTTER_API_VERSION', mutter_api_version)
+ jsconf.set10('HAVE_BLUETOOTH', bt_dep.found())
+ jsconf.set10('HAVE_NETWORKMANAGER', have_networkmanager)
++jsconf.set10('HAVE_PORTAL_HELPER', have_portal_helper)
+ jsconf.set('datadir', datadir)
+ jsconf.set('libexecdir', libexecdir)
+
+diff --git a/js/ui/status/network.js b/js/ui/status/network.js
+index 5913467454..27a5eeb823 100644
+--- a/js/ui/status/network.js
++++ b/js/ui/status/network.js
+@@ -4,6 +4,7 @@ const { Clutter, Gio, GLib, GObject, Meta, NM, Polkit, St } = imports.gi;
+ const Signals = imports.signals;
+
+ const Animation = imports.ui.animation;
++const Config = imports.misc.config;
+ const Main = imports.ui.main;
+ const PanelMenu = imports.ui.panelMenu;
+ const PopupMenu = imports.ui.popupMenu;
+@@ -1744,7 +1745,13 @@ class CaptivePortalHandler {
+ }
+
+ _onNotificationActivated(path) {
+- this._launchPortalHelper(path).catch(logError);
++ const context = global.create_app_launch_context(
++ global.get_current_time(), -1);
++
++ if (Config.HAVE_PORTAL_HELPER)
++ this._launchPortalHelper(path, context).catch(logError);
++ else
++ Gio.AppInfo.launch_default_for_uri(this._checkUri, context);
+
+ Main.overview.hide();
+ Main.panel.closeCalendar();
+@@ -1767,8 +1774,7 @@ class CaptivePortalHandler {
+ }
+ }
+
+- async _launchPortalHelper(path) {
+- const timestamp = global.get_current_time();
++ async _launchPortalHelper(path, context) {
+ if (!this._portalHelperProxy) {
+ this._portalHelperProxy = new Gio.DBusProxy({
+ g_connection: Gio.DBus.session,
+@@ -1790,6 +1796,7 @@ class CaptivePortalHandler {
+ }
+ }
+
++ const {timestamp} = context;
+ this._portalHelperProxy?.AuthenticateRemote(path, this._checkUri, timestamp);
+ this._connectivityQueue.add(path);
+ }
+diff --git a/meson.build b/meson.build
+index ff841dccf2..8feac29224 100644
+--- a/meson.build
++++ b/meson.build
+@@ -116,6 +116,11 @@ else
+ have_networkmanager = false
+ endif
+
++have_portal_helper = get_option('portal_helper')
++if have_portal_helper and not have_networkmanager
++ error('Portal helper requires networkmanager support')
++endif
++
+ if get_option('systemd')
+ libsystemd_dep = dependency('libsystemd')
+ systemd_dep = dependency('systemd')
+diff --git a/meson_options.txt b/meson_options.txt
+index ef76b73c34..7666fc5421 100644
+--- a/meson_options.txt
++++ b/meson_options.txt
+@@ -28,6 +28,12 @@ option('networkmanager',
+ description: 'Enable NetworkManager support'
+ )
+
++option('portal_helper',
++ type: 'boolean',
++ value: true,
++ description: 'Enable build-in network portal login'
++)
++
+ option('systemd',
+ type: 'boolean',
+ value: true,
+diff --git a/src/meson.build b/src/meson.build
+index d235c37438..3e1accf2e7 100644
+--- a/src/meson.build
++++ b/src/meson.build
+@@ -250,7 +250,7 @@ executable('gnome-shell', 'main.c',
+ install: true
+ )
+
+-if have_networkmanager
++if have_portal_helper
+ executable('gnome-shell-portal-helper',
+ 'gnome-shell-portal-helper.c', portal_resources,
+ c_args: tools_cflags,
+--
+2.45.2
+