This commit is contained in:
Dain Nilsson 2024-04-16 15:00:45 +02:00
commit c8cb6b3fe9
No known key found for this signature in database
GPG Key ID: F04367096FBA95E8
2 changed files with 58 additions and 5 deletions

View File

@ -74,8 +74,13 @@ https://developers.yubico.com/yubikey-manager/Device_Permissions.html[here].
For some configurations running Wayland, copying an OTP to clipboard only works For some configurations running Wayland, copying an OTP to clipboard only works
when the app has focus. If you are unable to reliably copy to clipboard from when the app has focus. If you are unable to reliably copy to clipboard from
the systray icon, you can set the environment variable `_YA_WL_CLIPFIX=1` to the systray icon, you can use a separate binary which take the payload to stdin
enable a workaround that attempts to give the window focus before copying. by defining the environment variable `_YA_TRAY_CLIPBOARD`. Note that this must
be an absolute path to a binary owned by root:root, and should not be
world-writable.
For example: `_YA_TRAY_CLIPBOARD=/usr/bin/wl-copy`.
NOTE: Only use a trusted binary, OTPs will be sent to this when copied to clipboard from the systray!
=== Command line interface === Command line interface
Looking for a command line option? Try our Looking for a command line option? Try our

View File

@ -22,9 +22,11 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:local_notifier/local_notifier.dart'; import 'package:local_notifier/local_notifier.dart';
import 'package:logging/logging.dart';
import 'package:tray_manager/tray_manager.dart'; import 'package:tray_manager/tray_manager.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
import '../app/logging.dart';
import '../app/models.dart'; import '../app/models.dart';
import '../app/shortcuts.dart'; import '../app/shortcuts.dart';
import '../app/state.dart'; import '../app/state.dart';
@ -35,6 +37,8 @@ import '../oath/views/utils.dart';
import 'oath/state.dart'; import 'oath/state.dart';
import 'state.dart'; import 'state.dart';
final _log = Logger('systray');
final _favoriteAccounts = final _favoriteAccounts =
Provider.autoDispose<(DevicePath?, List<OathCredential>)>( Provider.autoDispose<(DevicePath?, List<OathCredential>)>(
(ref) { (ref) {
@ -104,16 +108,19 @@ String _getIcon() {
class _Systray extends TrayListener { class _Systray extends TrayListener {
final Ref _ref; final Ref _ref;
String? _clipboardBinary;
int _lastClick = 0; int _lastClick = 0;
AppLocalizations _l10n; AppLocalizations _l10n;
DevicePath _devicePath = DevicePath([]); DevicePath _devicePath = DevicePath([]);
List<OathCredential> _credentials = []; List<OathCredential> _credentials = [];
bool _isHidden = false; bool _isHidden = false;
_Systray(this._ref) : _l10n = _ref.read(l10nProvider) { _Systray(this._ref) : _l10n = _ref.read(l10nProvider) {
_init(); _init();
} }
Future<void> _init() async { Future<void> _init() async {
unawaited(_initClipboardBinary());
await trayManager.setIcon(_getIcon(), isTemplate: true); await trayManager.setIcon(_getIcon(), isTemplate: true);
await _updateContextMenu(); await _updateContextMenu();
@ -121,6 +128,37 @@ class _Systray extends TrayListener {
trayManager.addListener(this); trayManager.addListener(this);
} }
Future<void> _initClipboardBinary() async {
final clipboardPath = Platform.environment['_YA_TRAY_CLIPBOARD'];
if (clipboardPath != null && Platform.isLinux) {
final file = File(clipboardPath);
if (!(await file.exists())) {
_log.warning(
'Not using custom binary for clipboard: $clipboardPath. File not found.');
return;
}
final resolved = await file.resolveSymbolicLinks();
final result = await Process.run('ls', ['-nd', '--', resolved],
environment: {'LC_ALL': 'C'});
if (result.exitCode == 0) {
final output = result.stdout as String;
//Eg. "-rwxr-xr-x 1 0 0 52384 Oct 7 2019 /usr/bin/wl-copy"
final isFile = output[0] == '-';
final noWorldWrite = output[8] == '-';
final parts = output.split(RegExp(r'\s+'));
final rootOwner = parts[2] == '0';
final rootGroup = parts[3] == '0';
//Ensure file, owned by root:root, not world writable
if (isFile && noWorldWrite && rootOwner && rootGroup) {
_clipboardBinary = resolved;
} else {
_log.warning('Not using custom binary for clipboard: $clipboardPath');
_log.debug('Refusing to use custom clipboard binary: $output');
}
}
}
}
void dispose() { void dispose() {
trayManager.removeListener(this); trayManager.removeListener(this);
trayManager.destroy(); trayManager.destroy();
@ -187,9 +225,19 @@ class _Systray extends TrayListener {
onClick: (_) async { onClick: (_) async {
final code = await _calculateCode(_devicePath, e, _ref); final code = await _calculateCode(_devicePath, e, _ref);
if (code != null) { if (code != null) {
await _ref if (_clipboardBinary != null) {
.read(clipboardProvider) // Copy to clipboard via another executable, which can be needed for Wayland
.setText(code.value, isSensitive: true); _log.debug(
'Using custom binary to copy to clipboard: $_clipboardBinary');
final process =
await Process.start(_clipboardBinary!, []);
process.stdin.writeln(code.value);
await process.stdin.close();
} else {
await _ref
.read(clipboardProvider)
.setText(code.value, isSensitive: true);
}
final notification = LocalNotification( final notification = LocalNotification(
title: _l10n.s_code_copied, title: _l10n.s_code_copied,
body: _l10n.p_target_copied_clipboard(label), body: _l10n.p_target_copied_clipboard(label),