diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs
index b80149a4ef..4df1105bf7 100644
--- a/src/Umbraco.Core/StringExtensions.cs
+++ b/src/Umbraco.Core/StringExtensions.cs
@@ -593,13 +593,14 @@ namespace Umbraco.Core
///
public static string ToUrlBase64(this string input)
{
- if (input == null) throw new ArgumentNullException("input");
+ if (input == null) throw new ArgumentNullException(nameof(input));
- if (String.IsNullOrEmpty(input)) return String.Empty;
+ if (string.IsNullOrEmpty(input))
+ return string.Empty;
+ //return Convert.ToBase64String(bytes).Replace(".", "-").Replace("/", "_").Replace("=", ",");
var bytes = Encoding.UTF8.GetBytes(input);
return UrlTokenEncode(bytes);
- //return Convert.ToBase64String(bytes).Replace(".", "-").Replace("/", "_").Replace("=", ",");
}
///
@@ -609,14 +610,14 @@ namespace Umbraco.Core
///
public static string FromUrlBase64(this string input)
{
- if (input == null) throw new ArgumentNullException("input");
+ if (input == null) throw new ArgumentNullException(nameof(input));
//if (input.IsInvalidBase64()) return null;
try
{
//var decodedBytes = Convert.FromBase64String(input.Replace("-", ".").Replace("_", "/").Replace(",", "="));
- byte[] decodedBytes = UrlTokenDecode(input);
+ var decodedBytes = UrlTokenDecode(input);
return decodedBytes != null ? Encoding.UTF8.GetString(decodedBytes) : null;
}
catch (FormatException)
@@ -795,42 +796,40 @@ namespace Umbraco.Core
internal static byte[] UrlTokenDecode(string input)
{
if (input == null)
+ throw new ArgumentNullException(nameof(input));
+
+ if (input.Length == 0)
+ return Array.Empty();
+
+ // calc array size - must be groups of 4
+ var arrayLength = input.Length;
+ var remain = arrayLength % 4;
+ if (remain != 0) arrayLength += 4 - remain;
+
+ var inArray = new char[arrayLength];
+ for (var i = 0; i < input.Length; i++)
{
- throw new ArgumentNullException("input");
- }
- int length = input.Length;
- if (length < 1)
- {
- return new byte[0];
- }
- int num2 = input[length - 1] - '0';
- if ((num2 < 0) || (num2 > 10))
- {
- return null;
- }
- char[] inArray = new char[(length - 1) + num2];
- for (int i = 0; i < (length - 1); i++)
- {
- char ch = input[i];
+ var ch = input[i];
switch (ch)
{
- case '-':
+ case '-': // restore '-' as '+'
inArray[i] = '+';
break;
- case '_':
+ case '_': // restore '_' as '/'
inArray[i] = '/';
break;
- default:
+ default: // keep char unchanged
inArray[i] = ch;
break;
}
}
- for (int j = length - 1; j < inArray.Length; j++)
- {
+
+ // pad with '='
+ for (var j = input.Length; j < inArray.Length; j++)
inArray[j] = '=';
- }
+
return Convert.FromBase64CharArray(inArray, 0, inArray.Length);
}
@@ -842,54 +841,40 @@ namespace Umbraco.Core
internal static string UrlTokenEncode(byte[] input)
{
if (input == null)
+ throw new ArgumentNullException(nameof(input));
+
+ if (input.Length == 0)
+ return string.Empty;
+
+ // base-64 digits are A-Z, a-z, 0-9, + and /
+ // the = char is used for trailing padding
+
+ var str = Convert.ToBase64String(input);
+
+ var pos = str.IndexOf('=');
+ if (pos < 0) pos = str.Length;
+
+ // replace chars that would cause problems in urls
+ var chArray = new char[pos];
+ for (var i = 0; i < pos; i++)
{
- throw new ArgumentNullException("input");
- }
- if (input.Length < 1)
- {
- return String.Empty;
- }
- string str = null;
- int index = 0;
- char[] chArray = null;
- str = Convert.ToBase64String(input);
- if (str == null)
- {
- return null;
- }
- index = str.Length;
- while (index > 0)
- {
- if (str[index - 1] != '=')
- {
- break;
- }
- index--;
- }
- chArray = new char[index + 1];
- chArray[index] = (char)((0x30 + str.Length) - index);
- for (int i = 0; i < index; i++)
- {
- char ch = str[i];
+ var ch = str[i];
switch (ch)
{
- case '+':
+ case '+': // replace '+' with '-'
chArray[i] = '-';
break;
- case '/':
+ case '/': // replace '/' with '_'
chArray[i] = '_';
break;
- case '=':
- chArray[i] = ch;
- break;
-
- default:
+ default: // keep char unchanged
chArray[i] = ch;
break;
}
}
+
return new string(chArray);
}
diff --git a/src/Umbraco.Tests/Strings/StringExtensionsTests.cs b/src/Umbraco.Tests/Strings/StringExtensionsTests.cs
index f0db3991b8..4c365d733f 100644
--- a/src/Umbraco.Tests/Strings/StringExtensionsTests.cs
+++ b/src/Umbraco.Tests/Strings/StringExtensionsTests.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
+using System.Text;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Composing;
@@ -202,6 +203,20 @@ namespace Umbraco.Tests.Strings
Assert.AreEqual(expected, result);
}
+ [TestCase("hello", "aGVsbG8")]
+ [TestCase("tad", "dGFk")]
+ [TestCase("AmqGr+Fd!~ééé", "QW1xR3IrRmQhfsOpw6nDqQ")]
+ public void UrlTokenEncoding(string value, string expected)
+ {
+ var bytes = Encoding.UTF8.GetBytes(value);
+ Console.WriteLine("base64: " + Convert.ToBase64String(bytes));
+ var encoded = StringExtensions.UrlTokenEncode(bytes);
+ Assert.AreEqual(expected, encoded);
+
+ var backBytes = StringExtensions.UrlTokenDecode(encoded);
+ var backString = Encoding.UTF8.GetString(backBytes);
+ Assert.AreEqual(value, backString);
+ }
// FORMAT STRINGS