diff --git a/NEventSocket.Tests/Applications/OriginateTests.cs b/NEventSocket.Tests/Applications/OriginateTests.cs index f7336ad..085b554 100644 --- a/NEventSocket.Tests/Applications/OriginateTests.cs +++ b/NEventSocket.Tests/Applications/OriginateTests.cs @@ -55,6 +55,17 @@ public void can_set_enterprise_channel_variables_and_channel_variables() Assert.That(options, Does.Contain("{c1='cv1',c2='cv2'}")); } + [Test] + public void must_escape_enterprise_channel_variables() + { + var options = new OriginateOptions + { + EnterpriseChannelVariables = new Dictionary { { "e1", "ev1" }, { "e2", "ev2" } }, + ChannelVariables = new Dictionary { { "c1", "cv1" }, { "c2", "cv2" } } + }.ToString(); + Assert.That(options, Does.Contain("ev1\\',e2='\\ev2\\'>{c1='cv1',c2='cv2'}")); + } + [Test] public void can_set_caller_id_type() { diff --git a/NEventSocket/FreeSwitch/OriginateOptions.cs b/NEventSocket/FreeSwitch/OriginateOptions.cs index 18eb1ca..fc01af1 100644 --- a/NEventSocket/FreeSwitch/OriginateOptions.cs +++ b/NEventSocket/FreeSwitch/OriginateOptions.cs @@ -289,7 +289,7 @@ private void AppendOriginateEnterpriseChannelVariablesString(StringBuilder sb) sb.Append("<"); - sb.Append(EnterpriseChannelVariables.ToOriginateString()); + sb.Append(EnterpriseChannelVariables.EscapeValues(new[] { '<', '>' }).ToOriginateString()); if (sb.Length > 1) { diff --git a/NEventSocket/Util/StringExtensions.cs b/NEventSocket/Util/StringExtensions.cs index 0918030..434e682 100644 --- a/NEventSocket/Util/StringExtensions.cs +++ b/NEventSocket/Util/StringExtensions.cs @@ -226,5 +226,41 @@ public static string ToOriginateString(this IDictionary dictiona return StringBuilderPool.ReturnAndFree(sb); } + + public static IDictionary EscapeValues(this IDictionary dictionary, IEnumerable charactersToEscape) + { + if (dictionary == null) + throw new ArgumentNullException(nameof(dictionary)); + + if (charactersToEscape == null) + throw new ArgumentNullException(nameof(charactersToEscape)); + + var escapeSet = charactersToEscape as HashSet ?? [.. charactersToEscape]; + var result = new Dictionary(); + + foreach (var kvp in dictionary) + { + var escapedValue = kvp.Value != null ? EscapeString(kvp.Value, escapeSet) : kvp.Value; + result[kvp.Key] = escapedValue; + } + + return result; + } + + private static string EscapeString(string input, HashSet charactersToEscape) + { + var sb = StringBuilderPool.Allocate(); + + foreach (char c in input) + { + if (charactersToEscape.Contains(c)) + { + sb.Append('\\'); + } + sb.Append(c); + } + + return StringBuilderPool.ReturnAndFree(sb); + } } } \ No newline at end of file