Skip to content

Commit baacde5

Browse files
committed
Fixed IMAP to treat '+' as a normal Atom token
Fixes issue #1956
1 parent 389098c commit baacde5

File tree

5 files changed

+83
-12
lines changed

5 files changed

+83
-12
lines changed

MailKit/Net/Imap/ImapCommand.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,7 @@ public bool Step ()
591591
token = Engine.ReadToken (CancellationToken);
592592
}
593593

594-
if (token.Type == ImapTokenType.Plus) {
594+
if (token == ImapToken.Plus) {
595595
// we've gotten a continuation response from the server
596596
var text = Engine.ReadLine (CancellationToken).Trim ();
597597

@@ -732,7 +732,7 @@ public async Task<bool> StepAsync ()
732732
token = await Engine.ReadTokenAsync (CancellationToken).ConfigureAwait (false);
733733
}
734734

735-
if (token.Type == ImapTokenType.Plus) {
735+
if (token == ImapToken.Plus) {
736736
// we've gotten a continuation response from the server
737737
var text = (await Engine.ReadLineAsync (CancellationToken).ConfigureAwait (false)).Trim ();
738738

MailKit/Net/Imap/ImapEngine.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1474,7 +1474,7 @@ void UpdateCapabilities (ImapTokenType sentinel, CancellationToken cancellationT
14741474
// like we did in v4.1.0 (and older), then we can avoid this exception.
14751475
//
14761476
// See https://github.com/jstedfast/MailKit/issues/1654 for details.
1477-
while (token.Type == ImapTokenType.Atom || token.Type == ImapTokenType.Plus) {
1477+
while (token.Type == ImapTokenType.Atom) {
14781478
var atom = token.Value.ToString ();
14791479

14801480
ProcessCapabilityToken (atom);
@@ -1501,7 +1501,7 @@ async Task UpdateCapabilitiesAsync (ImapTokenType sentinel, CancellationToken ca
15011501
// like we did in v4.1.0 (and older), then we can avoid this exception.
15021502
//
15031503
// See https://github.com/jstedfast/MailKit/issues/1654 for details.
1504-
while (token.Type == ImapTokenType.Atom || token.Type == ImapTokenType.Plus) {
1504+
while (token.Type == ImapTokenType.Atom) {
15051505
var atom = token.Value.ToString ();
15061506

15071507
ProcessCapabilityToken (atom);

MailKit/Net/Imap/ImapToken.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,11 @@ enum ImapTokenType {
4646
Asterisk = (int) '*',
4747
OpenBracket = (int) '[',
4848
CloseBracket = (int) ']',
49-
Plus = (int) '+',
5049
}
5150

5251
class ImapToken
5352
{
54-
public static readonly ImapToken Plus = new ImapToken (ImapTokenType.Plus, '+');
53+
public static readonly ImapToken Plus = new ImapToken (ImapTokenType.Atom, "+");
5554
public static readonly ImapToken Asterisk = new ImapToken (ImapTokenType.Asterisk, '*');
5655
public static readonly ImapToken OpenParen = new ImapToken (ImapTokenType.OpenParen, '(');
5756
public static readonly ImapToken CloseParen = new ImapToken (ImapTokenType.CloseParen, ')');
@@ -108,7 +107,6 @@ class ImapToken
108107
public static ImapToken Create (ImapTokenType type, char c)
109108
{
110109
switch (type) {
111-
case ImapTokenType.Plus: return Plus;
112110
case ImapTokenType.Asterisk: return Asterisk;
113111
case ImapTokenType.OpenParen: return OpenParen;
114112
case ImapTokenType.CloseParen: return CloseParen;
@@ -205,7 +203,6 @@ public override string ToString ()
205203
case ImapTokenType.QString: return MimeUtils.Quote ((string) Value);
206204
case ImapTokenType.Literal: return string.Format (CultureInfo.InvariantCulture, "{{{0}}}", (int) Value);
207205
case ImapTokenType.Eoln: return "'\\n'";
208-
case ImapTokenType.Plus: return "'+'";
209206
case ImapTokenType.OpenParen: return "'('";
210207
case ImapTokenType.CloseParen: return "')'";
211208
case ImapTokenType.Asterisk: return "'*'";

UnitTests/Net/Imap/ImapStreamTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -319,8 +319,8 @@ public void TestReadContinuationToken ()
319319
stream.Stream.Position = 0;
320320

321321
var token = stream.ReadToken (CancellationToken.None);
322-
Assert.That (token.Type, Is.EqualTo (ImapTokenType.Plus));
323-
Assert.That (token.ToString (), Is.EqualTo ("'+'"));
322+
Assert.That (token.Type, Is.EqualTo (ImapTokenType.Atom));
323+
Assert.That (token.ToString (), Is.EqualTo ("+"));
324324
}
325325
}
326326

@@ -334,8 +334,8 @@ public async Task TestReadContinuationTokenAsync ()
334334
stream.Stream.Position = 0;
335335

336336
var token = await stream.ReadTokenAsync (CancellationToken.None);
337-
Assert.That (token.Type, Is.EqualTo (ImapTokenType.Plus));
338-
Assert.That (token.ToString (), Is.EqualTo ("'+'"));
337+
Assert.That (token.Type, Is.EqualTo (ImapTokenType.Atom));
338+
Assert.That (token.ToString (), Is.EqualTo ("+"));
339339
}
340340
}
341341

UnitTests/Net/Imap/ImapUtilsTests.cs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4337,6 +4337,80 @@ public async Task TestParseAnnotationsExample3Async ()
43374337
}
43384338
}
43394339

4340+
[Test]
4341+
public void TestParseFlagsList ()
4342+
{
4343+
const string text = "(\\Answered \\Flagged \\Deleted \\Seen \\Draft + TAG TAG2 a b c d e f g h i j k l m n o p q r s t u v w x y z)\r\n";
4344+
4345+
using (var memory = new MemoryStream (Encoding.ASCII.GetBytes (text), false)) {
4346+
using (var tokenizer = new ImapStream (memory, new NullProtocolLogger ())) {
4347+
using (var engine = new ImapEngine (null)) {
4348+
var keywords = new HashSet<string> ();
4349+
MessageFlags flags;
4350+
4351+
engine.SetStream (tokenizer);
4352+
4353+
try {
4354+
flags = ImapUtils.ParseFlagsList (engine, "INBOX", keywords, CancellationToken.None);
4355+
} catch (Exception ex) {
4356+
Assert.Fail ($"Parsing FLAGS response failed: {ex}");
4357+
return;
4358+
}
4359+
4360+
var token = engine.ReadToken (CancellationToken.None);
4361+
Assert.That (token.Type, Is.EqualTo (ImapTokenType.Eoln), $"Expected new-line, but got: {token}");
4362+
4363+
Assert.That (flags, Is.EqualTo (MessageFlags.Answered | MessageFlags.Flagged | MessageFlags.Deleted | MessageFlags.Seen | MessageFlags.Draft), "message flags");
4364+
4365+
Assert.That (keywords, Has.Count.EqualTo (29), "keywords.Count");
4366+
Assert.That (keywords.Contains ("+"), Is.True, "Contains +");
4367+
Assert.That (keywords.Contains ("TAG"), Is.True, "Contains TAG");
4368+
Assert.That (keywords.Contains ("TAG2"), Is.True, "Contains TAG2");
4369+
4370+
for (char c = 'a'; c <= 'z'; c++)
4371+
Assert.That (keywords.Contains (c.ToString ()), Is.True, $"Contains {c}");
4372+
}
4373+
}
4374+
}
4375+
}
4376+
4377+
[Test]
4378+
public async Task TestParseFlagsListAsync ()
4379+
{
4380+
const string text = "(\\Answered \\Flagged \\Deleted \\Seen \\Draft + TAG TAG2 a b c d e f g h i j k l m n o p q r s t u v w x y z)\r\n";
4381+
4382+
using (var memory = new MemoryStream (Encoding.ASCII.GetBytes (text), false)) {
4383+
using (var tokenizer = new ImapStream (memory, new NullProtocolLogger ())) {
4384+
using (var engine = new ImapEngine (null)) {
4385+
var keywords = new HashSet<string> ();
4386+
MessageFlags flags;
4387+
4388+
engine.SetStream (tokenizer);
4389+
4390+
try {
4391+
flags = await ImapUtils.ParseFlagsListAsync (engine, "INBOX", keywords, CancellationToken.None);
4392+
} catch (Exception ex) {
4393+
Assert.Fail ($"Parsing FLAGS response failed: {ex}");
4394+
return;
4395+
}
4396+
4397+
var token = await engine.ReadTokenAsync (CancellationToken.None);
4398+
Assert.That (token.Type, Is.EqualTo (ImapTokenType.Eoln), $"Expected new-line, but got: {token}");
4399+
4400+
Assert.That (flags, Is.EqualTo (MessageFlags.Answered | MessageFlags.Flagged | MessageFlags.Deleted | MessageFlags.Seen | MessageFlags.Draft), "message flags");
4401+
4402+
Assert.That (keywords, Has.Count.EqualTo (29), "keywords.Count");
4403+
Assert.That (keywords.Contains ("+"), Is.True, "Contains +");
4404+
Assert.That (keywords.Contains ("TAG"), Is.True, "Contains TAG");
4405+
Assert.That (keywords.Contains ("TAG2"), Is.True, "Contains TAG2");
4406+
4407+
for (char c = 'a'; c <= 'z'; c++)
4408+
Assert.That (keywords.Contains (c.ToString ()), Is.True, $"Contains {c}");
4409+
}
4410+
}
4411+
}
4412+
}
4413+
43404414
ImapFolder CreateImapFolder (ImapFolderConstructorArgs args)
43414415
{
43424416
return new ImapFolder (args);

0 commit comments

Comments
 (0)