Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove UTF7 encoding from EncodingProvider #651

Merged
merged 1 commit into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions Ical.Net.Tests/EncodingProviderTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//
// Copyright ical.net project maintainers and contributors.
// Licensed under the MIT license.
//

#nullable enable
using System;
using Ical.Net.Serialization;
using NUnit.Framework;
using EncodingProvider = Ical.Net.Serialization.EncodingProvider;

namespace Ical.Net.Tests;

[TestFixture]
public class EncodingProviderTests
{
private EncodingProvider GetEncodingProvider() => new EncodingProvider(new SerializationContext());

[Test]
public void Encode_ShouldReturnEncodedString_WhenValidEncodingIsProvided()
{
const string encoding = "8BIT";
var data = "Hello"u8.ToArray();

var result = GetEncodingProvider().Encode(encoding, data);

Assert.That(result, Is.EqualTo("Hello"));
}

[Test]
public void Encode_ShouldBeNull_WhenInvalidEncodingIsProvided()
{
const string encoding = "Invalid-Encoding";
var data = "Hello"u8.ToArray();

Assert.That(GetEncodingProvider().Encode(encoding, data), Is.Null);
}

[Test]
public void Decode_ShouldReturnDecodedByteArray_WhenValidEncodingIsProvided()
{
const string encoding = "8BIT";
const string data = "Hello";

var result = GetEncodingProvider().DecodeString(encoding, data);

Assert.That(result, Is.EqualTo("Hello"u8.ToArray()));
}

[Test]
public void Decode_ShouldBeNull_WhenInvalidEncodingIsProvided()
{
const string encoding = "Invalid-Encoding";
const string data = "Hello";

Assert.That(GetEncodingProvider().DecodeString(encoding, data), Is.Null);
}

[Test]
public void Encode_ShouldReturnEncodedString_WithBase64Encoding()
{
const string encoding = "BASE64";
var data = "Hello"u8.ToArray();

var result = GetEncodingProvider().Encode(encoding, data);

Assert.That(result, Is.EqualTo("SGVsbG8=")); // "Hello" in Base64
}

[Test]
public void Decode_ShouldReturnDecodedByteArray_WithBase64Encoding()
{
const string encoding = "BASE64";
const string data = "SGVsbG8="; // "Hello" in Base64

var result = GetEncodingProvider().DecodeString(encoding, data);

Assert.That(result, Is.EqualTo("Hello"u8.ToArray()));
}

[Test]
public void Decode_ShouldThrow_WithInvalidBase64String()
{
const string encoding = "BASE64";
const string data = "InvalidBase64==="; // Invalid Base64 string

Assert.Throws<FormatException>(() => GetEncodingProvider().DecodeString(encoding, data));
}
}
210 changes: 89 additions & 121 deletions Ical.Net/Serialization/EncodingProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,179 +3,147 @@
// Licensed under the MIT license.
//

#nullable enable
using System;
using System.Text;

namespace Ical.Net.Serialization;

/// <summary>
/// Provides encoding and decoding services for byte arrays and strings.
/// </summary>
internal class EncodingProvider : IEncodingProvider
{
public delegate string EncoderDelegate(byte[] data);
private readonly SerializationContext _mSerializationContext;

public delegate byte[] DecoderDelegate(string value);
/// <summary>
/// Represents a method that encodes a byte array into a string.
/// </summary>
public delegate string? EncoderDelegate(byte[] data);

private readonly SerializationContext _mSerializationContext;
/// <summary>
/// Represents a method that decodes a string into a byte array.
/// </summary>
public delegate byte[] DecoderDelegate(string value);

/// <summary>
/// Creates a new instance of the <see cref="EncodingProvider"/> class.
/// </summary>
/// <param name="ctx"></param>
public EncodingProvider(SerializationContext ctx)
{
_mSerializationContext = ctx;
}

protected byte[] Decode7Bit(string value)
{
try
{
var utf7 = new UTF7Encoding();
return utf7.GetBytes(value);
}
catch
{
return null;
}
}

/// <summary>
/// Decodes an 8-bit string into a byte array.
/// </summary>
/// <param name="value"></param>
/// <returns>A byte array of the decoded string.</returns>
protected byte[] Decode8Bit(string value)
{
try
{
var utf8 = new UTF8Encoding();
return utf8.GetBytes(value);
}
catch
{
return null;
}
var utf8 = new UTF8Encoding();
return utf8.GetBytes(value);
}

/// <summary>
/// Decodes a base-64 encoded string into a byte array.
/// </summary>
/// <param name="value"></param>
/// <returns>A byte array of the decoded string.</returns>
protected byte[] DecodeBase64(string value)
{
try
{
return Convert.FromBase64String(value);
}
catch
{
return null;
}
return Convert.FromBase64String(value);
}

protected virtual DecoderDelegate GetDecoderFor(string encoding)
/// <summary>
/// Gets a decoder for the specified encoding.
/// </summary>
/// <param name="encoding"></param>
/// <returns></returns>
protected virtual DecoderDelegate? GetDecoderFor(string encoding)
{
if (encoding == null)
return encoding.ToUpper() switch
{
return null;
}

switch (encoding.ToUpper())
{
case "7BIT":
return Decode7Bit;
case "8BIT":
return Decode8Bit;
case "BASE64":
return DecodeBase64;
default:
return null;
}
}

protected string Encode7Bit(byte[] data)
{
try
{
var utf7 = new UTF7Encoding();
return utf7.GetString(data);
}
catch
{
return null;
}
"8BIT" => Decode8Bit,
"BASE64" => DecodeBase64,
_ => null,
};
}

