Skip to content

Commit c6967ce

Browse files
committedFeb 16, 2023
Added support for rfc8970 (IMAP4 Extension: Message Preview Generation)
1 parent bef1059 commit c6967ce

15 files changed

+835
-21
lines changed
 

‎MailKit/FetchRequest.cs

+12
Original file line numberDiff line numberDiff line change
@@ -135,5 +135,17 @@ public FetchRequest (MessageSummaryItems items, IEnumerable<string> headers) : t
135135
/// </remarks>
136136
/// <value>The set of headers to be fetched.</value>
137137
public HeaderSet Headers { get; set; }
138+
139+
#if ENABLE_LAZY_PREVIEW_API
140+
/// <summary>
141+
/// Get or set options to use when fetching <see cref="MessageSummaryItems.PreviewText"/>.
142+
/// </summary>
143+
/// <remarks>
144+
/// <para>Gets or sets options to use when fetching <see cref="MessageSummaryItems.PreviewText"/>.</para>
145+
/// <note type="note">These options are only used if <see cref="Items"/> includes the
146+
/// <see cref="MessageSummaryItems.PreviewText"/> value.</note>
147+
/// </remarks>
148+
public PreviewOptions PreviewOptions { get; set; }
149+
#endif
138150
}
139151
}

‎MailKit/IFetchRequest.cs

+12
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,17 @@ public interface IFetchRequest
7070
/// </remarks>
7171
/// <value>The set of headers to be fetched.</value>
7272
HeaderSet Headers { get; }
73+
74+
#if ENABLE_LAZY_PREVIEW_API
75+
/// <summary>
76+
/// Get the options to use when fetching <see cref="MessageSummaryItems.PreviewText"/>.
77+
/// </summary>
78+
/// <remarks>
79+
/// <para>Gets the options to use when fetching <see cref="MessageSummaryItems.PreviewText"/>.</para>
80+
/// <note type="note">These options are only used if <see cref="Items"/> includes the
81+
/// <see cref="MessageSummaryItems.PreviewText"/> value.</note>
82+
/// </remarks>
83+
PreviewOptions PreviewOptions { get; }
84+
#endif
7385
}
7486
}

‎MailKit/MailKit.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@
256256
<Compile Include="MetadataTag.cs" />
257257
<Compile Include="ModSeqChangedEventArgs.cs" />
258258
<Compile Include="NullProtocolLogger.cs" />
259+
<Compile Include="PreviewOptions.cs" />
259260
<Compile Include="ProgressStream.cs" />
260261
<Compile Include="ProtocolException.cs" />
261262
<Compile Include="ProtocolLogger.cs" />

‎MailKit/MailKitLite.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@
255255
<Compile Include="MetadataTag.cs" />
256256
<Compile Include="ModSeqChangedEventArgs.cs" />
257257
<Compile Include="NullProtocolLogger.cs" />
258+
<Compile Include="PreviewOptions.cs" />
258259
<Compile Include="ProgressStream.cs" />
259260
<Compile Include="ProtocolException.cs" />
260261
<Compile Include="ProtocolLogger.cs" />

‎MailKit/Net/Imap/ImapCapabilities.cs

+5
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,11 @@ public enum ImapCapabilities : ulong {
336336
/// </summary>
337337
SaveDate = 1L << 57,
338338

339+
/// <summary>
340+
/// The server supports the <a href="https://tools.ietf.org/html/rfc8970">PREVIEW</a> extension.
341+
/// </summary>
342+
Preview = 1L << 58,
343+
339344
#region GMail Extensions
340345

341346
/// <summary>

‎MailKit/Net/Imap/ImapEngine.cs

+2
Original file line numberDiff line numberDiff line change
@@ -1312,6 +1312,8 @@ void ProcessCapabilityToken (string atom)
13121312
Capabilities |= ImapCapabilities.Replace;
13131313
} else if (atom.Equals ("SAVEDATE", StringComparison.OrdinalIgnoreCase)) {
13141314
Capabilities |= ImapCapabilities.SaveDate;
1315+
} else if (atom.Equals ("PREVIEW", StringComparison.OrdinalIgnoreCase)) {
1316+
Capabilities |= ImapCapabilities.Preview;
13151317
} else if (atom.Equals ("XLIST", StringComparison.OrdinalIgnoreCase)) {
13161318
Capabilities |= ImapCapabilities.XList;
13171319
} else if (atom.Equals ("X-GM-EXT-1", StringComparison.OrdinalIgnoreCase)) {

‎MailKit/Net/Imap/ImapFolderFetch.cs

+27-8
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ void FetchSummaryItems (ImapEngine engine, MessageSummary message, FetchSummaryI
271271
message.InternalDate = ReadDateTimeOffsetToken (engine, atom, cancellationToken);
272272
message.Fields |= MessageSummaryItems.InternalDate;
273273
} else if (atom.Equals ("SAVEDATE", StringComparison.OrdinalIgnoreCase)) {
274-
message.SaveDate = ReadDateTimeOffsetToken (engine, atom, cancellationToken) ;
274+
message.SaveDate = ReadDateTimeOffsetToken (engine, atom, cancellationToken);
275275
message.Fields |= MessageSummaryItems.SaveDate;
276276
} else if (atom.Equals ("RFC822.SIZE", StringComparison.OrdinalIgnoreCase)) {
277277
token = engine.ReadToken (cancellationToken);
@@ -449,6 +449,10 @@ void FetchSummaryItems (ImapEngine engine, MessageSummary message, FetchSummaryI
449449
} else if (atom.Equals ("ANNOTATION", StringComparison.OrdinalIgnoreCase)) {
450450
message.Annotations = ImapUtils.ParseAnnotationsAsync (engine, false, cancellationToken).GetAwaiter ().GetResult ();
451451
message.Fields |= MessageSummaryItems.Annotations;
452+
} else if (atom.Equals ("PREVIEW", StringComparison.OrdinalIgnoreCase)) {
453+
format = string.Format (ImapEngine.GenericItemSyntaxErrorFormat, "PREVIEW", "{0}");
454+
message.PreviewText = ImapUtils.ReadNStringToken (engine, format, false, cancellationToken);
455+
message.Fields |= MessageSummaryItems.PreviewText;
452456
} else {
453457
// Unexpected or unknown token (such as XAOL.SPAM.REASON or XAOL-MSGID). Simply read 1 more token (the argument) and ignore.
454458
token = engine.ReadToken (cancellationToken);
@@ -681,6 +685,10 @@ async Task FetchSummaryItemsAsync (ImapEngine engine, MessageSummary message, Fe
681685
} else if (atom.Equals ("ANNOTATION", StringComparison.OrdinalIgnoreCase)) {
682686
message.Annotations = await ImapUtils.ParseAnnotationsAsync (engine, true, cancellationToken).ConfigureAwait (false);
683687
message.Fields |= MessageSummaryItems.Annotations;
688+
} else if (atom.Equals ("PREVIEW", StringComparison.OrdinalIgnoreCase)) {
689+
format = string.Format (ImapEngine.GenericItemSyntaxErrorFormat, "PREVIEW", "{0}");
690+
message.PreviewText = await ImapUtils.ReadNStringTokenAsync (engine, format, false, cancellationToken).ConfigureAwait (false);
691+
message.Fields |= MessageSummaryItems.PreviewText;
684692
} else {
685693
// Unexpected or unknown token (such as XAOL.SPAM.REASON or XAOL-MSGID). Simply read 1 more token (the argument) and ignore.
686694
token = await engine.ReadTokenAsync (cancellationToken).ConfigureAwait (false);
@@ -722,10 +730,11 @@ internal static string FormatSummaryItems (ImapEngine engine, IFetchRequest requ
722730
{
723731
var items = request.Items;
724732

725-
if ((items & MessageSummaryItems.PreviewText) != 0) {
733+
if ((engine.Capabilities & ImapCapabilities.Preview) == 0 && (items & MessageSummaryItems.PreviewText) != 0) {
726734
// if the user wants the preview text, we will also need the UIDs and BODYSTRUCTUREs
727735
// so that we can request a preview of the body text in subsequent FETCH requests.
728736
items |= MessageSummaryItems.BodyStructure | MessageSummaryItems.UniqueId;
737+
items &= ~MessageSummaryItems.PreviewText;
729738
previewText = true;
730739
} else {
731740
previewText = false;
@@ -737,16 +746,13 @@ internal static string FormatSummaryItems (ImapEngine engine, IFetchRequest requ
737746
}
738747

739748
if (engine.QuirksMode != ImapQuirksMode.GMail && !isNotify) {
740-
// first, eliminate the aliases...
741-
var alias = items & ~MessageSummaryItems.PreviewText;
742-
743-
if (alias == MessageSummaryItems.All)
749+
if (items == MessageSummaryItems.All)
744750
return "ALL";
745751

746-
if (alias == MessageSummaryItems.Full)
752+
if (items == MessageSummaryItems.Full)
747753
return "FULL";
748754

749-
if (alias == MessageSummaryItems.Fast)
755+
if (items == MessageSummaryItems.Fast)
750756
return "FAST";
751757
}
752758

@@ -790,6 +796,19 @@ internal static string FormatSummaryItems (ImapEngine engine, IFetchRequest requ
790796
tokens.Add ("SAVEDATE");
791797
}
792798

799+
if ((engine.Capabilities & ImapCapabilities.Preview) != 0) {
800+
if ((items & MessageSummaryItems.PreviewText) != 0) {
801+
#if ENABLE_LAZY_PREVIEW_API
802+
if (request.PreviewOptions == PreviewOptions.Lazy)
803+
tokens.Add ("PREVIEW (LAZY)");
804+
else
805+
tokens.Add ("PREVIEW");
806+
#else
807+
tokens.Add ("PREVIEW");
808+
#endif
809+
}
810+
}
811+
793812
if ((engine.Capabilities & ImapCapabilities.GMailExt1) != 0) {
794813
// now for the GMail extension items
795814
if ((items & MessageSummaryItems.GMailMessageId) != 0)

‎MailKit/Net/Imap/ImapUtils.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,7 @@ internal static ValueTask<string> ReadStringTokenAsync (ImapEngine engine, strin
719719
return new ValueTask<string> (value);
720720
}
721721

722-
static string ReadNStringToken (ImapEngine engine, string format, bool rfc2047, CancellationToken cancellationToken)
722+
internal static string ReadNStringToken (ImapEngine engine, string format, bool rfc2047, CancellationToken cancellationToken)
723723
{
724724
var token = engine.ReadToken (cancellationToken);
725725
string value;
@@ -744,7 +744,7 @@ static string ReadNStringToken (ImapEngine engine, string format, bool rfc2047,
744744
return value;
745745
}
746746

747-
static async ValueTask<string> ReadNStringTokenAsync (ImapEngine engine, string format, bool rfc2047, CancellationToken cancellationToken)
747+
internal static async ValueTask<string> ReadNStringTokenAsync (ImapEngine engine, string format, bool rfc2047, CancellationToken cancellationToken)
748748
{
749749
var token = await engine.ReadTokenAsync (cancellationToken).ConfigureAwait (false);
750750
string value;

‎MailKit/PreviewOptions.cs

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//
2+
// PreviewOptions.cs
3+
//
4+
// Author: Jeffrey Stedfast <jestedfa@microsoft.com>
5+
//
6+
// Copyright (c) 2013-2023 .NET Foundation and Contributors
7+
//
8+
// Permission is hereby granted, free of charge, to any person obtaining a copy
9+
// of this software and associated documentation files (the "Software"), to deal
10+
// in the Software without restriction, including without limitation the rights
11+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
// copies of the Software, and to permit persons to whom the Software is
13+
// furnished to do so, subject to the following conditions:
14+
//
15+
// The above copyright notice and this permission notice shall be included in
16+
// all copies or substantial portions of the Software.
17+
//
18+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
// THE SOFTWARE.
25+
//
26+
27+
namespace MailKit {
28+
#if ENABLE_LAZY_PREVIEW_API
29+
30+
/// <summary>
31+
/// A set of options for fetching the preview text for messages.
32+
/// </summary>
33+
public enum PreviewOptions
34+
{
35+
/// <summary>
36+
/// No options specified.
37+
/// </summary>
38+
None,
39+
40+
/// <summary>
41+
/// The preview text should only be fetched if the server has it intstantly available (cached).
42+
/// </summary>
43+
Lazy
44+
}
45+
#endif
46+
}

‎RFCs.md

+1
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,4 @@ MailKit implements the following IETF specifications:
116116
* [8508](https://tools.ietf.org/html/rfc8508): IMAP REPLACE Extension (Updates rfc3501)
117117
* [8514](https://tools.ietf.org/html/rfc8514): Internet Message Access Protocol (IMAP) - SAVEDATE Extension
118118
* [8689](https://tools.ietf.org/html/rfc8689): SMTP Require TLS Option
119+
* [8970](https://tools.ietf.org/html/rfc8970): IMAP4 Extension: Message Preview Generation

‎UnitTests/Net/Imap/ImapFolderFetchTests.cs

+264-11
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,259 @@ public void TestNotSupportedExceptions ()
466466
}
467467

468468
static readonly string[] PreviewTextValues = {
469+
"Planet Fitness https://view.email.planetfitness.com/?qs=9a098a031cabde68c0a4260051cd6fe473a2e997a53678ff26b4b199a711a9d2ad0536530d6f837c246b09f644d42016ecfb298f930b7af058e9e454b34f3d818ceb3052ae317b1ac4594aab28a2d788 View web ver",
470+
"Don't miss our celebrity guest Monday evening",
471+
"Planet Fitness https://view.email.planetfitness.com/?qs=9a098a031cabde68c0a4260051cd6fe473a2e997a53678ff26b4b199a711a9d2ad0536530d6f837c246b09f644d42016ecfb298f930b7af058e9e454b34f3d818ceb3052ae317b1ac4594aab28a2d788 View web ver",
472+
"Planet Fitness https://view.email.planetfitness.com/?qs=9a098a031cabde68c0a4260051cd6fe473a2e997a53678ff26b4b199a711a9d2ad0536530d6f837c246b09f644d42016ecfb298f930b7af058e9e454b34f3d818ceb3052ae317b1ac4594aab28a2d788 View web ver",
473+
"Don't miss our celebrity guest Monday evening",
474+
"Planet Fitness https://view.email.planetfitness.com/?qs=9a098a031cabde68c0a4260051cd6fe473a2e997a53678ff26b4b199a711a9d2ad0536530d6f837c246b09f644d42016ecfb298f930b7af058e9e454b34f3d818ceb3052ae317b1ac4594aab28a2d788 View web ver"
475+
};
476+
477+
static List<ImapReplayCommand> CreateFetchPreviewTextCommands ()
478+
{
479+
return new List<ImapReplayCommand> {
480+
new ImapReplayCommand ("", "gmail.greeting.txt"),
481+
new ImapReplayCommand ("A00000000 CAPABILITY\r\n", "gmail.capability.txt"),
482+
new ImapReplayCommand ("A00000001 AUTHENTICATE PLAIN AHVzZXJuYW1lAHBhc3N3b3Jk\r\n", "gmail.authenticate+preview.txt"),
483+
new ImapReplayCommand ("A00000002 NAMESPACE\r\n", "gmail.namespace.txt"),
484+
new ImapReplayCommand ("A00000003 LIST \"\" \"INBOX\" RETURN (SUBSCRIBED CHILDREN)\r\n", "gmail.list-inbox.txt"),
485+
new ImapReplayCommand ("A00000004 XLIST \"\" \"*\"\r\n", "gmail.xlist.txt"),
486+
new ImapReplayCommand ("A00000005 LIST \"\" \"%\"\r\n", "gmail.list-personal.txt"),
487+
new ImapReplayCommand ("A00000006 EXAMINE INBOX (CONDSTORE)\r\n", "gmail.examine-inbox.txt"),
488+
new ImapReplayCommand ("A00000007 UID FETCH 1:* (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE PREVIEW)\r\n", "gmail.fetch-preview.txt"),
489+
new ImapReplayCommand ("A00000008 FETCH 1:6 (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE PREVIEW)\r\n", "gmail.fetch-preview.txt"),
490+
new ImapReplayCommand ("A00000009 FETCH 1:* (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE PREVIEW)\r\n", "gmail.fetch-preview.txt")
491+
};
492+
}
493+
494+
[Test]
495+
public void TestFetchPreviewText ()
496+
{
497+
var commands = CreateFetchPreviewTextCommands ();
498+
499+
using (var client = new ImapClient ()) {
500+
try {
501+
client.ReplayConnect ("localhost", new ImapReplayStream (commands, false));
502+
} catch (Exception ex) {
503+
Assert.Fail ("Did not expect an exception in Connect: {0}", ex);
504+
}
505+
506+
// Note: Do not try XOAUTH2
507+
client.AuthenticationMechanisms.Remove ("XOAUTH2");
508+
509+
try {
510+
client.Authenticate ("username", "password");
511+
} catch (Exception ex) {
512+
Assert.Fail ("Did not expect an exception in Authenticate: {0}", ex);
513+
}
514+
515+
// disable LIST-EXTENDED
516+
client.Capabilities &= ~ImapCapabilities.ListExtended;
517+
518+
var personal = client.GetFolder (client.PersonalNamespaces[0]);
519+
var folders = personal.GetSubfolders ();
520+
Assert.AreEqual (client.Inbox, folders[0], "Expected the first folder to be the Inbox.");
521+
Assert.AreEqual ("[Gmail]", folders[1].FullName, "Expected the second folder to be [Gmail].");
522+
Assert.AreEqual (FolderAttributes.NoSelect | FolderAttributes.HasChildren, folders[1].Attributes, "Expected [Gmail] folder to be \\Noselect \\HasChildren.");
523+
524+
var inbox = client.Inbox;
525+
526+
inbox.Open (FolderAccess.ReadOnly);
527+
528+
var messages = inbox.Fetch (UniqueIdRange.All, MessageSummaryItems.All | MessageSummaryItems.PreviewText);
529+
for (int i = 0; i < messages.Count; i++)
530+
Assert.AreEqual (PreviewTextValues[i], messages[i].PreviewText);
531+
532+
messages = inbox.Fetch (new int[] { 0, 1, 2, 3, 4, 5 }, MessageSummaryItems.All | MessageSummaryItems.PreviewText);
533+
for (int i = 0; i < messages.Count; i++)
534+
Assert.AreEqual (PreviewTextValues[i], messages[i].PreviewText);
535+
536+
messages = inbox.Fetch (0, -1, MessageSummaryItems.All | MessageSummaryItems.PreviewText);
537+
for (int i = 0; i < messages.Count; i++)
538+
Assert.AreEqual (PreviewTextValues[i], messages[i].PreviewText);
539+
540+
client.Disconnect (false);
541+
}
542+
}
543+
544+
[Test]
545+
public async Task TestFetchPreviewTextAsync ()
546+
{
547+
var commands = CreateFetchPreviewTextCommands ();
548+
549+
using (var client = new ImapClient ()) {
550+
try {
551+
await client.ReplayConnectAsync ("localhost", new ImapReplayStream (commands, true));
552+
} catch (Exception ex) {
553+
Assert.Fail ("Did not expect an exception in Connect: {0}", ex);
554+
}
555+
556+
// Note: Do not try XOAUTH2
557+
client.AuthenticationMechanisms.Remove ("XOAUTH2");
558+
559+
try {
560+
await client.AuthenticateAsync ("username", "password");
561+
} catch (Exception ex) {
562+
Assert.Fail ("Did not expect an exception in Authenticate: {0}", ex);
563+
}
564+
565+
// disable LIST-EXTENDED
566+
client.Capabilities &= ~ImapCapabilities.ListExtended;
567+
568+
var personal = client.GetFolder (client.PersonalNamespaces[0]);
569+
var folders = await personal.GetSubfoldersAsync ();
570+
Assert.AreEqual (client.Inbox, folders[0], "Expected the first folder to be the Inbox.");
571+
Assert.AreEqual ("[Gmail]", folders[1].FullName, "Expected the second folder to be [Gmail].");
572+
Assert.AreEqual (FolderAttributes.NoSelect | FolderAttributes.HasChildren, folders[1].Attributes, "Expected [Gmail] folder to be \\Noselect \\HasChildren.");
573+
574+
var inbox = client.Inbox;
575+
576+
await inbox.OpenAsync (FolderAccess.ReadOnly);
577+
578+
var messages = await inbox.FetchAsync (UniqueIdRange.All, MessageSummaryItems.All | MessageSummaryItems.PreviewText);
579+
for (int i = 0; i < messages.Count; i++)
580+
Assert.AreEqual (PreviewTextValues[i], messages[i].PreviewText);
581+
582+
messages = await inbox.FetchAsync (new int[] { 0, 1, 2, 3, 4, 5 }, MessageSummaryItems.All | MessageSummaryItems.PreviewText);
583+
for (int i = 0; i < messages.Count; i++)
584+
Assert.AreEqual (PreviewTextValues[i], messages[i].PreviewText);
585+
586+
messages = await inbox.FetchAsync (0, -1, MessageSummaryItems.All | MessageSummaryItems.PreviewText);
587+
for (int i = 0; i < messages.Count; i++)
588+
Assert.AreEqual (PreviewTextValues[i], messages[i].PreviewText);
589+
590+
await client.DisconnectAsync (false);
591+
}
592+
}
593+
594+
#if ENABLE_LAZY_PREVIEW_API
595+
static List<ImapReplayCommand> CreateFetchLazyPreviewTextCommands ()
596+
{
597+
return new List<ImapReplayCommand> {
598+
new ImapReplayCommand ("", "gmail.greeting.txt"),
599+
new ImapReplayCommand ("A00000000 CAPABILITY\r\n", "gmail.capability.txt"),
600+
new ImapReplayCommand ("A00000001 AUTHENTICATE PLAIN AHVzZXJuYW1lAHBhc3N3b3Jk\r\n", "gmail.authenticate+preview.txt"),
601+
new ImapReplayCommand ("A00000002 NAMESPACE\r\n", "gmail.namespace.txt"),
602+
new ImapReplayCommand ("A00000003 LIST \"\" \"INBOX\" RETURN (SUBSCRIBED CHILDREN)\r\n", "gmail.list-inbox.txt"),
603+
new ImapReplayCommand ("A00000004 XLIST \"\" \"*\"\r\n", "gmail.xlist.txt"),
604+
new ImapReplayCommand ("A00000005 LIST \"\" \"%\"\r\n", "gmail.list-personal.txt"),
605+
new ImapReplayCommand ("A00000006 EXAMINE INBOX (CONDSTORE)\r\n", "gmail.examine-inbox.txt"),
606+
new ImapReplayCommand ("A00000007 UID FETCH 1:* (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE PREVIEW (LAZY))\r\n", "gmail.fetch-preview.txt"),
607+
new ImapReplayCommand ("A00000008 FETCH 1:6 (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE PREVIEW (LAZY))\r\n", "gmail.fetch-preview.txt"),
608+
new ImapReplayCommand ("A00000009 FETCH 1:* (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE PREVIEW (LAZY))\r\n", "gmail.fetch-preview.txt")
609+
};
610+
}
611+
612+
[Test]
613+
public void TestFetchLazyPreviewText ()
614+
{
615+
var commands = CreateFetchLazyPreviewTextCommands ();
616+
617+
using (var client = new ImapClient ()) {
618+
try {
619+
client.ReplayConnect ("localhost", new ImapReplayStream (commands, false));
620+
} catch (Exception ex) {
621+
Assert.Fail ("Did not expect an exception in Connect: {0}", ex);
622+
}
623+
624+
// Note: Do not try XOAUTH2
625+
client.AuthenticationMechanisms.Remove ("XOAUTH2");
626+
627+
try {
628+
client.Authenticate ("username", "password");
629+
} catch (Exception ex) {
630+
Assert.Fail ("Did not expect an exception in Authenticate: {0}", ex);
631+
}
632+
633+
// disable LIST-EXTENDED
634+
client.Capabilities &= ~ImapCapabilities.ListExtended;
635+
636+
var personal = client.GetFolder (client.PersonalNamespaces[0]);
637+
var folders = personal.GetSubfolders ();
638+
Assert.AreEqual (client.Inbox, folders[0], "Expected the first folder to be the Inbox.");
639+
Assert.AreEqual ("[Gmail]", folders[1].FullName, "Expected the second folder to be [Gmail].");
640+
Assert.AreEqual (FolderAttributes.NoSelect | FolderAttributes.HasChildren, folders[1].Attributes, "Expected [Gmail] folder to be \\Noselect \\HasChildren.");
641+
642+
var inbox = client.Inbox;
643+
644+
inbox.Open (FolderAccess.ReadOnly);
645+
646+
var request = new FetchRequest (MessageSummaryItems.All | MessageSummaryItems.PreviewText) {
647+
PreviewOptions = PreviewOptions.Lazy
648+
};
649+
650+
var messages = inbox.Fetch (UniqueIdRange.All, request);
651+
for (int i = 0; i < messages.Count; i++)
652+
Assert.AreEqual (PreviewTextValues[i], messages[i].PreviewText);
653+
654+
messages = inbox.Fetch (new int[] { 0, 1, 2, 3, 4, 5 }, request);
655+
for (int i = 0; i < messages.Count; i++)
656+
Assert.AreEqual (PreviewTextValues[i], messages[i].PreviewText);
657+
658+
messages = inbox.Fetch (0, -1, request);
659+
for (int i = 0; i < messages.Count; i++)
660+
Assert.AreEqual (PreviewTextValues[i], messages[i].PreviewText);
661+
662+
client.Disconnect (false);
663+
}
664+
}
665+
666+
[Test]
667+
public async Task TestFetchLazyPreviewTextAsync ()
668+
{
669+
var commands = CreateFetchLazyPreviewTextCommands ();
670+
671+
using (var client = new ImapClient ()) {
672+
try {
673+
await client.ReplayConnectAsync ("localhost", new ImapReplayStream (commands, true));
674+
} catch (Exception ex) {
675+
Assert.Fail ("Did not expect an exception in Connect: {0}", ex);
676+
}
677+
678+
// Note: Do not try XOAUTH2
679+
client.AuthenticationMechanisms.Remove ("XOAUTH2");
680+
681+
try {
682+
await client.AuthenticateAsync ("username", "password");
683+
} catch (Exception ex) {
684+
Assert.Fail ("Did not expect an exception in Authenticate: {0}", ex);
685+
}
686+
687+
// disable LIST-EXTENDED
688+
client.Capabilities &= ~ImapCapabilities.ListExtended;
689+
690+
var personal = client.GetFolder (client.PersonalNamespaces[0]);
691+
var folders = await personal.GetSubfoldersAsync ();
692+
Assert.AreEqual (client.Inbox, folders[0], "Expected the first folder to be the Inbox.");
693+
Assert.AreEqual ("[Gmail]", folders[1].FullName, "Expected the second folder to be [Gmail].");
694+
Assert.AreEqual (FolderAttributes.NoSelect | FolderAttributes.HasChildren, folders[1].Attributes, "Expected [Gmail] folder to be \\Noselect \\HasChildren.");
695+
696+
var inbox = client.Inbox;
697+
698+
await inbox.OpenAsync (FolderAccess.ReadOnly);
699+
700+
var request = new FetchRequest (MessageSummaryItems.All | MessageSummaryItems.PreviewText) {
701+
PreviewOptions = PreviewOptions.Lazy
702+
};
703+
704+
var messages = await inbox.FetchAsync (UniqueIdRange.All, request);
705+
for (int i = 0; i < messages.Count; i++)
706+
Assert.AreEqual (PreviewTextValues[i], messages[i].PreviewText);
707+
708+
messages = await inbox.FetchAsync (new int[] { 0, 1, 2, 3, 4, 5 }, request);
709+
for (int i = 0; i < messages.Count; i++)
710+
Assert.AreEqual (PreviewTextValues[i], messages[i].PreviewText);
711+
712+
messages = await inbox.FetchAsync (0, -1, request);
713+
for (int i = 0; i < messages.Count; i++)
714+
Assert.AreEqual (PreviewTextValues[i], messages[i].PreviewText);
715+
716+
await client.DisconnectAsync (false);
717+
}
718+
}
719+
#endif
720+
721+
static readonly string[] SimulatedPreviewTextValues = {
469722
"Planet Fitness https://view.email.planetfitness.com/?qs=9a098a031cabde68c0a4260051cd6fe473a2e997a53678ff26b4b199a711a9d2ad0536530d6f837c246b09f644d42016ecfb298f930b7af058e9e454b34f3d818ceb3052ae317b1ac4594aab28a2d788 View web ver…",
470723
"Don’t miss our celebrity guest Monday evening",
471724
"Planet Fitness https://view.email.planetfitness.com/?qs=9a098a031cabde68c0a4260051cd6fe473a2e997a53678ff26b4b199a711a9d2ad0536530d6f837c246b09f644d42016ecfb298f930b7af058e9e454b34f3d818ceb3052ae317b1ac4594aab28a2d788 View web ver…",
@@ -474,7 +727,7 @@ public void TestNotSupportedExceptions ()
474727
"Planet Fitness https://view.email.planetfitness.com/?qs=9a098a031cabde68c0a4260051cd6fe473a2e997a53678ff26b4b199a711a9d2ad0536530d6f837c246b09f644d42016ecfb298f930b7af058e9e454b34f3d818ceb3052ae317b1ac4594aab28a2d788 View web ver…"
475728
};
476729

477-
static List<ImapReplayCommand> CreateFetchPreviewTextCommands ()
730+
static List<ImapReplayCommand> CreateFetchSimulatedPreviewTextCommands ()
478731
{
479732
return new List<ImapReplayCommand> {
480733
new ImapReplayCommand ("", "gmail.greeting.txt"),
@@ -501,9 +754,9 @@ static List<ImapReplayCommand> CreateFetchPreviewTextCommands ()
501754
}
502755

503756
[Test]
504-
public void TestFetchPreviewText ()
757+
public void TestFetchSimulatedPreviewText ()
505758
{
506-
var commands = CreateFetchPreviewTextCommands ();
759+
var commands = CreateFetchSimulatedPreviewTextCommands ();
507760

508761
using (var client = new ImapClient ()) {
509762
try {
@@ -536,24 +789,24 @@ public void TestFetchPreviewText ()
536789

537790
var messages = inbox.Fetch (UniqueIdRange.All, MessageSummaryItems.Full | MessageSummaryItems.PreviewText);
538791
for (int i = 0; i < messages.Count; i++)
539-
Assert.AreEqual (PreviewTextValues[i], messages[i].PreviewText);
792+
Assert.AreEqual (SimulatedPreviewTextValues[i], messages[i].PreviewText);
540793

541794
messages = inbox.Fetch (new int[] { 0, 1, 2, 3, 4, 5 }, MessageSummaryItems.Full | MessageSummaryItems.PreviewText);
542795
for (int i = 0; i < messages.Count; i++)
543-
Assert.AreEqual (PreviewTextValues[i], messages[i].PreviewText);
796+
Assert.AreEqual (SimulatedPreviewTextValues[i], messages[i].PreviewText);
544797

545798
messages = inbox.Fetch (0, -1, MessageSummaryItems.Full | MessageSummaryItems.PreviewText);
546799
for (int i = 0; i < messages.Count; i++)
547-
Assert.AreEqual (PreviewTextValues[i], messages[i].PreviewText);
800+
Assert.AreEqual (SimulatedPreviewTextValues[i], messages[i].PreviewText);
548801

549802
client.Disconnect (false);
550803
}
551804
}
552805

553806
[Test]
554-
public async Task TestFetchPreviewTextAsync ()
807+
public async Task TestFetchSimulatedPreviewTextAsync ()
555808
{
556-
var commands = CreateFetchPreviewTextCommands ();
809+
var commands = CreateFetchSimulatedPreviewTextCommands ();
557810

558811
using (var client = new ImapClient ()) {
559812
try {
@@ -586,15 +839,15 @@ public async Task TestFetchPreviewTextAsync ()
586839

587840
var messages = await inbox.FetchAsync (UniqueIdRange.All, MessageSummaryItems.Full | MessageSummaryItems.PreviewText);
588841
for (int i = 0; i < messages.Count; i++)
589-
Assert.AreEqual (PreviewTextValues[i], messages[i].PreviewText);
842+
Assert.AreEqual (SimulatedPreviewTextValues[i], messages[i].PreviewText);
590843

591844
messages = await inbox.FetchAsync (new int[] { 0, 1, 2, 3, 4, 5 }, MessageSummaryItems.Full | MessageSummaryItems.PreviewText);
592845
for (int i = 0; i < messages.Count; i++)
593-
Assert.AreEqual (PreviewTextValues[i], messages[i].PreviewText);
846+
Assert.AreEqual (SimulatedPreviewTextValues[i], messages[i].PreviewText);
594847

595848
messages = await inbox.FetchAsync (0, -1, MessageSummaryItems.Full | MessageSummaryItems.PreviewText);
596849
for (int i = 0; i < messages.Count; i++)
597-
Assert.AreEqual (PreviewTextValues[i], messages[i].PreviewText);
850+
Assert.AreEqual (SimulatedPreviewTextValues[i], messages[i].PreviewText);
598851

599852
await client.DisconnectAsync (false);
600853
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
* CAPABILITY IMAP4rev1 UNSELECT IDLE NAMESPACE QUOTA ID XLIST CHILDREN X-GM-EXT-1 UIDPLUS COMPRESS=DEFLATE ENABLE MOVE CONDSTORE ESEARCH UTF8=ACCEPT LIST-EXTENDED LIST-STATUS LITERAL- APPENDLIMIT=35651584 PREVIEW
2+
A######## OK username authenticated (Success)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
* 1 FETCH (UID 1 RFC822.SIZE 507 MODSEQ (41847) INTERNALDATE "03-Mar-2016 17:25:01 +0000" FLAGS (\Seen) ENVELOPE ("Thu, 03 Mar 2016 12:25:01 -0500" "text/plain message" (("Example From" NIL "from" "example.com")) (("Example From" NIL "from" "example.com")) (("Example Reply-To" NIL "reply-to" "example.com")) (("Example To" NIL "to" "example.com")) (("Example Recipient #1" NIL "recipient1" "example.com")("Example Recipient #2" NIL "recipient2" "example.com")("Example Recipient #3" NIL "recipient3" "example.com")) NIL NIL "<2T1YDYWYKXT4.7B2PY7UJXN1U3@Jeffrey-Stedfasts-2013-iMac.local>") PREVIEW "Planet Fitness https://view.email.planetfitness.com/?qs=9a098a031cabde68c0a4260051cd6fe473a2e997a53678ff26b4b199a711a9d2ad0536530d6f837c246b09f644d42016ecfb298f930b7af058e9e454b34f3d818ceb3052ae317b1ac4594aab28a2d788 View web ver")
2+
* 2 FETCH (UID 2 RFC822.SIZE 506 MODSEQ (41847) INTERNALDATE "03-Mar-2016 17:48:38 +0000" FLAGS (\Seen) ENVELOPE ("Thu, 03 Mar 2016 12:46:38 -0500" "text/html message" (("Example From" NIL "from" "example.com")) (("Example From" NIL "from" "example.com")) (("Example Reply-To" NIL "reply-to" "example.com")) (("Example To" NIL "to" "example.com")) (("Example Recipient #1" NIL "recipient1" "example.com")("Example Recipient #2" NIL "recipient2" "example.com")("Example Recipient #3" NIL "recipient3" "example.com")) NIL NIL "<K8NZOG3ZKXT4.32EL5QYOKY4B@Jeffrey-Stedfasts-2013-iMac.local>") PREVIEW "Don't miss our celebrity guest Monday evening")
3+
* 3 FETCH (UID 3 RFC822.SIZE 507 MODSEQ (41847) INTERNALDATE "03-Mar-2016 17:49:29 +0000" FLAGS (\Seen) ENVELOPE ("Thu, 03 Mar 2016 12:47:29 -0500" "multipart/alternative message" (("Example From" NIL "from" "example.com")) (("Example From" NIL "from" "example.com")) (("Example Reply-To" NIL "reply-to" "example.com")) (("Example To" NIL "to" "example.com")) (("Example Recipient #1" NIL "recipient1" "example.com")("Example Recipient #2" NIL "recipient2" "example.com")("Example Recipient #3" NIL "recipient3" "example.com")) NIL NIL "<014Y2P3ZKXT4.TMD6XILA8I5N2@Jeffrey-Stedfasts-2013-iMac.local>") PREVIEW "Planet Fitness https://view.email.planetfitness.com/?qs=9a098a031cabde68c0a4260051cd6fe473a2e997a53678ff26b4b199a711a9d2ad0536530d6f837c246b09f644d42016ecfb298f930b7af058e9e454b34f3d818ceb3052ae317b1ac4594aab28a2d788 View web ver")
4+
* 4 FETCH (UID 4 RFC822.SIZE 507 MODSEQ (41847) INTERNALDATE "03-Mar-2016 17:25:01 +0000" FLAGS (\Seen) ENVELOPE ("Thu, 03 Mar 2016 12:50:01 -0500" "text/plain message" (("Example From" NIL "from" "example.com")) (("Example From" NIL "from" "example.com")) (("Example Reply-To" NIL "reply-to" "example.com")) (("Example To" NIL "to" "example.com")) (("Example Recipient #1" NIL "recipient1" "example.com")("Example Recipient #2" NIL "recipient2" "example.com")("Example Recipient #3" NIL "recipient3" "example.com")) NIL NIL "<2T1YDYWYKXT4.7B2PY7UJXN1U3@Jeffrey-Stedfasts-2013-iMac.local>") PREVIEW "Planet Fitness https://view.email.planetfitness.com/?qs=9a098a031cabde68c0a4260051cd6fe473a2e997a53678ff26b4b199a711a9d2ad0536530d6f837c246b09f644d42016ecfb298f930b7af058e9e454b34f3d818ceb3052ae317b1ac4594aab28a2d788 View web ver")
5+
* 5 FETCH (UID 5 RFC822.SIZE 506 MODSEQ (41847) INTERNALDATE "03-Mar-2016 17:48:38 +0000" FLAGS (\Seen) ENVELOPE ("Thu, 03 Mar 2016 12:51:38 -0500" "text/html message" (("Example From" NIL "from" "example.com")) (("Example From" NIL "from" "example.com")) (("Example Reply-To" NIL "reply-to" "example.com")) (("Example To" NIL "to" "example.com")) (("Example Recipient #1" NIL "recipient1" "example.com")("Example Recipient #2" NIL "recipient2" "example.com")("Example Recipient #3" NIL "recipient3" "example.com")) NIL NIL "<K8NZOG3ZKXT4.32EL5QYOKY4B@Jeffrey-Stedfasts-2013-iMac.local>") PREVIEW "Don't miss our celebrity guest Monday evening")
6+
* 6 FETCH (UID 6 RFC822.SIZE 507 MODSEQ (41847) INTERNALDATE "03-Mar-2016 17:49:29 +0000" FLAGS (\Seen) ENVELOPE ("Thu, 03 Mar 2016 12:52:29 -0500" "multipart/alternative message" (("Example From" NIL "from" "example.com")) (("Example From" NIL "from" "example.com")) (("Example Reply-To" NIL "reply-to" "example.com")) (("Example To" NIL "to" "example.com")) (("Example Recipient #1" NIL "recipient1" "example.com")("Example Recipient #2" NIL "recipient2" "example.com")("Example Recipient #3" NIL "recipient3" "example.com")) NIL NIL "<014Y2P3ZKXT4.TMD6XILA8I5N2@Jeffrey-Stedfasts-2013-iMac.local>") PREVIEW "Planet Fitness https://view.email.planetfitness.com/?qs=9a098a031cabde68c0a4260051cd6fe473a2e997a53678ff26b4b199a711a9d2ad0536530d6f837c246b09f644d42016ecfb298f930b7af058e9e454b34f3d818ceb3052ae317b1ac4594aab28a2d788 View web ver")
7+
A######## OK Success

‎UnitTests/UnitTests.csproj

+2
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@
365365
<EmbeddedResource Include="Net\Imap\Resources\gmail\append.50.txt" />
366366
<EmbeddedResource Include="Net\Imap\Resources\gmail\authenticate.txt" />
367367
<EmbeddedResource Include="Net\Imap\Resources\gmail\authenticate+create-special-use.txt" />
368+
<EmbeddedResource Include="Net\Imap\Resources\gmail\authenticate+preview.txt" />
368369
<EmbeddedResource Include="Net\Imap\Resources\gmail\authenticate+statussize+objectid.txt" />
369370
<EmbeddedResource Include="Net\Imap\Resources\gmail\authenticate+webalert.txt" />
370371
<EmbeddedResource Include="Net\Imap\Resources\gmail\capability.txt" />
@@ -398,6 +399,7 @@
398399
<EmbeddedResource Include="Net\Imap\Resources\gmail\fetch.43.txt" />
399400
<EmbeddedResource Include="Net\Imap\Resources\gmail\fetch.50.txt" />
400401
<EmbeddedResource Include="Net\Imap\Resources\gmail\fetch-objectid.txt" />
402+
<EmbeddedResource Include="Net\Imap\Resources\gmail\fetch-preview.txt" />
401403
<EmbeddedResource Include="Net\Imap\Resources\gmail\fetch-previewtext-bodystructure.txt" />
402404
<EmbeddedResource Include="Net\Imap\Resources\gmail\fetch-previewtext-peek-html-only.txt" />
403405
<EmbeddedResource Include="Net\Imap\Resources\gmail\fetch-previewtext-peek-text-alternative.txt" />

‎rfc/rfc8970.txt

+451
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.