From 70eea9d62859bb4cb339f4001ce55628713a261b Mon Sep 17 00:00:00 2001 From: Cody Batt Date: Thu, 25 Jun 2026 11:17:28 -0600 Subject: [PATCH] Include %Port% token in default RDP templates (#203) Replace the hardcoded 'server port:i:3389' with 'server port:i:%Port%' in the default and example RDP templates. This allows SCALUS to connect using non-default RDP ports specified in the connection URI. Also fix ParseTemplate to preserve values with multiple colons (so custom templates can use formats like %Host%:%Port% in full address if desired), and add a port default of 3389 when no port is specified in standard URIs. Adds a test verifying that custom templates using %Host% (without port) in full address work correctly with server port handling the port separately. --- scripts/examples/RdpRemoteAppTemplate.rdp | 2 +- scripts/examples/WinRdpTemplate.rdp | 2 +- scripts/examples/exampleRdpTemplate.rdp | 2 +- src/Resources/Default.rdp | 2 +- src/UrlParser/DefaultRdpUrlParser.cs | 8 ++++- test/TestDefaultRdpUrlParser.cs | 44 +++++++++++++++++++++++ 6 files changed, 55 insertions(+), 5 deletions(-) diff --git a/scripts/examples/RdpRemoteAppTemplate.rdp b/scripts/examples/RdpRemoteAppTemplate.rdp index 24e0306..3ba7422 100644 --- a/scripts/examples/RdpRemoteAppTemplate.rdp +++ b/scripts/examples/RdpRemoteAppTemplate.rdp @@ -19,7 +19,7 @@ redirectdrives:i:1 remoteapplicationmode:i:1 remoteapplicationprogram:s:%Remoteapplicationprogram% remoteapplicationname:s:%Remoteapplicationname% -server port:i:3389 +server port:i:%Port% session bpp:i:32 span monitors:i:1 use redirection server name:i:1 diff --git a/scripts/examples/WinRdpTemplate.rdp b/scripts/examples/WinRdpTemplate.rdp index 97e90c4..2f52a86 100644 --- a/scripts/examples/WinRdpTemplate.rdp +++ b/scripts/examples/WinRdpTemplate.rdp @@ -27,7 +27,7 @@ remoteapplicationname:s:%Remoteapplicationname% remoteapplicationprogram:s:%Remoteapplicationprogram% remoteapplicationcmdline:s:%Remoteapplicationcmdline% screen mode id:i:1 -server port:i:3389 +server port:i:%Port% smart sizing:i:%AlternateShell?0:1% span monitors:i:1 use redirection server name:i:1 diff --git a/scripts/examples/exampleRdpTemplate.rdp b/scripts/examples/exampleRdpTemplate.rdp index 67e8509..ad2e52e 100644 --- a/scripts/examples/exampleRdpTemplate.rdp +++ b/scripts/examples/exampleRdpTemplate.rdp @@ -60,7 +60,7 @@ remoteapplicationname:s:%Remoteapplicationname% remoteapplicationprogram:s:%Remoteapplicationprogram% screen mode id:i:1 selectedmonitors:s: -server port:i:3389 +server port:i:%Port% session bpp:i:32 shell working directory:s: singlemoninwindowedmode:i:0 diff --git a/src/Resources/Default.rdp b/src/Resources/Default.rdp index 20984e8..b263774 100644 --- a/src/Resources/Default.rdp +++ b/src/Resources/Default.rdp @@ -60,7 +60,7 @@ remoteapplicationname:s:%Remoteapplicationname% remoteapplicationprogram:s:%Remoteapplicationprogram% screen mode id:i:1 selectedmonitors:s: -server port:i:3389 +server port:i:%Port% session bpp:i:32 shell working directory:s: singlemoninwindowedmode:i:0 diff --git a/src/UrlParser/DefaultRdpUrlParser.cs b/src/UrlParser/DefaultRdpUrlParser.cs index d847133..4c0497e 100644 --- a/src/UrlParser/DefaultRdpUrlParser.cs +++ b/src/UrlParser/DefaultRdpUrlParser.cs @@ -177,6 +177,12 @@ public override IDictionary Parse(string url) } Parse(result); + + // Ensure port defaults to 3389 if not specified in the URL + if (!Dictionary.ContainsKey(Token.Port) || string.IsNullOrEmpty(Dictionary[Token.Port])) + { + Dictionary[Token.Port] = "3389"; + } } else { @@ -360,7 +366,7 @@ private static Dictionary ParseTemplate(IEnumerable list continue; } - dict[line[0]] = line[1] + ":" + line[2]; + dict[line[0]] = string.Join(":", line, 1, line.Length - 1); } return dict; diff --git a/test/TestDefaultRdpUrlParser.cs b/test/TestDefaultRdpUrlParser.cs index 189552f..d68411c 100644 --- a/test/TestDefaultRdpUrlParser.cs +++ b/test/TestDefaultRdpUrlParser.cs @@ -175,6 +175,50 @@ public void TestRdpTemplate1() } } + [Fact] + public void TestRdpTemplateWithPortInServerPortOnly() + { + var template = Path.GetTempFileName(); + // Test that a custom template with %Host% (no port) in full address + // and %Port% in server port works correctly — the port should be in + // server port only, not in full address. + + var lines = new List + { + $"full address:s:%{Token.Host}%", + $"server port:i:%{Token.Port}%", + $"username:s:%{Token.User}%", + }; + File.WriteAllLines(template, lines); + + using (var sut = new DefaultRdpUrlParser(new Dto.ParserConfig { UseTemplateFile = template })) + { + var url = "rdp://full+address=s:myhostname:33891&username=s:testuser/"; + var dictionary = sut.Parse(url); + Assert.Equal("myhostname", dictionary[Token.Host]); + Assert.Equal("33891", dictionary[Token.Port]); + Assert.Equal("testuser", dictionary[Token.User]); + + var tempfile = dictionary[Token.GeneratedFile]; + var fileLines = File.ReadAllLines(tempfile); + var count = 0; + var names = new List(); + + foreach (var one in fileLines) + { + // full address should have host only (no port) since template uses %Host% without %Port% + if (check(one, "full address", "s:myhostname", names, ref count)) continue; + if (check(one, "server port", "i:33891", names, ref count)) continue; + if (check(one, "username", "s:testuser", names, ref count)) continue; + } + Assert.Equal(3, count); + Assert.Equal(count, fileLines.Length); + } + if (File.Exists(template)) + { + File.Delete(template); + } + } [Fact] public void TestRdpTemplate2()