/// <summary>
/// Encodes a byte array into an 8-bit string.
/// </summary>
/// <param name="data"></param>
/// <returns>An 8-bit string, if encoding is successful, else <see langword="null"/></returns>
protected string Encode8Bit(byte[] data)
{
try
{
var utf8 = new UTF8Encoding();
return utf8.GetString(data);
}
catch
{
return null;
}
var utf8 = new UTF8Encoding();
return utf8.GetString(data);
}

/// <summary>
/// Encodes a byte array into a base-64 encoded string.
/// </summary>
/// <param name="data"></param>
/// <returns>A base-64 encoded string.</returns>
protected string EncodeBase64(byte[] data)
{
try
{
return Convert.ToBase64String(data);
}
catch
{
return null;
}
return Convert.ToBase64String(data);
}

protected virtual EncoderDelegate GetEncoderFor(string encoding)
/// <summary>
/// Gets an encoder for the specified encoding.
/// </summary>
/// <param name="encoding"></param>
/// <returns></returns>
protected virtual EncoderDelegate? GetEncoderFor(string encoding)
{
if (encoding == null)
{
return null;
}

switch (encoding.ToUpper())
return encoding.ToUpper() switch
{
case "7BIT":
return Encode7Bit;
case "8BIT":
return Encode8Bit;
case "BASE64":
return EncodeBase64;
default:
return null;
}
"8BIT" => Encode8Bit,
"BASE64" => EncodeBase64,
_ => null
};
}

public string Encode(string encoding, byte[] data)
/// <summary>
/// Encodes a byte array into a string.
/// </summary>
/// <param name="encoding"></param>
/// <param name="data"></param>
/// <returns>A string representation of <paramref name="data"/> using the specified <see paramref="encoding"/>, or <see langword="null"/> if encoding fails.</returns>
public string? Encode(string encoding, byte[] data)
{
if (encoding == null || data == null)
{
return null;
}

var encoder = GetEncoderFor(encoding);
//var wrapped = TextUtil.FoldLines(encoder?.Invoke(data));
//return wrapped;
return encoder?.Invoke(data);
}

public string DecodeString(string encoding, string value)
/// <summary>
/// Encodes a string into an encoded string by using the <see cref="EncodingStack"/> service.
/// </summary>
/// <param name="encoding"></param>
/// <param name="value"></param>
/// <returns>A string representation of <paramref name="value"/> using the specified <see paramref="encoding"/>.</returns>
/// <exception cref="FormatException">Base64 string is invalid.</exception>
public string? DecodeString(string encoding, string value)
{
if (encoding == null || value == null)
{
return null;
}

var data = DecodeData(encoding, value);
if (data == null)
{
return null;
}

// Decode the string into the current encoding
var encodingStack = _mSerializationContext.GetService(typeof(EncodingStack)) as EncodingStack;
return encodingStack.Current.GetString(data);
return data != null ? encodingStack?.Current.GetString(data) : null;
}

public byte[] DecodeData(string encoding, string value)
/// <summary>
/// Decodes an encoded string into a byte array.
/// </summary>
/// <param name="encoding"></param>
/// <param name="value"></param>
/// <returns>A string representation of <paramref name="value"/> using the specified <see paramref="encoding"/>, or <see langword="null"/> when decoding fails.</returns>
public byte[]? DecodeData(string encoding, string value)
{
if (encoding == null || value == null)
{
return null;
}

var decoder = GetDecoderFor(encoding);
return decoder?.Invoke(value);
}
}
}
Loading