Skip to content

Commit c900bc6

Browse files
Alprema_cpAlprema_cp
authored andcommitted
ActiveUp.Net.Common: Added some failing tests that show an issue in Codec.RFC2047Decode(): The RFC is respected, but some senders don't respect it, so the parser needs to be more resilient
ActiveUp.Net.Common: Added ExtendString.SplitAndKeepDelimiters in preparation for the new RFC2047 parser
1 parent 344ccdd commit c900bc6

8 files changed

Lines changed: 251 additions & 81 deletions

File tree

Class Library/ActiveUp.Net.Common/ActiveUp.Net.Common.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@
177177
<Compile Include="TraceInfoCollection.cs" />
178178
<Compile Include="TrustAllCertificatePolicy.cs" />
179179
<Compile Include="UsenetXrefList.cs" />
180+
<Compile Include="Utils\ExtendString.cs" />
180181
<Compile Include="Validator.cs" />
181182
</ItemGroup>
182183
<ItemGroup>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
5+
namespace ActiveUp.Net.Common.Utils
6+
{
7+
public static class ExtendString
8+
{
9+
public static IEnumerable<string> SplitAndKeepDelimiters(this string input, params char[] delimiters)
10+
{
11+
return SplitAndKeepDelimiters(input, StringSplitOptions.None, delimiters);
12+
}
13+
14+
public static IEnumerable<string> SplitAndKeepDelimiters(this string input, StringSplitOptions options, params char[] delimiters)
15+
{
16+
var previousDelimiterIndex = 0;
17+
for (var i = 0; i < input.Length; ++i)
18+
{
19+
if (delimiters.Contains(input[i]))
20+
{
21+
var token = input.Substring(previousDelimiterIndex, i - previousDelimiterIndex);
22+
if (options != StringSplitOptions.RemoveEmptyEntries || !string.IsNullOrEmpty(token))
23+
yield return token;
24+
yield return new string(input[i], 1);
25+
previousDelimiterIndex = i + 1;
26+
}
27+
}
28+
29+
var lastToken = input.Substring(previousDelimiterIndex, input.Length - previousDelimiterIndex);
30+
if (options != StringSplitOptions.RemoveEmptyEntries || !string.IsNullOrEmpty(lastToken))
31+
yield return lastToken;
32+
}
33+
}
34+
}

