mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-11-29 23:20:03 +03:00
Merge PR #1535
This commit is contained in:
commit
c8cb6b3fe9
@ -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
|
||||||
|
@ -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),
|
||||||
|
Loading…
Reference in New Issue
Block a user