From 036b5f898a9a9265cfb51fa50a024dc99c8e4684 Mon Sep 17 00:00:00 2001 From: crschnick Date: Thu, 25 Jun 2026 18:13:10 +0000 Subject: [PATCH] rdp fixes --- .../java/io/xpipe/app/rdp/FreeRdpClient.java | 47 ++++++++++++++----- .../io/xpipe/app/rdp/RdpLaunchConfig.java | 2 +- .../io/xpipe/app/rdp/RemminaRdpClient.java | 11 ++--- .../java/io/xpipe/app/util/RemminaHelper.java | 4 +- 4 files changed, 43 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/io/xpipe/app/rdp/FreeRdpClient.java b/app/src/main/java/io/xpipe/app/rdp/FreeRdpClient.java index 5fcd38f63..3dff6925c 100644 --- a/app/src/main/java/io/xpipe/app/rdp/FreeRdpClient.java +++ b/app/src/main/java/io/xpipe/app/rdp/FreeRdpClient.java @@ -1,5 +1,6 @@ package io.xpipe.app.rdp; +import io.xpipe.app.core.AppInstallation; import io.xpipe.app.issue.ErrorEventFactory; import io.xpipe.app.process.CommandBuilder; import io.xpipe.app.process.CommandSupport; @@ -12,6 +13,8 @@ import lombok.Builder; import lombok.Value; import lombok.extern.jackson.Jacksonized; +import java.util.Map; + @JsonTypeName("freeRdp") @Value @Jacksonized @@ -97,22 +100,34 @@ public class FreeRdpClient implements ExternalRdpClient { .add("-themes") .add("/size:100%"); + if (configuration.getPassword() != null) { + b.add(argument("/p", configuration.getPassword().getSecretValue())); + } + var gateway = configuration.getGateway(); if (gateway != null) { - b.add("/g").addQuoted(gateway.getHost()); - if (gateway.getUsername() != null) { - b.add("/gu").addQuoted(gateway.getUsername()); - } - if (gateway.getPassword() != null) { - var escapedPw = gateway.getPassword().getSecretValue().replaceAll("'", "\\\\'"); - b.add("/gp:'" + escapedPw + "'"); + if (exec.isV3()) { + // Horrible quoting rules: https://github.com/FreeRDP/FreeRDP/issues/11396 + String s = "g:" + gateway.getHost(); + if (gateway.getUsername() != null) { + s += "," + gatewayArgument("u", gateway.getUsername()); + } + if (gateway.getPassword() != null) { + s += "," + gatewayArgument("p", gateway.getPassword().getSecretValue()); + } + b.add(gatewayArgument("/gateway", s)); + } else { + b.add(argument("/g", gateway.getHost())); + if (gateway.getUsername() != null) { + b.add(argument("/gu", gateway.getUsername())); + } + if (gateway.getPassword() != null) { + b.add(argument("/gp", gateway.getPassword().getSecretValue())); + } } } - if (configuration.getPassword() != null) { - var escapedPw = configuration.getPassword().getSecretValue().replaceAll("'", "\\\\'"); - b.add("/p:'" + escapedPw + "'"); - } + b.fixedEnvironment("FREERDP_ASKPASS", AppInstallation.ofCurrent().getCliExecutablePath().toString()); try (var sc = LocalShell.getShell().start()) { var cmd = sc.getShellDialect().launchAsync(b, true); @@ -120,6 +135,16 @@ public class FreeRdpClient implements ExternalRdpClient { } } + private String gatewayArgument(String key, String value) { + var escaped = value.replaceAll("\"", "\\\\\""); + return "\"" + key + ":" + escaped + "\""; + } + + private String argument(String key, String value) { + var escaped = value.replaceAll("'", "\\\\'"); + return key + ":'" + escaped + "'"; + } + @Override public boolean supportsPasswordPassing(RdpLaunchConfig config) { return true; diff --git a/app/src/main/java/io/xpipe/app/rdp/RdpLaunchConfig.java b/app/src/main/java/io/xpipe/app/rdp/RdpLaunchConfig.java index 452eac70d..75912d906 100644 --- a/app/src/main/java/io/xpipe/app/rdp/RdpLaunchConfig.java +++ b/app/src/main/java/io/xpipe/app/rdp/RdpLaunchConfig.java @@ -26,7 +26,7 @@ public class RdpLaunchConfig { return Optional.ofNullable(domain); } - public String getUserWithoutDomain() { + public String getUsernameWithoutDomain() { if (username.contains("\\")) { return username.split("\\\\")[1]; } else { diff --git a/app/src/main/java/io/xpipe/app/rdp/RemminaRdpClient.java b/app/src/main/java/io/xpipe/app/rdp/RemminaRdpClient.java index 140449afa..f815817e3 100644 --- a/app/src/main/java/io/xpipe/app/rdp/RemminaRdpClient.java +++ b/app/src/main/java/io/xpipe/app/rdp/RemminaRdpClient.java @@ -28,17 +28,14 @@ public class RemminaRdpClient implements ExternalApplicationType.LinuxApplicatio } } - var encrypted = RemminaHelper.encryptPassword(configuration.getPassword()); - if (encrypted.isPresent()) { - var file = RemminaHelper.writeRemminaRdpConfigFile(configuration); - launch(CommandBuilder.of().add("-c").addFile(file.toString())); - return; - } + var file = RemminaHelper.writeRemminaRdpConfigFile(configuration); + LocalFileTracker.deleteOnExit(file); + launch(CommandBuilder.of().add("-c").addFile(file.toString())); } @Override public boolean supportsPasswordPassing(RdpLaunchConfig config) { - return config.isRemoteApp(); + return true; } @Override diff --git a/app/src/main/java/io/xpipe/app/util/RemminaHelper.java b/app/src/main/java/io/xpipe/app/util/RemminaHelper.java index ffa4c982c..00abd9bd9 100644 --- a/app/src/main/java/io/xpipe/app/util/RemminaHelper.java +++ b/app/src/main/java/io/xpipe/app/util/RemminaHelper.java @@ -95,10 +95,10 @@ public class RemminaHelper { window_maximize=%s%s """.formatted( configuration.getTitle(), - configuration.getUsername(), + configuration.getUsernameWithoutDomain(), configuration.getDomain().orElse(""), configuration.getHost(), - configuration.getPassword() != null ? encryptPassword(configuration.getPassword()) : "", + configuration.getPassword() != null ? encryptPassword(configuration.getPassword()).orElse("") : "", w, h, maximize,