diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index d5336e2e1..65bdc00f4 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -1547,7 +1547,7 @@ Future initUniLinks() async { if (initialLink == null) { return false; } - return parseRustdeskUri(initialLink); + return handleUriLink(uriString: initialLink); } catch (err) { debugPrintStack(label: "$err"); return false; @@ -1568,7 +1568,7 @@ StreamSubscription? listenUniLinks({handleByFlutter = true}) { debugPrint("A uri was received: $uri."); if (uri != null) { if (handleByFlutter) { - callUniLinksUriHandler(uri); + handleUriLink(uri: uri); } else { bind.sendUrlScheme(url: uri.toString()); } @@ -1581,90 +1581,142 @@ StreamSubscription? listenUniLinks({handleByFlutter = true}) { return sub; } -/// Handle command line arguments -/// -/// * Returns true if we successfully handle the startup arguments. -bool checkArguments() { - if (kBootArgs.isNotEmpty) { - final ret = parseRustdeskUri(kBootArgs.first); - if (ret) { - return true; +enum UriLinkType { + remoteDesktop, + fileTransfer, + portForward, + rdp, +} + +// uri link handler +bool handleUriLink({List? cmdArgs, Uri? uri, String? uriString}) { + List? args; + if (cmdArgs != null) { + args = cmdArgs; + if (args.isNotEmpty && args[0].startsWith(kUniLinksPrefix)) { + final uri = Uri.tryParse(args[0]); + if (uri != null) { + args = urlLinkToCmdArgs(uri); + } + } + } else if (uri != null) { + args = urlLinkToCmdArgs(uri); + } else if (uriString != null) { + final uri = Uri.tryParse(uriString); + if (uri != null) { + args = urlLinkToCmdArgs(uri); } } - // bootArgs:[--connect, 362587269, --switch_uuid, e3d531cc-5dce-41e0-bd06-5d4a2b1eec05] - // check connect args - var connectIndex = kBootArgs.indexOf("--connect"); - if (connectIndex == -1) { - return false; - } - String? id = - kBootArgs.length <= connectIndex + 1 ? null : kBootArgs[connectIndex + 1]; - String? password = - kBootArgs.length <= connectIndex + 2 ? null : kBootArgs[connectIndex + 2]; - if (password != null && password.startsWith("--")) { - password = null; - } - final switchUuidIndex = kBootArgs.indexOf("--switch_uuid"); - String? switchUuid = kBootArgs.length <= switchUuidIndex + 1 - ? null - : kBootArgs[switchUuidIndex + 1]; - if (id != null) { - if (id.startsWith(kUniLinksPrefix)) { - return parseRustdeskUri(id); - } else { - // remove "--connect xxx" in the `bootArgs` array - kBootArgs.removeAt(connectIndex); - kBootArgs.removeAt(connectIndex); - // fallback to peer id - Future.delayed(Duration.zero, () { - rustDeskWinManager.newRemoteDesktop(id, - password: password, switch_uuid: switchUuid); - }); - return true; + if (args == null) return false; + + UriLinkType? type; + String? id; + String? password; + String? switchUuid; + bool? forceRelay; + for (int i = 0; i < args.length; i++) { + switch (args[i]) { + case '--connect': + case '--play': + type = UriLinkType.remoteDesktop; + id = args[i + 1]; + i++; + break; + case '--file-transfer': + type = UriLinkType.fileTransfer; + id = args[i + 1]; + i++; + break; + case '--port-forward': + type = UriLinkType.portForward; + id = args[i + 1]; + i++; + break; + case '--rdp': + type = UriLinkType.rdp; + id = args[i + 1]; + i++; + break; + case '--password': + password = args[i + 1]; + i++; + break; + case '--switch_uuid': + switchUuid = args[i + 1]; + i++; + break; + case '--relay': + forceRelay = true; + break; + default: + break; } } + if (type != null && id != null) { + switch (type) { + case UriLinkType.remoteDesktop: + Future.delayed(Duration.zero, () { + rustDeskWinManager.newRemoteDesktop(id!, + password: password, + switch_uuid: switchUuid, + forceRelay: forceRelay); + }); + break; + case UriLinkType.fileTransfer: + Future.delayed(Duration.zero, () { + rustDeskWinManager.newFileTransfer(id!, + password: password, forceRelay: forceRelay); + }); + break; + case UriLinkType.portForward: + Future.delayed(Duration.zero, () { + rustDeskWinManager.newPortForward(id!, false, + password: password, forceRelay: forceRelay); + }); + break; + case UriLinkType.rdp: + Future.delayed(Duration.zero, () { + rustDeskWinManager.newPortForward(id!, true, + password: password, forceRelay: forceRelay); + }); + break; + } + + return true; + } + return false; } -/// Parse `rustdesk://` unilinks -/// -/// Returns true if we successfully handle the uri provided. -/// [Functions] -/// 1. New Connection: rustdesk://connection/new/your_peer_id -bool parseRustdeskUri(String uriPath) { - final uri = Uri.tryParse(uriPath); - if (uri == null) { - debugPrint("uri is not valid: $uriPath"); - return false; - } - return callUniLinksUriHandler(uri); -} - -/// uri handler -/// -/// Returns true if we successfully handle the uri provided. -bool callUniLinksUriHandler(Uri uri) { - debugPrint("uni links called: $uri"); - // new connection - String peerId; +List? urlLinkToCmdArgs(Uri uri) { + String? command; + String? id; if (uri.authority == "connection" && uri.path.startsWith("/new/")) { - peerId = uri.path.substring("/new/".length); - } else if (uri.authority == "connect") { - peerId = uri.path.substring(1); - } else if (uri.authority.length > 2 && uri.path.length <= 1) { - // "/" or "" - peerId = uri.authority; - } else { - return false; + // For compatibility + command = '--connect'; + id = uri.path.substring("/new/".length); + } else if (['connect', "play", 'file-transfer', 'port-forward', 'rdp'] + .contains(uri.authority)) { + command = '--${uri.authority}'; + if (uri.path.length > 1) { + id = uri.path.substring(1); + } } - var param = uri.queryParameters; - String? switch_uuid = param["switch_uuid"]; - String? password = param["password"]; - Future.delayed(Duration.zero, () { - rustDeskWinManager.newRemoteDesktop(peerId, - password: password, switch_uuid: switch_uuid); - }); - return true; + + List args = List.empty(growable: true); + if (command != null && id != null) { + args.add(command); + args.add(id); + var param = uri.queryParameters; + String? password = param["password"]; + if (password != null) args.addAll(['--password', password]); + String? switch_uuid = param["switch_uuid"]; + if (switch_uuid != null) args.addAll(['--switch_uuid', switch_uuid]); + if (param["relay"] != null) args.add("--relay"); + return args; + } + + return null; } connectMainDesktop(String id, diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index eae3f1d69..73d10a957 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -52,10 +52,12 @@ class FileManagerPage extends StatefulWidget { const FileManagerPage( {Key? key, required this.id, + required this.password, required this.tabController, this.forceRelay}) : super(key: key); final String id; + final String? password; final bool? forceRelay; final DesktopTabController tabController; @@ -79,7 +81,10 @@ class _FileManagerPageState extends State void initState() { super.initState(); _ffi = FFI(); - _ffi.start(widget.id, isFileTransfer: true, forceRelay: widget.forceRelay); + _ffi.start(widget.id, + isFileTransfer: true, + password: widget.password, + forceRelay: widget.forceRelay); WidgetsBinding.instance.addPostFrameCallback((_) { _ffi.dialogManager .showLoading(translate('Connecting...'), onCancel: closeConnection); diff --git a/flutter/lib/desktop/pages/file_manager_tab_page.dart b/flutter/lib/desktop/pages/file_manager_tab_page.dart index d41397833..f994e461c 100644 --- a/flutter/lib/desktop/pages/file_manager_tab_page.dart +++ b/flutter/lib/desktop/pages/file_manager_tab_page.dart @@ -44,6 +44,7 @@ class _FileManagerTabPageState extends State { page: FileManagerPage( key: ValueKey(params['id']), id: params['id'], + password: params['password'], tabController: tabController, forceRelay: params['forceRelay'], ))); @@ -72,6 +73,7 @@ class _FileManagerTabPageState extends State { page: FileManagerPage( key: ValueKey(id), id: id, + password: args['password'], tabController: tabController, forceRelay: args['forceRelay'], ))); diff --git a/flutter/lib/desktop/pages/port_forward_page.dart b/flutter/lib/desktop/pages/port_forward_page.dart index 602a93d5c..6a3b7db13 100644 --- a/flutter/lib/desktop/pages/port_forward_page.dart +++ b/flutter/lib/desktop/pages/port_forward_page.dart @@ -28,11 +28,13 @@ class PortForwardPage extends StatefulWidget { const PortForwardPage( {Key? key, required this.id, + required this.password, required this.tabController, required this.isRDP, this.forceRelay}) : super(key: key); final String id; + final String password; final DesktopTabController tabController; final bool isRDP; final bool? forceRelay; @@ -55,6 +57,7 @@ class _PortForwardPageState extends State _ffi = FFI(); _ffi.start(widget.id, isPortForward: true, + password: widget.password, forceRelay: widget.forceRelay, isRdp: widget.isRDP); Get.put(_ffi, tag: 'pf_${widget.id}'); diff --git a/flutter/lib/desktop/pages/port_forward_tab_page.dart b/flutter/lib/desktop/pages/port_forward_tab_page.dart index 751fc696c..df824e431 100644 --- a/flutter/lib/desktop/pages/port_forward_tab_page.dart +++ b/flutter/lib/desktop/pages/port_forward_tab_page.dart @@ -43,6 +43,7 @@ class _PortForwardTabPageState extends State { page: PortForwardPage( key: ValueKey(params['id']), id: params['id'], + password: params['password'], tabController: tabController, isRDP: isRDP, forceRelay: params['forceRelay'], @@ -77,6 +78,7 @@ class _PortForwardTabPageState extends State { page: PortForwardPage( key: ValueKey(args['id']), id: id, + password: args['password'], isRDP: isRDP, tabController: tabController, forceRelay: args['forceRelay'], diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index eb4901686..6ff703f6a 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -134,7 +134,7 @@ void runMainApp(bool startService) async { // Check the startup argument, if we successfully handle the argument, we keep the main window hidden. final handledByUniLinks = await initUniLinks(); debugPrint("handled by uni links: $handledByUniLinks"); - if (handledByUniLinks || checkArguments()) { + if (handledByUniLinks || handleUriLink(cmdArgs: kBootArgs)) { windowManager.hide(); } else { windowManager.show(); diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 74d993a64..4eeb8e84f 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -248,7 +248,7 @@ class FfiModel with ChangeNotifier { onUrlSchemeReceived(Map evt) { final url = evt['url'].toString().trim(); - if (url.startsWith(kUniLinksPrefix) && parseRustdeskUri(url)) { + if (url.startsWith(kUniLinksPrefix) && handleUriLink(uriString: url)) { return; } switch (url) { diff --git a/flutter/lib/utils/multi_window_manager.dart b/flutter/lib/utils/multi_window_manager.dart index ccd976fa6..1b5eaf2a1 100644 --- a/flutter/lib/utils/multi_window_manager.dart +++ b/flutter/lib/utils/multi_window_manager.dart @@ -84,10 +84,12 @@ class RustDeskMultiWindowManager { } } - Future newFileTransfer(String remoteId, {bool? forceRelay}) async { + Future newFileTransfer(String remoteId, + {String? password, bool? forceRelay}) async { var msg = jsonEncode({ "type": WindowType.FileTransfer.index, "id": remoteId, + "password": password, "forceRelay": forceRelay, }); @@ -117,11 +119,12 @@ class RustDeskMultiWindowManager { } Future newPortForward(String remoteId, bool isRDP, - {bool? forceRelay}) async { + {String? password, bool? forceRelay}) async { final msg = jsonEncode({ "type": WindowType.PortForward.index, "id": remoteId, "isRDP": isRDP, + "password": password, "forceRelay": forceRelay, }); diff --git a/src/core_main.rs b/src/core_main.rs index 40c387722..a4dee3fce 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -19,15 +19,23 @@ pub fn core_main() -> Option> { let mut _is_elevate = false; let mut _is_run_as_system = false; let mut _is_quick_support = false; - let mut _is_flutter_connect = false; + let mut _is_flutter_invoke_new_connection = false; let mut arg_exe = Default::default(); for arg in std::env::args() { if i == 0 { arg_exe = arg; } else if i > 0 { #[cfg(feature = "flutter")] - if arg == "--connect" || arg == "--play" { - _is_flutter_connect = true; + if [ + "--connect", + "--play", + "--file-transfer", + "--port-forward", + "--rdp", + ] + .contains(&arg.as_str()) + { + _is_flutter_invoke_new_connection = true; } if arg == "--elevate" { _is_elevate = true; @@ -63,7 +71,7 @@ pub fn core_main() -> Option> { } } #[cfg(feature = "flutter")] - if _is_flutter_connect { + if _is_flutter_invoke_new_connection { return core_main_invoke_new_connection(std::env::args()); } let click_setup = cfg!(windows) && args.is_empty() && crate::common::is_setup(&arg_exe); @@ -318,38 +326,48 @@ fn import_config(path: &str) { /// If it returns [`Some`], then the process will continue, and flutter gui will be started. #[cfg(feature = "flutter")] fn core_main_invoke_new_connection(mut args: std::env::Args) -> Option> { - args.position(|element| { - return element == "--connect" || element == "--play"; - })?; - let mut peer_id = args.next().unwrap_or("".to_string()); - if peer_id.is_empty() { - eprintln!("please provide a valid peer id"); - return None; - } - let app_name = crate::get_app_name(); - let ext = format!(".{}", app_name.to_lowercase()); - if peer_id.ends_with(&ext) { - peer_id = peer_id.replace(&ext, ""); - } - let mut switch_uuid = None; - while let Some(item) = args.next() { - if item == "--switch_uuid" { - switch_uuid = args.next(); + let mut authority = None; + let mut id = None; + let mut param_array = vec![]; + while let Some(arg) = args.next() { + match arg.as_str() { + "--connect" | "--play" | "--file-transfer" | "--port-forward" | "--rdp" => { + authority = Some((&arg.to_string()[2..]).to_owned()); + id = args.next(); + } + "--password" => { + if let Some(password) = args.next() { + param_array.push(format!("password={password}")); + } + } + "--relay" => { + param_array.push(format!("relay=true")); + } + // inner + "--switch_uuid" => { + if let Some(switch_uuid) = args.next() { + param_array.push(format!("switch_uuid={switch_uuid}")); + } + } + _ => {} } } - let mut param_array = vec![]; - if switch_uuid.is_some() { - let switch_uuid = switch_uuid.map_or("".to_string(), |p| format!("switch_uuid={}", p)); - param_array.push(switch_uuid); + let mut uni_links = Default::default(); + if let Some(authority) = authority { + if let Some(mut id) = id { + let app_name = crate::get_app_name(); + let ext = format!(".{}", app_name.to_lowercase()); + if id.ends_with(&ext) { + id = id.replace(&ext, ""); + } + let params = param_array.join("&"); + let params_flag = if params.is_empty() { "" } else { "?" }; + uni_links = format!("rustdesk://{}/{}{}{}", authority, id, params_flag, params); + } + } + if uni_links.is_empty() { + return None; } - - let params = param_array.join("&"); - let params_flag = if params.is_empty() { "" } else { "?" }; - #[allow(unused)] - let uni_links = format!( - "rustdesk://connection/new/{}{}{}", - peer_id, params_flag, params - ); #[cfg(target_os = "linux")] return try_send_by_dbus(uni_links);