diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 751020d9..9d20d5dc 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -690,7 +690,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 876; + CURRENT_PROJECT_VERSION = 877; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; @@ -700,7 +700,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.876; + MARKETING_VERSION = 1.0.877; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -826,7 +826,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 876; + CURRENT_PROJECT_VERSION = 877; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; @@ -836,7 +836,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.876; + MARKETING_VERSION = 1.0.877; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -854,7 +854,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 876; + CURRENT_PROJECT_VERSION = 877; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; @@ -864,7 +864,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.876; + MARKETING_VERSION = 1.0.877; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -885,7 +885,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 876; + CURRENT_PROJECT_VERSION = 877; DEVELOPMENT_TEAM = BA88US33G6; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -898,7 +898,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.0.876; + MARKETING_VERSION = 1.0.877; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; @@ -924,7 +924,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 876; + CURRENT_PROJECT_VERSION = 877; DEVELOPMENT_TEAM = BA88US33G6; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -937,7 +937,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.0.876; + MARKETING_VERSION = 1.0.877; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -960,7 +960,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 876; + CURRENT_PROJECT_VERSION = 877; DEVELOPMENT_TEAM = BA88US33G6; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -973,7 +973,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.0.876; + MARKETING_VERSION = 1.0.877; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -996,7 +996,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 876; + CURRENT_PROJECT_VERSION = 877; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_PREVIEWS = YES; @@ -1008,7 +1008,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.876; + MARKETING_VERSION = 1.0.877; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; @@ -1037,7 +1037,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 876; + CURRENT_PROJECT_VERSION = 877; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_PREVIEWS = YES; @@ -1049,7 +1049,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.876; + MARKETING_VERSION = 1.0.877; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; PRODUCT_NAME = ServerBox; @@ -1075,7 +1075,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 876; + CURRENT_PROJECT_VERSION = 877; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_PREVIEWS = YES; @@ -1087,7 +1087,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.876; + MARKETING_VERSION = 1.0.877; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; PRODUCT_NAME = ServerBox; diff --git a/lib/core/extension/context/dialog.dart b/lib/core/extension/context/dialog.dart index 4939ea40..93b96789 100644 --- a/lib/core/extension/context/dialog.dart +++ b/lib/core/extension/context/dialog.dart @@ -61,13 +61,16 @@ extension DialogX on BuildContext { static final _recoredPwd = {}; + /// Show a dialog to input password + /// + /// [hostId] set it to null to skip remembering the password Future showPwdDialog({ - String? user, - required String hostId, + String? hostId, + String? title, }) async { if (!mounted) return null; return await showRoundDialog( - title: Text(user ?? l10n.pwd), + title: Text(title ?? hostId ?? l10n.pwd), child: Input( controller: TextEditingController(text: _recoredPwd[hostId]), autoFocus: true, @@ -75,7 +78,7 @@ extension DialogX on BuildContext { obscureText: true, onSubmitted: (val) { pop(val); - if (Stores.setting.rememberPwdInMem.fetch()) { + if (hostId != null && Stores.setting.rememberPwdInMem.fetch()) { _recoredPwd[hostId] = val; } }, diff --git a/lib/core/extension/ssh_client.dart b/lib/core/extension/ssh_client.dart index 55c65e5e..e7771fec 100644 --- a/lib/core/extension/ssh_client.dart +++ b/lib/core/extension/ssh_client.dart @@ -81,9 +81,9 @@ extension SSHClientX on SSHClient { isRequestingPwd = true; final user = Miscs.pwdRequestWithUserReg.firstMatch(data)?.group(1); if (context == null) return; - final pwd = await context.showPwdDialog(user: user, hostId: id); + final pwd = await context.showPwdDialog(title: user, hostId: id); if (pwd == null || pwd.isEmpty) { - session.kill(SSHSignal.INT); + session.kill(SSHSignal.TERM); } else { session.stdin.add('$pwd\n'.uint8List); } diff --git a/lib/core/utils/auth.dart b/lib/core/utils/auth.dart new file mode 100644 index 00000000..43227148 --- /dev/null +++ b/lib/core/utils/auth.dart @@ -0,0 +1,20 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:toolbox/core/extension/context/dialog.dart'; +import 'package:toolbox/data/model/server/server_private_info.dart'; +import 'package:toolbox/data/res/provider.dart'; + +abstract final class KeybordInteractive { + static FutureOr?> defaultHandle( + ServerPrivateInfo spi, { + BuildContext? ctx, + }) async { + try { + final res = await (ctx ?? Pros.app.ctx)?.showPwdDialog(title: spi.id); + return res == null ? null : [res]; + } catch (e) { + return null; + } + } +} diff --git a/lib/core/utils/server.dart b/lib/core/utils/server.dart index d17b0278..433064a1 100644 --- a/lib/core/utils/server.dart +++ b/lib/core/utils/server.dart @@ -56,6 +56,9 @@ Future genClient( /// /// Must pass this param when use multi-thread and key login ServerPrivateInfo? jumpSpi, + + /// Handle keyboard-interactive authentication + FutureOr?> Function(SSHUserInfoRequest)? onKeyboardInteractive, }) async { onStatus?.call(GenSSHClientStatus.socket); @@ -109,6 +112,9 @@ Future genClient( socket, username: spi.user, onPasswordRequest: () => spi.pwd, + onUserInfoRequest: onKeyboardInteractive, + printDebug: debugPrint, + printTrace: debugPrint, ); } privateKey ??= getPrivateKey(keyId); @@ -119,5 +125,8 @@ Future genClient( username: spi.user, // Must use [compute] here, instead of [Computer.shared.start] identities: await compute(loadIndentity, privateKey), + onUserInfoRequest: onKeyboardInteractive, + printDebug: debugPrint, + printTrace: debugPrint, ); } diff --git a/lib/data/provider/app.dart b/lib/data/provider/app.dart index 77f46d12..a7900d72 100644 --- a/lib/data/provider/app.dart +++ b/lib/data/provider/app.dart @@ -9,4 +9,6 @@ class AppProvider extends ChangeNotifier { } bool moveBg = true; + + BuildContext? ctx; } diff --git a/lib/data/provider/server.dart b/lib/data/provider/server.dart index f8c8b7aa..b77a5e2d 100644 --- a/lib/data/provider/server.dart +++ b/lib/data/provider/server.dart @@ -6,6 +6,7 @@ import 'package:dartssh2/dartssh2.dart'; import 'package:flutter/material.dart'; import 'package:toolbox/core/extension/ssh_client.dart'; import 'package:toolbox/core/extension/stringx.dart'; +import 'package:toolbox/core/utils/auth.dart'; import 'package:toolbox/core/utils/platform/path.dart'; import 'package:toolbox/data/model/app/shell_func.dart'; import 'package:toolbox/data/model/server/system.dart'; @@ -278,15 +279,14 @@ class ServerProvider extends ChangeNotifier { s.client = await genClient( spi, timeout: Duration(seconds: Stores.setting.timeout.fetch()), + onKeyboardInteractive: (_) => KeybordInteractive.defaultHandle(spi), ); final time2 = DateTime.now(); final spentTime = time2.difference(time1).inMilliseconds; if (spi.jumpId == null) { Loggers.app.info('Connected to ${spi.name} in $spentTime ms.'); } else { - Loggers.app.info( - 'Connected to ${spi.name} via jump server in $spentTime ms.', - ); + Loggers.app.info('Jump to ${spi.name} in $spentTime ms.'); } } catch (e) { TryLimiter.inc(sid); @@ -316,6 +316,11 @@ class ServerProvider extends ChangeNotifier { s.status.err = e.toString(); _setServerState(s, ServerState.failed); return; + } on SSHAuthFailError catch (e) { + TryLimiter.inc(sid); + s.status.err = e.toString(); + _setServerState(s, ServerState.failed); + return; } catch (e) { Loggers.app.warning('Write script to ${spi.name} by shell', e); @@ -351,6 +356,8 @@ class ServerProvider extends ChangeNotifier { } } + if (s.state == ServerState.connecting) return; + /// Keep [finished] state, or the UI will be refreshed to [loading] state /// instead of the '$Temp | $Uptime'. /// eg: '32C | 7 days' diff --git a/lib/data/res/build_data.dart b/lib/data/res/build_data.dart index 6bad109d..1ef335e8 100644 --- a/lib/data/res/build_data.dart +++ b/lib/data/res/build_data.dart @@ -2,9 +2,9 @@ class BuildData { static const String name = "ServerBox"; - static const int build = 876; + static const int build = 877; static const String engine = "3.19.6"; - static const String buildAt = "2024-04-26 23:44:14"; - static const int modifications = 4; + static const String buildAt = "2024-05-07 15:17:19"; + static const int modifications = 15; static const int script = 45; } diff --git a/lib/data/res/github_id.dart b/lib/data/res/github_id.dart index f850feb4..7228e2d2 100644 --- a/lib/data/res/github_id.dart +++ b/lib/data/res/github_id.dart @@ -70,5 +70,6 @@ abstract final class GithubIds { 'zj1123581321', 'pctoolsx', 'pgs666', + 'FHU-yezi', }; } diff --git a/lib/view/page/home/home.dart b/lib/view/page/home/home.dart index 9113c4f5..38bd7438 100644 --- a/lib/view/page/home/home.dart +++ b/lib/view/page/home/home.dart @@ -120,6 +120,7 @@ class _HomePageState extends State @override Widget build(BuildContext context) { super.build(context); + Pros.app.ctx = context; return Scaffold( drawer: _buildDrawer(), diff --git a/lib/view/page/server/tab.dart b/lib/view/page/server/tab.dart index 3307ca2f..a578e6ad 100644 --- a/lib/view/page/server/tab.dart +++ b/lib/view/page/server/tab.dart @@ -16,6 +16,7 @@ import 'package:toolbox/core/utils/platform/base.dart'; import 'package:toolbox/core/utils/share.dart'; import 'package:toolbox/data/model/app/shell_func.dart'; import 'package:toolbox/data/model/server/try_limiter.dart'; +import 'package:toolbox/data/res/color.dart'; import 'package:toolbox/data/res/provider.dart'; import 'package:toolbox/data/res/store.dart'; import 'package:toolbox/view/widget/percent_circle.dart'; @@ -432,8 +433,48 @@ class _ServerPageState extends State } Widget _buildServerCardTitle(Server s) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 7), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ConstrainedBox( + constraints: BoxConstraints(maxWidth: _media.size.width / 2.3), + child: Text( + s.spi.name, + style: UIs.text13Bold, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + const Icon( + Icons.keyboard_arrow_right, + size: 17, + color: Colors.grey, + ), + const Spacer(), + _buildTopRightText(s), + _buildTopRightWidget(s), + ], + ), + ); + } + + Widget _buildTopRightWidget(Server s) { Widget rightCorner = UIs.placeholder; - if (s.state == ServerState.failed) { + if (s.state == ServerState.connecting) { + rightCorner = Padding( + padding: const EdgeInsets.symmetric(horizontal: 7), + child: SizedBox( + width: 21, + height: 21, + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation(primaryColor), + ), + ), + ); + } else if (s.state == ServerState.failed) { rightCorner = InkWell( onTap: () { TryLimiter.reset(s.spi.id); @@ -464,31 +505,7 @@ class _ServerPageState extends State } else if (Stores.setting.serverTabUseOldUI.fetch()) { rightCorner = ServerFuncBtnsTopRight(spi: s.spi); } - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 7), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - ConstrainedBox( - constraints: BoxConstraints(maxWidth: _media.size.width / 2.3), - child: Text( - s.spi.name, - style: UIs.text13Bold, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - const Icon( - Icons.keyboard_arrow_right, - size: 17, - color: Colors.grey, - ), - const Spacer(), - _buildTopRightText(s), - rightCorner, - ], - ), - ); + return rightCorner; } Widget _buildTopRightText(Server s) { diff --git a/lib/view/page/ssh/page.dart b/lib/view/page/ssh/page.dart index 1d789b80..eb099e93 100644 --- a/lib/view/page/ssh/page.dart +++ b/lib/view/page/ssh/page.dart @@ -9,6 +9,7 @@ import 'package:provider/provider.dart'; import 'package:toolbox/core/extension/context/common.dart'; import 'package:toolbox/core/extension/context/dialog.dart'; import 'package:toolbox/core/extension/context/locale.dart'; +import 'package:toolbox/core/utils/auth.dart'; import 'package:toolbox/core/utils/platform/base.dart'; import 'package:toolbox/core/utils/server.dart'; import 'package:toolbox/core/utils/share.dart'; @@ -384,9 +385,16 @@ class _SSHPageState extends State with AutomaticKeepAliveClientMixin { Future _initTerminal() async { _writeLn('Connecting...\r\n'); - _client ??= await genClient(widget.spi); - _writeLn('Starting shell...\r\n'); + _client ??= await genClient( + widget.spi, + onStatus: (p0) { + _writeLn(p0.toString()); + }, + onKeyboardInteractive: (_) => + KeybordInteractive.defaultHandle(widget.spi), + ); + _writeLn('Starting shell...\r\n'); final session = await _client?.shell( pty: SSHPtyConfig( width: _terminal.viewWidth, diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index b5f5c54e..a680e345 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -471,7 +471,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 876; + CURRENT_PROJECT_VERSION = 877; DEVELOPMENT_TEAM = BA88US33G6; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Server Box"; @@ -481,7 +481,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 1.0.876; + MARKETING_VERSION = 1.0.877; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "Server Box"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -608,7 +608,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 876; + CURRENT_PROJECT_VERSION = 877; DEVELOPMENT_TEAM = BA88US33G6; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Server Box"; @@ -618,7 +618,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 1.0.876; + MARKETING_VERSION = 1.0.877; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "Server Box"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -638,7 +638,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 876; + CURRENT_PROJECT_VERSION = 877; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=macosx*]" = BA88US33G6; INFOPLIST_FILE = Runner/Info.plist; @@ -649,7 +649,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 1.0.876; + MARKETING_VERSION = 1.0.877; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "Server Box"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/pubspec.lock b/pubspec.lock index 0d1e3b30..e4b7e556 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -262,10 +262,9 @@ packages: dartssh2: dependency: "direct main" description: - name: dartssh2 - sha256: "48d35ad9b697627b59f9ebeab4e4936266dc153cff96f02c997d3fe30df4d80a" - url: "https://pub.dev" - source: hosted + path: "../dartssh2" + relative: true + source: path version: "2.9.1-pre" dbus: dependency: transitive diff --git a/pubspec.yaml b/pubspec.yaml index f21bdcf9..fb220706 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,11 +19,12 @@ dependencies: after_layout: ^1.1.0 url_launcher: ^6.1.8 countly_flutter: ^24.4.0 - dartssh2: ^2.9.1-pre + dartssh2: #^2.9.1-pre # newer version has some issues # git: # ref: master # url: https://github.com/lollipopkit/dartssh2 + path: ../dartssh2 logging: ^1.0.2 circle_chart: git: