Skip to content

Commit e3bac2b

Browse files
committed
Updated ImapTokenCache to be able to handle non-ASCII tokens
1 parent 5494469 commit e3bac2b

File tree

2 files changed

+39
-20
lines changed

2 files changed

+39
-20
lines changed

MailKit/Net/Imap/ImapToken.cs

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -127,19 +127,6 @@ public static ImapToken Create (ImapTokenType type, int literalLength)
127127
return new ImapToken (type, literalLength);
128128
}
129129

130-
static bool IsAscii (ByteArrayBuilder builder)
131-
{
132-
for (int i = 0; i < builder.Length; i++) {
133-
byte c = builder[i];
134-
135-
// Disregard any non-ASCII tokens.
136-
if (c < 32 || c >= 127)
137-
return false;
138-
}
139-
140-
return true;
141-
}
142-
143130
static bool IsCacheable (ByteArrayBuilder builder)
144131
{
145132
if (builder.Length < 2 || builder.Length > 32)
@@ -153,7 +140,7 @@ static bool IsCacheable (ByteArrayBuilder builder)
153140
if (builder[0] >= (byte) 'A' && builder[0] <= (byte) 'Z' && builder[1] >= (byte) '0' && builder[1] <= (byte) '9')
154141
return false;
155142

156-
return IsAscii (builder);
143+
return true;
157144
}
158145

159146
public static ImapToken Create (ImapTokenType type, ByteArrayBuilder builder)
@@ -169,7 +156,7 @@ public static ImapToken Create (ImapTokenType type, ByteArrayBuilder builder)
169156
return token;
170157
}
171158

172-
cachable = IsAscii (builder);
159+
cachable = true;
173160
} else if (type == ImapTokenType.Atom) {
174161
if (builder.Equals ("NIL", true)) {
175162
// Look for the cached NIL token that matches this capitalization.
@@ -218,7 +205,7 @@ public static ImapToken Create (ImapTokenType type, ByteArrayBuilder builder)
218205

219206
cachable = IsCacheable (builder);
220207
} else if (type == ImapTokenType.QString) {
221-
cachable = IsAscii (builder);
208+
cachable = true;
222209
}
223210

224211
if (cachable)

MailKit/Net/Imap/ImapTokenCache.cs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,29 @@ class ImapTokenCache
3737
readonly Dictionary<ImapTokenKey, LinkedListNode<ImapTokenItem>> cache;
3838
readonly LinkedList<ImapTokenItem> list;
3939
readonly ImapTokenKey lookupKey;
40+
readonly Decoder[] decoders;
41+
readonly char[] chars;
4042

4143
public ImapTokenCache ()
4244
{
4345
cache = new Dictionary<ImapTokenKey, LinkedListNode<ImapTokenItem>> ();
4446
list = new LinkedList<ImapTokenItem> ();
4547
lookupKey = new ImapTokenKey ();
48+
49+
// Start with the assumption that token values will be valid UTF-8 and then fall back to iso-8859-1.
50+
decoders = new Decoder[2] {
51+
TextEncodings.UTF8.GetDecoder (),
52+
TextEncodings.Latin1.GetDecoder ()
53+
};
54+
55+
chars = new char[128];
4656
}
4757

4858
public ImapToken AddOrGet (ImapTokenType type, ByteArrayBuilder builder)
4959
{
5060
lock (cache) {
5161
// lookupKey is a pre-allocated key used for lookups
52-
lookupKey.Init (type, builder.GetBuffer (), builder.Length);
62+
lookupKey.Init (decoders, chars, type, builder.GetBuffer (), builder.Length);
5363

5464
if (cache.TryGetValue (lookupKey, out var node)) {
5565
// move the node to the head of the list
@@ -102,7 +112,7 @@ public ImapTokenKey (ImapTokenType type, string key)
102112
Init (type, key);
103113
}
104114

105-
public void Init (ImapTokenType type, byte[] key, int length)
115+
public void Init (Decoder[] decoders, char[] chars, ImapTokenType type, byte[] key, int length)
106116
{
107117
this.type = type;
108118
this.byteArrayKey = key;
@@ -111,8 +121,30 @@ public void Init (ImapTokenType type, byte[] key, int length)
111121

112122
var hash = new HashCode ();
113123
hash.Add ((int) type);
114-
for (int i = 0; i < length; i++)
115-
hash.Add ((char) key[i]);
124+
125+
foreach (var decoder in decoders) {
126+
bool completed;
127+
int index = 0;
128+
129+
do {
130+
try {
131+
decoder.Convert (key, index, length - index, chars, 0, chars.Length, true, out var bytesUsed, out var charsUsed, out completed);
132+
index += bytesUsed;
133+
134+
for (int i = 0; i < charsUsed; i++)
135+
hash.Add (chars[i]);
136+
} catch (DecoderFallbackException) {
137+
// Restart the hash...
138+
hash = new HashCode ();
139+
hash.Add ((int) type);
140+
completed = false;
141+
break;
142+
}
143+
} while (!completed);
144+
145+
if (completed)
146+
break;
147+
}
116148

117149
this.hashCode = hash.ToHashCode ();
118150
}

0 commit comments

Comments
 (0)