Class Library/ActiveUp.Net.Tests/ActiveUp.Net.Tests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,10 @@
7272
</ItemGroup>
7373
<ItemGroup>
7474
<Compile Include="CommonTests.cs" />
75+
<Compile Include="Common\CodecTests.cs" />
7576
<Compile Include="Common\ParserTests.cs" />
7677
<Compile Include="Common\MimePartTests.cs" />
78+
<Compile Include="Common\Utils\ExtendStringTests.cs" />
7779
<Compile Include="FluentTests.cs" />
7880
<Compile Include="ImapTests.cs" />
7981
<Compile Include="SmtpTests.cs" />
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
using System;
2+
using ActiveUp.Net.Mail;
3+
using NUnit.Framework;
4+
5+
namespace ActiveUp.Net.Tests.Common
6+
{
7+
public class CodecTests
8+
{
9+
/// <summary>
10+
/// Headers to be decoded
11+
/// </summary>
12+
private static string[] sampleEncodedHeaders = new []
13+
{
14+
"From: =?US-ASCII?Q?Keith_Moore?= <[email protected]>",
15+
"To: =?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <[email protected]>",
16+
"CC: =?ISO-8859-1?Q?Andr=E9?= Pirard <[email protected]>",
17+
"Subject: =?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=\n=?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=",
18+
"From: =?ISO-8859-1?Q?Olle_J=E4rnefors?= <[email protected]>",
19+
20+
"Subject: Time for ISO 10646?",
21+
"To: Dave Crocker <[email protected]>",
22+
23+
"From: =?ISO-8859-1?Q?Patrik_F=E4ltstr=F6m?= <[email protected]>",
24+
"Subject: Re: RFC-HDR care and feeding",
25+
"To: Greg Vaudreuil <[email protected]>, Ned Freed\n <[email protected]>, Keith Moore <[email protected]>",
26+
"Subject: Test of new header generator",
27+
"MIME-Version: 1.0",
28+
"Content-type: text/plain; charset=ISO-8859-1",
29+
"(=?ISO-8859-1?Q?a?=)",
30+
"(=?ISO-8859-1?Q?a?= b)",
31+
"(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)",
32+
"(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)",
33+
"(=?ISO-8859-1?Q?a?=\n =?ISO-8859-1?Q?b?=)",
34+
"(=?ISO-8859-1?Q?a_b?=)",
35+
"(=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?=)"
36+
};
37+
38+
/// <summary>
39+
/// Decoded headers
40+
/// </summary>
41+
private static string[] expectedDecodedHeaders = new []
42+
{
43+
"From: Keith Moore <[email protected]>",
44+
"To: Keld Jørn Simonsen <[email protected]>",
45+
"CC: André Pirard <[email protected]>",
46+
"Subject: If you can read this you understand the example.",
47+
"From: Olle Järnefors <[email protected]>",
48+
49+
"Subject: Time for ISO 10646?",
50+
"To: Dave Crocker <[email protected]>",
51+
52+
"From: Patrik Fältström <[email protected]>",
53+
"Subject: Re: RFC-HDR care and feeding",
54+
"To: Greg Vaudreuil <[email protected]>, Ned Freed\n <[email protected]>, Keith Moore <[email protected]>",
55+
"Subject: Test of new header generator",
56+
"MIME-Version: 1.0",
57+
"Content-type: text/plain; charset=ISO-8859-1",
58+
"(a)",
59+
"(a b)",
60+
"(ab)",
61+
"(ab)",
62+
"(ab)",
63+
"(a b)",
64+
"(a b)"
65+
};
66+
67+
[Test]
68+
public void should_handle_examples_from_the_rfc_2047()
69+
{
70+
for (var i = 0; i < sampleEncodedHeaders.GetLength(0); i++)
71+
Codec.RFC2047Decode(sampleEncodedHeaders[i]).ShouldEqual(expectedDecodedHeaders[i]);
72+
}
73+
74+
[Test]
75+
public void should_decode_RFC2047_encoded_words()
76+
{
77+
// Second, ensure that an encoded sentence from Codec.RFC2047Encode is correctly decoded
78+
var sampleText = "Je suis Liégeois et je suis prêt à rencontrer Asger Jørnow";
79+
var encodedText = Codec.RFC2047Encode(sampleText, "iso-8859-1");
80+
sampleText.ShouldEqual(Codec.RFC2047Decode(encodedText));
81+
}
82+
83+
[Test]
84+
public void toto()
85+
{
86+
var decodedString = Codec.RFC2047Decode(
87+
@"=?utf-8?B?0JjQpNClIC0gKtCT0LDQt9C/0YDQvtC8KiDQvtGC0LzQtdC9?=
88+
=?utf-8?B?0LjQuyDRgtC10L3QtNC10YDRiyDQvdCwINC30LDQutGD0L/QutGDINGC?=
89+
=?utf-8?B?0YDRg9CxINCx0L7Qu9GM0YjQvtCz0L4g0LTQuNCw0LzQtdGC0YDQsCDQ?=
90+
=?utf-8?B?vdCwIDEyINC80LvRgNC0INGA0YPQsdC70LXQuS4=?=");
91+
92+
decodedString.ShouldEqual("ИФХ - *Газпром* отменил тендеры на закупку труб большого диаметра на 12 млрд рублей.");
93+
}
94+
95+
[Test]
96+
public void should_handle_mixed_content()
97+
{
98+
var decodedString = Codec.RFC2047Decode("This is some russian:\t=?utf-8?B?0JjQpNClIC0gKtCT0LDQt9C/0YDQvtC8KiDQvtGC0LzQtdC9?= wasn't that cool?");
99+
100+
decodedString.ShouldEqual("This is some russian:\tИФХ - *Газпром* отмен wasn't that cool?");
101+
}
102+
103+
[Test]
104+
public void should_support_different_encoding_after_line_break()
105+
{
106+
throw new NotImplementedException();
107+
}
108+
109+
[Test]
110+
public void should_test_one_word()
111+
{
112+
throw new NotImplementedException();
113+
}
114+
115+
[Test]
116+
public void should_test_one_space()
117+
{
118+
throw new NotImplementedException();
119+
}
120+
}
121+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using System;
2+
using NUnit.Framework;
3+
using ActiveUp.Net.Common.Utils;
4+
5+
namespace ActiveUp.Net.Tests.Common.Utils
6+
{
7+
[TestFixture]
8+
public class ExtendStringTests
9+
{
10+
[Test]
11+
public void should_split_string_using_a_single_token()
12+
{
13+
var input = "i like pineapples";
14+
15+
var parts = input.SplitAndKeepDelimiters(' ');
16+
17+
parts.ShouldEqual(new[] { "i", " ", "like", " ", "pineapples" });
18+
19+
}
20+
[Test]
21+
public void should_split_string_using_a_repeated_token()
22+
{
23+
var input = "i like pineapples";
24+
25+
var parts = input.SplitAndKeepDelimiters(' ');
26+
27+
parts.ShouldEqual(new[] { "i", " ", "like", " ", "", " ", "pineapples" });
28+
29+
}
30+
31+
[Test]
32+
public void should_remove_empty_tokens_if_asked()
33+
{
34+
var input = "i like pineapples";
35+
36+
var parts = input.SplitAndKeepDelimiters(StringSplitOptions.RemoveEmptyEntries, ' ');
37+
38+
parts.ShouldEqual(new[] { "i", " ", "like", " ", " ", "pineapples" });
39+
40+
}
41+
42+
[Test]
43+
public void should_split_string_using_a_multiple_tokens()
44+
{
45+
var input = "i like\tpineapples";
46+
47+
var parts = input.SplitAndKeepDelimiters(' ', '\t');
48+
49+
parts.ShouldEqual(new[] { "i", " ", "like", "\t", "pineapples" });
50+
}
51+
52+
[Test]
53+
public void should_handle_delimiters_on_the_sides_of_the_string()
54+
{
55+
var input = " i like\tpineapples ";
56+
57+
var parts = input.SplitAndKeepDelimiters(' ', '\t');
58+
59+
parts.ShouldEqual(new[] { "", " ", "i", " ", "like", "\t", "pineapples", " ", "" });
60+
}
61+
62+
[Test]
63+
public void should_remove_empty_token_at_the_end_if_asked()
64+
{
65+
var input = " i like\tpineapples ";
66+
67+
var parts = input.SplitAndKeepDelimiters(StringSplitOptions.RemoveEmptyEntries, ' ', '\t');
68+
69+
parts.ShouldEqual(new[] { " ", "i", " ", "like", "\t", "pineapples", " " });
70+
}
71+
}
72+
}

Class Library/ActiveUp.Net.Tests/CommonTests.cs

Lines changed: 0 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -11,86 +11,6 @@ namespace ActiveUp.Net.Tests
1111
[TestFixture(Description = "ActiveUp.Net.Common library related tests")]
1212
public class CommonTests
1313
{
14-
#region Fields
15-
16-
/// <summary>
17-
/// Headers to be decoded
18-
/// </summary>
19-
private static string[] encodedHeaders = new string[]
20-
{
21-
"From: =?US-ASCII?Q?Keith_Moore?= <[email protected]>",
22-
"To: =?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <[email protected]>",
23-
"CC: =?ISO-8859-1?Q?Andr=E9?= Pirard <[email protected]>",
24-
"Subject: =?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=\n=?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=",
25-
"From: =?ISO-8859-1?Q?Olle_J=E4rnefors?= <[email protected]>",
26-
27-
"Subject: Time for ISO 10646?",
28-
"To: Dave Crocker <[email protected]>",
29-
30-
"From: =?ISO-8859-1?Q?Patrik_F=E4ltstr=F6m?= <[email protected]>",
31-
"Subject: Re: RFC-HDR care and feeding",
32-
"To: Greg Vaudreuil <[email protected]>, Ned Freed\n <[email protected]>, Keith Moore <[email protected]>",
33-
"Subject: Test of new header generator",
34-
"MIME-Version: 1.0",
35-
"Content-type: text/plain; charset=ISO-8859-1",
36-
"(=?ISO-8859-1?Q?a?=)",
37-
"(=?ISO-8859-1?Q?a?= b)",
38-
"(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)",
39-
"(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)",
40-
"(=?ISO-8859-1?Q?a?=\n =?ISO-8859-1?Q?b?=)",
41-
"(=?ISO-8859-1?Q?a_b?=)",
42-
"(=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?=)"
43-
};
44-
45-
/// <summary>
46-
/// Decoded headers
47-
/// </summary>
48-
private static string[] decodedHeaders = new string[]
49-
{
50-
"From: Keith Moore <[email protected]>",
51-
"To: Keld Jørn Simonsen <[email protected]>",
52-
"CC: André Pirard <[email protected]>",
53-
"Subject: If you can read this you understand the example.",
54-
"From: Olle Järnefors <[email protected]>",
55-
56-
"Subject: Time for ISO 10646?",
57-
"To: Dave Crocker <[email protected]>",
58-
59-
"From: Patrik Fältström <[email protected]>",
60-
"Subject: Re: RFC-HDR care and feeding",
61-
"To: Greg Vaudreuil <[email protected]>, Ned Freed\n <[email protected]>, Keith Moore <[email protected]>",
62-
"Subject: Test of new header generator",
63-
"MIME-Version: 1.0",
64-
"Content-type: text/plain; charset=ISO-8859-1",
65-
"(a)",
66-
"(a b)",
67-
"(ab)",
68-
"(ab)",
69-
"(ab)",
70-
"(a b)",
71-
"(a b)"
72-
};
73-
74-
#endregion Fields
75-
76-
/// <summary>
77-
/// This test is used to verify that Codec.RFC2047Decode correctly retrieve RFC2047 encoded words
78-
/// </summary>
79-
[Test(Description = "This test is used to verify that Codec.RFC2047Decode correctly retrieve RFC2047 encoded words")]
80-
public void CodecRFC2047DecodeTests()
81-
{
82-
// First check example directly taken from RFC2047
83-
for (int i = 0; i < encodedHeaders.GetLength(0); i++)
84-
{
85-
Assert.AreEqual(decodedHeaders[i], Codec.RFC2047Decode(encodedHeaders[i]), string.Format("error in decoding : {0}", encodedHeaders[i]));
86-
}
87-
88-
// Second, ensure that an encoded sentence from Codec.RFC2047Encode is correctly decoded
89-
string decodedText = "Je suis Liégeois et je suis prêt à rencontrer Asger Jørnow";
90-
string encodedText = Codec.RFC2047Encode(decodedText, "iso-8859-1");
91-
Assert.AreEqual(decodedText, Codec.RFC2047Decode(encodedText));
92-
}
93-
9414
[Test(Description = "Verify the correct parsing of multiple trace entries in a mail and use the latest one as message.ReceivedDate")]
9515
public void ParseMultipleMessageTrace()
9616
{

Class Library/ActiveUp.Net.Tests/ImapTests.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Diagnostics;
33
using System.IO;
4+
using System.Linq;
45
using ActiveUp.Net.Mail;
56
using ActiveUp.Net.Security;
67
using NUnit.Framework;
@@ -14,6 +15,7 @@ public class ImapTests
1415
private const int _imapPort = 993;
1516
private const string _imapServerAddress = "imap.gmail.com";
1617

18+
1719
[Test, Ignore("Manual tests")]
1820
public void GetGmailLabels()
1921
{
@@ -61,5 +63,24 @@ public void GetsBase64MimeVersion()
6163
File.WriteAllText(Path.Combine(tempFolder, "base64_message.eml"), message.ToMimeString(false, true));
6264
Process.Start(tempFolder);
6365
}
66+
67+
[Test, Ignore("Manual tests")]
68+
public void GetMailWithWeirdSubject()
69+
{
70+
var mail = GetCrappyEmailFromGmail();
71+
}
72+
73+
public Message GetCrappyEmailFromGmail()
74+
{
75+
var client = new Imap4Client();
76+
client.ConnectSsl(_imapServerAddress, _imapPort);
77+
client.Login(_imapLogin, _imapPassword);
78+
79+
80+
var inbox = client.SelectMailbox(@"[Gmail]/All Mail");
81+
82+
var matchingOrdinals = inbox.Search("X-GM-MSGID 1439630496790426424");
83+
return inbox.Fetch.MessageObjectWithGMailExtensions(matchingOrdinals.First());
84+
}
6485
}
6586
}

Samples/CS/ActiveUp.Net.Samples/ActiveUp.Net.Samples.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,6 @@
349349
<DependentUpon>Resources.resx</DependentUpon>
350350
<DesignTime>True</DesignTime>
351351
</Compile>
352-
<None Include="app.config" />
353352
<None Include="Properties\Settings.settings">
354353
<Generator>SettingsSingleFileGenerator</Generator>
355354
<LastGenOutput>Settings.Designer.cs</LastGenOutput>

0 commit comments

Comments
 (0)