diff options
Diffstat (limited to 'portal-notify.patch')
-rw-r--r-- | portal-notify.patch | 499 |
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 + |