diff --git a/flutter/lib/common/widgets/login.dart b/flutter/lib/common/widgets/login.dart index fa64e0eb5..826949456 100644 --- a/flutter/lib/common/widgets/login.dart +++ b/flutter/lib/common/widgets/login.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_hbb/common/hbbs/hbbs.dart'; import 'package:flutter_hbb/models/platform_model.dart'; import 'package:flutter_hbb/models/user_model.dart'; @@ -11,6 +12,7 @@ import 'package:url_launcher/url_launcher.dart'; import '../../common.dart'; import './dialog.dart'; +import './oidc_auth_status.dart'; const kOpSvgList = [ 'github', @@ -23,6 +25,8 @@ const kOpSvgList = [ 'auth0', 'microsoft' ]; +const _requestingAccountAuth = 'Requesting account auth'; +const _waitingAccountAuth = 'Waiting account auth'; class _OidcProviderBranding { final String label; @@ -90,6 +94,7 @@ class ButtonOP extends StatelessWidget { final Color primaryColor; final double height; final Function() onTap; + final bool Function() canStartAuth; const ButtonOP({ Key? key, @@ -99,6 +104,7 @@ class ButtonOP extends StatelessWidget { required this.primaryColor, required this.height, required this.onTap, + required this.canStartAuth, }) : super(key: key); @override @@ -111,11 +117,10 @@ class ButtonOP extends StatelessWidget { width: 200, child: Obx(() => ElevatedButton( style: ElevatedButton.styleFrom( - backgroundColor: curOP.value.isEmpty || curOP.value == op - ? primaryColor - : Colors.grey, + backgroundColor: primaryColor, ).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)), - onPressed: curOP.value.isEmpty || curOP.value == op ? onTap : null, + onPressed: + curOP.value == 'rustdesk' || !canStartAuth() ? null : onTap, child: Row( children: [ SizedBox( @@ -145,15 +150,120 @@ class ConfigOP { ConfigOP({required this.op, required this.icon}); } +class _OidcAuthController { + final RxString curOP = ''.obs; + Future _pendingOperation = Future.value(); + int _authAttempt = 0; + bool _closed = false; + final _cancelInProgress = false.obs; + + bool _isCurrent(int authAttempt, String op) { + return !_closed && authAttempt == _authAttempt && curOP.value == op; + } + + Future start(String op) { + if (!canStart()) { + return Future.value(false); + } + final authAttempt = ++_authAttempt; + curOP.value = op; + // Web auth must start during the original user gesture so popups are allowed. + if (isWeb) { + return _startWeb(authAttempt, op); + } + final completer = Completer(); + _pendingOperation = _pendingOperation.then((_) async { + if (!_isCurrent(authAttempt, op)) { + completer.complete(false); + return; + } + try { + await bind.mainAccountAuthCancel(); + if (!_isCurrent(authAttempt, op)) { + completer.complete(false); + return; + } + await bind.mainAccountAuth(op: op, rememberMe: true); + completer.complete(_isCurrent(authAttempt, op)); + } catch (error, stackTrace) { + completer.completeError(error, stackTrace); + } + }); + return completer.future; + } + + Future _startWeb(int authAttempt, String op) async { + await bind.mainAccountAuth(op: op, rememberMe: true); + return _isCurrent(authAttempt, op); + } + + bool canStart() { + return !_closed && !_cancelInProgress.value; + } + + Future cancelCurrent(String op) { + if (!canStart() || curOP.value != op) { + return Future.value(false); + } + final authAttempt = ++_authAttempt; + final completer = Completer(); + _cancelInProgress.value = true; + _pendingOperation = _pendingOperation.then((_) async { + try { + await bind.mainAccountAuthCancel(); + completer.complete(_isCurrent(authAttempt, op)); + } catch (error, stackTrace) { + completer.completeError(error, stackTrace); + } finally { + _cancelInProgress.value = false; + } + }); + return completer.future; + } + + Future _cancelBackend() async { + try { + await bind.mainAccountAuthCancel(); + } catch (error, stackTrace) { + debugPrint('Failed to cancel account authentication $error'); + debugPrintStack(stackTrace: stackTrace); + } + } + + Future close() async { + if (_closed) { + return; + } + final hasActiveOidcAuth = + curOP.value.isNotEmpty && curOP.value != 'rustdesk'; + _closed = true; + _authAttempt++; + curOP.value = ''; + if (hasActiveOidcAuth) { + await _cancelBackend(); + } + await _pendingOperation; + if (hasActiveOidcAuth) { + await _cancelBackend(); + } + } +} + class WidgetOP extends StatefulWidget { final ConfigOP config; final RxString curOP; final Function(Map) cbLogin; + final Future Function(String) startAuth; + final Future Function(String) cancelAuth; + final bool Function() canStartAuth; const WidgetOP({ Key? key, required this.config, required this.curOP, required this.cbLogin, + required this.startAuth, + required this.cancelAuth, + required this.canStartAuth, }) : super(key: key); @override @@ -164,6 +274,8 @@ class WidgetOP extends StatefulWidget { class _WidgetOPState extends State { Timer? _updateTimer; + bool _isAuthStatusQueryInFlight = false; + int _authAttempt = 0; String _stateMsg = ''; String _failedMsg = ''; String _url = ''; @@ -174,55 +286,180 @@ class _WidgetOPState extends State { _updateTimer?.cancel(); } - _beginQueryState() { + _beginQueryState(int authAttempt) { + _updateTimer?.cancel(); + unawaited(_runAuthStatusQuery(() => _updateState(authAttempt))); _updateTimer = Timer.periodic(Duration(seconds: 1), (timer) { - _updateState(); + unawaited(_runAuthStatusQuery(() => _updateState(authAttempt))); }); } - _updateState() { - bind.mainAccountAuthResult().then((result) { - if (result.isEmpty) { + Future _runAuthStatusQuery(Future Function() query) async { + if (_isAuthStatusQueryInFlight) { + return; + } + _isAuthStatusQueryInFlight = true; + try { + await query(); + } finally { + _isAuthStatusQueryInFlight = false; + } + } + + Future _launchAuthUrl(String url) async { + try { + final launched = await launchUrl( + Uri.parse(url), + mode: LaunchMode.externalApplication, + ); + if (!launched) { + debugPrint('Failed to open OIDC authentication URL'); + } + } catch (error, stackTrace) { + debugPrint( + 'Failed to open OIDC authentication URL (${error.runtimeType})'); + debugPrintStack(stackTrace: stackTrace); + } + } + + Future _copyAuthUrl(String url) async { + try { + await Clipboard.setData(ClipboardData(text: url)); + showToast( + translate('Copied'), + ); + } catch (error, stackTrace) { + debugPrint( + 'Failed to copy OIDC authentication URL (${error.runtimeType})'); + debugPrintStack(stackTrace: stackTrace); + showToast(translate('Failed')); + } + } + + void _runCurrentAuthUrlAction( + int authAttempt, + String authUrl, + Future Function(String) action, + ) { + if (!mounted || + authAttempt != _authAttempt || + widget.curOP.value != widget.config.op || + authUrl.isEmpty || + _url != authUrl) { + return; + } + unawaited(action(authUrl)); + } + + void _invalidateAuthAttempt() { + _authAttempt++; + _url = ''; + } + + bool _isCurrentAuthAttempt(int authAttempt) { + return mounted && + authAttempt == _authAttempt && + widget.curOP.value == widget.config.op; + } + + Future _handleAuthFailure( + int authAttempt, + Object error, + String operation, + ) async { + debugPrint('Failed to $operation $error'); + if (!_isCurrentAuthAttempt(authAttempt)) { + return; + } + _updateTimer?.cancel(); + setState(() => _failedMsg = 'Failed'); + try { + final canceled = await widget.cancelAuth(widget.config.op); + if (!canceled || !_isCurrentAuthAttempt(authAttempt)) { + return; + } + } catch (cancelError, stackTrace) { + debugPrint('Failed to cancel account authentication $cancelError'); + debugPrintStack(stackTrace: stackTrace); + return; + } + setState(() { + _invalidateAuthAttempt(); + widget.curOP.value = ''; + }); + } + + Future _updateState(int authAttempt) { + if (!mounted || + authAttempt != _authAttempt || + widget.curOP.value != widget.config.op) { + _updateTimer?.cancel(); + return Future.value(); + } + return bind.mainAccountAuthResult().then((result) { + if (!mounted || + authAttempt != _authAttempt || + widget.curOP.value != widget.config.op || + result.isEmpty) { return; } final resultMap = jsonDecode(result); if (resultMap == null) { return; } - final String stateMsg = resultMap['state_msg']; + final String backendStateMsg = resultMap['state_msg']; String failedMsg = resultMap['failed_msg']; final String? url = resultMap['url']; + final stateMsg = backendStateMsg == _requestingAccountAuth && + (url == null || url.isEmpty) + ? _waitingAccountAuth + : backendStateMsg; final bool urlLaunched = (resultMap['url_launched'] as bool?) ?? false; final authBody = resultMap['auth_body']; - if (_stateMsg != stateMsg || _failedMsg != failedMsg) { - if (_url.isEmpty && url != null && url.isNotEmpty) { - if (!urlLaunched) { - launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication); - } - _url = url; - } - if (authBody != null) { - _updateTimer?.cancel(); - widget.curOP.value = ''; - widget.cbLogin(authBody as Map); - } - - setState(() { - _stateMsg = stateMsg; - _failedMsg = failedMsg; - if (failedMsg.isNotEmpty) { - widget.curOP.value = ''; - _updateTimer?.cancel(); - } - }); + if (authBody != null) { + _updateTimer?.cancel(); + _invalidateAuthAttempt(); + widget.curOP.value = ''; + widget.cbLogin(authBody as Map); + return; } - }); + final stateChanged = _stateMsg != stateMsg || _failedMsg != failedMsg; + final newUrl = _url.isEmpty && url != null && url.isNotEmpty ? url : null; + if (!stateChanged && newUrl == null) { + return; + } + setState(() { + _stateMsg = stateMsg; + _failedMsg = failedMsg; + if (newUrl != null) { + _url = newUrl; + } + if (failedMsg.isNotEmpty) { + _invalidateAuthAttempt(); + widget.curOP.value = ''; + _updateTimer?.cancel(); + } + }); + if (newUrl != null && failedMsg.isEmpty && !urlLaunched) { + unawaited(_launchAuthUrl(newUrl)); + } + }).catchError( + (e) => _handleAuthFailure( + authAttempt, + e, + 'query account authentication', + ), + ); } - _resetState() { - _stateMsg = ''; - _failedMsg = ''; - _url = ''; + int _resetState() { + _updateTimer?.cancel(); + setState(() { + _invalidateAuthAttempt(); + _stateMsg = _waitingAccountAuth; + _failedMsg = ''; + }); + return _authAttempt; } @override @@ -235,11 +472,31 @@ class _WidgetOPState extends State { icon: widget.config.icon, primaryColor: str2color(widget.config.op, 0x7f), height: 36, + canStartAuth: widget.canStartAuth, onTap: () async { - _resetState(); - widget.curOP.value = widget.config.op; - await bind.mainAccountAuth(op: widget.config.op, rememberMe: true); - _beginQueryState(); + if (!widget.canStartAuth()) { + return; + } + final authAttempt = _resetState(); + try { + final started = await widget.startAuth(widget.config.op); + if (!started) { + return; + } + } catch (e) { + await _handleAuthFailure( + authAttempt, + e, + 'start account authentication', + ); + return; + } + if (!mounted || + authAttempt != _authAttempt || + widget.curOP.value != widget.config.op) { + return; + } + _beginQueryState(authAttempt); }, ), Obx(() { @@ -247,6 +504,8 @@ class _WidgetOPState extends State { widget.curOP.value != widget.config.op) { _failedMsg = ''; } + final authAttempt = _authAttempt; + final authUrl = _url; return Offstage( offstage: _failedMsg.isEmpty && widget.curOP.value != widget.config.op, @@ -256,11 +515,20 @@ class _WidgetOPState extends State { if (_stateMsg.isNotEmpty && _failedMsg.isEmpty) Padding( padding: const EdgeInsets.only(top: 8.0), - child: SelectableText( - translate(_stateMsg), - style: DefaultTextStyle.of(context) - .style - .copyWith(fontSize: 12), + child: OidcAuthStatus( + message: translate(_stateMsg), + browserFallbackPrompt: translate( + "Browser didn't open? Use the url below to sign in.", + ), + authUrl: authUrl, + copyLabel: translate('Copy to clipboard'), + onCopy: authUrl.isEmpty + ? null + : () => _runCurrentAuthUrlAction( + authAttempt, + authUrl, + _copyAuthUrl, + ), ), ), if (_failedMsg.isNotEmpty) @@ -304,34 +572,6 @@ class _WidgetOPState extends State { ), ); }), - Obx( - () => Offstage( - offstage: widget.curOP.value != widget.config.op, - child: const SizedBox( - height: 5.0, - ), - ), - ), - Obx( - () => Offstage( - offstage: widget.curOP.value != widget.config.op, - child: ConstrainedBox( - constraints: BoxConstraints(maxHeight: 20), - child: ElevatedButton( - onPressed: () { - widget.curOP.value = ''; - _updateTimer?.cancel(); - _resetState(); - bind.mainAccountAuthCancel(); - }, - child: Text( - translate('Cancel'), - style: TextStyle(fontSize: 15), - ), - ), - ), - ), - ), ], ); } @@ -341,12 +581,18 @@ class LoginWidgetOP extends StatelessWidget { final List ops; final RxString curOP; final Function(Map) cbLogin; + final Future Function(String) startAuth; + final Future Function(String) cancelAuth; + final bool Function() canStartAuth; LoginWidgetOP({ Key? key, required this.ops, required this.curOP, required this.cbLogin, + required this.startAuth, + required this.cancelAuth, + required this.canStartAuth, }) : super(key: key); @override @@ -357,6 +603,9 @@ class LoginWidgetOP extends StatelessWidget { config: op, curOP: curOP, cbLogin: cbLogin, + startAuth: startAuth, + cancelAuth: cancelAuth, + canStartAuth: canStartAuth, ), const Divider( indent: 5, @@ -434,12 +683,11 @@ class LoginWidgetUserPass extends StatelessWidget { translate('Login'), style: TextStyle(fontSize: 16), ), - onPressed: - curOP.value.isEmpty || curOP.value == 'rustdesk' - ? () { - onLogin(); - } - : null, + onPressed: curOP.value.isEmpty && !isInProgress + ? () { + onLogin(); + } + : null, )), ), ])), @@ -450,8 +698,28 @@ class LoginWidgetUserPass extends StatelessWidget { const kAuthReqTypeOidc = 'oidc/'; +Future? _activeLoginDialog; + // call this directly -Future loginDialog() async { +Future loginDialog() { + final activeDialog = _activeLoginDialog; + if (activeDialog != null) { + return activeDialog; + } + final dialog = _openLoginDialogOnce(); + _activeLoginDialog = dialog; + return dialog; +} + +Future _openLoginDialogOnce() async { + try { + return await _openLoginDialog(); + } finally { + _activeLoginDialog = null; + } +} + +Future _openLoginDialog() async { var username = TextEditingController(text: UserModel.getLocalUserInfo()?['name'] ?? ''); var password = TextEditingController(); @@ -461,12 +729,13 @@ Future loginDialog() async { String? usernameMsg; String? passwordMsg; var isInProgress = false; - final RxString curOP = ''.obs; + final oidcAuth = _OidcAuthController(); + final curOP = oidcAuth.curOP; // Track hover state for the close icon bool isCloseHovered = false; final loginOptions = [].obs; - final loginOptionsError = Rxn(); + final loginOptionsError = Rxn(); final loginOptionsInProgress = false.obs; fetchLoginOptions() async { loginOptionsInProgress.value = true; @@ -475,7 +744,7 @@ Future loginDialog() async { loginOptionsError.value = null; } catch (e) { debugPrint("queryOidcLoginOptions failed: $e"); - loginOptionsError.value = e.toString(); + loginOptionsError.value = e; } finally { loginOptionsInProgress.value = false; } @@ -555,6 +824,9 @@ Future loginDialog() async { } onLogin() async { + if (curOP.value.isNotEmpty || isInProgress) { + return; + } // validate if (username.text.isEmpty) { setState(() => usernameMsg = translate('Username missed')); @@ -593,7 +865,7 @@ Future loginDialog() async { const SizedBox(height: 8.0), // NOT use Offstage to wrap LinearProgressIndicator if (inProgress) const LinearProgressIndicator(), - if (!inProgress) + if (!inProgress && error is! RequestException) Text( translate('network_error_tip'), style: const TextStyle(fontSize: 12), @@ -608,7 +880,7 @@ Future loginDialog() async { ), if (!inProgress) SelectableText( - error, + error.toString(), style: const TextStyle(fontSize: 11, color: Colors.red), textAlign: TextAlign.center, ), @@ -635,6 +907,9 @@ Future loginDialog() async { .map((e) => ConfigOP(op: e['name'], icon: e['icon'])) .toList(), curOP: curOP, + startAuth: oidcAuth.start, + cancelAuth: oidcAuth.cancelCurrent, + canStartAuth: oidcAuth.canStart, cbLogin: (Map authBody) async { LoginResponse? resp; try { @@ -716,7 +991,7 @@ Future loginDialog() async { onCancel: onDialogCancel, onSubmit: onLogin, ); - }); + }).whenComplete(oidcAuth.close); if (res != null) { await UserModel.updateOtherModels(); diff --git a/flutter/lib/common/widgets/oidc_auth_status.dart b/flutter/lib/common/widgets/oidc_auth_status.dart new file mode 100644 index 000000000..d351e81be --- /dev/null +++ b/flutter/lib/common/widgets/oidc_auth_status.dart @@ -0,0 +1,157 @@ +import 'package:flutter/material.dart'; + +const _statusFontSize = 12.0; +const _statusSpacing = 4.0; +const _messageActionSpacing = 8.0; +const _desktopActionSize = 28.0; +const _touchPlatforms = { + TargetPlatform.android, + TargetPlatform.iOS, + TargetPlatform.fuchsia, +}; + +class OidcAuthStatus extends StatelessWidget { + final String message; + final String browserFallbackPrompt; + final String authUrl; + final String copyLabel; + final VoidCallback? onCopy; + + const OidcAuthStatus({ + super.key, + required this.message, + required this.browserFallbackPrompt, + required this.authUrl, + required this.copyLabel, + this.onCopy, + }); + + @override + Widget build(BuildContext context) { + final messageStyle = + DefaultTextStyle.of(context).style.copyWith(fontSize: _statusFontSize); + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SelectableText(message, style: messageStyle), + if (authUrl.isNotEmpty) + Padding( + padding: const EdgeInsets.only(top: _messageActionSpacing), + child: _OidcAuthFallback( + browserFallbackPrompt: browserFallbackPrompt, + authUrl: authUrl, + copyLabel: copyLabel, + onCopy: onCopy, + ), + ), + ], + ); + } +} + +class _OidcAuthFallback extends StatefulWidget { + final String browserFallbackPrompt; + final String authUrl; + final String copyLabel; + final VoidCallback? onCopy; + + const _OidcAuthFallback({ + required this.browserFallbackPrompt, + required this.authUrl, + required this.copyLabel, + required this.onCopy, + }); + + @override + State<_OidcAuthFallback> createState() => _OidcAuthFallbackState(); +} + +class _OidcAuthFallbackState extends State<_OidcAuthFallback> { + bool _expanded = false; + + @override + void didUpdateWidget(covariant _OidcAuthFallback oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.authUrl != widget.authUrl) { + _expanded = false; + } + } + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final helperStyle = DefaultTextStyle.of(context).style.copyWith( + fontSize: _statusFontSize, + color: theme.colorScheme.onSurfaceVariant, + ); + final linkColor = theme.brightness == Brightness.dark + ? Colors.blue.shade300 + : Colors.blue.shade800; + final isTouchPlatform = _touchPlatforms.contains(theme.platform); + final actionSize = + isTouchPlatform ? kMinInteractiveDimension : _desktopActionSize; + final urlStyle = + DefaultTextStyle.of(context).style.copyWith(fontSize: _statusFontSize); + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + widget.browserFallbackPrompt, + style: helperStyle, + textAlign: TextAlign.center, + ), + Padding( + padding: const EdgeInsets.only(top: _statusSpacing), + child: _buildUrl(urlStyle, linkColor, actionSize), + ), + ], + ); + } + + void _copyAndExpand() { + setState(() => _expanded = true); + widget.onCopy?.call(); + } + + Widget _buildUrl(TextStyle urlStyle, Color linkColor, double actionSize) { + final collapsedUrl = SizedBox( + width: double.infinity, + child: TextButton( + style: TextButton.styleFrom( + foregroundColor: linkColor, + minimumSize: Size(0, actionSize), + padding: EdgeInsets.zero, + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + visualDensity: VisualDensity.standard, + ), + onPressed: _copyAndExpand, + child: Text( + widget.authUrl, + maxLines: 1, + overflow: TextOverflow.ellipsis, + softWrap: false, + style: urlStyle.copyWith( + color: linkColor, + decoration: TextDecoration.underline, + ), + ), + ), + ); + final collapsedChild = widget.onCopy == null + ? collapsedUrl + : Tooltip(message: widget.copyLabel, child: collapsedUrl); + return Container( + width: double.infinity, + constraints: BoxConstraints(minHeight: actionSize), + alignment: Alignment.centerLeft, + padding: const EdgeInsets.symmetric(horizontal: _messageActionSpacing), + decoration: BoxDecoration( + border: Border.all(color: Theme.of(context).dividerColor), + borderRadius: BorderRadius.circular(_statusSpacing), + ), + child: _expanded + ? SelectableText(widget.authUrl, style: urlStyle) + : collapsedChild, + ); + } +} diff --git a/flutter/lib/models/user_model.dart b/flutter/lib/models/user_model.dart index 9ebb6f76b..405a9fadd 100644 --- a/flutter/lib/models/user_model.dart +++ b/flutter/lib/models/user_model.dart @@ -234,8 +234,9 @@ class UserModel { return loginResponse; } - /// Throws on network failure so callers can surface the error and offer a - /// retry; returns an empty list when the server has no third-party login. + /// Throws on network failures, non-success responses, and invalid response + /// data. Returns an empty list when no API server is configured or a + /// successful response contains no third-party login options. static Future> queryOidcLoginOptions() async { final url = await bind.mainGetApiServer(); if (url.trim().isEmpty) return []; diff --git a/src/hbbs_http/account.rs b/src/hbbs_http/account.rs index 46f6969ee..634c95383 100644 --- a/src/hbbs_http/account.rs +++ b/src/hbbs_http/account.rs @@ -113,7 +113,7 @@ pub struct OidcSession { failed_msg: String, code_url: Option, auth_body: Option, - keep_querying: bool, + auth_attempt: u64, running: bool, query_timeout: Duration, } @@ -140,7 +140,7 @@ impl OidcSession { failed_msg: "".to_owned(), code_url: None, auth_body: None, - keep_querying: false, + auth_attempt: 0, running: false, query_timeout: Duration::from_secs(QUERY_TIMEOUT_SECS), } @@ -192,12 +192,8 @@ impl OidcSession { body: String, } - let resp = crate::http_request_sync( - url.to_string(), - "GET".to_owned(), - None, - "{}".to_owned(), - )?; + let resp = + crate::http_request_sync(url.to_string(), "GET".to_owned(), None, "{}".to_owned())?; let resp = serde_json::from_str::(&resp)?; HbbHttpResponse::parse(&resp.body) } @@ -205,7 +201,6 @@ impl OidcSession { fn reset(&mut self) { self.state_msg = REQUESTING_ACCOUNT_AUTH; self.failed_msg = "".to_owned(); - self.keep_querying = true; self.running = false; self.code_url = None; self.auth_body = None; @@ -220,49 +215,92 @@ impl OidcSession { self.running = false; } + fn start_auth_attempt(&mut self) -> u64 { + self.auth_attempt = self.auth_attempt.wrapping_add(1); + self.auth_attempt + } + + fn cancel_auth_attempt(&mut self) { + self.auth_attempt = self.auth_attempt.wrapping_add(1); + } + + fn is_current_auth_attempt(&self, auth_attempt: u64) -> bool { + self.auth_attempt == auth_attempt + } + + fn auth_attempt_is_current(auth_attempt: u64) -> bool { + OIDC_SESSION + .read() + .unwrap() + .is_current_auth_attempt(auth_attempt) + } + + fn set_state_if_current(auth_attempt: u64, state_msg: &'static str, failed_msg: String) { + let mut session = OIDC_SESSION.write().unwrap(); + if session.is_current_auth_attempt(auth_attempt) { + session.set_state(state_msg, failed_msg); + } + } + fn sleep(secs: f32) { std::thread::sleep(std::time::Duration::from_secs_f32(secs)); } - fn auth_task(api_server: String, op: String, id: String, uuid: String, remember_me: bool) { + fn auth_task( + api_server: String, + op: String, + id: String, + uuid: String, + remember_me: bool, + auth_attempt: u64, + ) { let auth_request_res = Self::auth(&api_server, &op, &id, &uuid); log::info!("Request oidc auth result: {:?}", &auth_request_res); + if !Self::auth_attempt_is_current(auth_attempt) { + return; + } let code_url = match auth_request_res { Ok(HbbHttpResponse::<_>::Data(code_url)) => code_url, Ok(HbbHttpResponse::<_>::Error(err)) => { - OIDC_SESSION - .write() - .unwrap() - .set_state(REQUESTING_ACCOUNT_AUTH, err); + Self::set_state_if_current(auth_attempt, REQUESTING_ACCOUNT_AUTH, err); return; } Ok(_) => { - OIDC_SESSION - .write() - .unwrap() - .set_state(REQUESTING_ACCOUNT_AUTH, "Invalid auth response".to_owned()); + Self::set_state_if_current( + auth_attempt, + REQUESTING_ACCOUNT_AUTH, + "Invalid auth response".to_owned(), + ); return; } Err(err) => { - OIDC_SESSION - .write() - .unwrap() - .set_state(REQUESTING_ACCOUNT_AUTH, err.to_string()); + Self::set_state_if_current(auth_attempt, REQUESTING_ACCOUNT_AUTH, err.to_string()); return; } }; - OIDC_SESSION - .write() - .unwrap() - .set_state(WAITING_ACCOUNT_AUTH, "".to_owned()); - OIDC_SESSION.write().unwrap().code_url = Some(code_url.clone()); + { + let mut session = OIDC_SESSION.write().unwrap(); + if !session.is_current_auth_attempt(auth_attempt) { + return; + } + session.set_state(WAITING_ACCOUNT_AUTH, "".to_owned()); + session.code_url = Some(code_url.clone()); + } let begin = Instant::now(); let query_timeout = OIDC_SESSION.read().unwrap().query_timeout; - while OIDC_SESSION.read().unwrap().keep_querying && begin.elapsed() < query_timeout { - match Self::query(&api_server, &code_url.code, &id, &uuid) { + while Self::auth_attempt_is_current(auth_attempt) && begin.elapsed() < query_timeout { + let query_result = Self::query(&api_server, &code_url.code, &id, &uuid); + if !Self::auth_attempt_is_current(auth_attempt) { + return; + } + match query_result { Ok(HbbHttpResponse::<_>::Data(auth_body)) => { + let mut session = OIDC_SESSION.write().unwrap(); + if !session.is_current_auth_attempt(auth_attempt) { + return; + } if auth_body.r#type == "access_token" { if remember_me { LocalConfig::set_option( @@ -281,21 +319,15 @@ impl OidcSession { ); } } - OIDC_SESSION - .write() - .unwrap() - .set_state(LOGIN_ACCOUNT_AUTH, "".to_owned()); - OIDC_SESSION.write().unwrap().auth_body = Some(auth_body); + session.set_state(LOGIN_ACCOUNT_AUTH, "".to_owned()); + session.auth_body = Some(auth_body); return; } Ok(HbbHttpResponse::<_>::Error(err)) => { if err.contains("No authed oidc is found") { // ignore, keep querying } else { - OIDC_SESSION - .write() - .unwrap() - .set_state(WAITING_ACCOUNT_AUTH, err); + Self::set_state_if_current(auth_attempt, WAITING_ACCOUNT_AUTH, err); return; } } @@ -310,14 +342,9 @@ impl OidcSession { Self::sleep(QUERY_INTERVAL_SECS); } - if begin.elapsed() >= query_timeout { - OIDC_SESSION - .write() - .unwrap() - .set_state(WAITING_ACCOUNT_AUTH, "timeout".to_owned()); + if begin.elapsed() >= query_timeout && Self::auth_attempt_is_current(auth_attempt) { + Self::set_state_if_current(auth_attempt, WAITING_ACCOUNT_AUTH, "timeout".to_owned()); } - - // no need to handle "keep_querying == false" } fn set_state(&mut self, state_msg: &'static str, failed_msg: String) { @@ -339,11 +366,17 @@ impl OidcSession { uuid: String, remember_me: bool, ) { - Self::auth_cancel(); + let auth_attempt = OIDC_SESSION.write().unwrap().start_auth_attempt(); Self::wait_stop_querying(); - OIDC_SESSION.write().unwrap().before_task(); + { + let mut session = OIDC_SESSION.write().unwrap(); + if !session.is_current_auth_attempt(auth_attempt) { + return; + } + session.before_task(); + } std::thread::spawn(move || { - Self::auth_task(api_server, op, id, uuid, remember_me); + Self::auth_task(api_server, op, id, uuid, remember_me, auth_attempt); OIDC_SESSION.write().unwrap().after_task(); }); } @@ -358,7 +391,7 @@ impl OidcSession { } pub fn auth_cancel() { - OIDC_SESSION.write().unwrap().keep_querying = false; + OIDC_SESSION.write().unwrap().cancel_auth_attempt(); } pub fn get_result() -> AuthResult { diff --git a/src/lang/ar.rs b/src/lang/ar.rs index b0c695b81..2189648d9 100644 --- a/src/lang/ar.rs +++ b/src/lang/ar.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "اتصال الوسيط"), ("Secure Connection", "اتصال آمن"), ("Insecure Connection", "اتصال غير آمن"), - ("Continue", ""), ("Scale original", "المقياس الأصلي"), ("Scale adaptive", "مقياس التكيف"), ("General", "عام"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "تم حظر عنوان IP الخاص بك من قبل الطرف الآخر"), ("id_whitelist_caveat_tip", "يتم الإبلاغ عن المعرف من قبل العميل المتصل. القائمة البيضاء تقلل من التعرض ولا تغني عن كلمة المرور أو 2FA"), ("whitelist_cidr_tip", "يتم دعم صيغة CIDR، مثال: 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/be.rs b/src/lang/be.rs index 1c726b71a..ac302f3af 100644 --- a/src/lang/be.rs +++ b/src/lang/be.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Рэтрансляванае падключэнне"), ("Secure Connection", "Бяспечнае падключэнне"), ("Insecure Connection", "Нябяспечнае падключэнне"), - ("Continue", ""), ("Scale original", "Арыгінальны маштаб"), ("Scale adaptive", "Адаптыўны маштаб"), ("General", "Агульныя"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Ваш IP-адрас заблакаваны аддаленай прыладай"), ("id_whitelist_caveat_tip", "ID паведамляецца кліентам, які падключаецца. Белы спіс памяншае паверхню атакі і не замяняе пароль або 2FA"), ("whitelist_cidr_tip", "Падтрымліваецца натацыя CIDR, напрыклад: 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/bg.rs b/src/lang/bg.rs index 43380a92b..c339270c0 100644 --- a/src/lang/bg.rs +++ b/src/lang/bg.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Релейна връзка"), ("Secure Connection", "Сигурна връзка"), ("Insecure Connection", "Несигурна връзка"), - ("Continue", ""), ("Scale original", "Оригинален мащаб"), ("Scale adaptive", "Приспособимо мащабиране"), ("General", "Основен"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Вашият IP адрес е блокиран от отсрещната страна"), ("id_whitelist_caveat_tip", "ID се съобщава от свързващия се клиент. Белият списък намалява изложеността и не замества паролата или 2FA"), ("whitelist_cidr_tip", "Поддържа се CIDR нотация, например: 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 001d12b7f..d3b0ae7e0 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Connexió amb repetidor"), ("Secure Connection", "Connexió segura"), ("Insecure Connection", "Connexió no segura"), - ("Continue", ""), ("Scale original", "Escala original"), ("Scale adaptive", "Escala adaptativa"), ("General", "General"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "La vostra IP està bloquejada per l'altre extrem"), ("id_whitelist_caveat_tip", "L'ID és informat pel client que es connecta. Aquesta llista blanca redueix l'exposició i no substitueix la contrasenya ni la 2FA"), ("whitelist_cidr_tip", "S'admet la notació CIDR, per exemple 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index dff0a2e2d..7423cceb3 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "中继连接"), ("Secure Connection", "安全连接"), ("Insecure Connection", "非安全连接"), - ("Continue", "继续"), ("Scale original", "原始尺寸"), ("Scale adaptive", "适应窗口"), ("General", "常规"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "你的 IP 已被对方阻止"), ("id_whitelist_caveat_tip", "ID 由对端客户端上报,白名单用于减少暴露面,不能替代密码或 2FA"), ("whitelist_cidr_tip", "支持 CIDR 写法,例如 192.168.1.0/24"), + ("Continue", "继续"), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 420913038..abd4e60aa 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Připojení předávací server"), ("Secure Connection", "Zabezpečené připojení"), ("Insecure Connection", "Nezabezpečené připojení"), - ("Continue", ""), ("Scale original", "Originální měřítko"), ("Scale adaptive", "Adaptivní měřítko"), ("General", "Obecné"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Vaše IP adresa je protistranou blokována"), ("id_whitelist_caveat_tip", "ID je hlášeno připojujícím se klientem. Tento seznam snižuje vystavení a nenahrazuje heslo ani 2FA"), ("whitelist_cidr_tip", "Je podporován zápis CIDR, například 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index f7579b22b..0ecab9098 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Viderestillingsforbindelse"), ("Secure Connection", "Sikker forbindelse"), ("Insecure Connection", "Usikker forbindelse"), - ("Continue", ""), ("Scale original", "Original skalering"), ("Scale adaptive", "Adaptiv skalering"), ("General", "Generelt"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Din IP-adresse er blokeret af modparten"), ("id_whitelist_caveat_tip", "ID'et rapporteres af den klient, der opretter forbindelse. Whitelisten reducerer eksponeringen og erstatter ikke adgangskode eller 2FA"), ("whitelist_cidr_tip", "CIDR-notation understøttes, f.eks. 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 92e888591..d71dfa6ce 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Relay-Verbindung"), ("Secure Connection", "Sichere Verbindung"), ("Insecure Connection", "Unsichere Verbindung"), - ("Continue", "Weiter"), ("Scale original", "Keine Skalierung"), ("Scale adaptive", "Anpassbare Skalierung"), ("General", "Allgemein"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Ihre IP-Adresse wird von der Gegenstelle blockiert"), ("id_whitelist_caveat_tip", "Die ID wird vom verbindenden Client gemeldet. Die Whitelist verringert die Angriffsfläche und ersetzt weder Passwort noch 2FA."), ("whitelist_cidr_tip", "Die CIDR-Notation wird unterstützt, z. B. 192.168.1.0/24"), + ("Continue", "Weiter"), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index e3fc94564..5ba349a9c 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Αναμεταδιδόμενη σύνδεση"), ("Secure Connection", "Ασφαλής σύνδεση"), ("Insecure Connection", "Μη ασφαλής σύνδεση"), - ("Continue", ""), ("Scale original", "Κλιμάκωση πρωτότυπου"), ("Scale adaptive", "Προσαρμοσμένη κλίμακα"), ("General", "Γενικά"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Η διεύθυνση IP σας έχει αποκλειστεί από τον απομακρυσμένο υπολογιστή"), ("id_whitelist_caveat_tip", "Το ID αναφέρεται από τον πελάτη που συνδέεται. Η λίστα επιτρεπόμενων μειώνει την έκθεση και δεν αντικαθιστά τον κωδικό πρόσβασης ή το 2FA"), ("whitelist_cidr_tip", "Υποστηρίζεται η σημειογραφία CIDR, π.χ. 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 7af41cd3f..e6cc0cae5 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Relajsa Konekto"), ("Secure Connection", "Sekura Konekto"), ("Insecure Connection", "Nesekura Konekto"), - ("Continue", ""), ("Scale original", "Skalo originalo"), ("Scale adaptive", "Skalo adapta"), ("General", "Ĝenerala"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Via IP estas blokita de la alia flanko"), ("id_whitelist_caveat_tip", "La ID estas raportata de la konektiĝanta kliento. La blanka listo malpliigas la eksponiĝon kaj ne anstataŭas la pasvorton aŭ 2FA"), ("whitelist_cidr_tip", "La notacio CIDR estas subtenata, ekzemple 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 1e592934a..2e7ace9cf 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Conexión Relay"), ("Secure Connection", "Conexión segura"), ("Insecure Connection", "Conexión insegura"), - ("Continue", ""), ("Scale original", "Escala original"), ("Scale adaptive", "Escala adaptativa"), ("General", "General"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Tu IP está bloqueada por el dispositivo remoto"), ("id_whitelist_caveat_tip", "El ID lo comunica el cliente que se conecta. Esta lista blanca reduce la exposición y no sustituye a la contraseña ni al 2FA"), ("whitelist_cidr_tip", "Se admite la notación CIDR, por ejemplo 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/et.rs b/src/lang/et.rs index 9eccaa6c2..238c84c88 100644 --- a/src/lang/et.rs +++ b/src/lang/et.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Releeühendus"), ("Secure Connection", "Turvaline ühendus"), ("Insecure Connection", "Ebaturvaline ühendus"), - ("Continue", ""), ("Scale original", "Originaalskaala"), ("Scale adaptive", "Kohanduv skaala"), ("General", "Üldine"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Teine pool on sinu IP-aadressi blokeerinud"), ("id_whitelist_caveat_tip", "ID edastab ühenduv klient. Lubamisloend vähendab eksponeeritust ega asenda parooli või 2FA-d"), ("whitelist_cidr_tip", "Toetatud on CIDR-tähistus, näiteks 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eu.rs b/src/lang/eu.rs index a3b752a50..3fd38eb55 100644 --- a/src/lang/eu.rs +++ b/src/lang/eu.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Konexio igorria"), ("Secure Connection", "Konexio segurua"), ("Insecure Connection", "Konexio ez-segurua"), - ("Continue", ""), ("Scale original", "Jatorrizko eskala"), ("Scale adaptive", "Eskala moldagarria"), ("General", "Orokorra"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Beste aldeak zure IP helbidea blokeatu du"), ("id_whitelist_caveat_tip", "IDa konektatzen den bezeroak jakinarazten du. Zerrenda honek esposizioa murrizten du eta ez du pasahitza edo 2FA ordezkatzen"), ("whitelist_cidr_tip", "CIDR notazioa onartzen da, adibidez 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 3cdbcb3bc..1e4039be7 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Relay ارتباط"), ("Secure Connection", "ارتباط امن"), ("Insecure Connection", "ارتباط غیر امن"), - ("Continue", ""), ("Scale original", "مقیاس اصلی"), ("Scale adaptive", "مقیاس تطبیقی"), ("General", "عمومی"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "نشانی IP شما توسط طرف مقابل مسدود شده است"), ("id_whitelist_caveat_tip", "شناسه توسط کلاینت متصل شونده گزارش می شود. لیست مجاز سطح در معرض بودن را کاهش می دهد و جایگزین رمز عبور یا 2FA نیست"), ("whitelist_cidr_tip", "نماد CIDR پشتیبانی می شود، برای مثال 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fi.rs b/src/lang/fi.rs index 0edd04d45..2a21ba049 100644 --- a/src/lang/fi.rs +++ b/src/lang/fi.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Välitetty yhteys"), ("Secure Connection", "Suojattu yhteys"), ("Insecure Connection", "Suojaamaton yhteys"), - ("Continue", ""), ("Scale original", "Skaalaa alkuperäinen"), ("Scale adaptive", "Mukautuva skaalaus"), ("General", "Yleiset"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Vastapuoli on estänyt IP-osoitteesi"), ("id_whitelist_caveat_tip", "ID on yhdistävän asiakkaan ilmoittama. Sallintalista pienentää altistusta eikä korvaa salasanaa tai 2FA:ta"), ("whitelist_cidr_tip", "CIDR-merkintä on tuettu, esimerkiksi 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index aa822413f..8359587a2 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Connexion via relais"), ("Secure Connection", "Connexion sécurisée"), ("Insecure Connection", "Connexion non sécurisée"), - ("Continue", ""), ("Scale original", "Échelle originale"), ("Scale adaptive", "Échelle adaptative"), ("General", "Général"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Votre adresse IP est bloquée par l’appareil distant"), ("id_whitelist_caveat_tip", "L’ID est déclaré par le client qui se connecte. Cette liste blanche réduit l’exposition et ne remplace ni le mot de passe ni la 2FA"), ("whitelist_cidr_tip", "La notation CIDR est prise en charge, par exemple 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ge.rs b/src/lang/ge.rs index b3fe30dd9..97c3e9171 100644 --- a/src/lang/ge.rs +++ b/src/lang/ge.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "რეტრანსლირებული კავშირი"), ("Secure Connection", "უსაფრთხო კავშირი"), ("Insecure Connection", "არაუსაფრთხო კავშირი"), - ("Continue", ""), ("Scale original", "ორიგინალური მასშტაბი"), ("Scale adaptive", "ადაპტირებადი მასშტაბი"), ("General", "ზოგადი"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "თქვენი IP მისამართი დაბლოკილია მეორე მხარის მიერ"), ("id_whitelist_caveat_tip", "ID-ს აცხადებს დამაკავშირებელი კლიენტი. თეთრი სია ამცირებს ექსპოზიციას და ვერ ჩაანაცვლებს პაროლს ან 2FA-ს"), ("whitelist_cidr_tip", "მხარდაჭერილია CIDR ჩანაწერი, მაგალითად 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/gu.rs b/src/lang/gu.rs index a150047d7..c9c2c9177 100644 --- a/src/lang/gu.rs +++ b/src/lang/gu.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "રિલે કનેક્શન"), ("Secure Connection", "સુરક્ષિત કનેક્શન"), ("Insecure Connection", "અસુરક્ષિત કનેક્શન"), - ("Continue", ""), ("Scale original", "મૂળ સ્કેલ"), ("Scale adaptive", "એડેપ્ટિવ સ્કેલ"), ("General", "સામાન્ય"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "તમારું IP સામેના પક્ષ દ્વારા બ્લોક કરવામાં આવ્યું છે"), ("id_whitelist_caveat_tip", "ID કનેક્ટ થતા ક્લાયન્ટ દ્વારા જણાવવામાં આવે છે. વ્હાઇટલિસ્ટ એક્સપોઝર ઘટાડે છે અને પાસવર્ડ કે 2FA નો વિકલ્પ નથી"), ("whitelist_cidr_tip", "CIDR નોટેશન સપોર્ટેડ છે, ઉदાહરણ તરીકે 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/he.rs b/src/lang/he.rs index dfe37733d..3ea0d7626 100644 --- a/src/lang/he.rs +++ b/src/lang/he.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "חיבור באמצעות ממסר"), ("Secure Connection", "חיבור מאובטח"), ("Insecure Connection", "חיבור לא מאובטח"), - ("Continue", ""), ("Scale original", "קנה מידה מקורי"), ("Scale adaptive", "קנה מידה מותאם"), ("General", "כללי"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "כתובת ה-IP שלך נחסמה על ידי הצד המרוחק"), ("id_whitelist_caveat_tip", "המזהה מדווח על ידי הלקוח המתחבר. הרשימה הלבנה מצמצמת חשיפה ואינה מחליפה סיסמה או 2FA"), ("whitelist_cidr_tip", "יש תמיכה בסימון CIDR, לדוגמה 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hi.rs b/src/lang/hi.rs index a146053df..e3851a0d8 100644 --- a/src/lang/hi.rs +++ b/src/lang/hi.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "रिले कनेक्शन"), ("Secure Connection", "सुरक्षित कनेक्शन"), ("Insecure Connection", "असुरक्षित कनेक्शन"), - ("Continue", ""), ("Scale original", "मूल पैमाना"), ("Scale adaptive", "अनुकूली पैमाना"), ("General", "सामान्य"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "आपका IP दूसरे पक्ष द्वारा अवरुद्ध कर दिया गया है"), ("id_whitelist_caveat_tip", "ID कनेक्ट करने वाले क्लाइंट द्वारा बताई जाती है। श्वेतसूची जोखिम कम करती है और पासवर्ड या 2FA का विकल्प नहीं है"), ("whitelist_cidr_tip", "CIDR नोटेशन समर्थित है, उदाहरण के लिए 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hr.rs b/src/lang/hr.rs index 220bafac5..ee894b0e7 100644 --- a/src/lang/hr.rs +++ b/src/lang/hr.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Posredna veza"), ("Secure Connection", "Sigurna veza"), ("Insecure Connection", "Nesigurna veza"), - ("Continue", ""), ("Scale original", "Skaliraj izvornik"), ("Scale adaptive", "Prilagođeno skaliranje"), ("General", "Općenito"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Vašu IP adresu je blokiralo udaljeno računalo"), ("id_whitelist_caveat_tip", "ID prijavljuje klijent koji se povezuje. Ova lista smanjuje izloženost i ne zamjenjuje lozinku ni 2FA"), ("whitelist_cidr_tip", "Podržan je CIDR zapis, primjerice 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 733598c3f..14a85f1f7 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Kapcsolódás továbbító-kiszolgálón keresztül"), ("Secure Connection", "Biztonságos kapcsolat"), ("Insecure Connection", "Nem biztonságos kapcsolat"), - ("Continue", ""), ("Scale original", "Eredeti méretarány"), ("Scale adaptive", "Adaptív méretarány"), ("General", "Általános"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Az IP-címét a távoli fél letiltotta"), ("id_whitelist_caveat_tip", "Az azonosítót a csatlakozó kliens jelenti. Az engedélyezési lista csökkenti a kitettséget, és nem helyettesíti a jelszót vagy a 2FA-t"), ("whitelist_cidr_tip", "A CIDR jelölés támogatott, például 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 0482df425..7ba387e48 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Koneksi Relay"), ("Secure Connection", "Koneksi aman"), ("Insecure Connection", "Koneksi Tidak Aman"), - ("Continue", ""), ("Scale original", "Skala asli"), ("Scale adaptive", "Skala adaptif"), ("General", "Umum"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "IP Anda diblokir oleh perangkat remote"), ("id_whitelist_caveat_tip", "ID dilaporkan oleh klien yang terhubung. Daftar ini mengurangi paparan dan bukan pengganti kata sandi atau 2FA"), ("whitelist_cidr_tip", "Notasi CIDR didukung, misalnya 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index b002381ef..1297972df 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Connessione relay"), ("Secure Connection", "Connessione sicura"), ("Insecure Connection", "Connessione non sicura"), - ("Continue", "Continua"), ("Scale original", "Scala originale"), ("Scale adaptive", "Scala adattiva"), ("General", "Generale"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Il tuo IP è bloccato dal dispositivo remoto"), ("id_whitelist_caveat_tip", "L'ID è dichiarato dal client che si connette. Questo elenco riduce l'esposizione e non sostituisce la password o la 2FA"), ("whitelist_cidr_tip", "È supportata la notazione CIDR, ad esempio 192.168.1.0/24"), + ("Continue", "Continua"), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 9494d19f3..ba6e6cb09 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "中継接続"), ("Secure Connection", "安全な接続"), ("Insecure Connection", "安全でない接続"), - ("Continue", ""), ("Scale original", "オリジナルのサイズ"), ("Scale adaptive", "ウィンドウに合わせる"), ("General", "一般"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "あなたの IP アドレスは接続先によってブロックされています"), ("id_whitelist_caveat_tip", "ID は接続するクライアントから申告されます。ホワイトリストは露出を減らすもので、パスワードや 2FA の代わりにはなりません"), ("whitelist_cidr_tip", "CIDR 表記に対応しています。例: 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 186476a3c..f60af542b 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -773,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "귀하의 IP가 상대방에 의해 차단되었습니다"), ("id_whitelist_caveat_tip", "ID는 연결하는 클라이언트가 보고합니다. 화이트리스트는 노출을 줄이는 것으로 비밀번호나 2FA를 대체하지 않습니다"), ("whitelist_cidr_tip", "CIDR 표기를 지원합니다. 예: 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 419d80de2..fc59efde3 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Релай Қосылым"), ("Secure Connection", "Қауіпсіз Қосылым"), ("Insecure Connection", "Қатерлі Қосылым"), - ("Continue", ""), ("Scale original", "Scale original"), ("Scale adaptive", "Scale adaptive"), ("General", "Жалпы"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Сіздің IP-мекенжайыңыз қарсы тараппен бұғатталған"), ("id_whitelist_caveat_tip", "ID қосылатын клиентпен хабарланады. Ақ-тізім әсер ету аумағын азайтады және құпия сөзді немесе 2FA-ны алмастырмайды"), ("whitelist_cidr_tip", "CIDR жазбасына қолдау көрсетіледі, мысалы 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index 012ec1316..3589a2fb3 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Tarpinė jungtis"), ("Secure Connection", "Saugus ryšys"), ("Insecure Connection", "Nesaugus ryšys"), - ("Continue", ""), ("Scale original", "Pakeisti originalų mastelį"), ("Scale adaptive", "Pritaikomas mastelis"), ("General", "Bendra"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Jūsų IP adresą užblokavo nuotolinis įrenginys"), ("id_whitelist_caveat_tip", "ID praneša prisijungiantis klientas. Šis sąrašas sumažina atakos paviršių ir nepakeičia slaptažodžio ar 2FA"), ("whitelist_cidr_tip", "Palaikomas CIDR žymėjimas, pavyzdžiui 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lv.rs b/src/lang/lv.rs index ee901974b..d4101d6db 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Releja savienojums"), ("Secure Connection", "Drošs savienojums"), ("Insecure Connection", "Nedrošs savienojums"), - ("Continue", ""), ("Scale original", "Mērogs oriģināls"), ("Scale adaptive", "Mērogs adaptīvs"), ("General", "Vispārīgi"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Jūsu IP adresi ir bloķējusi otra puse"), ("id_whitelist_caveat_tip", "ID paziņo klients, kas veido savienojumu. Baltais saraksts samazina pakļautību un neaizstāj paroli vai 2FA"), ("whitelist_cidr_tip", "Tiek atbalstīts CIDR pieraksts, piemēram 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ml.rs b/src/lang/ml.rs index b6faa4655..d93760b50 100644 --- a/src/lang/ml.rs +++ b/src/lang/ml.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "റിലേ കണക്ഷൻ"), ("Secure Connection", "സുരക്ഷിതമായ കണക്ഷൻ"), ("Insecure Connection", "സുരക്ഷിതമല്ലാത്ത കണക്ഷൻ"), - ("Continue", ""), ("Scale original", "ഒറിജിനൽ വലിപ്പം"), ("Scale adaptive", "അഡാപ്റ്റീവ് വലിപ്പം"), ("General", "പൊതുവായവ"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "നിങ്ങളുടെ IP വിലാസം മറുവശം ബ്ലോക്ക് ചെയ്തിരിക്കുന്നു"), ("id_whitelist_caveat_tip", "കണക്റ്റ് ചെയ്യുന്ന ക്ലയന്റാണ് ID റിപ്പോർട്ട് ചെയ്യുന്നത്. വൈറ്റ്‌ലിസ്റ്റ് എക്സ്പോഷർ കുറയ്ക്കുന്നു; പാസ്‌വേഡിനോ 2FA-യ്ക്കോ പകരമല്ല"), ("whitelist_cidr_tip", "CIDR നൊട്ടേഷൻ പിന്തുണയ്ക്കുന്നു, ഉദാഹരണത്തിന് 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nb.rs b/src/lang/nb.rs index b15f1c59f..3cc71a96b 100644 --- a/src/lang/nb.rs +++ b/src/lang/nb.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Viderekoblet tilkobling"), ("Secure Connection", "Sikker tilkobling"), ("Insecure Connection", "Usikker tilkobling"), - ("Continue", ""), ("Scale original", "Original skalering"), ("Scale adaptive", "Adaptiv skalering"), ("General", "Generelt"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "IP-adressen din er blokkert av motparten"), ("id_whitelist_caveat_tip", "ID-en rapporteres av klienten som kobler til. Hvitelisten reduserer eksponeringen og erstatter ikke passord eller 2FA"), ("whitelist_cidr_tip", "CIDR-notasjon støttes, for eksempel 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 9f3b9f7d3..769371fd8 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Relay-verbinding"), ("Secure Connection", "Beveiligde verbinding"), ("Insecure Connection", "Onveilige verbinding"), - ("Continue", "Doorgaan"), ("Scale original", "Oorspronkelijk formaat"), ("Scale adaptive", "Automatisch schalen"), ("General", "Algemeen"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Uw IP wordt door de ander geblokkeerd"), ("id_whitelist_caveat_tip", "De ID wordt vermeld door de verbindende client. Deze witte lijst vermindert de zichtbaarheid en vervangt niet het wachtwoord of 2FA."), ("whitelist_cidr_tip", "CIDR-notatie wordt ondersteund, bijv. 192.168.1.0/24"), + ("Continue", "Doorgaan"), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 8971a31b6..df5c53439 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Połączenie przez bramkę"), ("Secure Connection", "Połączenie szyfrowane"), ("Insecure Connection", "Połączenie nieszyfrowane"), - ("Continue", "Kontynuuj"), ("Scale original", "Skalowanie oryginalne"), ("Scale adaptive", "Dopasuj do wyświetlacza"), ("General", "Ogólne"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Twój adres IP został zablokowany przez drugą stronę"), ("id_whitelist_caveat_tip", "ID jest zgłaszane przez łączącego się klienta. Biała lista zmniejsza ekspozycję i nie zastępuje hasła ani 2FA"), ("whitelist_cidr_tip", "Obsługiwana jest notacja CIDR, na przykład 192.168.1.0/24"), + ("Continue", "Kontynuuj"), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index e730efcb6..79420e73b 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Conexão de relé"), ("Secure Connection", "Conexão segura"), ("Insecure Connection", "Conexão insegura"), - ("Continue", ""), ("Scale original", "Escala original"), ("Scale adaptive", "Escala adaptável"), ("General", "Geral"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "O seu IP está bloqueado pelo dispositivo remoto"), ("id_whitelist_caveat_tip", "O ID é comunicado pelo cliente que se liga. A whitelist reduz a exposição e não substitui a palavra-passe nem o 2FA"), ("whitelist_cidr_tip", "A notação CIDR é suportada, por exemplo 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 07d72b268..8d44d6140 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Conexão via Relay"), ("Secure Connection", "Conexão Segura"), ("Insecure Connection", "Conexão Insegura"), - ("Continue", "Continuar"), ("Scale original", "Escala original"), ("Scale adaptive", "Escala adaptada"), ("General", "Geral"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Seu IP foi bloqueado pelo dispositivo remoto"), ("id_whitelist_caveat_tip", "O ID é informado pelo cliente que se conecta. A lista reduz a exposição e não substitui a senha ou o 2FA"), ("whitelist_cidr_tip", "A notação CIDR é suportada, por exemplo 192.168.1.0/24"), + ("Continue", "Continuar"), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index c703af648..4499df1bd 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Conexiune prin retransmisie"), ("Secure Connection", "Conexiune securizată"), ("Insecure Connection", "Conexiune nesecurizată"), - ("Continue", ""), ("Scale original", "Dimensiune originală"), ("Scale adaptive", "Scalare automată"), ("General", "General"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Adresa ta IP este blocată de dispozitivul de la distanță"), ("id_whitelist_caveat_tip", "ID-ul este raportat de clientul care se conectează. Lista albă reduce expunerea și nu înlocuiește parola sau 2FA"), ("whitelist_cidr_tip", "Notația CIDR este acceptată, de exemplu 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index b808b5cd3..459549f97 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Ретранслируемое подключение"), ("Secure Connection", "Безопасное подключение"), ("Insecure Connection", "Небезопасное подключение"), - ("Continue", ""), ("Scale original", "Оригинальный масштаб"), ("Scale adaptive", "Адаптивный масштаб"), ("General", "Общие"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Ваш IP-адрес заблокирован удалённым устройством"), ("id_whitelist_caveat_tip", "ID сообщается подключающимся клиентом. Белый список уменьшает поверхность атаки и не заменяет пароль или 2FA"), ("whitelist_cidr_tip", "Поддерживается нотация CIDR, например 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sc.rs b/src/lang/sc.rs index cbe9103d1..1ccfcf7dc 100644 --- a/src/lang/sc.rs +++ b/src/lang/sc.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Connessione tramudada (relay)"), ("Secure Connection", "Connessione segura"), ("Insecure Connection", "Connessione non segura"), - ("Continue", ""), ("Scale original", "Iscala originale"), ("Scale adaptive", "Iscala adativa"), ("General", "Generale"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "S'indiritzu IP tuo est blocadu dae s'àtera parte"), ("id_whitelist_caveat_tip", "S'ID est decraradu dae su cliente chi si connetet. Custu elencu minimat s'espositzione e non sostituit sa crae o su 2FA"), ("whitelist_cidr_tip", "Sa notatzione CIDR est suportada, pro esempru 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index d67c7b866..3d4993115 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Reléové pripojenie"), ("Secure Connection", "Zabezpečené pripojenie"), ("Insecure Connection", "Nezabezpečené pripojenie"), - ("Continue", ""), ("Scale original", "Pôvodná mierka"), ("Scale adaptive", "Prispôsobivá mierka"), ("General", "Všeobecné"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Vaša IP adresa je blokovaná protistranou"), ("id_whitelist_caveat_tip", "ID nahlasuje pripájajúci sa klient. Tento zoznam znižuje vystavenie a nenahrádza heslo ani 2FA"), ("whitelist_cidr_tip", "Je podporovaný zápis CIDR, napríklad 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 6bcea9097..10fc5d909 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Posredovana povezava"), ("Secure Connection", "Zavarovana povezava"), ("Insecure Connection", "Nezavarovana povezava"), - ("Continue", ""), ("Scale original", "Originalna velikost"), ("Scale adaptive", "Prilagojena velikost"), ("General", "Splošno"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Vaš IP je blokirala oddaljena naprava"), ("id_whitelist_caveat_tip", "ID sporoči odjemalec, ki se povezuje. Seznam zmanjšuje izpostavljenost in ne nadomešča gesla ali 2FA"), ("whitelist_cidr_tip", "Podprt je zapis CIDR, na primer 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 330248890..91f5d4c7a 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Lidhja rele"), ("Secure Connection", "Lidhje e sigurt"), ("Insecure Connection", "Lidhje e pasigurt"), - ("Continue", ""), ("Scale original", "Shkalla origjinale"), ("Scale adaptive", " E përsjhtatshme në shkallë"), ("General", "Gjeneral"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "IP-ja juaj është bllokuar nga pala tjetër"), ("id_whitelist_caveat_tip", "ID-ja raportohet nga klienti që lidhet. Lista e bardhë zvogëlon ekspozimin dhe nuk zëvendëson fjalëkalimin ose 2FA"), ("whitelist_cidr_tip", "Mbështetet shënimi CIDR, për shembull 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 43eb13b90..b79eccf5b 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Posredna konekcija"), ("Secure Connection", "Bezbedna konekcija"), ("Insecure Connection", "Nebezbedna konekcija"), - ("Continue", ""), ("Scale original", "Skaliraj original"), ("Scale adaptive", "Adaptivno skaliranje"), ("General", "Uopšteno"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Vašu IP adresu je blokirala druga strana"), ("id_whitelist_caveat_tip", "ID prijavljuje klijent koji se povezuje. Ova lista smanjuje izloženost i ne zamenjuje lozinku ni 2FA"), ("whitelist_cidr_tip", "Podržan je CIDR zapis, na primer 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index d3b04793f..79dd316cd 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Relayanslutning"), ("Secure Connection", "Säker anslutning"), ("Insecure Connection", "Osäker anslutning"), - ("Continue", ""), ("Scale original", "Skala orginal"), ("Scale adaptive", "Skala adaptivt"), ("General", "Generellt"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Din IP-adress är blockerad av motparten"), ("id_whitelist_caveat_tip", "ID:t rapporteras av klienten som ansluter. Vitlistan minskar exponeringen och ersätter inte lösenord eller 2FA"), ("whitelist_cidr_tip", "CIDR-notation stöds, till exempel 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ta.rs b/src/lang/ta.rs index 3b1782895..376af972e 100644 --- a/src/lang/ta.rs +++ b/src/lang/ta.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "ரிலே இணைப்பு"), ("Secure Connection", "பாதுகாப்பான இணைப்பு"), ("Insecure Connection", "பாதுகாப்பற்ற இணைப்பு"), - ("Continue", ""), ("Scale original", "அசல் அளவு"), ("Scale adaptive", "தகவமைப்பு அளவு"), ("General", "பொது"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "உங்கள் IP முகவரி மறுமுனையால் தடுக்கப்பட்டுள்ளது"), ("id_whitelist_caveat_tip", "இணைக்கும் கிளையண்டே ID-ஐ தெரிவிக்கிறது. அனுமதிப்பட்டியல் வெளிப்பாட்டைக் குறைக்கிறது; கடவுச்சொல் அல்லது 2FA-க்கு மாற்றாகாது"), ("whitelist_cidr_tip", "CIDR குறியீடு ஆதரிக்கப்படுகிறது, எடுத்துக்காட்டாக 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 24e3a5062..f16cf1ebc 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", ""), ("Secure Connection", ""), ("Insecure Connection", ""), - ("Continue", ""), ("Scale original", ""), ("Scale adaptive", ""), ("General", ""), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", ""), ("id_whitelist_caveat_tip", ""), ("whitelist_cidr_tip", ""), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 17a050e84..bd87cf5a7 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "การเชื่อมต่อแบบ Relay "), ("Secure Connection", "การเชื่อมต่อที่ปลอดภัย"), ("Insecure Connection", "การเชื่อมต่อที่ไม่ปลอดภัย"), - ("Continue", ""), ("Scale original", "ขนาดเดิม"), ("Scale adaptive", "ขนาดยืดหยุ่น"), ("General", "ทั่วไป"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "IP ของคุณถูกบล็อกโดยฝั่งตรงข้าม"), ("id_whitelist_caveat_tip", "ID ถูกรายงานโดยไคลเอนต์ที่เชื่อมต่อ ไวท์ลิสต์ช่วยลดการเปิดเผยและไม่สามารถใช้แทนรหัสผ่านหรือ 2FA ได้"), ("whitelist_cidr_tip", "รองรับรูปแบบ CIDR เช่น 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 955ec9673..2925ce792 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Aktarmalı Bağlantı"), ("Secure Connection", "Güvenli Bağlantı"), ("Insecure Connection", "Güvenli Olmayan Bağlantı"), - ("Continue", ""), ("Scale original", "Orijinal ölçekte"), ("Scale adaptive", "Uyarlanabilir ölçekte"), ("General", "Genel"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "IP adresiniz karşı taraf tarafından engellendi"), ("id_whitelist_caveat_tip", "ID, bağlanan istemci tarafından bildirilir. Bu liste maruziyeti azaltır; parolanın veya 2FA'nın yerini tutmaz"), ("whitelist_cidr_tip", "CIDR gösterimi desteklenir, örneğin 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 71b853e99..0401d80b7 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "中繼連線"), ("Secure Connection", "安全連線"), ("Insecure Connection", "非安全連線"), - ("Continue", ""), ("Scale original", "原始尺寸"), ("Scale adaptive", "適應視窗"), ("General", "一般"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "你的 IP 已被對方封鎖"), ("id_whitelist_caveat_tip", "ID 由對端用戶端回報,白名單用於減少暴露面,不能取代密碼或 2FA"), ("whitelist_cidr_tip", "支援 CIDR 寫法,例如 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/uk.rs b/src/lang/uk.rs index 5de8a5572..7e55426d1 100644 --- a/src/lang/uk.rs +++ b/src/lang/uk.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Ретрансльоване підключення"), ("Secure Connection", "Безпечне підключення"), ("Insecure Connection", "Небезпечне підключення"), - ("Continue", ""), ("Scale original", "Оригінальний масштаб"), ("Scale adaptive", "Адаптивний масштаб"), ("General", "Загальні"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "Вашу IP-адресу заблоковано віддаленим пристроєм"), ("id_whitelist_caveat_tip", "ID повідомляється клієнтом, що підключається. Білий список зменшує поверхню атаки і не замінює пароль або 2FA"), ("whitelist_cidr_tip", "Підтримується нотація CIDR, наприклад 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vi.rs b/src/lang/vi.rs index d32c7ff2e..af358831e 100644 --- a/src/lang/vi.rs +++ b/src/lang/vi.rs @@ -332,7 +332,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Kết nối chuyển tiếp"), ("Secure Connection", "Kết nối bảo mật"), ("Insecure Connection", "Kết nối không bảo mật"), - ("Continue", ""), ("Scale original", "Tỷ lệ gốc"), ("Scale adaptive", "Tỷ lệ thích ứng"), ("General", "Chung"), @@ -774,5 +773,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your ip is blocked by the peer", "IP của bạn đã bị phía bên kia chặn"), ("id_whitelist_caveat_tip", "ID do máy khách kết nối tự khai báo. Danh sách trắng giúp giảm mức độ lộ diện và không thay thế mật khẩu hay 2FA"), ("whitelist_cidr_tip", "Hỗ trợ ký hiệu CIDR, ví dụ 192.168.1.0/24"), + ("Continue", ""), + ("Browser didn't open? Use the url below to sign in.", ""), ].iter().cloned().collect(); }