Merge pull request #4901 from 21pages/urilink
flutter: file transfer/port forward/rdp command line support
This commit is contained in:
commit
daa59af5be
@ -1547,7 +1547,7 @@ Future<bool> 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<String>? cmdArgs, Uri? uri, String? uriString}) {
|
||||
List<String>? 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<String>? 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<String> 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,
|
||||
|
@ -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<FileManagerPage>
|
||||
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);
|
||||
|
@ -44,6 +44,7 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
|
||||
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<FileManagerTabPage> {
|
||||
page: FileManagerPage(
|
||||
key: ValueKey(id),
|
||||
id: id,
|
||||
password: args['password'],
|
||||
tabController: tabController,
|
||||
forceRelay: args['forceRelay'],
|
||||
)));
|
||||
|
@ -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<PortForwardPage>
|
||||
_ffi = FFI();
|
||||
_ffi.start(widget.id,
|
||||
isPortForward: true,
|
||||
password: widget.password,
|
||||
forceRelay: widget.forceRelay,
|
||||
isRdp: widget.isRDP);
|
||||
Get.put(_ffi, tag: 'pf_${widget.id}');
|
||||
|
@ -43,6 +43,7 @@ class _PortForwardTabPageState extends State<PortForwardTabPage> {
|
||||
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<PortForwardTabPage> {
|
||||
page: PortForwardPage(
|
||||
key: ValueKey(args['id']),
|
||||
id: id,
|
||||
password: args['password'],
|
||||
isRDP: isRDP,
|
||||
tabController: tabController,
|
||||
forceRelay: args['forceRelay'],
|
||||
|
@ -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();
|
||||
|
@ -248,7 +248,7 @@ class FfiModel with ChangeNotifier {
|
||||
|
||||
onUrlSchemeReceived(Map<String, dynamic> evt) {
|
||||
final url = evt['url'].toString().trim();
|
||||
if (url.startsWith(kUniLinksPrefix) && parseRustdeskUri(url)) {
|
||||
if (url.startsWith(kUniLinksPrefix) && handleUriLink(uriString: url)) {
|
||||
return;
|
||||
}
|
||||
switch (url) {
|
||||
|
@ -84,10 +84,12 @@ class RustDeskMultiWindowManager {
|
||||
}
|
||||
}
|
||||
|
||||
Future<dynamic> newFileTransfer(String remoteId, {bool? forceRelay}) async {
|
||||
Future<dynamic> 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<dynamic> 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,
|
||||
});
|
||||
|
||||
|
@ -19,15 +19,23 @@ pub fn core_main() -> Option<Vec<String>> {
|
||||
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<Vec<String>> {
|
||||
}
|
||||
}
|
||||
#[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<Vec<String>> {
|
||||
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);
|
||||
|
Loading…
Reference in New Issue
Block a user