From 3adad3a495b071ea8b264b2cf64f2b8067d8369e Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Thu, 19 Feb 2026 10:29:50 -0500 Subject: [PATCH 01/44] Updated IMessageSummary, BodyPart* and Envelope to support nullability --- MailKit/BodyPart.cs | 71 ++++++++++++++++++------------------ MailKit/BodyPartBasic.cs | 18 ++++----- MailKit/BodyPartMessage.cs | 4 +- MailKit/BodyPartMultipart.cs | 6 +-- MailKit/Envelope.cs | 47 ++++++++++++------------ MailKit/IMessageSummary.cs | 24 ++++++------ MailKit/MessageSummary.cs | 52 +++++++++++++------------- 7 files changed, 113 insertions(+), 109 deletions(-) diff --git a/MailKit/BodyPart.cs b/MailKit/BodyPart.cs index b20236984d..94b98dbba8 100644 --- a/MailKit/BodyPart.cs +++ b/MailKit/BodyPart.cs @@ -28,6 +28,7 @@ using System.Text; using System.Globalization; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using MimeKit; using MimeKit.Utils; @@ -103,7 +104,7 @@ internal static void Encode (StringBuilder builder, uint value) builder.Append (value.ToString (CultureInfo.InvariantCulture)); } - internal static void Encode (StringBuilder builder, string value) + internal static void Encode (StringBuilder builder, string? value) { if (value != null) MimeUtils.AppendQuoted (builder, value); @@ -111,7 +112,7 @@ internal static void Encode (StringBuilder builder, string value) builder.Append ("NIL"); } - internal static void Encode (StringBuilder builder, Uri location) + internal static void Encode (StringBuilder builder, Uri? location) { if (location != null) MimeUtils.AppendQuoted (builder, location.ToString ()); @@ -119,7 +120,7 @@ internal static void Encode (StringBuilder builder, Uri location) builder.Append ("NIL"); } - internal static void Encode (StringBuilder builder, string[] values) + internal static void Encode (StringBuilder builder, string[]? values) { if (values == null || values.Length == 0) { builder.Append ("NIL"); @@ -159,7 +160,7 @@ internal static void Encode (StringBuilder builder, IList parameters) builder.Append (')'); } - internal static void Encode (StringBuilder builder, ContentDisposition disposition) + internal static void Encode (StringBuilder builder, ContentDisposition? disposition) { if (disposition == null) { builder.Append ("NIL"); @@ -197,7 +198,7 @@ internal static void Encode (StringBuilder builder, BodyPartCollection parts) } } - internal static void Encode (StringBuilder builder, Envelope envelope) + internal static void Encode (StringBuilder builder, Envelope? envelope) { if (envelope == null) { builder.Append ("NIL"); @@ -207,7 +208,7 @@ internal static void Encode (StringBuilder builder, Envelope envelope) envelope.Encode (builder); } - internal static void Encode (StringBuilder builder, BodyPart body) + internal static void Encode (StringBuilder builder, BodyPart? body) { if (body == null) { builder.Append ("NIL"); @@ -268,7 +269,7 @@ static bool TryParse (string text, ref int index, out uint value) return index > startIndex; } - static bool TryParse (string text, ref int index, out string nstring) + static bool TryParse (string text, ref int index, out string? nstring) { nstring = null; @@ -316,7 +317,7 @@ static bool TryParse (string text, ref int index, out string nstring) return true; } - static bool TryParse (string text, ref int index, out string[] values) + static bool TryParse (string text, ref int index, out string[]? values) { values = null; @@ -346,7 +347,7 @@ static bool TryParse (string text, ref int index, out string[] values) if (text[index] == ')') break; - if (!TryParse (text, ref index, out string value)) + if (!TryParse (text, ref index, out string? value)) return false; list.Add (value); @@ -361,11 +362,11 @@ static bool TryParse (string text, ref int index, out string[] values) return true; } - static bool TryParse (string text, ref int index, out Uri uri) + static bool TryParse (string text, ref int index, out Uri? uri) { uri = null; - if (!TryParse (text, ref index, out string nstring)) + if (!TryParse (text, ref index, out string? nstring)) return false; if (!string.IsNullOrEmpty (nstring)) { @@ -378,7 +379,7 @@ static bool TryParse (string text, ref int index, out Uri uri) return true; } - static bool TryParse (string text, ref int index, out IList parameters) + static bool TryParse (string text, ref int index, [NotNullWhen (true)] out IList? parameters) { parameters = null; @@ -409,10 +410,10 @@ static bool TryParse (string text, ref int index, out IList parameter if (text[index] == ')') break; - if (!TryParse (text, ref index, out string name)) + if (!TryParse (text, ref index, out string? name) || name == null) return false; - if (!TryParse (text, ref index, out string value)) + if (!TryParse (text, ref index, out string? value) || value == null) return false; parameters.Add (new Parameter (name, value)); @@ -426,7 +427,7 @@ static bool TryParse (string text, ref int index, out IList parameter return true; } - static bool TryParse (string text, ref int index, out ContentDisposition disposition) + static bool TryParse (string text, ref int index, out ContentDisposition? disposition) { disposition = null; @@ -447,10 +448,10 @@ static bool TryParse (string text, ref int index, out ContentDisposition disposi index++; - if (!TryParse (text, ref index, out string value)) + if (!TryParse (text, ref index, out string? value) || value == null) return false; - if (!TryParse (text, ref index, out IList parameters)) + if (!TryParse (text, ref index, out IList? parameters)) return false; if (index >= text.Length || text[index] != ')') @@ -466,9 +467,9 @@ static bool TryParse (string text, ref int index, out ContentDisposition disposi return true; } - static bool TryParse (string text, ref int index, bool multipart, out ContentType contentType) + static bool TryParse (string text, ref int index, bool multipart, [NotNullWhen (true)] out ContentType? contentType) { - string type, subtype; + string? type, subtype; contentType = null; @@ -488,7 +489,7 @@ static bool TryParse (string text, ref int index, bool multipart, out ContentTyp if (!TryParse (text, ref index, out subtype)) return false; - if (!TryParse (text, ref index, out IList parameters)) + if (!TryParse (text, ref index, out IList? parameters)) return false; contentType = new ContentType (type ?? "application", subtype ?? "octet-stream"); @@ -499,7 +500,7 @@ static bool TryParse (string text, ref int index, bool multipart, out ContentTyp return true; } - static bool TryParse (string text, ref int index, string prefix, out IList children) + static bool TryParse (string text, ref int index, string prefix, [NotNullWhen (true)] out IList? children) { string path; int id = 1; @@ -517,7 +518,7 @@ static bool TryParse (string text, ref int index, string prefix, out IList children)) + if (!TryParse (text, ref index, prefix, out IList? children)) return false; foreach (var child in children) @@ -592,10 +593,10 @@ static bool TryParse (string text, ref int index, string path, out BodyPart part part = multipart; } else { - BodyPartMessage message = null; - BodyPartText txt = null; - BodyPartBasic basic; - string nstring; + BodyPartMessage? message = null; + BodyPartText? txt = null; + BodyPartBasic? basic; + string? nstring; if (!TryParse (text, ref index, false, out contentType)) return false; @@ -650,12 +651,12 @@ static bool TryParse (string text, ref int index, string path, out BodyPart part basic.ContentLocation = location; if (message != null) { - if (!Envelope.TryParse (text, ref index, out Envelope envelope)) + if (!Envelope.TryParse (text, ref index, out Envelope? envelope)) return false; message.Envelope = envelope; - if (!TryParse (text, ref index, path, out BodyPart body)) + if (!TryParse (text, ref index, path, out BodyPart? body)) return false; message.Body = body; @@ -698,7 +699,7 @@ static bool TryParse (string text, ref int index, string path, out BodyPart part /// /// is . /// - public static bool TryParse (string text, out BodyPart part) + public static bool TryParse (string text, out BodyPart? part) { if (text == null) throw new ArgumentNullException (nameof (text)); diff --git a/MailKit/BodyPartBasic.cs b/MailKit/BodyPartBasic.cs index f16cd9b126..86bb98b242 100644 --- a/MailKit/BodyPartBasic.cs +++ b/MailKit/BodyPartBasic.cs @@ -59,7 +59,7 @@ public BodyPartBasic () /// Gets the Content-Id of the body part, if available. /// /// The content identifier. - public string ContentId { + public string? ContentId { get; set; } @@ -70,7 +70,7 @@ public string ContentId { /// Gets the Content-Description of the body part, if available. /// /// The content description. - public string ContentDescription { + public string? ContentDescription { get; set; } @@ -83,7 +83,7 @@ public string ContentDescription { /// method to parse this value into a usable . /// /// The content transfer encoding. - public string ContentTransferEncoding { + public string? ContentTransferEncoding { get; set; } @@ -107,7 +107,7 @@ public uint Octets { /// Gets the MD5 hash of the content, if available. /// /// The content md5. - public string ContentMd5 { + public string? ContentMd5 { get; set; } @@ -121,7 +121,7 @@ public string ContentMd5 { /// summary information from an . /// /// The content disposition. - public ContentDisposition ContentDisposition { + public ContentDisposition? ContentDisposition { get; set; } @@ -135,7 +135,7 @@ public ContentDisposition ContentDisposition { /// summary information from an . /// /// The content language. - public string[] ContentLanguage { + public string[]? ContentLanguage { get; set; } @@ -149,7 +149,7 @@ public string[] ContentLanguage { /// summary information from an . /// /// The content location. - public Uri ContentLocation { + public Uri? ContentLocation { get; set; } @@ -179,9 +179,9 @@ public bool IsAttachment { /// fetching summary information from an . /// /// The name of the file. - public string FileName { + public string? FileName { get { - string filename = null; + string? filename = null; if (ContentDisposition != null) filename = ContentDisposition.FileName; diff --git a/MailKit/BodyPartMessage.cs b/MailKit/BodyPartMessage.cs index c13d0cd909..6fdca120c0 100644 --- a/MailKit/BodyPartMessage.cs +++ b/MailKit/BodyPartMessage.cs @@ -53,7 +53,7 @@ public BodyPartMessage () /// Gets the envelope of the message, if available. /// /// The envelope. - public Envelope Envelope { + public Envelope? Envelope { get; set; } @@ -64,7 +64,7 @@ public Envelope Envelope { /// Gets the body structure of the message. /// /// The body structure. - public BodyPart Body { + public BodyPart? Body { get; set; } diff --git a/MailKit/BodyPartMultipart.cs b/MailKit/BodyPartMultipart.cs index 427eb1ecfb..549e32ba95 100644 --- a/MailKit/BodyPartMultipart.cs +++ b/MailKit/BodyPartMultipart.cs @@ -67,7 +67,7 @@ public BodyPartCollection BodyParts { /// Gets the Content-Disposition of the body part, if available. /// /// The content disposition. - public ContentDisposition ContentDisposition { + public ContentDisposition? ContentDisposition { get; set; } @@ -78,7 +78,7 @@ public ContentDisposition ContentDisposition { /// Gets the Content-Language of the body part, if available. /// /// The content language. - public string[] ContentLanguage { + public string[]? ContentLanguage { get; set; } @@ -89,7 +89,7 @@ public string[] ContentLanguage { /// Gets the Content-Location of the body part, if available. /// /// The content location. - public Uri ContentLocation { + public Uri? ContentLocation { get; set; } diff --git a/MailKit/Envelope.cs b/MailKit/Envelope.cs index c7ef1f2467..9efeb3e959 100644 --- a/MailKit/Envelope.cs +++ b/MailKit/Envelope.cs @@ -28,6 +28,7 @@ using System.Text; using System.Linq; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using MimeKit; using MimeKit.Utils; @@ -136,7 +137,7 @@ public InternetAddressList Bcc { /// The Message-Id that the message is replying to. /// /// The Message-Id that the message is replying to. - public string InReplyTo { + public string? InReplyTo { get; set; } @@ -158,7 +159,7 @@ public DateTimeOffset? Date { /// Gets the ID of the message, if available. /// /// The message identifier. - public string MessageId { + public string? MessageId { get; set; } @@ -169,7 +170,7 @@ public string MessageId { /// Gets the subject of the message. /// /// The subject. - public string Subject { + public string? Subject { get; set; } @@ -351,7 +352,7 @@ static bool IsNIL (string text, int index) return string.Compare (text, index, "NIL", 0, 3, StringComparison.Ordinal) == 0; } - static bool TryParse (string text, ref int index, out string nstring) + static bool TryParse (string text, ref int index, out string? nstring) { nstring = null; @@ -399,7 +400,7 @@ static bool TryParse (string text, ref int index, out string nstring) return true; } - static bool TryParse (string text, ref int index, out InternetAddress addr) + static bool TryParse (string text, ref int index, [NotNullWhen (true)] out InternetAddress? addr) { addr = null; @@ -408,16 +409,16 @@ static bool TryParse (string text, ref int index, out InternetAddress addr) index++; - if (!TryParse (text, ref index, out string name)) + if (!TryParse (text, ref index, out string? name)) return false; - if (!TryParse (text, ref index, out string route)) + if (!TryParse (text, ref index, out string? route)) return false; - if (!TryParse (text, ref index, out string user)) + if (!TryParse (text, ref index, out string? user)) return false; - if (!TryParse (text, ref index, out string domain)) + if (!TryParse (text, ref index, out string? domain)) return false; while (index < text.Length && text[index] == ' ') @@ -445,7 +446,7 @@ static bool TryParse (string text, ref int index, out InternetAddress addr) return true; } - static bool TryParse (string text, ref int index, out InternetAddressList list) + static bool TryParse (string text, ref int index, [NotNullWhen (true)] out InternetAddressList? list) { list = null; @@ -480,7 +481,7 @@ static bool TryParse (string text, ref int index, out InternetAddressList list) if (text[index] == ')') break; - if (!TryParse (text, ref index, out InternetAddress addr)) + if (!TryParse (text, ref index, out InternetAddress? addr)) return false; if (addr != null) { @@ -510,7 +511,7 @@ static bool TryParse (string text, ref int index, out InternetAddressList list) return true; } - internal static bool TryParse (string text, ref int index, out Envelope envelope) + internal static bool TryParse (string text, ref int index, out Envelope? envelope) { DateTimeOffset? date = null; @@ -530,7 +531,7 @@ internal static bool TryParse (string text, ref int index, out Envelope envelope index++; - if (!TryParse (text, ref index, out string nstring)) + if (!TryParse (text, ref index, out string? nstring)) return false; if (nstring != null) { @@ -540,31 +541,31 @@ internal static bool TryParse (string text, ref int index, out Envelope envelope date = value; } - if (!TryParse (text, ref index, out string subject)) + if (!TryParse (text, ref index, out string? subject)) return false; - if (!TryParse (text, ref index, out InternetAddressList from)) + if (!TryParse (text, ref index, out InternetAddressList? from)) return false; - if (!TryParse (text, ref index, out InternetAddressList sender)) + if (!TryParse (text, ref index, out InternetAddressList? sender)) return false; - if (!TryParse (text, ref index, out InternetAddressList replyto)) + if (!TryParse (text, ref index, out InternetAddressList? replyto)) return false; - if (!TryParse (text, ref index, out InternetAddressList to)) + if (!TryParse (text, ref index, out InternetAddressList? to)) return false; - if (!TryParse (text, ref index, out InternetAddressList cc)) + if (!TryParse (text, ref index, out InternetAddressList? cc)) return false; - if (!TryParse (text, ref index, out InternetAddressList bcc)) + if (!TryParse (text, ref index, out InternetAddressList? bcc)) return false; - if (!TryParse (text, ref index, out string inreplyto)) + if (!TryParse (text, ref index, out string? inreplyto)) return false; - if (!TryParse (text, ref index, out string messageid)) + if (!TryParse (text, ref index, out string? messageid)) return false; if (index >= text.Length || text[index] != ')') @@ -602,7 +603,7 @@ internal static bool TryParse (string text, ref int index, out Envelope envelope /// /// is . /// - public static bool TryParse (string text, out Envelope envelope) + public static bool TryParse (string text, out Envelope? envelope) { if (text == null) throw new ArgumentNullException (nameof (text)); diff --git a/MailKit/IMessageSummary.cs b/MailKit/IMessageSummary.cs index c5444d8fa7..5aa9969102 100644 --- a/MailKit/IMessageSummary.cs +++ b/MailKit/IMessageSummary.cs @@ -55,7 +55,7 @@ public interface IMessageSummary /// Gets the folder that the message belongs to, if available. /// /// The folder. - IMailFolder Folder { + IMailFolder? Folder { get; } @@ -83,7 +83,7 @@ IMailFolder Folder { /// methods. /// /// The body structure of the message. - BodyPart Body { get; } + BodyPart? Body { get; } /// /// Gets the text body part of the message if it exists. @@ -100,7 +100,7 @@ IMailFolder Folder { /// /// /// The text body if it exists; otherwise, . - BodyPartText TextBody { get; } + BodyPartText? TextBody { get; } /// /// Gets the html body part of the message if it exists. @@ -117,7 +117,7 @@ IMailFolder Folder { /// /// /// The html body if it exists; otherwise, . - BodyPartText HtmlBody { get; } + BodyPartText? HtmlBody { get; } /// /// Gets the body parts of the message. @@ -168,7 +168,7 @@ IMailFolder Folder { /// methods. /// /// The preview text. - string PreviewText { get; } + string? PreviewText { get; } /// /// Gets the envelope of the message, if available. @@ -186,7 +186,7 @@ IMailFolder Folder { /// methods. /// /// The envelope of the message. - Envelope Envelope { get; } + Envelope? Envelope { get; } /// /// Gets the normalized subject. @@ -257,7 +257,7 @@ IMailFolder Folder { /// methods. /// /// The message annotations. - IReadOnlyList Annotations { get; } + IReadOnlyList? Annotations { get; } /// /// Gets the list of headers, if available. @@ -271,7 +271,7 @@ IMailFolder Folder { /// /// /// The list of headers. - HeaderList Headers { get; } + HeaderList? Headers { get; } /// /// Gets the internal date of the message, if available. @@ -341,7 +341,7 @@ IMailFolder Folder { /// methods. /// /// The references. - MessageIdList References { get; } + MessageIdList? References { get; } /// /// Get the globally unique identifier for the message, if available. @@ -357,7 +357,7 @@ IMailFolder Folder { /// OBJECTID extension. /// /// The globally unique message identifier. - string EmailId { get; } + string? EmailId { get; } /// /// Get the globally unique thread identifier for the message, if available. @@ -373,7 +373,7 @@ IMailFolder Folder { /// OBJECTID extension. /// /// The globally unique thread identifier. - string ThreadId { get; } + string? ThreadId { get; } /// /// Gets the unique identifier of the message, if available. @@ -441,7 +441,7 @@ IMailFolder Folder { /// methods. /// /// The GMail labels. - IList GMailLabels { get; } + IList? GMailLabels { get; } #endregion } diff --git a/MailKit/MessageSummary.cs b/MailKit/MessageSummary.cs index 29b18f9e70..9f017dd1aa 100644 --- a/MailKit/MessageSummary.cs +++ b/MailKit/MessageSummary.cs @@ -27,6 +27,7 @@ using System; using System.Linq; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using MimeKit; using MimeKit.Utils; @@ -50,9 +51,9 @@ namespace MailKit { /// public class MessageSummary : IMessageSummary { - IReadOnlySetOfStrings keywords; + IReadOnlySetOfStrings? keywords; int threadableReplyDepth = -1; - string normalizedSubject; + string? normalizedSubject; /// /// Initializes a new instance of the class. @@ -94,6 +95,7 @@ public MessageSummary (IMailFolder folder, int index) : this (index) Folder = folder; } + [MemberNotNull ("normalizedSubject")] void UpdateThreadableSubject () { if (normalizedSubject != null) @@ -114,7 +116,7 @@ void UpdateThreadableSubject () /// Gets the folder that the message belongs to, if available. /// /// The folder. - public IMailFolder Folder { + public IMailFolder? Folder { get; private set; } @@ -144,14 +146,14 @@ public MessageSummaryItems Fields { /// methods. /// /// The body structure of the message. - public BodyPart Body { + public BodyPart? Body { get; set; } - static BodyPart GetMultipartRelatedRoot (BodyPartMultipart related) + static BodyPart? GetMultipartRelatedRoot (BodyPartMultipart related) { - string start = related.ContentType.Parameters["start"]; - string contentId; + string? start = related.ContentType.Parameters["start"]; + string? contentId; if (start == null) return related.BodyParts.Count > 0 ? related.BodyParts[0] : null; @@ -172,11 +174,11 @@ static BodyPart GetMultipartRelatedRoot (BodyPartMultipart related) return null; } - static bool TryGetMultipartAlternativeBody (BodyPartMultipart multipart, bool html, out BodyPartText body) + static bool TryGetMultipartAlternativeBody (BodyPartMultipart multipart, bool html, [NotNullWhen (true)] out BodyPartText? body) { // walk the multipart/alternative children backwards from greatest level of faithfulness to the least faithful for (int i = multipart.BodyParts.Count - 1; i >= 0; i--) { - BodyPartText text = null; + BodyPartText? text = null; if (multipart.BodyParts[i] is BodyPartMultipart multi) { if (multi.ContentType.IsMimeType ("multipart", "related")) { @@ -201,10 +203,10 @@ static bool TryGetMultipartAlternativeBody (BodyPartMultipart multipart, bool ht return false; } - static bool TryGetMessageBody (BodyPartMultipart multipart, bool html, out BodyPartText body) + static bool TryGetMessageBody (BodyPartMultipart multipart, bool html, [NotNullWhen (true)] out BodyPartText? body) { - BodyPartMultipart multi; - BodyPartText text; + BodyPartMultipart? multi; + BodyPartText? text; if (multipart.ContentType.IsMimeType ("multipart", "alternative")) return TryGetMultipartAlternativeBody (multipart, html, out body); @@ -276,10 +278,10 @@ static bool TryGetMessageBody (BodyPartMultipart multipart, bool html, out BodyP /// /// /// The text body if it exists; otherwise, . - public BodyPartText TextBody { + public BodyPartText? TextBody { get { if (Body is BodyPartMultipart multipart) { - if (TryGetMessageBody (multipart, false, out BodyPartText plain)) + if (TryGetMessageBody (multipart, false, out BodyPartText? plain)) return plain; } else { if (Body is BodyPartText text && text.IsPlain) @@ -305,10 +307,10 @@ public BodyPartText TextBody { /// /// /// The html body if it exists; otherwise, . - public BodyPartText HtmlBody { + public BodyPartText? HtmlBody { get { if (Body is BodyPartMultipart multipart) { - if (TryGetMessageBody (multipart, true, out BodyPartText html)) + if (TryGetMessageBody (multipart, true, out BodyPartText? html)) return html; } else { if (Body is BodyPartText text && text.IsHtml) @@ -319,7 +321,7 @@ public BodyPartText HtmlBody { } } - static IEnumerable EnumerateBodyParts (BodyPart entity, bool attachmentsOnly) + static IEnumerable EnumerateBodyParts (BodyPart? entity, bool attachmentsOnly) { if (entity == null) yield break; @@ -394,7 +396,7 @@ public IEnumerable Attachments { /// methods. /// /// The preview text. - public string PreviewText { + public string? PreviewText { get; set; } @@ -414,7 +416,7 @@ public string PreviewText { /// methods. /// /// The envelope of the message. - public Envelope Envelope { + public Envelope? Envelope { get; set; } @@ -512,7 +514,7 @@ public IReadOnlySetOfStrings Keywords { /// methods. /// /// The message annotations. - public IReadOnlyList Annotations { + public IReadOnlyList? Annotations { get; set; } @@ -528,7 +530,7 @@ public IReadOnlyList Annotations { /// /// /// The list of headers. - public HeaderList Headers { + public HeaderList? Headers { get; set; } @@ -608,7 +610,7 @@ public ulong? ModSeq { /// methods. /// /// The references. - public MessageIdList References { + public MessageIdList? References { get; set; } @@ -626,7 +628,7 @@ public MessageIdList References { /// OBJECTID extension. /// /// The globally unique message identifier. - public string EmailId { + public string? EmailId { get; set; } @@ -644,7 +646,7 @@ public string EmailId { /// OBJECTID extension. /// /// The globally unique thread identifier. - public string ThreadId { + public string? ThreadId { get; set; } @@ -722,7 +724,7 @@ public ulong? GMailThreadId { /// methods. /// /// The GMail labels. - public IList GMailLabels { + public IList? GMailLabels { get; set; } From 3c08fcd824f47d857e1631418c4771e006d85838 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Thu, 19 Feb 2026 10:30:43 -0500 Subject: [PATCH 02/44] Updated ClientMetrics for nullability --- MailKit/Net/ClientMetrics.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MailKit/Net/ClientMetrics.cs b/MailKit/Net/ClientMetrics.cs index 459b99b589..437a5f1a0b 100644 --- a/MailKit/Net/ClientMetrics.cs +++ b/MailKit/Net/ClientMetrics.cs @@ -101,7 +101,7 @@ static bool TryGetErrorType (Exception exception, out string errorType) return true; } - internal static TagList GetTags (Uri uri, Exception ex) + internal static TagList GetTags (Uri uri, Exception? ex) { var tags = new TagList { { "url.scheme", uri.Scheme }, @@ -115,7 +115,7 @@ internal static TagList GetTags (Uri uri, Exception ex) return tags; } - public void RecordClientDisconnected (long startTimestamp, Uri uri, Exception ex = null) + public void RecordClientDisconnected (long startTimestamp, Uri uri, Exception? ex = null) { if (ConnectionDuration.Enabled) { var duration = TimeSpan.FromTicks (Stopwatch.GetTimestamp () - startTimestamp).TotalSeconds; From 78491d7f423db86dcf75d9c5b39f43c3daf3cb47 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Thu, 19 Feb 2026 10:33:10 -0500 Subject: [PATCH 03/44] Updated FolderQuota for nullability --- MailKit/FolderQuota.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MailKit/FolderQuota.cs b/MailKit/FolderQuota.cs index 8f12d706a3..89883c1d11 100644 --- a/MailKit/FolderQuota.cs +++ b/MailKit/FolderQuota.cs @@ -43,7 +43,7 @@ public class FolderQuota /// Creates a new with the specified root. /// /// The quota root. - public FolderQuota (IMailFolder quotaRoot) + public FolderQuota (IMailFolder? quotaRoot) { QuotaRoot = quotaRoot; } @@ -59,7 +59,7 @@ public FolderQuota (IMailFolder quotaRoot) /// /// /// The quota root. - public IMailFolder QuotaRoot { + public IMailFolder? QuotaRoot { get; private set; } From f6eb6b5c9af539e2c61eb610be02f9635360c8a6 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Thu, 19 Feb 2026 10:34:20 -0500 Subject: [PATCH 04/44] Started working on nullability for the IMAP API --- MailKit/IMailFolder.cs | 98 ++++++++-------- MailKit/IMailStore.cs | 8 +- MailKit/MailFolder.cs | 100 ++++++++-------- MailKit/MailKit.csproj | 1 + MailKit/MailStore.cs | 8 +- MailKit/Net/Imap/ImapClient.cs | 34 +++--- MailKit/Net/Imap/ImapCommand.cs | 23 ++-- MailKit/Net/Imap/ImapEngine.cs | 83 +++++++------- MailKit/Net/Imap/ImapFolder.cs | 153 +++++++++++++------------ MailKit/Net/Imap/ImapFolderFetch.cs | 135 +++++++++++----------- MailKit/Net/Imap/ImapFolderFlags.cs | 14 +-- MailKit/Net/Imap/ImapFolderSearch.cs | 105 ++++++++--------- MailKit/Net/Imap/ImapImplementation.cs | 28 ++--- MailKit/Net/Imap/ImapUtils.cs | 67 +++++------ 14 files changed, 436 insertions(+), 421 deletions(-) diff --git a/MailKit/IMailFolder.cs b/MailKit/IMailFolder.cs index f6bba8b700..ac57cef729 100644 --- a/MailKit/IMailFolder.cs +++ b/MailKit/IMailFolder.cs @@ -205,7 +205,7 @@ public interface IMailFolder : IEnumerable /// OBJECTID extension. /// /// The unique folder identifier. - string Id { get; } + string? Id { get; } /// /// Get whether or not the folder is subscribed. @@ -650,7 +650,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - IMailFolder Create (string name, bool isMessageFolder, CancellationToken cancellationToken = default); + IMailFolder? Create (string name, bool isMessageFolder, CancellationToken cancellationToken = default); /// /// Asynchronously create a new subfolder with the given name. @@ -692,7 +692,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task CreateAsync (string name, bool isMessageFolder, CancellationToken cancellationToken = default); + Task CreateAsync (string name, bool isMessageFolder, CancellationToken cancellationToken = default); /// /// Create a new subfolder with the given name. @@ -739,7 +739,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - IMailFolder Create (string name, IEnumerable specialUses, CancellationToken cancellationToken = default); + IMailFolder? Create (string name, IEnumerable specialUses, CancellationToken cancellationToken = default); /// /// Asynchronously create a new subfolder with the given name. @@ -786,7 +786,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task CreateAsync (string name, IEnumerable specialUses, CancellationToken cancellationToken = default); + Task CreateAsync (string name, IEnumerable specialUses, CancellationToken cancellationToken = default); /// /// Create a new subfolder with the given name. @@ -831,7 +831,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - IMailFolder Create (string name, SpecialFolder specialUse, CancellationToken cancellationToken = default); + IMailFolder? Create (string name, SpecialFolder specialUse, CancellationToken cancellationToken = default); /// /// Asynchronously create a new subfolder with the given name. @@ -876,7 +876,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task CreateAsync (string name, SpecialFolder specialUse, CancellationToken cancellationToken = default); + Task CreateAsync (string name, SpecialFolder specialUse, CancellationToken cancellationToken = default); /// /// Rename the folder. @@ -2262,7 +2262,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - string GetMetadata (MetadataTag tag, CancellationToken cancellationToken = default); + string? GetMetadata (MetadataTag tag, CancellationToken cancellationToken = default); /// /// Asynchronously gets the specified metadata. @@ -2297,7 +2297,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task GetMetadataAsync (MetadataTag tag, CancellationToken cancellationToken = default); + Task GetMetadataAsync (MetadataTag tag, CancellationToken cancellationToken = default); /// /// Gets the specified metadata. @@ -4617,7 +4617,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - HeaderList GetHeaders (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress progress = null); + HeaderList GetHeaders (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the specified message headers. @@ -4659,7 +4659,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task GetHeadersAsync (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetHeadersAsync (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the specified body part headers. @@ -4705,7 +4705,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - HeaderList GetHeaders (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null); + HeaderList GetHeaders (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the specified body part headers. @@ -4751,7 +4751,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task GetHeadersAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetHeadersAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the specified message headers. @@ -4793,7 +4793,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - HeaderList GetHeaders (int index, CancellationToken cancellationToken = default, ITransferProgress progress = null); + HeaderList GetHeaders (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the specified message headers. @@ -4835,7 +4835,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task GetHeadersAsync (int index, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetHeadersAsync (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the specified body part headers. @@ -4881,7 +4881,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - HeaderList GetHeaders (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null); + HeaderList GetHeaders (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the specified body part headers. @@ -4927,7 +4927,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task GetHeadersAsync (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetHeadersAsync (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the specified message. @@ -4972,7 +4972,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - MimeMessage GetMessage (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress progress = null); + MimeMessage GetMessage (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the specified message. @@ -5017,7 +5017,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task GetMessageAsync (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetMessageAsync (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the specified message. @@ -5062,7 +5062,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - MimeMessage GetMessage (int index, CancellationToken cancellationToken = default, ITransferProgress progress = null); + MimeMessage GetMessage (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the specified message. @@ -5107,7 +5107,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task GetMessageAsync (int index, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetMessageAsync (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the specified body part. @@ -5156,7 +5156,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - MimeEntity GetBodyPart (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null); + MimeEntity GetBodyPart (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the specified body part. @@ -5205,7 +5205,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task GetBodyPartAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetBodyPartAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the specified body part. @@ -5251,7 +5251,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - MimeEntity GetBodyPart (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null); + MimeEntity GetBodyPart (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the specified body part. @@ -5297,7 +5297,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task GetBodyPartAsync (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetBodyPartAsync (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get a message stream. @@ -5342,7 +5342,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Stream GetStream (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Stream GetStream (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get a message stream. @@ -5387,7 +5387,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task GetStreamAsync (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetStreamAsync (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get a message stream. @@ -5432,7 +5432,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Stream GetStream (int index, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Stream GetStream (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get a message stream. @@ -5477,7 +5477,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task GetStreamAsync (int index, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetStreamAsync (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get a substream of the specified message. @@ -5529,7 +5529,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Stream GetStream (UniqueId uid, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Stream GetStream (UniqueId uid, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get a substream of the specified message. @@ -5581,7 +5581,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task GetStreamAsync (UniqueId uid, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetStreamAsync (UniqueId uid, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get a substream of the specified message. @@ -5632,7 +5632,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Stream GetStream (int index, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Stream GetStream (int index, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get a substream of the specified message. @@ -5683,7 +5683,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task GetStreamAsync (int index, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetStreamAsync (int index, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get a body part as a stream. @@ -5732,7 +5732,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Stream GetStream (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Stream GetStream (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get a body part as a stream. @@ -5781,7 +5781,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task GetStreamAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetStreamAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get a body part as a stream. @@ -5827,7 +5827,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Stream GetStream (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Stream GetStream (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get a body part as a stream. @@ -5873,7 +5873,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task GetStreamAsync (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetStreamAsync (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get a substream of the specified body part. @@ -5929,7 +5929,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Stream GetStream (UniqueId uid, BodyPart part, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Stream GetStream (UniqueId uid, BodyPart part, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get a substream of the specified body part. @@ -5985,7 +5985,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task GetStreamAsync (UniqueId uid, BodyPart part, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetStreamAsync (UniqueId uid, BodyPart part, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get a substream of the specified body part. @@ -6040,7 +6040,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Stream GetStream (int index, BodyPart part, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Stream GetStream (int index, BodyPart part, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get a substream of the specified body part. @@ -6095,7 +6095,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task GetStreamAsync (int index, BodyPart part, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetStreamAsync (int index, BodyPart part, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get a substream of the specified message. @@ -6146,7 +6146,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Stream GetStream (UniqueId uid, string section, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Stream GetStream (UniqueId uid, string section, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get a substream of the specified message. @@ -6197,7 +6197,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task GetStreamAsync (UniqueId uid, string section, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetStreamAsync (UniqueId uid, string section, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get a substream of the specified message. @@ -6255,7 +6255,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Stream GetStream (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Stream GetStream (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get a substream of the specified message. @@ -6313,7 +6313,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task GetStreamAsync (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetStreamAsync (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get a substream of the specified message. @@ -6361,7 +6361,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Stream GetStream (int index, string section, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Stream GetStream (int index, string section, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get a substream of the specified message. @@ -6409,7 +6409,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task GetStreamAsync (int index, string section, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetStreamAsync (int index, string section, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get a substream of the specified message. @@ -6466,7 +6466,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Stream GetStream (int index, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Stream GetStream (int index, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get a substream of the specified message. @@ -6523,7 +6523,7 @@ public interface IMailFolder : IEnumerable /// /// The command failed. /// - Task GetStreamAsync (int index, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetStreamAsync (int index, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Store message flags and keywords for a message. diff --git a/MailKit/IMailStore.cs b/MailKit/IMailStore.cs index 7c3cc8364a..46c305c6b1 100644 --- a/MailKit/IMailStore.cs +++ b/MailKit/IMailStore.cs @@ -97,7 +97,7 @@ public interface IMailStore : IMailService /// where all new messages are delivered. /// /// The Inbox folder. - IMailFolder Inbox { get; } + IMailFolder? Inbox { get; } /// /// Enable the quick resynchronization feature. @@ -198,7 +198,7 @@ public interface IMailStore : IMailService /// /// is out of range. /// - IMailFolder GetFolder (SpecialFolder folder); + IMailFolder? GetFolder (SpecialFolder folder); /// /// Get the folder for the specified namespace. @@ -442,7 +442,7 @@ public interface IMailStore : IMailService /// The requested metadata value. /// The metadata tag. /// The cancellation token. - string GetMetadata (MetadataTag tag, CancellationToken cancellationToken = default); + string? GetMetadata (MetadataTag tag, CancellationToken cancellationToken = default); /// /// Asynchronously gets the specified metadata. @@ -453,7 +453,7 @@ public interface IMailStore : IMailService /// The requested metadata value. /// The metadata tag. /// The cancellation token. - Task GetMetadataAsync (MetadataTag tag, CancellationToken cancellationToken = default); + Task GetMetadataAsync (MetadataTag tag, CancellationToken cancellationToken = default); /// /// Gets the specified metadata. diff --git a/MailKit/MailFolder.cs b/MailKit/MailFolder.cs index dc078e1882..d20089b74e 100644 --- a/MailKit/MailFolder.cs +++ b/MailKit/MailFolder.cs @@ -271,7 +271,7 @@ public string Name { /// OBJECTID extension. /// /// The unique folder identifier. - public string Id { + public string? Id { get; protected set; } @@ -746,7 +746,7 @@ public int Count { /// /// The command failed. /// - public abstract IMailFolder Create (string name, bool isMessageFolder, CancellationToken cancellationToken = default); + public abstract IMailFolder? Create (string name, bool isMessageFolder, CancellationToken cancellationToken = default); /// /// Asynchronously create a new subfolder with the given name. @@ -788,7 +788,7 @@ public int Count { /// /// The command failed. /// - public abstract Task CreateAsync (string name, bool isMessageFolder, CancellationToken cancellationToken = default); + public abstract Task CreateAsync (string name, bool isMessageFolder, CancellationToken cancellationToken = default); /// /// Create a new subfolder with the given name. @@ -835,7 +835,7 @@ public int Count { /// /// The command failed. /// - public abstract IMailFolder Create (string name, IEnumerable specialUses, CancellationToken cancellationToken = default); + public abstract IMailFolder? Create (string name, IEnumerable specialUses, CancellationToken cancellationToken = default); /// /// Asynchronously create a new subfolder with the given name. @@ -882,7 +882,7 @@ public int Count { /// /// The command failed. /// - public abstract Task CreateAsync (string name, IEnumerable specialUses, CancellationToken cancellationToken = default); + public abstract Task CreateAsync (string name, IEnumerable specialUses, CancellationToken cancellationToken = default); /// /// Create a new subfolder with the given name. @@ -927,7 +927,7 @@ public int Count { /// /// The command failed. /// - public virtual IMailFolder Create (string name, SpecialFolder specialUse, CancellationToken cancellationToken = default) + public virtual IMailFolder? Create (string name, SpecialFolder specialUse, CancellationToken cancellationToken = default) { return Create (name, new [] { specialUse }, cancellationToken); } @@ -975,7 +975,7 @@ public virtual IMailFolder Create (string name, SpecialFolder specialUse, Cancel /// /// The command failed. /// - public virtual Task CreateAsync (string name, SpecialFolder specialUse, CancellationToken cancellationToken = default) + public virtual Task CreateAsync (string name, SpecialFolder specialUse, CancellationToken cancellationToken = default) { return CreateAsync (name, new [] { specialUse }, cancellationToken); } @@ -2360,7 +2360,7 @@ public virtual Task> GetSubfoldersAsync (bool subscribedOnly /// /// The command failed. /// - public abstract string GetMetadata (MetadataTag tag, CancellationToken cancellationToken = default); + public abstract string? GetMetadata (MetadataTag tag, CancellationToken cancellationToken = default); /// /// Asynchronously gets the specified metadata. @@ -2395,7 +2395,7 @@ public virtual Task> GetSubfoldersAsync (bool subscribedOnly /// /// The command failed. /// - public abstract Task GetMetadataAsync (MetadataTag tag, CancellationToken cancellationToken = default); + public abstract Task GetMetadataAsync (MetadataTag tag, CancellationToken cancellationToken = default); /// /// Get the specified metadata. @@ -4801,7 +4801,7 @@ public virtual Task MoveToAsync (int index, IMailFolder destination, Cancellatio /// /// The command failed. /// - public abstract HeaderList GetHeaders (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract HeaderList GetHeaders (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the specified message headers. @@ -4843,7 +4843,7 @@ public virtual Task MoveToAsync (int index, IMailFolder destination, Cancellatio /// /// The command failed. /// - public abstract Task GetHeadersAsync (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Task GetHeadersAsync (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the specified body part headers. @@ -4889,7 +4889,7 @@ public virtual Task MoveToAsync (int index, IMailFolder destination, Cancellatio /// /// The command failed. /// - public abstract HeaderList GetHeaders (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract HeaderList GetHeaders (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the specified body part headers. @@ -4935,7 +4935,7 @@ public virtual Task MoveToAsync (int index, IMailFolder destination, Cancellatio /// /// The command failed. /// - public abstract Task GetHeadersAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Task GetHeadersAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the specified message headers. @@ -4977,7 +4977,7 @@ public virtual Task MoveToAsync (int index, IMailFolder destination, Cancellatio /// /// The command failed. /// - public abstract HeaderList GetHeaders (int index, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract HeaderList GetHeaders (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the specified message headers. @@ -5019,7 +5019,7 @@ public virtual Task MoveToAsync (int index, IMailFolder destination, Cancellatio /// /// The command failed. /// - public abstract Task GetHeadersAsync (int index, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Task GetHeadersAsync (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the specified body part headers. @@ -5065,7 +5065,7 @@ public virtual Task MoveToAsync (int index, IMailFolder destination, Cancellatio /// /// The command failed. /// - public abstract HeaderList GetHeaders (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract HeaderList GetHeaders (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the specified body part headers. @@ -5111,7 +5111,7 @@ public virtual Task MoveToAsync (int index, IMailFolder destination, Cancellatio /// /// The command failed. /// - public abstract Task GetHeadersAsync (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Task GetHeadersAsync (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the specified message. @@ -5156,7 +5156,7 @@ public virtual Task MoveToAsync (int index, IMailFolder destination, Cancellatio /// /// The command failed. /// - public abstract MimeMessage GetMessage (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract MimeMessage GetMessage (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the specified message. @@ -5201,7 +5201,7 @@ public virtual Task MoveToAsync (int index, IMailFolder destination, Cancellatio /// /// The command failed. /// - public abstract Task GetMessageAsync (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Task GetMessageAsync (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the specified message. @@ -5246,7 +5246,7 @@ public virtual Task MoveToAsync (int index, IMailFolder destination, Cancellatio /// /// The command failed. /// - public abstract MimeMessage GetMessage (int index, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract MimeMessage GetMessage (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the specified message. @@ -5291,7 +5291,7 @@ public virtual Task MoveToAsync (int index, IMailFolder destination, Cancellatio /// /// The command failed. /// - public abstract Task GetMessageAsync (int index, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Task GetMessageAsync (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the specified body part. @@ -5340,7 +5340,7 @@ public virtual Task MoveToAsync (int index, IMailFolder destination, Cancellatio /// /// The command failed. /// - public abstract MimeEntity GetBodyPart (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract MimeEntity GetBodyPart (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the specified body part. @@ -5386,7 +5386,7 @@ public virtual Task MoveToAsync (int index, IMailFolder destination, Cancellatio /// /// The command failed. /// - public abstract Task GetBodyPartAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Task GetBodyPartAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the specified body part. @@ -5432,7 +5432,7 @@ public virtual Task MoveToAsync (int index, IMailFolder destination, Cancellatio /// /// The command failed. /// - public abstract MimeEntity GetBodyPart (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract MimeEntity GetBodyPart (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the specified body part. @@ -5478,7 +5478,7 @@ public virtual Task MoveToAsync (int index, IMailFolder destination, Cancellatio /// /// The command failed. /// - public abstract Task GetBodyPartAsync (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Task GetBodyPartAsync (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get a message stream. @@ -5523,7 +5523,7 @@ public virtual Task MoveToAsync (int index, IMailFolder destination, Cancellatio /// /// The command failed. /// - public virtual Stream GetStream (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual Stream GetStream (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return GetStream (uid, string.Empty, cancellationToken, progress); } @@ -5571,7 +5571,7 @@ public virtual Stream GetStream (UniqueId uid, CancellationToken cancellationTok /// /// The command failed. /// - public virtual Task GetStreamAsync (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual Task GetStreamAsync (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return GetStreamAsync (uid, string.Empty, cancellationToken, progress); } @@ -5619,7 +5619,7 @@ public virtual Task GetStreamAsync (UniqueId uid, CancellationToken canc /// /// The command failed. /// - public virtual Stream GetStream (int index, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual Stream GetStream (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return GetStream (index, string.Empty, cancellationToken, progress); } @@ -5667,7 +5667,7 @@ public virtual Stream GetStream (int index, CancellationToken cancellationToken /// /// The command failed. /// - public virtual Task GetStreamAsync (int index, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual Task GetStreamAsync (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return GetStreamAsync (index, string.Empty, cancellationToken, progress); } @@ -5722,7 +5722,7 @@ public virtual Task GetStreamAsync (int index, CancellationToken cancell /// /// The command failed. /// - public abstract Stream GetStream (UniqueId uid, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Stream GetStream (UniqueId uid, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get a substream of the specified message. @@ -5774,7 +5774,7 @@ public virtual Task GetStreamAsync (int index, CancellationToken cancell /// /// The command failed. /// - public abstract Task GetStreamAsync (UniqueId uid, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Task GetStreamAsync (UniqueId uid, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get a substream of the specified message. @@ -5825,7 +5825,7 @@ public virtual Task GetStreamAsync (int index, CancellationToken cancell /// /// The command failed. /// - public abstract Stream GetStream (int index, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Stream GetStream (int index, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get a substream of the specified message. @@ -5876,7 +5876,7 @@ public virtual Task GetStreamAsync (int index, CancellationToken cancell /// /// The command failed. /// - public abstract Task GetStreamAsync (int index, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Task GetStreamAsync (int index, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get a body part as a stream. @@ -5925,7 +5925,7 @@ public virtual Task GetStreamAsync (int index, CancellationToken cancell /// /// The command failed. /// - public virtual Stream GetStream (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual Stream GetStream (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (!uid.IsValid) throw new ArgumentException ("The uid is invalid.", nameof (uid)); @@ -5983,7 +5983,7 @@ public virtual Stream GetStream (UniqueId uid, BodyPart part, CancellationToken /// /// The command failed. /// - public virtual Task GetStreamAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual Task GetStreamAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (!uid.IsValid) throw new ArgumentException ("The uid is invalid.", nameof (uid)); @@ -6038,7 +6038,7 @@ public virtual Task GetStreamAsync (UniqueId uid, BodyPart part, Cancell /// /// The command failed. /// - public virtual Stream GetStream (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual Stream GetStream (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); @@ -6093,7 +6093,7 @@ public virtual Stream GetStream (int index, BodyPart part, CancellationToken can /// /// The command failed. /// - public virtual Task GetStreamAsync (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual Task GetStreamAsync (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); @@ -6158,7 +6158,7 @@ public virtual Task GetStreamAsync (int index, BodyPart part, Cancellati /// /// The command failed. /// - public virtual Stream GetStream (UniqueId uid, BodyPart part, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual Stream GetStream (UniqueId uid, BodyPart part, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (!uid.IsValid) throw new ArgumentException ("The uid is invalid.", nameof (uid)); @@ -6229,7 +6229,7 @@ public virtual Stream GetStream (UniqueId uid, BodyPart part, int offset, int co /// /// The command failed. /// - public virtual Task GetStreamAsync (UniqueId uid, BodyPart part, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual Task GetStreamAsync (UniqueId uid, BodyPart part, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (!uid.IsValid) throw new ArgumentException ("The uid is invalid.", nameof (uid)); @@ -6299,7 +6299,7 @@ public virtual Task GetStreamAsync (UniqueId uid, BodyPart part, int off /// /// The command failed. /// - public virtual Stream GetStream (int index, BodyPart part, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual Stream GetStream (int index, BodyPart part, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); @@ -6369,7 +6369,7 @@ public virtual Stream GetStream (int index, BodyPart part, int offset, int count /// /// The command failed. /// - public virtual Task GetStreamAsync (int index, BodyPart part, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual Task GetStreamAsync (int index, BodyPart part, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); @@ -6435,7 +6435,7 @@ public virtual Task GetStreamAsync (int index, BodyPart part, int offset /// /// The command failed. /// - public abstract Stream GetStream (UniqueId uid, string section, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Stream GetStream (UniqueId uid, string section, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get a substream of the specified message. @@ -6486,7 +6486,7 @@ public virtual Task GetStreamAsync (int index, BodyPart part, int offset /// /// The command failed. /// - public abstract Task GetStreamAsync (UniqueId uid, string section, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Task GetStreamAsync (UniqueId uid, string section, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get a substream of the specified message. @@ -6544,7 +6544,7 @@ public virtual Task GetStreamAsync (int index, BodyPart part, int offset /// /// The command failed. /// - public abstract Stream GetStream (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Stream GetStream (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get a substream of the specified message. @@ -6602,7 +6602,7 @@ public virtual Task GetStreamAsync (int index, BodyPart part, int offset /// /// The command failed. /// - public abstract Task GetStreamAsync (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Task GetStreamAsync (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get a substream of the specified message. @@ -6650,7 +6650,7 @@ public virtual Task GetStreamAsync (int index, BodyPart part, int offset /// /// The command failed. /// - public abstract Stream GetStream (int index, string section, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Stream GetStream (int index, string section, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get a substream of the specified body part. @@ -6698,7 +6698,7 @@ public virtual Task GetStreamAsync (int index, BodyPart part, int offset /// /// The command failed. /// - public abstract Task GetStreamAsync (int index, string section, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Task GetStreamAsync (int index, string section, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get a substream of the specified message. @@ -6755,7 +6755,7 @@ public virtual Task GetStreamAsync (int index, BodyPart part, int offset /// /// The command failed. /// - public abstract Stream GetStream (int index, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Stream GetStream (int index, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get a substream of the specified body part. @@ -6812,7 +6812,7 @@ public virtual Task GetStreamAsync (int index, BodyPart part, int offset /// /// The command failed. /// - public abstract Task GetStreamAsync (int index, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Task GetStreamAsync (int index, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Store message flags and keywords for a message. @@ -9442,7 +9442,7 @@ protected virtual void OnParentFolderRenamed () { } - void OnParentFolderRenamed (object sender, FolderRenamedEventArgs e) + void OnParentFolderRenamed (object? sender, FolderRenamedEventArgs e) { var oldFullName = FullName; diff --git a/MailKit/MailKit.csproj b/MailKit/MailKit.csproj index c5adfc7c02..583213e8c3 100644 --- a/MailKit/MailKit.csproj +++ b/MailKit/MailKit.csproj @@ -24,6 +24,7 @@ false false MailKit + enable true mailkit.snk true diff --git a/MailKit/MailStore.cs b/MailKit/MailStore.cs index 579a6fbb37..d344174bef 100644 --- a/MailKit/MailStore.cs +++ b/MailKit/MailStore.cs @@ -120,7 +120,7 @@ public abstract HashSet ThreadingAlgorithms { /// This property will only be available after the client has been authenticated. /// /// The Inbox folder. - public abstract IMailFolder Inbox { + public abstract IMailFolder? Inbox { get; } @@ -232,7 +232,7 @@ public abstract IMailFolder Inbox { /// /// The is not authenticated. /// - public abstract IMailFolder GetFolder (SpecialFolder folder); + public abstract IMailFolder? GetFolder (SpecialFolder folder); /// /// Get the folder for the specified namespace. @@ -520,7 +520,7 @@ public virtual Task> GetFoldersAsync (FolderNamespace @namesp /// /// The command failed. /// - public abstract string GetMetadata (MetadataTag tag, CancellationToken cancellationToken = default); + public abstract string? GetMetadata (MetadataTag tag, CancellationToken cancellationToken = default); /// /// Asynchronously gets the specified metadata. @@ -555,7 +555,7 @@ public virtual Task> GetFoldersAsync (FolderNamespace @namesp /// /// The command failed. /// - public abstract Task GetMetadataAsync (MetadataTag tag, CancellationToken cancellationToken = default); + public abstract Task GetMetadataAsync (MetadataTag tag, CancellationToken cancellationToken = default); /// /// Gets the specified metadata. diff --git a/MailKit/Net/Imap/ImapClient.cs b/MailKit/Net/Imap/ImapClient.cs index 823fe874fe..10e045f3ff 100644 --- a/MailKit/Net/Imap/ImapClient.cs +++ b/MailKit/Net/Imap/ImapClient.cs @@ -35,6 +35,7 @@ using System.Threading.Tasks; using System.Collections.Generic; using System.Security.Authentication; +using System.Diagnostics.CodeAnalysis; using System.Security.Cryptography.X509Certificates; using MailKit.Security; @@ -258,7 +259,7 @@ protected virtual ImapFolder CreateImapFolder (ImapFolderConstructorArgs args) return folder; } - bool ValidateRemoteCertificate (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) + bool ValidateRemoteCertificate (object? sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { bool valid; @@ -1276,13 +1277,14 @@ public override void Authenticate (Encoding encoding, ICredentials credentials, try { int capabilitiesVersion = engine.CapabilitiesVersion; var uri = new Uri ("imap://" + engine.Uri.Host); - NetworkCredential cred; - ImapCommand ic = null; + NetworkCredential? cred; + ImapCommand? ic = null; SaslMechanism sasl; string id; foreach (var authmech in SaslMechanism.Rank (engine.AuthenticationMechanisms)) { - cred = credentials.GetCredential (uri, authmech); + if ((cred = credentials.GetCredential (uri, authmech)) == null) + continue; if ((sasl = SaslMechanism.Create (authmech, encoding, cred)) == null) continue; @@ -2252,7 +2254,7 @@ public override bool SupportsQuotas { /// /// The is not authenticated. /// - public override IMailFolder Inbox { + public override IMailFolder? Inbox { get { CheckDisposed (); CheckConnected (); @@ -2288,7 +2290,7 @@ public override IMailFolder Inbox { /// /// The IMAP server does not support the SPECIAL-USE nor XLIST extensions. /// - public override IMailFolder GetFolder (SpecialFolder folder) + public override IMailFolder? GetFolder (SpecialFolder folder) { CheckDisposed (); CheckConnected (); @@ -2470,12 +2472,12 @@ ImapCommand QueueGetMetadataCommand (MetadataTag tag, CancellationToken cancella return ic; } - string ProcessGetMetadataResponse (ImapCommand ic, MetadataTag tag) + string? ProcessGetMetadataResponse (ImapCommand ic, MetadataTag tag) { ic.ThrowIfNotOk ("GETMETADATA"); var metadata = (MetadataCollection) ic.UserData; - string value = null; + string? value = null; for (int i = 0; i < metadata.Count; i++) { if (metadata[i].EncodedName.Length == 0 && metadata[i].Tag.Id == tag.Id) { @@ -2523,7 +2525,7 @@ string ProcessGetMetadataResponse (ImapCommand ic, MetadataTag tag) /// /// The server replied with a NO or BAD response. /// - public override string GetMetadata (MetadataTag tag, CancellationToken cancellationToken = default) + public override string? GetMetadata (MetadataTag tag, CancellationToken cancellationToken = default) { var ic = QueueGetMetadataCommand (tag, cancellationToken); @@ -2532,7 +2534,7 @@ public override string GetMetadata (MetadataTag tag, CancellationToken cancellat return ProcessGetMetadataResponse (ic, tag); } - bool TryQueueGetMetadataCommand (MetadataOptions options, IEnumerable tags, CancellationToken cancellationToken, out ImapCommand ic) + bool TryQueueGetMetadataCommand (MetadataOptions options, IEnumerable tags, CancellationToken cancellationToken, [NotNullWhen (true)] out ImapCommand? ic) { if (options == null) throw new ArgumentNullException (nameof (options)); @@ -2659,7 +2661,7 @@ public override MetadataCollection GetMetadata (MetadataOptions options, IEnumer return ProcessGetMetadataResponse (ic, options); } - bool TryQueueSetMetadataCommand (MetadataCollection metadata, CancellationToken cancellationToken, out ImapCommand ic) + bool TryQueueSetMetadataCommand (MetadataCollection metadata, CancellationToken cancellationToken, [NotNullWhen (true)] out ImapCommand? ic) { if (metadata == null) throw new ArgumentNullException (nameof (metadata)); @@ -2753,22 +2755,22 @@ public override void SetMetadata (MetadataCollection metadata, CancellationToken #endregion - void OnEngineMetadataChanged (object sender, MetadataChangedEventArgs e) + void OnEngineMetadataChanged (object? sender, MetadataChangedEventArgs e) { OnMetadataChanged (e.Metadata); } - void OnEngineFolderCreated (object sender, FolderCreatedEventArgs e) + void OnEngineFolderCreated (object? sender, FolderCreatedEventArgs e) { OnFolderCreated (e.Folder); } - void OnEngineAlert (object sender, AlertEventArgs e) + void OnEngineAlert (object? sender, AlertEventArgs e) { OnAlert (e.Message); } - void OnEngineWebAlert (object sender, WebAlertEventArgs e) + void OnEngineWebAlert (object? sender, WebAlertEventArgs e) { OnWebAlert (e.WebUri, e.Message); } @@ -2800,7 +2802,7 @@ protected virtual void OnWebAlert (Uri uri, string message) WebAlert?.Invoke (this, new WebAlertEventArgs (uri, message)); } - void OnEngineDisconnected (object sender, EventArgs e) + void OnEngineDisconnected (object? sender, EventArgs e) { if (connecting) return; diff --git a/MailKit/Net/Imap/ImapCommand.cs b/MailKit/Net/Imap/ImapCommand.cs index 772dc34a3b..5952caf3d9 100644 --- a/MailKit/Net/Imap/ImapCommand.cs +++ b/MailKit/Net/Imap/ImapCommand.cs @@ -30,6 +30,7 @@ using System.Globalization; using System.Threading.Tasks; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using MimeKit; using MimeKit.Utils; @@ -76,10 +77,10 @@ enum ImapStringType { class ImapCommandPart { public readonly byte[] Command; - public readonly ImapLiteral Literal; + public readonly ImapLiteral? Literal; public readonly bool WaitForContinuation; - public ImapCommandPart (byte[] command, ImapLiteral literal, bool wait = true) + public ImapCommandPart (byte[] command, ImapLiteral? literal, bool wait = true) { WaitForContinuation = wait; Command = command; @@ -99,20 +100,20 @@ class ImapCommand static readonly byte[] LiteralTokenPrefix = { (byte) '{' }; public Dictionary UntaggedHandlers { get; private set; } - public ImapContinuationHandler ContinuationHandler { get; set; } + public ImapContinuationHandler? ContinuationHandler { get; set; } public CancellationToken CancellationToken { get; private set; } public ImapCommandStatus Status { get; internal set; } public ImapCommandResponse Response { get; internal set; } - public ITransferProgress Progress { get; internal set; } - public Exception Exception { get; internal set; } + public ITransferProgress? Progress { get; internal set; } + public Exception? Exception { get; internal set; } public readonly List RespCodes; - public string ResponseText { get; internal set; } - public ImapFolder Folder { get; private set; } - public object UserData { get; internal set; } + public string? ResponseText { get; internal set; } + public ImapFolder? Folder { get; private set; } + public object? UserData { get; internal set; } public bool ListReturnsSubscribed { get; internal set; } public bool Logout { get; private set; } public bool Lsub { get; internal set; } - public string Tag { get; private set; } + public string? Tag { get; private set; } public bool Bye { get; internal set; } readonly List parts = new List (); @@ -140,7 +141,7 @@ class ImapCommand /// -or- /// is . /// - public ImapCommand (ImapEngine engine, CancellationToken cancellationToken, ImapFolder folder, FormatOptions options, string format, params object[] args) + public ImapCommand (ImapEngine engine, CancellationToken cancellationToken, ImapFolder? folder, FormatOptions options, string format, params object[] args) { if (engine == null) throw new ArgumentNullException (nameof (engine)); @@ -264,7 +265,7 @@ public ImapCommand (ImapEngine engine, CancellationToken cancellationToken, Imap /// -or- /// is . /// - public ImapCommand (ImapEngine engine, CancellationToken cancellationToken, ImapFolder folder, string format, params object[] args) + public ImapCommand (ImapEngine engine, CancellationToken cancellationToken, ImapFolder? folder, string format, params object[] args) : this (engine, cancellationToken, folder, FormatOptions.Default, format, args) { } diff --git a/MailKit/Net/Imap/ImapEngine.cs b/MailKit/Net/Imap/ImapEngine.cs index 0fa729130b..aefa8a938d 100644 --- a/MailKit/Net/Imap/ImapEngine.cs +++ b/MailKit/Net/Imap/ImapEngine.cs @@ -34,6 +34,7 @@ using System.Globalization; using System.Threading.Tasks; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using MimeKit; @@ -119,10 +120,10 @@ public ImapFolderNameComparer (char directorySeparator) DirectorySeparator = directorySeparator; } - public bool Equals (string x, string y) + public bool Equals (string? x, string? y) { - x = ImapUtils.CanonicalizeMailboxName (x, DirectorySeparator); - y = ImapUtils.CanonicalizeMailboxName (y, DirectorySeparator); + x = ImapUtils.CanonicalizeMailboxName (x!, DirectorySeparator); + y = ImapUtils.CanonicalizeMailboxName (y!, DirectorySeparator); return x == y; } @@ -158,7 +159,7 @@ class ImapEngine : IDisposable readonly List queue; long clientConnectedTimestamp; internal char TagPrefix; - ImapCommand current; + ImapCommand? current; MimeParser parser; internal int Tag; bool disposed; @@ -438,7 +439,7 @@ public FolderNamespaceCollection OtherNamespaces { /// Gets the selected folder. /// /// The selected folder. - public ImapFolder Selected { + public ImapFolder? Selected { get; internal set; } @@ -470,7 +471,7 @@ internal bool NotifySelectedNewExpunge { /// Gets the Inbox folder. /// /// The Inbox folder. - public ImapFolder Inbox { + public ImapFolder? Inbox { get; private set; } @@ -478,7 +479,7 @@ public ImapFolder Inbox { /// Gets the special folder containing an aggregate of all messages. /// /// The folder containing all messages. - public ImapFolder All { + public ImapFolder? All { get; private set; } @@ -486,7 +487,7 @@ public ImapFolder All { /// Gets the special archive folder. /// /// The archive folder. - public ImapFolder Archive { + public ImapFolder? Archive { get; private set; } @@ -494,7 +495,7 @@ public ImapFolder Archive { /// Gets the special folder containing drafts. /// /// The drafts folder. - public ImapFolder Drafts { + public ImapFolder? Drafts { get; private set; } @@ -502,7 +503,7 @@ public ImapFolder Drafts { /// Gets the special folder containing flagged messages. /// /// The flagged folder. - public ImapFolder Flagged { + public ImapFolder? Flagged { get; private set; } @@ -510,7 +511,7 @@ public ImapFolder Flagged { /// Gets the special folder containing important messages. /// /// The important folder. - public ImapFolder Important { + public ImapFolder? Important { get; private set; } @@ -518,7 +519,7 @@ public ImapFolder Important { /// Gets the special folder containing junk messages. /// /// The junk folder. - public ImapFolder Junk { + public ImapFolder? Junk { get; private set; } @@ -526,7 +527,7 @@ public ImapFolder Junk { /// Gets the special folder containing sent messages. /// /// The sent. - public ImapFolder Sent { + public ImapFolder? Sent { get; private set; } @@ -534,7 +535,7 @@ public ImapFolder Sent { /// Gets the folder containing deleted messages. /// /// The trash folder. - public ImapFolder Trash { + public ImapFolder? Trash { get; private set; } @@ -623,7 +624,7 @@ internal void SetStream (ImapStream stream) Stream = stream; } - public NetworkOperation StartNetworkOperation (NetworkOperationKind kind, Uri uri = null) + public NetworkOperation StartNetworkOperation (NetworkOperationKind kind, Uri? uri = null) { #if NET6_0_OR_GREATER return NetworkOperation.Start (kind, uri ?? Uri, Telemetry.ImapClient.ActivitySource, metrics); @@ -831,7 +832,7 @@ public async Task ConnectAsync (ImapStream stream, CancellationToken cancellatio } } - void RecordClientDisconnected (Exception ex) + void RecordClientDisconnected (Exception? ex) { #if NET6_0_OR_GREATER metrics?.RecordClientDisconnected (clientConnectedTimestamp, Uri, ex); @@ -846,7 +847,7 @@ void RecordClientDisconnected (Exception ex) /// Disconnects the . /// /// The exception that is causing the disconnection. - public void Disconnect (Exception ex) + public void Disconnect (Exception? ex) { RecordClientDisconnected (ex); @@ -1477,7 +1478,7 @@ void UpdateCapabilities (ImapTokenType sentinel, CancellationToken cancellationT while (token.Type == ImapTokenType.Atom) { var atom = token.Value.ToString (); - ProcessCapabilityToken (atom); + ProcessCapabilityToken (atom!); token = ReadToken (cancellationToken); } @@ -1504,7 +1505,7 @@ async Task UpdateCapabilitiesAsync (ImapTokenType sentinel, CancellationToken ca while (token.Type == ImapTokenType.Atom) { var atom = token.Value.ToString (); - ProcessCapabilityToken (atom); + ProcessCapabilityToken (atom!); token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); } @@ -2527,7 +2528,7 @@ public async ValueTask ParseResponseCodeAsync (bool isTagged, return code; } - static bool UpdateSimpleStatusValue (ImapFolder folder, string atom, ImapToken token) + static bool UpdateSimpleStatusValue (ImapFolder? folder, string atom, ImapToken token) { uint count, uid; ulong modseq; @@ -2750,8 +2751,8 @@ static bool IsOkNoOrBad (string atom, out ImapUntaggedResult result) internal void ProcessUntaggedResponse (CancellationToken cancellationToken) { var token = ReadToken (cancellationToken); - var folder = current.Folder ?? Selected; - ImapUntaggedHandler handler; + var folder = current!.Folder ?? Selected; + ImapUntaggedHandler? handler; string atom; // Note: work around broken IMAP servers such as home.pl which sends "* [COPYUID ...]" resp-codes @@ -2813,7 +2814,7 @@ internal void ProcessUntaggedResponse (CancellationToken cancellationToken) } else if (atom.Equals ("FLAGS", StringComparison.OrdinalIgnoreCase)) { var keywords = new HashSet (StringComparer.Ordinal); var flags = ImapUtils.ParseFlagsList (this, atom, keywords, cancellationToken); - folder.UpdateAcceptedFlags (flags, keywords); + folder!.UpdateAcceptedFlags (flags, keywords); token = ReadToken (cancellationToken); AssertToken (token, ImapTokenType.Eoln, GenericUntaggedResponseSyntaxErrorFormat, atom, token); @@ -2903,8 +2904,8 @@ internal void ProcessUntaggedResponse (CancellationToken cancellationToken) internal async Task ProcessUntaggedResponseAsync (CancellationToken cancellationToken) { var token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); - var folder = current.Folder ?? Selected; - ImapUntaggedHandler handler; + var folder = current!.Folder ?? Selected; + ImapUntaggedHandler? handler; string atom; // Note: work around broken IMAP servers such as home.pl which sends "* [COPYUID ...]" resp-codes @@ -2966,7 +2967,7 @@ internal async Task ProcessUntaggedResponseAsync (CancellationToken cancellation } else if (atom.Equals ("FLAGS", StringComparison.OrdinalIgnoreCase)) { var keywords = new HashSet (StringComparer.Ordinal); var flags = await ImapUtils.ParseFlagsListAsync (this, atom, keywords, cancellationToken).ConfigureAwait (false); - folder.UpdateAcceptedFlags (flags, keywords); + folder!.UpdateAcceptedFlags (flags, keywords); token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); AssertToken (token, ImapTokenType.Eoln, GenericUntaggedResponseSyntaxErrorFormat, atom, token); @@ -3072,7 +3073,7 @@ void PopNextCommand () void OnImapProtocolException (ImapProtocolException ex) { - var ic = current; + var ic = current!; Disconnect (ex); @@ -3088,7 +3089,7 @@ void OnImapProtocolException (ImapProtocolException ex) } if (!string.IsNullOrEmpty (ic.ResponseText)) - throw new ImapProtocolException (ic.ResponseText); + throw new ImapProtocolException (ic.ResponseText!); } } @@ -3099,7 +3100,7 @@ void Iterate () { PopNextCommand (); - current.Status = ImapCommandStatus.Active; + current!.Status = ImapCommandStatus.Active; try { while (current.Step ()) { @@ -3126,7 +3127,7 @@ async Task IterateAsync () { PopNextCommand (); - current.Status = ImapCommandStatus.Active; + current!.Status = ImapCommandStatus.Active; try { while (await current.StepAsync ().ConfigureAwait (false)) { @@ -3257,7 +3258,7 @@ public IEnumerable QueueCommands (CancellationToken cancellationTok /// The formatting options. /// The command format. /// The command arguments. - public ImapCommand QueueCommand (CancellationToken cancellationToken, ImapFolder folder, FormatOptions options, string format, params object[] args) + public ImapCommand QueueCommand (CancellationToken cancellationToken, ImapFolder? folder, FormatOptions options, string format, params object[] args) { var ic = new ImapCommand (this, cancellationToken, folder, options, format, args); QueueCommand (ic); @@ -3272,7 +3273,7 @@ public ImapCommand QueueCommand (CancellationToken cancellationToken, ImapFolder /// The folder that the command operates on. /// The command format. /// The command arguments. - public ImapCommand QueueCommand (CancellationToken cancellationToken, ImapFolder folder, string format, params object[] args) + public ImapCommand QueueCommand (CancellationToken cancellationToken, ImapFolder? folder, string format, params object[] args) { return QueueCommand (cancellationToken, folder, FormatOptions.Default, format, args); } @@ -3331,12 +3332,12 @@ public void CacheFolder (ImapFolder folder) /// if the folder was retrieved from the cache; otherwise, . /// The encoded folder name. /// The cached folder. - public bool TryGetCachedFolder (string encodedName, out ImapFolder folder) + public bool TryGetCachedFolder (string encodedName, [NotNullWhen (true)] out ImapFolder? folder) { return FolderCache.TryGetValue (encodedName, out folder); } - bool RequiresParentLookup (ImapFolder folder, out string encodedParentName) + bool RequiresParentLookup (ImapFolder folder, [NotNullWhen (true)] out string? encodedParentName) { encodedParentName = null; @@ -3554,7 +3555,7 @@ public async Task QueryNamespacesAsync (CancellationToken c return ic.Response; } - internal static ImapFolder GetFolder (List folders, string encodedName) + internal static ImapFolder? GetFolder (List folders, string encodedName) { for (int i = 0; i < folders.Count; i++) { if (encodedName.Equals (folders[i].EncodedName, StringComparison.OrdinalIgnoreCase)) @@ -3790,9 +3791,9 @@ public async Task QuerySpecialFoldersAsync (CancellationToken cancellationToken) ImapFolder ProcessGetQuotaRootResponse (ImapCommand ic, string quotaRoot, out List list) { - ImapFolder folder; + ImapFolder? folder; - list = (List) ic.UserData; + list = (List) ic.UserData!; ic.ThrowIfNotOk ("LIST"); @@ -3873,9 +3874,9 @@ ImapCommand QueueGetFolderCommand (string encodedName, CancellationToken cancell static ImapFolder ProcessGetFolderResponse (ImapCommand ic, string path, string encodedName, out List list) { - ImapFolder folder; + ImapFolder? folder; - list = (List) ic.UserData; + list = (List) ic.UserData!; ic.ThrowIfNotOk ("LIST"); @@ -4064,7 +4065,7 @@ static IList ToListOfIMailFolder (List list) public IList GetFolders (FolderNamespace @namespace, StatusItems items, bool subscribedOnly, CancellationToken cancellationToken) { var ic = QueueGetFoldersCommand (@namespace, items, subscribedOnly, cancellationToken, out bool status); - var list = (List) ic.UserData; + var list = (List) ic.UserData!; Run (ic); @@ -4096,7 +4097,7 @@ public IList GetFolders (FolderNamespace @namespace, StatusItems it public async Task> GetFoldersAsync (FolderNamespace @namespace, StatusItems items, bool subscribedOnly, CancellationToken cancellationToken) { var ic = QueueGetFoldersCommand (@namespace, items, subscribedOnly, cancellationToken, out bool status); - var list = (List) ic.UserData; + var list = (List) ic.UserData!; await RunAsync (ic).ConfigureAwait (false); diff --git a/MailKit/Net/Imap/ImapFolder.cs b/MailKit/Net/Imap/ImapFolder.cs index e1ae18c84b..70314cedff 100644 --- a/MailKit/Net/Imap/ImapFolder.cs +++ b/MailKit/Net/Imap/ImapFolder.cs @@ -30,6 +30,7 @@ using System.Globalization; using System.Threading.Tasks; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using MimeKit; @@ -84,6 +85,7 @@ public ImapFolder (ImapFolderConstructorArgs args) InitializeProperties (args); } + [MemberNotNull ("Engine", "EncodedName")] void InitializeProperties (ImapFolderConstructorArgs args) { DirectorySeparator = args.DirectorySeparator; @@ -242,7 +244,7 @@ protected override void OnParentFolderRenamed () } } - void ProcessResponseCodes (ImapCommand ic, IMailFolder folder, bool throwNotFound = true) + void ProcessResponseCodes (ImapCommand ic, IMailFolder? folder, bool throwNotFound = true) { bool tryCreate = false; @@ -329,9 +331,9 @@ static string SelectOrExamine (FolderAccess access) static Task UntaggedQResyncFetchHandler (ImapEngine engine, ImapCommand ic, int index, bool doAsync) { if (doAsync) - return ic.Folder.OnUntaggedFetchResponseAsync (engine, index, ic.CancellationToken); + return ic.Folder!.OnUntaggedFetchResponseAsync (engine, index, ic.CancellationToken); - ic.Folder.OnUntaggedFetchResponse (engine, index, ic.CancellationToken); + ic.Folder!.OnUntaggedFetchResponse (engine, index, ic.CancellationToken); return Task.CompletedTask; } @@ -673,11 +675,11 @@ public override Task OpenAsync (FolderAccess access, CancellationT return OpenAsync (ic, access); } - ImapCommand QueueCloseCommand (bool expunge, CancellationToken cancellationToken) + ImapCommand? QueueCloseCommand (bool expunge, CancellationToken cancellationToken) { CheckState (true, expunge); - ImapCommand ic; + ImapCommand? ic; if (expunge) { ic = Engine.QueueCommand (cancellationToken, this, "CLOSE\r\n"); @@ -810,10 +812,10 @@ ImapCommand QueueGetCreatedFolderCommand (string encodedName, CancellationToken return ic; } - IMailFolder ProcessGetCreatedFolderResponse (ImapCommand ic, string encodedName, string id, bool specialUse) + IMailFolder? ProcessGetCreatedFolderResponse (ImapCommand ic, string encodedName, string? id, bool specialUse) { - var list = (List) ic.UserData; - ImapFolder folder; + var list = (List) ic.UserData!; + ImapFolder? folder; ProcessResponseCodes (ic, null); @@ -865,7 +867,7 @@ MailboxIdResponseCode ProcessCreateResponse (ImapCommand ic) return (MailboxIdResponseCode) ic.GetResponseCode (ImapResponseCodeType.MailboxId); } - IMailFolder Create (ImapCommand ic, string encodedName, bool specialUse, CancellationToken cancellationToken) + IMailFolder? Create (ImapCommand ic, string encodedName, bool specialUse, CancellationToken cancellationToken) { Engine.Run (ic); @@ -879,7 +881,7 @@ IMailFolder Create (ImapCommand ic, string encodedName, bool specialUse, Cancell return ProcessGetCreatedFolderResponse (ic, encodedName, id, specialUse); } - async Task CreateAsync (ImapCommand ic, string encodedName, bool specialUse, CancellationToken cancellationToken) + async Task CreateAsync (ImapCommand ic, string encodedName, bool specialUse, CancellationToken cancellationToken) { await Engine.RunAsync (ic).ConfigureAwait (false); @@ -933,7 +935,7 @@ async Task CreateAsync (ImapCommand ic, string encodedName, bool sp /// /// The server replied with a NO or BAD response. /// - public override IMailFolder Create (string name, bool isMessageFolder, CancellationToken cancellationToken = default) + public override IMailFolder? Create (string name, bool isMessageFolder, CancellationToken cancellationToken = default) { var ic = QueueCreateCommand (name, isMessageFolder, cancellationToken, out var encodedName); @@ -980,7 +982,7 @@ public override IMailFolder Create (string name, bool isMessageFolder, Cancellat /// /// The server replied with a NO or BAD response. /// - public override Task CreateAsync (string name, bool isMessageFolder, CancellationToken cancellationToken = default) + public override Task CreateAsync (string name, bool isMessageFolder, CancellationToken cancellationToken = default) { var ic = QueueCreateCommand (name, isMessageFolder, cancellationToken, out var encodedName); @@ -1090,7 +1092,7 @@ ImapCommand QueueCreateCommand (string name, IEnumerable specialU /// /// The server replied with a NO or BAD response. /// - public override IMailFolder Create (string name, IEnumerable specialUses, CancellationToken cancellationToken = default) + public override IMailFolder? Create (string name, IEnumerable specialUses, CancellationToken cancellationToken = default) { var ic = QueueCreateCommand (name, specialUses, cancellationToken, out var encodedName); @@ -1142,7 +1144,7 @@ public override IMailFolder Create (string name, IEnumerable spec /// /// The server replied with a NO or BAD response. /// - public override Task CreateAsync (string name, IEnumerable specialUses, CancellationToken cancellationToken = default) + public override Task CreateAsync (string name, IEnumerable specialUses, CancellationToken cancellationToken = default) { var ic = QueueCreateCommand (name, specialUses, cancellationToken, out var encodedName); @@ -1623,7 +1625,7 @@ public override async Task UnsubscribeAsync (CancellationToken cancellationToken ProcessUnsubscribeResponse (ic); } - ImapCommand QueueGetSubfoldersCommand (StatusItems items, bool subscribedOnly, CancellationToken cancellationToken, out List list, out bool status) + ImapCommand? QueueGetSubfoldersCommand (StatusItems items, bool subscribedOnly, CancellationToken cancellationToken, out List? list, out bool status) { CheckState (false, false); @@ -1781,12 +1783,12 @@ public override IList GetSubfolders (StatusItems items, bool subscr Engine.Run (ic); - var children = ProcessGetSubfoldersResponse (ic, list, out var unparented); + var children = ProcessGetSubfoldersResponse (ic, list!, out var unparented); // Note: if any folders returned in the LIST command are unparented, have the ImapEngine look up their // parent folders now so that they are not left in an inconsistent state. if (unparented) - Engine.LookupParentFolders (list, cancellationToken); + Engine.LookupParentFolders (list!, cancellationToken); if (status) { for (int i = 0; i < children.Count; i++) { @@ -1838,12 +1840,12 @@ public override async Task> GetSubfoldersAsync (StatusItems i await Engine.RunAsync (ic).ConfigureAwait (false); - var children = ProcessGetSubfoldersResponse (ic, list, out var unparented); + var children = ProcessGetSubfoldersResponse (ic, list!, out var unparented); // Note: if any folders returned in the LIST command are unparented, have the ImapEngine look up their // parent folders now so that they are not left in an inconsistent state. if (unparented) - await Engine.LookupParentFoldersAsync (list, cancellationToken).ConfigureAwait (false); + await Engine.LookupParentFoldersAsync (list!, cancellationToken).ConfigureAwait (false); if (status) { for (int i = 0; i < children.Count; i++) { @@ -1855,7 +1857,7 @@ public override async Task> GetSubfoldersAsync (StatusItems i return children; } - ImapCommand QueueGetSubfolderCommand (string name, CancellationToken cancellationToken, out List list, out string fullName, out string encodedName, out ImapFolder folder) + ImapCommand? QueueGetSubfolderCommand (string name, CancellationToken cancellationToken, out List? list, out string? fullName, out string? encodedName, out ImapFolder? folder) { if (name == null) throw new ArgumentNullException (nameof (name)); @@ -1895,9 +1897,9 @@ ImapCommand QueueGetSubfolderCommand (string name, CancellationToken cancellatio return ic; } - ImapFolder ProcessGetSubfolderResponse (ImapCommand ic, List list, string encodedName) + ImapFolder? ProcessGetSubfolderResponse (ImapCommand ic, List list, string encodedName) { - ImapFolder folder; + ImapFolder? folder; ProcessResponseCodes (ic, null); @@ -1957,16 +1959,16 @@ public override IMailFolder GetSubfolder (string name, CancellationToken cancell Engine.Run (ic); - folder = ProcessGetSubfolderResponse (ic, list, encodedName); + folder = ProcessGetSubfolderResponse (ic, list!, encodedName!); - if (list.Count > 1 || folder == null) { + if (list!.Count > 1 || folder == null) { // Note: if any folders returned in the LIST command are unparented, have the ImapEngine look up their // parent folders now so that they are not left in an inconsistent state. Engine.LookupParentFolders (list, cancellationToken); } if (folder == null) - throw new FolderNotFoundException (fullName); + throw new FolderNotFoundException (fullName!); return folder; } @@ -2019,16 +2021,16 @@ public override async Task GetSubfolderAsync (string name, Cancella await Engine.RunAsync (ic).ConfigureAwait (false); - folder = ProcessGetSubfolderResponse (ic, list, encodedName); + folder = ProcessGetSubfolderResponse (ic, list!, encodedName!); - if (list.Count > 1 || folder == null) { + if (list!.Count > 1 || folder == null) { // Note: if any folders returned in the LIST command are unparented, have the ImapEngine look up their // parent folders now so that they are not left in an inconsistent state. await Engine.LookupParentFoldersAsync (list, cancellationToken).ConfigureAwait (false); } if (folder == null) - throw new FolderNotFoundException (fullName); + throw new FolderNotFoundException (fullName!); return folder; } @@ -2134,7 +2136,7 @@ public override async Task CheckAsync (CancellationToken cancellationToken = def ProcessCheckResponse (ic); } - ImapCommand QueueStatusCommand (StatusItems items, CancellationToken cancellationToken) + ImapCommand? QueueStatusCommand (StatusItems items, CancellationToken cancellationToken) { if ((Engine.Capabilities & ImapCapabilities.Status) == 0) throw new NotSupportedException ("The IMAP server does not support the STATUS command."); @@ -2282,7 +2284,7 @@ public override Task StatusAsync (StatusItems items, CancellationToken cancellat static void ParseAcl (ImapEngine engine, ImapCommand ic) { string format = string.Format (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ACL", "{0}"); - var acl = (AccessControlList) ic.UserData; + var acl = (AccessControlList) ic.UserData!; string name, rights; ImapToken token; @@ -2302,7 +2304,7 @@ static void ParseAcl (ImapEngine engine, ImapCommand ic) static async Task ParseAclAsync (ImapEngine engine, ImapCommand ic) { string format = string.Format (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ACL", "{0}"); - var acl = (AccessControlList) ic.UserData; + var acl = (AccessControlList) ic.UserData!; string name, rights; ImapToken token; @@ -2351,7 +2353,7 @@ AccessControlList ProcessGetAccessControlListResponse (ImapCommand ic) ic.ThrowIfNotOk ("GETACL"); - return (AccessControlList) ic.UserData; + return (AccessControlList) ic.UserData!; } /// @@ -2439,7 +2441,7 @@ public override async Task GetAccessControlListAsync (Cancell static void ParseListRights (ImapEngine engine, ImapCommand ic) { string format = string.Format (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "LISTRIGHTS", "{0}"); - var access = (AccessRights) ic.UserData; + var access = (AccessRights) ic.UserData!; ImapToken token; // read the mailbox name @@ -2460,7 +2462,7 @@ static void ParseListRights (ImapEngine engine, ImapCommand ic) static async Task ParseListRightsAsync (ImapEngine engine, ImapCommand ic) { string format = string.Format (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "LISTRIGHTS", "{0}"); - var access = (AccessRights) ic.UserData; + var access = (AccessRights) ic.UserData!; ImapToken token; // read the mailbox name @@ -2513,7 +2515,7 @@ AccessRights ProcessGetAccessRightsResponse (ImapCommand ic) ic.ThrowIfNotOk ("LISTRIGHTS"); - return (AccessRights) ic.UserData; + return (AccessRights) ic.UserData!; } /// @@ -2609,7 +2611,7 @@ public override async Task GetAccessRightsAsync (string name, Canc static void ParseMyRights (ImapEngine engine, ImapCommand ic) { string format = string.Format (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "MYRIGHTS", "{0}"); - var access = (AccessRights) ic.UserData; + var access = (AccessRights) ic.UserData!; // read the mailbox name ImapUtils.ReadStringToken (engine, format, ic.CancellationToken); @@ -2621,7 +2623,7 @@ static void ParseMyRights (ImapEngine engine, ImapCommand ic) static async Task ParseMyRightsAsync (ImapEngine engine, ImapCommand ic) { string format = string.Format (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "MYRIGHTS", "{0}"); - var access = (AccessRights) ic.UserData; + var access = (AccessRights) ic.UserData!; // read the mailbox name await ImapUtils.ReadStringTokenAsync (engine, format, ic.CancellationToken).ConfigureAwait (false); @@ -2662,7 +2664,7 @@ AccessRights ProcessGetMyAccessRightsResponse (ImapCommand ic) ic.ThrowIfNotOk ("MYRIGHTS"); - return (AccessRights) ic.UserData; + return (AccessRights) ic.UserData!; } /// @@ -3196,15 +3198,15 @@ ImapCommand QueueGetMetadataCommand (MetadataTag tag, CancellationToken cancella return ic; } - string ProcessGetMetadataResponse (ImapCommand ic, MetadataTag tag) + string? ProcessGetMetadataResponse (ImapCommand ic, MetadataTag tag) { - var metadata = (MetadataCollection) ic.UserData; + var metadata = (MetadataCollection) ic.UserData!; ProcessResponseCodes (ic, null); ic.ThrowIfNotOk ("GETMETADATA"); - string value = null; + string? value = null; for (int i = 0; i < metadata.Count; i++) { if (metadata[i].EncodedName == EncodedName && metadata[i].Tag.Id == tag.Id) { @@ -3252,7 +3254,7 @@ string ProcessGetMetadataResponse (ImapCommand ic, MetadataTag tag) /// /// The server replied with a NO or BAD response. /// - public override string GetMetadata (MetadataTag tag, CancellationToken cancellationToken = default) + public override string? GetMetadata (MetadataTag tag, CancellationToken cancellationToken = default) { var ic = QueueGetMetadataCommand (tag, cancellationToken); @@ -3294,7 +3296,7 @@ public override string GetMetadata (MetadataTag tag, CancellationToken cancellat /// /// The server replied with a NO or BAD response. /// - public override async Task GetMetadataAsync (MetadataTag tag, CancellationToken cancellationToken = default) + public override async Task GetMetadataAsync (MetadataTag tag, CancellationToken cancellationToken = default) { var ic = QueueGetMetadataCommand (tag, cancellationToken); @@ -3303,7 +3305,7 @@ public override async Task GetMetadataAsync (MetadataTag tag, Cancellati return ProcessGetMetadataResponse (ic, tag); } - ImapCommand QueueGetMetadataCommand (MetadataOptions options, IEnumerable tags, CancellationToken cancellationToken) + ImapCommand? QueueGetMetadataCommand (MetadataOptions options, IEnumerable tags, CancellationToken cancellationToken) { if (options == null) throw new ArgumentNullException (nameof (options)); @@ -3375,7 +3377,7 @@ MetadataCollection ProcessGetMetadataResponse (ImapCommand ic, MetadataOptions o if (metadata != null && metadata.SubType == MetadataResponseCodeSubType.LongEntries) options.LongEntries = metadata.Value; - return Engine.FilterMetadata ((MetadataCollection) ic.UserData, EncodedName); + return Engine.FilterMetadata ((MetadataCollection) ic.UserData!, EncodedName); } /// @@ -3480,7 +3482,7 @@ public override async Task GetMetadataAsync (MetadataOptions return ProcessGetMetadataResponse (ic, options); } - ImapCommand QueueSetMetadataCommand (MetadataCollection metadata, CancellationToken cancellationToken) + ImapCommand? QueueSetMetadataCommand (MetadataCollection metadata, CancellationToken cancellationToken) { if (metadata == null) throw new ArgumentNullException (nameof (metadata)); @@ -3650,7 +3652,7 @@ public Dictionary Quotas { static void ParseQuotaRoot (ImapEngine engine, ImapCommand ic) { var format = string.Format (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "QUOTAROOT", "{0}"); - var ctx = (QuotaContext) ic.UserData; + var ctx = (QuotaContext) ic.UserData!; // The first token should be the mailbox name ImapUtils.ReadStringToken (engine, format, ic.CancellationToken); @@ -3669,7 +3671,7 @@ static void ParseQuotaRoot (ImapEngine engine, ImapCommand ic) static async Task ParseQuotaRootAsync (ImapEngine engine, ImapCommand ic) { var format = string.Format (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "QUOTAROOT", "{0}"); - var ctx = (QuotaContext) ic.UserData; + var ctx = (QuotaContext) ic.UserData!; // The first token should be the mailbox name await ImapUtils.ReadStringTokenAsync (engine, format, ic.CancellationToken).ConfigureAwait (false); @@ -3707,7 +3709,7 @@ static void ParseQuota (ImapEngine engine, ImapCommand ic) { var format = string.Format (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "QUOTA", "{0}"); var quotaRoot = ImapUtils.ReadStringToken (engine, format, ic.CancellationToken); - var ctx = (QuotaContext) ic.UserData; + var ctx = (QuotaContext) ic.UserData!; var quota = new Quota (); var token = engine.ReadToken (ic.CancellationToken); @@ -3757,7 +3759,7 @@ static async Task ParseQuotaAsync (ImapEngine engine, ImapCommand ic) { var format = string.Format (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "QUOTA", "{0}"); var quotaRoot = await ImapUtils.ReadStringTokenAsync (engine, format, ic.CancellationToken).ConfigureAwait (false); - var ctx = (QuotaContext) ic.UserData; + var ctx = (QuotaContext) ic.UserData!; var quota = new Quota (); var token = await engine.ReadTokenAsync (ic.CancellationToken).ConfigureAwait (false); @@ -3840,9 +3842,9 @@ ImapCommand QueueGetQuotaCommand (CancellationToken cancellationToken) return ic; } - bool TryProcessGetQuotaResponse (ImapCommand ic, out string encodedName, out Quota quota) + bool TryProcessGetQuotaResponse (ImapCommand ic, [NotNullWhen (true)] out string? encodedName, [NotNullWhen (true)] out Quota? quota) { - var ctx = (QuotaContext) ic.UserData; + var ctx = (QuotaContext) ic.UserData!; ProcessResponseCodes (ic, null); @@ -4001,7 +4003,7 @@ ImapCommand QueueSetQuotaCommand (uint? messageLimit, uint? storageLimit, Cancel FolderQuota ProcessSetQuotaResponse (ImapCommand ic) { - var ctx = (QuotaContext) ic.UserData; + var ctx = (QuotaContext) ic.UserData!; ProcessResponseCodes (ic, null); @@ -4652,9 +4654,10 @@ ImapCommand QueueMultiAppendCommand (FormatOptions options, IList uids, IMailFolder destination) CheckValidDestination (destination); } - void GetCopiedUids (ImapCommand ic, ref UniqueIdSet src, ref UniqueIdSet dest) + static void GetCopiedUids (ImapCommand ic, ref UniqueIdSet? src, ref UniqueIdSet? dest) { var copy = (CopyUidResponseCode) ic.GetResponseCode (ImapResponseCodeType.CopyUid); @@ -5366,12 +5369,12 @@ void GetCopiedUids (ImapCommand ic, ref UniqueIdSet src, ref UniqueIdSet dest) src = copy.SrcUidSet; } else { dest.AddRange (copy.DestUidSet); - src.AddRange (copy.SrcUidSet); + src!.AddRange (copy.SrcUidSet); } } } - void ProcessCopyToResponse (ImapCommand ic, IMailFolder destination, ref UniqueIdSet src, ref UniqueIdSet dest) + void ProcessCopyToResponse (ImapCommand ic, IMailFolder destination, ref UniqueIdSet? src, ref UniqueIdSet? dest) { ProcessCopyToResponse (ic, destination); @@ -5443,8 +5446,8 @@ public override UniqueIdMap CopyTo (IList uids, IMailFolder destinatio return UniqueIdMap.Empty; } - UniqueIdSet dest = null; - UniqueIdSet src = null; + UniqueIdSet? dest = null; + UniqueIdSet? src = null; foreach (var ic in Engine.QueueCommands (cancellationToken, this, "UID COPY %s %F\r\n", uids, destination)) { Engine.Run (ic); @@ -5455,7 +5458,7 @@ public override UniqueIdMap CopyTo (IList uids, IMailFolder destinatio if (dest == null) return UniqueIdMap.Empty; - return new UniqueIdMap (src, dest); + return new UniqueIdMap (src!, dest); } /// @@ -5523,8 +5526,8 @@ public override async Task CopyToAsync (IList uids, IMail return UniqueIdMap.Empty; } - UniqueIdSet dest = null; - UniqueIdSet src = null; + UniqueIdSet? dest = null; + UniqueIdSet? src = null; foreach (var ic in Engine.QueueCommands (cancellationToken, this, "UID COPY %s %F\r\n", uids, destination)) { await Engine.RunAsync (ic).ConfigureAwait (false); @@ -5535,10 +5538,10 @@ public override async Task CopyToAsync (IList uids, IMail if (dest == null) return UniqueIdMap.Empty; - return new UniqueIdMap (src, dest); + return new UniqueIdMap (src!, dest); } - void ProcessMoveToResponse (ImapCommand ic, IMailFolder destination, ref UniqueIdSet src, ref UniqueIdSet dest) + void ProcessMoveToResponse (ImapCommand ic, IMailFolder destination, ref UniqueIdSet? src, ref UniqueIdSet? dest) { ProcessMoveToResponse (ic, destination); @@ -5625,8 +5628,8 @@ public override UniqueIdMap MoveTo (IList uids, IMailFolder destinatio if (uids.Count == 0) return UniqueIdMap.Empty; - UniqueIdSet dest = null; - UniqueIdSet src = null; + UniqueIdSet? dest = null; + UniqueIdSet? src = null; foreach (var ic in Engine.QueueCommands (cancellationToken, this, "UID MOVE %s %F\r\n", uids, destination)) { Engine.Run (ic); @@ -5637,7 +5640,7 @@ public override UniqueIdMap MoveTo (IList uids, IMailFolder destinatio if (dest == null) return UniqueIdMap.Empty; - return new UniqueIdMap (src, dest); + return new UniqueIdMap (src!, dest); } /// @@ -5720,8 +5723,8 @@ public override async Task MoveToAsync (IList uids, IMail if (uids.Count == 0) return UniqueIdMap.Empty; - UniqueIdSet dest = null; - UniqueIdSet src = null; + UniqueIdSet? dest = null; + UniqueIdSet? src = null; foreach (var ic in Engine.QueueCommands (cancellationToken, this, "UID MOVE %s %F\r\n", uids, destination)) { await Engine.RunAsync (ic).ConfigureAwait (false); @@ -5732,7 +5735,7 @@ public override async Task MoveToAsync (IList uids, IMail if (dest == null) return UniqueIdMap.Empty; - return new UniqueIdMap (src, dest); + return new UniqueIdMap (src!, dest); } void ValidateArguments (IList indexes, IMailFolder destination) @@ -5743,7 +5746,7 @@ void ValidateArguments (IList indexes, IMailFolder destination) CheckValidDestination (destination); } - ImapCommand QueueCopyToCommand (IList indexes, IMailFolder destination, CancellationToken cancellationToken) + ImapCommand? QueueCopyToCommand (IList indexes, IMailFolder destination, CancellationToken cancellationToken) { ValidateArguments (indexes, destination); @@ -5890,7 +5893,7 @@ public override async Task CopyToAsync (IList indexes, IMailFolder destinat ProcessCopyToResponse (ic, destination); } - ImapCommand QueueMoveToCommand (IList indexes, IMailFolder destination, CancellationToken cancellationToken) + ImapCommand? QueueMoveToCommand (IList indexes, IMailFolder destination, CancellationToken cancellationToken) { ValidateArguments (indexes, destination); @@ -6122,7 +6125,7 @@ void OnFetchAsyncCompleted (MessageSummary message) uid = message.UniqueId; if ((message.Fields & MessageSummaryItems.Flags) != 0) { - var args = new MessageFlagsChangedEventArgs (index, message.Flags.Value, (HashSet) message.Keywords) { + var args = new MessageFlagsChangedEventArgs (index, message.Flags!.Value, (HashSet) message.Keywords) { ModSeq = message.ModSeq, UniqueId = uid }; @@ -6149,7 +6152,7 @@ void OnFetchAsyncCompleted (MessageSummary message) } if ((message.Fields & MessageSummaryItems.ModSeq) != 0) { - var args = new ModSeqChangedEventArgs (index, message.ModSeq.Value) { + var args = new ModSeqChangedEventArgs (index, message.ModSeq!.Value) { UniqueId = uid }; diff --git a/MailKit/Net/Imap/ImapFolderFetch.cs b/MailKit/Net/Imap/ImapFolderFetch.cs index 952d0ce1a7..c30247dfee 100644 --- a/MailKit/Net/Imap/ImapFolderFetch.cs +++ b/MailKit/Net/Imap/ImapFolderFetch.cs @@ -32,6 +32,7 @@ using System.Globalization; using System.Threading.Tasks; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using MimeKit; using MimeKit.IO; @@ -93,7 +94,7 @@ public void Add (int index, MessageSummary message) Messages.Add (message); } - public bool TryGetValue (int index, out MessageSummary message) + public bool TryGetValue (int index, [NotNullWhen (true)] out MessageSummary? message) { int i; @@ -107,7 +108,7 @@ public bool TryGetValue (int index, out MessageSummary message) return true; } - public void OnMessageExpunged (object sender, MessageEventArgs args) + public void OnMessageExpunged (object? sender, MessageEventArgs args) { int index = BinarySearch (args.Index, false); @@ -1814,9 +1815,9 @@ public Section (Stream stream, int index, UniqueId? uid, string name, int offset abstract class FetchStreamContextBase : IDisposable { public readonly List
Sections = new List
(); - readonly ITransferProgress progress; + readonly ITransferProgress? progress; - public FetchStreamContextBase (ITransferProgress progress) + protected FetchStreamContextBase (ITransferProgress? progress) { this.progress = progress; } @@ -1830,7 +1831,7 @@ public virtual Task AddAsync (Section section, CancellationToken cancellationTok return Task.CompletedTask; } - public virtual bool Contains (int index, string specifier, out Section section) + public virtual bool Contains (int index, string specifier, [NotNullWhen (true)] out Section? section) { section = null; return false; @@ -1868,7 +1869,7 @@ public void Dispose () class FetchStreamContext : FetchStreamContextBase { - public FetchStreamContext (ITransferProgress progress) : base (progress) + public FetchStreamContext (ITransferProgress? progress) : base (progress) { } @@ -1877,7 +1878,7 @@ public override void Add (Section section, CancellationToken cancellationToken) Sections.Add (section); } - public bool TryGetSection (UniqueId uid, string specifier, out Section section, bool remove = false) + public bool TryGetSection (UniqueId uid, string specifier, [NotNullWhen (true)] out Section? section, bool remove = false) { for (int i = 0; i < Sections.Count; i++) { var item = Sections[i]; @@ -1899,7 +1900,7 @@ public bool TryGetSection (UniqueId uid, string specifier, out Section section, return false; } - public bool TryGetSection (int index, string specifier, out Section section, bool remove = false) + public bool TryGetSection (int index, string specifier, [NotNullWhen (true)] out Section section, bool remove = false) { for (int i = 0; i < Sections.Count; i++) { var item = Sections[i]; @@ -1937,7 +1938,7 @@ void FetchStream (ImapEngine engine, ImapCommand ic, int index) var labels = new MessageLabelsChangedEventArgs (index); var flags = new MessageFlagsChangedEventArgs (index); var modSeq = new ModSeqChangedEventArgs (index); - var ctx = (FetchStreamContextBase) ic.UserData; + var ctx = (FetchStreamContextBase) ic.UserData!; var sectionBuilder = new StringBuilder (); bool annotationsChanged = false; bool modSeqChanged = false; @@ -2195,7 +2196,7 @@ async Task FetchStreamAsync (ImapEngine engine, ImapCommand ic, int index) var labels = new MessageLabelsChangedEventArgs (index); var flags = new MessageFlagsChangedEventArgs (index); var modSeq = new ModSeqChangedEventArgs (index); - var ctx = (FetchStreamContextBase) ic.UserData; + var ctx = (FetchStreamContextBase) ic.UserData!; var sectionBuilder = new StringBuilder (); bool annotationsChanged = false; bool modSeqChanged = false; @@ -2485,7 +2486,7 @@ static string GetBodyPartQuery (string partSpec, bool headersOnly, out string[] return query; } - ImapCommand QueueGetHeadersCommand (UniqueId uid, CancellationToken cancellationToken, ITransferProgress progress, out FetchStreamContext ctx) + ImapCommand QueueGetHeadersCommand (UniqueId uid, CancellationToken cancellationToken, ITransferProgress? progress, out FetchStreamContext ctx) { if (!uid.IsValid) throw new ArgumentException ("The uid is invalid.", nameof (uid)); @@ -2549,7 +2550,7 @@ void ProcessGetHeadersResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId /// /// The server replied with a NO or BAD response. /// - public override HeaderList GetHeaders (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override HeaderList GetHeaders (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetHeadersCommand (uid, cancellationToken, progress, out var ctx); Section section; @@ -2605,7 +2606,7 @@ public override HeaderList GetHeaders (UniqueId uid, CancellationToken cancellat /// /// The server replied with a NO or BAD response. /// - public override async Task GetHeadersAsync (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override async Task GetHeadersAsync (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetHeadersCommand (uid, cancellationToken, progress, out var ctx); Section section; @@ -2621,7 +2622,7 @@ public override async Task GetHeadersAsync (UniqueId uid, Cancellati return await ParseHeadersAsync (section.Stream, cancellationToken).ConfigureAwait (false); } - ImapCommand QueueGetHeadersCommand (UniqueId uid, string partSpecifier, CancellationToken cancellationToken, ITransferProgress progress, out FetchStreamContext ctx, out string[] tags) + ImapCommand QueueGetHeadersCommand (UniqueId uid, string partSpecifier, CancellationToken cancellationToken, ITransferProgress? progress, out FetchStreamContext ctx, out string[] tags) { if (!uid.IsValid) throw new ArgumentException ("The uid is invalid.", nameof (uid)); @@ -2645,8 +2646,10 @@ void ProcessGetHeadersResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId { ProcessFetchResponse (ic); - if (!ctx.TryGetSection (uid, tags[0], out section, true)) + if (!ctx.TryGetSection (uid, tags[0], out var sect, true)) throw new MessageNotFoundException ("The IMAP server did not return the requested body part headers."); + + section = sect; } /// @@ -2693,7 +2696,7 @@ void ProcessGetHeadersResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId /// /// The server replied with a NO or BAD response. /// - public virtual HeaderList GetHeaders (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual HeaderList GetHeaders (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetHeadersCommand (uid, partSpecifier, cancellationToken, progress, out var ctx, out var tags); Section section; @@ -2753,7 +2756,7 @@ public virtual HeaderList GetHeaders (UniqueId uid, string partSpecifier, Cancel /// /// The server replied with a NO or BAD response. /// - public virtual async Task GetHeadersAsync (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual async Task GetHeadersAsync (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetHeadersCommand (uid, partSpecifier, cancellationToken, progress, out var ctx, out var tags); Section section; @@ -2813,7 +2816,7 @@ public virtual async Task GetHeadersAsync (UniqueId uid, string part /// /// The server replied with a NO or BAD response. /// - public override HeaderList GetHeaders (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override HeaderList GetHeaders (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (!uid.IsValid) throw new ArgumentException ("The uid is invalid.", nameof (uid)); @@ -2868,7 +2871,7 @@ public override HeaderList GetHeaders (UniqueId uid, BodyPart part, Cancellation /// /// The server replied with a NO or BAD response. /// - public override Task GetHeadersAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override Task GetHeadersAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (!uid.IsValid) throw new ArgumentException ("The uid is invalid.", nameof (uid)); @@ -2879,7 +2882,7 @@ public override Task GetHeadersAsync (UniqueId uid, BodyPart part, C return GetHeadersAsync (uid, part.PartSpecifier, cancellationToken, progress); } - ImapCommand QueueGetHeadersCommand (int index, CancellationToken cancellationToken, ITransferProgress progress, out FetchStreamContext ctx) + ImapCommand QueueGetHeadersCommand (int index, CancellationToken cancellationToken, ITransferProgress? progress, out FetchStreamContext ctx) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); @@ -2943,7 +2946,7 @@ void ProcessGetHeadersResponse (ImapCommand ic, FetchStreamContext ctx, int inde /// /// The server replied with a NO or BAD response. /// - public override HeaderList GetHeaders (int index, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override HeaderList GetHeaders (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetHeadersCommand (index, cancellationToken, progress, out var ctx); Section section; @@ -2999,7 +3002,7 @@ public override HeaderList GetHeaders (int index, CancellationToken cancellation /// /// The server replied with a NO or BAD response. /// - public override async Task GetHeadersAsync (int index, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override async Task GetHeadersAsync (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetHeadersCommand (index, cancellationToken, progress, out var ctx); Section section; @@ -3015,7 +3018,7 @@ public override async Task GetHeadersAsync (int index, CancellationT return await ParseHeadersAsync (section.Stream, cancellationToken).ConfigureAwait (false); } - ImapCommand QueueGetHeadersCommand (int index, string partSpecifier, CancellationToken cancellationToken, ITransferProgress progress, out FetchStreamContext ctx, out string[] tags) + ImapCommand QueueGetHeadersCommand (int index, string partSpecifier, CancellationToken cancellationToken, ITransferProgress? progress, out FetchStreamContext ctx, out string[] tags) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); @@ -3087,7 +3090,7 @@ void ProcessGetHeadersResponse (ImapCommand ic, FetchStreamContext ctx, int inde /// /// The server replied with a NO or BAD response. /// - public virtual HeaderList GetHeaders (int index, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual HeaderList GetHeaders (int index, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetHeadersCommand (index, partSpecifier, cancellationToken, progress, out var ctx, out var tags); Section section; @@ -3147,7 +3150,7 @@ public virtual HeaderList GetHeaders (int index, string partSpecifier, Cancellat /// /// The server replied with a NO or BAD response. /// - public virtual async Task GetHeadersAsync (int index, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual async Task GetHeadersAsync (int index, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetHeadersCommand (index, partSpecifier, cancellationToken, progress, out var ctx, out var tags); Section section; @@ -3207,7 +3210,7 @@ public virtual async Task GetHeadersAsync (int index, string partSpe /// /// The server replied with a NO or BAD response. /// - public override HeaderList GetHeaders (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override HeaderList GetHeaders (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); @@ -3262,7 +3265,7 @@ public override HeaderList GetHeaders (int index, BodyPart part, CancellationTok /// /// The server replied with a NO or BAD response. /// - public override Task GetHeadersAsync (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override Task GetHeadersAsync (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); @@ -3337,7 +3340,7 @@ void ProcessGetMessageResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId /// /// The server replied with a NO or BAD response. /// - public override MimeMessage GetMessage (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override MimeMessage GetMessage (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetMessageCommand (uid, cancellationToken, progress, out var ctx); Section section; @@ -3393,7 +3396,7 @@ public override MimeMessage GetMessage (UniqueId uid, CancellationToken cancella /// /// The server replied with a NO or BAD response. /// - public override async Task GetMessageAsync (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override async Task GetMessageAsync (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetMessageCommand (uid, cancellationToken, progress, out var ctx); Section section; @@ -3473,7 +3476,7 @@ void ProcessGetMessageResponse (ImapCommand ic, FetchStreamContext ctx, int inde /// /// The server replied with a NO or BAD response. /// - public override MimeMessage GetMessage (int index, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override MimeMessage GetMessage (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetMessageCommand (index, cancellationToken, progress, out var ctx); Section section; @@ -3529,7 +3532,7 @@ public override MimeMessage GetMessage (int index, CancellationToken cancellatio /// /// The server replied with a NO or BAD response. /// - public override async Task GetMessageAsync (int index, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override async Task GetMessageAsync (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetMessageCommand (index, cancellationToken, progress, out var ctx); Section section; @@ -3565,7 +3568,7 @@ ImapCommand QueueGetBodyPartCommand (UniqueId uid, string partSpecifier, Cancell return ic; } - void ProcessGetBodyPartResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId uid, string[] tags, out ChainedStream chained, out bool dispose) + void ProcessGetBodyPartResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId uid, string[] tags, out ChainedStream? chained, out bool dispose) { ProcessFetchResponse (ic); @@ -3648,7 +3651,7 @@ void RemoveMessageHeaders (MimeEntity entity) /// /// The server replied with a NO or BAD response. /// - public virtual MimeEntity GetBodyPart (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual MimeEntity GetBodyPart (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetBodyPartCommand (uid, partSpecifier, cancellationToken, progress, out var ctx, out var tags); ChainedStream chained; @@ -3719,7 +3722,7 @@ public virtual MimeEntity GetBodyPart (UniqueId uid, string partSpecifier, Cance /// /// The server replied with a NO or BAD response. /// - public virtual async Task GetBodyPartAsync (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual async Task GetBodyPartAsync (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetBodyPartCommand (uid, partSpecifier, cancellationToken, progress, out var ctx, out var tags); ChainedStream chained; @@ -3788,7 +3791,7 @@ public virtual async Task GetBodyPartAsync (UniqueId uid, string par /// /// The server replied with a NO or BAD response. /// - public override MimeEntity GetBodyPart (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override MimeEntity GetBodyPart (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (!uid.IsValid) throw new ArgumentException ("The uid is invalid.", nameof (uid)); @@ -3846,7 +3849,7 @@ public override MimeEntity GetBodyPart (UniqueId uid, BodyPart part, Cancellatio /// /// The server replied with a NO or BAD response. /// - public override Task GetBodyPartAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override Task GetBodyPartAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (!uid.IsValid) throw new ArgumentException ("The uid is invalid.", nameof (uid)); @@ -3878,7 +3881,7 @@ ImapCommand QueueGetBodyPartCommand (int index, string partSpecifier, Cancellati return ic; } - void ProcessGetBodyPartResponse (ImapCommand ic, FetchStreamContext ctx, int index, string[] tags, out ChainedStream chained, out bool dispose) + void ProcessGetBodyPartResponse (ImapCommand ic, FetchStreamContext ctx, int index, string[] tags, out ChainedStream? chained, out bool dispose) { ProcessFetchResponse (ic); @@ -3948,7 +3951,7 @@ void ProcessGetBodyPartResponse (ImapCommand ic, FetchStreamContext ctx, int ind /// /// The server replied with a NO or BAD response. /// - public virtual MimeEntity GetBodyPart (int index, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual MimeEntity GetBodyPart (int index, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetBodyPartCommand (index, partSpecifier, cancellationToken, progress, out var ctx, out var tags); ChainedStream chained; @@ -4016,7 +4019,7 @@ public virtual MimeEntity GetBodyPart (int index, string partSpecifier, Cancella /// /// The server replied with a NO or BAD response. /// - public virtual async Task GetBodyPartAsync (int index, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual async Task GetBodyPartAsync (int index, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetBodyPartCommand (index, partSpecifier, cancellationToken, progress, out var ctx, out var tags); ChainedStream chained; @@ -4082,7 +4085,7 @@ public virtual async Task GetBodyPartAsync (int index, string partSp /// /// The server replied with a NO or BAD response. /// - public override MimeEntity GetBodyPart (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override MimeEntity GetBodyPart (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); @@ -4137,7 +4140,7 @@ public override MimeEntity GetBodyPart (int index, BodyPart part, CancellationTo /// /// The server replied with a NO or BAD response. /// - public override Task GetBodyPartAsync (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override Task GetBodyPartAsync (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); @@ -4148,7 +4151,7 @@ public override Task GetBodyPartAsync (int index, BodyPart part, Can return GetBodyPartAsync (index, part.PartSpecifier, cancellationToken, progress); } - ImapCommand QueueGetStreamCommand (UniqueId uid, int offset, int count, CancellationToken cancellationToken, ITransferProgress progress, out FetchStreamContext ctx) + ImapCommand? QueueGetStreamCommand (UniqueId uid, int offset, int count, CancellationToken cancellationToken, ITransferProgress? progress, out FetchStreamContext? ctx) { if (!uid.IsValid) throw new ArgumentException ("The uid is invalid.", nameof (uid)); @@ -4179,8 +4182,10 @@ void ProcessGetStreamResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId { ProcessFetchResponse (ic); - if (!ctx.TryGetSection (uid, string.Empty, out section, true)) + if (!ctx.TryGetSection (uid, string.Empty, out var sect, true)) throw new MessageNotFoundException ("The IMAP server did not return the requested stream."); + + section = sect; } /// @@ -4233,7 +4238,7 @@ void ProcessGetStreamResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId /// /// The server replied with a NO or BAD response. /// - public override Stream GetStream (UniqueId uid, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override Stream GetStream (UniqueId uid, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetStreamCommand (uid, offset, count, cancellationToken, progress, out var ctx); @@ -4303,7 +4308,7 @@ public override Stream GetStream (UniqueId uid, int offset, int count, Cancellat /// /// The server replied with a NO or BAD response. /// - public override async Task GetStreamAsync (UniqueId uid, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override async Task GetStreamAsync (UniqueId uid, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetStreamCommand (uid, offset, count, cancellationToken, progress, out var ctx); @@ -4323,7 +4328,7 @@ public override async Task GetStreamAsync (UniqueId uid, int offset, int return section.Stream; } - ImapCommand QueueGetStreamCommand (int index, int offset, int count, CancellationToken cancellationToken, ITransferProgress progress, out FetchStreamContext ctx) + ImapCommand? QueueGetStreamCommand (int index, int offset, int count, CancellationToken cancellationToken, ITransferProgress progress, out FetchStreamContext? ctx) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); @@ -4407,7 +4412,7 @@ void ProcessGetStreamResponse (ImapCommand ic, FetchStreamContext ctx, int index /// /// The server replied with a NO or BAD response. /// - public override Stream GetStream (int index, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override Stream GetStream (int index, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetStreamCommand (index, offset, count, cancellationToken, progress, out var ctx); @@ -4476,7 +4481,7 @@ public override Stream GetStream (int index, int offset, int count, Cancellation /// /// The server replied with a NO or BAD response. /// - public override async Task GetStreamAsync (int index, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override async Task GetStreamAsync (int index, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetStreamCommand (index, offset, count, cancellationToken, progress, out var ctx); @@ -4573,7 +4578,7 @@ void ProcessGetStreamResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId /// /// The server replied with a NO or BAD response. /// - public override Stream GetStream (UniqueId uid, string section, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override Stream GetStream (UniqueId uid, string section, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetStreamCommand (uid, section, cancellationToken, progress, out var ctx); Section s; @@ -4638,7 +4643,7 @@ public override Stream GetStream (UniqueId uid, string section, CancellationToke /// /// The server replied with a NO or BAD response. /// - public override async Task GetStreamAsync (UniqueId uid, string section, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override async Task GetStreamAsync (UniqueId uid, string section, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetStreamCommand (uid, section, cancellationToken, progress, out var ctx); Section s; @@ -4654,7 +4659,7 @@ public override async Task GetStreamAsync (UniqueId uid, string section, return s.Stream; } - ImapCommand QueueGetStreamCommand (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken, ITransferProgress progress, out FetchStreamContext ctx) + ImapCommand? QueueGetStreamCommand (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken, ITransferProgress progress, out FetchStreamContext? ctx) { if (!uid.IsValid) throw new ArgumentException ("The uid is invalid.", nameof (uid)); @@ -4742,7 +4747,7 @@ ImapCommand QueueGetStreamCommand (UniqueId uid, string section, int offset, int /// /// The server replied with a NO or BAD response. /// - public override Stream GetStream (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override Stream GetStream (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetStreamCommand (uid, section, offset, count, cancellationToken, progress, out var ctx); @@ -4818,7 +4823,7 @@ public override Stream GetStream (UniqueId uid, string section, int offset, int /// /// The server replied with a NO or BAD response. /// - public override async Task GetStreamAsync (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override async Task GetStreamAsync (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetStreamCommand (uid, section, offset, count, cancellationToken, progress, out var ctx); @@ -4913,7 +4918,7 @@ void ProcessGetStreamResponse (ImapCommand ic, FetchStreamContext ctx, int index /// /// The server replied with a NO or BAD response. /// - public override Stream GetStream (int index, string section, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override Stream GetStream (int index, string section, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetStreamCommand (index, section, cancellationToken, progress, out var ctx); Section s; @@ -4975,7 +4980,7 @@ public override Stream GetStream (int index, string section, CancellationToken c /// /// The server replied with a NO or BAD response. /// - public override async Task GetStreamAsync (int index, string section, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override async Task GetStreamAsync (int index, string section, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetStreamCommand (index, section, cancellationToken, progress, out var ctx); Section s; @@ -4991,7 +4996,7 @@ public override async Task GetStreamAsync (int index, string section, Ca return s.Stream; } - ImapCommand QueueGetStreamCommand (int index, string section, int offset, int count, CancellationToken cancellationToken, ITransferProgress progress, out FetchStreamContext ctx) + ImapCommand? QueueGetStreamCommand (int index, string section, int offset, int count, CancellationToken cancellationToken, ITransferProgress progress, out FetchStreamContext? ctx) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); @@ -5080,7 +5085,7 @@ ImapCommand QueueGetStreamCommand (int index, string section, int offset, int co /// /// The server replied with a NO or BAD response. /// - public override Stream GetStream (int index, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override Stream GetStream (int index, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetStreamCommand (index, section, offset, count, cancellationToken, progress, out var ctx); @@ -5155,7 +5160,7 @@ public override Stream GetStream (int index, string section, int offset, int cou /// /// The server replied with a NO or BAD response. /// - public override async Task GetStreamAsync (int index, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override async Task GetStreamAsync (int index, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetStreamCommand (index, section, offset, count, cancellationToken, progress, out var ctx); @@ -5180,7 +5185,7 @@ class FetchStreamCallbackContext : FetchStreamContextBase readonly ImapFolder folder; readonly object callback; - public FetchStreamCallbackContext (ImapFolder folder, object callback, ITransferProgress progress) : base (progress) + public FetchStreamCallbackContext (ImapFolder folder, object callback, ITransferProgress? progress) : base (progress) { this.folder = folder; this.callback = callback; @@ -5306,7 +5311,7 @@ IEnumerable QueueGetStreamsCommands (FetchStreamCallbackContext ctx /// /// The server replied with a NO or BAD response. /// - public virtual void GetStreams (IList uids, ImapFetchStreamCallback callback, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual void GetStreams (IList uids, ImapFetchStreamCallback callback, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { ValidateArguments (uids, callback); @@ -5365,7 +5370,7 @@ public virtual void GetStreams (IList uids, ImapFetchStreamCallback ca /// /// The server replied with a NO or BAD response. /// - public virtual async Task GetStreamsAsync (IList uids, ImapFetchStreamAsyncCallback callback, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual async Task GetStreamsAsync (IList uids, ImapFetchStreamAsyncCallback callback, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { ValidateArguments (uids, callback); @@ -5450,7 +5455,7 @@ ImapCommand QueueGetStreamsCommand (FetchStreamCallbackContext ctx, IList i /// /// The server replied with a NO or BAD response. /// - public virtual void GetStreams (IList indexes, ImapFetchStreamCallback callback, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual void GetStreams (IList indexes, ImapFetchStreamCallback callback, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { ValidateArguments (indexes, callback); @@ -5509,7 +5514,7 @@ public virtual void GetStreams (IList indexes, ImapFetchStreamCallback call /// /// The server replied with a NO or BAD response. /// - public virtual async Task GetStreamsAsync (IList indexes, ImapFetchStreamAsyncCallback callback, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual async Task GetStreamsAsync (IList indexes, ImapFetchStreamAsyncCallback callback, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { ValidateArguments (indexes, callback); @@ -5595,7 +5600,7 @@ ImapCommand QueueGetStreamsCommand (FetchStreamCallbackContext ctx, int min, int /// /// The server replied with a NO or BAD response. /// - public virtual void GetStreams (int min, int max, ImapFetchStreamCallback callback, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual void GetStreams (int min, int max, ImapFetchStreamCallback callback, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { ValidateArguments (min, max, callback); @@ -5655,7 +5660,7 @@ public virtual void GetStreams (int min, int max, ImapFetchStreamCallback callba /// /// The server replied with a NO or BAD response. /// - public virtual async Task GetStreamsAsync (int min, int max, ImapFetchStreamAsyncCallback callback, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual async Task GetStreamsAsync (int min, int max, ImapFetchStreamAsyncCallback callback, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { ValidateArguments (min, max, callback); diff --git a/MailKit/Net/Imap/ImapFolderFlags.cs b/MailKit/Net/Imap/ImapFolderFlags.cs index ff1258fa66..778971461e 100644 --- a/MailKit/Net/Imap/ImapFolderFlags.cs +++ b/MailKit/Net/Imap/ImapFolderFlags.cs @@ -39,7 +39,7 @@ public partial class ImapFolder static readonly IStoreFlagsRequest AddDeletedFlag = new StoreFlagsRequest (StoreAction.Add, MessageFlags.Deleted) { Silent = true }; static readonly IStoreFlagsRequest RemoveDeletedFlag = new StoreFlagsRequest (StoreAction.Remove, MessageFlags.Deleted) { Silent = true }; - static void ProcessUnmodified (ImapCommand ic, ref UniqueIdSet uids, ulong? modseq) + static void ProcessUnmodified (ImapCommand ic, ref UniqueIdSet? uids, ulong? modseq) { if (modseq.HasValue) { foreach (var rc in ic.RespCodes.OfType ()) { @@ -172,7 +172,7 @@ IEnumerable QueueStoreCommands (IList uids, IStoreFlagsRe /// public override IList Store (IList uids, IStoreFlagsRequest request, CancellationToken cancellationToken = default) { - UniqueIdSet unmodified = null; + UniqueIdSet? unmodified = null; foreach (var ic in QueueStoreCommands (uids, request, cancellationToken)) { Engine.Run (ic); @@ -236,7 +236,7 @@ public override IList Store (IList uids, IStoreFlagsRequest /// public override async Task> StoreAsync (IList uids, IStoreFlagsRequest request, CancellationToken cancellationToken = default) { - UniqueIdSet unmodified = null; + UniqueIdSet? unmodified = null; foreach (var ic in QueueStoreCommands (uids, request, cancellationToken)) { await Engine.RunAsync (ic).ConfigureAwait (false); @@ -252,7 +252,7 @@ public override async Task> StoreAsync (IList uids, IS return unmodified; } - ImapCommand QueueStoreCommand (IList indexes, IStoreFlagsRequest request, CancellationToken cancellationToken) + ImapCommand? QueueStoreCommand (IList indexes, IStoreFlagsRequest request, CancellationToken cancellationToken) { if (indexes == null) throw new ArgumentNullException (nameof (indexes)); @@ -569,7 +569,7 @@ IEnumerable QueueStoreCommands (IList uids, IStoreLabelsR /// public override IList Store (IList uids, IStoreLabelsRequest request, CancellationToken cancellationToken = default) { - UniqueIdSet unmodified = null; + UniqueIdSet? unmodified = null; foreach (var ic in QueueStoreCommands (uids, request, cancellationToken)) { Engine.Run (ic); @@ -633,7 +633,7 @@ public override IList Store (IList uids, IStoreLabelsRequest /// public override async Task> StoreAsync (IList uids, IStoreLabelsRequest request, CancellationToken cancellationToken = default) { - UniqueIdSet unmodified = null; + UniqueIdSet? unmodified = null; foreach (var ic in QueueStoreCommands (uids, request, cancellationToken)) { await Engine.RunAsync (ic).ConfigureAwait (false); @@ -649,7 +649,7 @@ public override async Task> StoreAsync (IList uids, IS return unmodified; } - ImapCommand QueueStoreCommand (IList indexes, IStoreLabelsRequest request, CancellationToken cancellationToken) + ImapCommand? QueueStoreCommand (IList indexes, IStoreLabelsRequest request, CancellationToken cancellationToken) { if (indexes == null) throw new ArgumentNullException (nameof (indexes)); diff --git a/MailKit/Net/Imap/ImapFolderSearch.cs b/MailKit/Net/Imap/ImapFolderSearch.cs index 15ba2e9e02..bbf8c6dcc1 100644 --- a/MailKit/Net/Imap/ImapFolderSearch.cs +++ b/MailKit/Net/Imap/ImapFolderSearch.cs @@ -31,6 +31,7 @@ using System.Globalization; using System.Threading.Tasks; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using MailKit.Search; @@ -53,7 +54,7 @@ static string FormatDateTime (DateTime date) return date.ToString ("d-MMM-yyyy", CultureInfo.InvariantCulture); } - bool IsBadCharset (ImapCommand ic, string charset) + bool IsBadCharset (ImapCommand ic, string? charset) { // Note: if `charset` is null, then the charset is actually US-ASCII... return ic.Response == ImapCommandResponse.No && @@ -61,7 +62,7 @@ bool IsBadCharset (ImapCommand ic, string charset) charset != null && !Engine.SupportedCharsets.Contains (charset); } - void AddTextArgument (StringBuilder builder, List args, string text, ref string charset) + void AddTextArgument (StringBuilder builder, List args, string text, ref string? charset) { if (IsAscii (text)) { builder.Append ("%S"); @@ -85,7 +86,7 @@ void AddTextArgument (StringBuilder builder, List args, string text, ref args.Add (buffer); } - void AddKeywordArgument (StringBuilder builder, List args, string text, ref string charset) + void AddKeywordArgument (StringBuilder builder, List args, string text, ref string? charset) { // Note: Technically, the IMAP RFC states that keywords are not allowed to start with '\', // but some non-rfc-compliant IMAP servers do it anyway, so we need to allow it here. @@ -108,7 +109,7 @@ void AddKeywordArgument (StringBuilder builder, List args, string text, } } - void BuildQuery (StringBuilder builder, SearchQuery query, List args, bool parens, ref string charset) + void BuildQuery (StringBuilder builder, SearchQuery query, List args, bool parens, ref string? charset) { AnnotationSearchQuery annotation; NumericSearchQuery numeric; @@ -396,7 +397,7 @@ void BuildQuery (StringBuilder builder, SearchQuery query, List args, bo } } - string BuildQueryExpression (SearchQuery query, List args, out string charset) + string BuildQueryExpression (SearchQuery query, List args, out string? charset) { var builder = new StringBuilder (); @@ -557,17 +558,17 @@ static void ParseESearchResults (ImapEngine engine, ImapCommand ic, SearchResult var min = ImapEngine.ParseNumber (token, true, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - results.Min = new UniqueId (ic.Folder.UidValidity, min); + results.Min = new UniqueId (ic.Folder!.UidValidity, min); } else if (atom.Equals ("MAX", StringComparison.OrdinalIgnoreCase)) { ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ESEARCH", token); var max = ImapEngine.ParseNumber (token, true, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - results.Max = new UniqueId (ic.Folder.UidValidity, max); + results.Max = new UniqueId (ic.Folder!.UidValidity, max); } else if (atom.Equals ("ALL", StringComparison.OrdinalIgnoreCase)) { ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ESEARCH", token); - var uids = ImapEngine.ParseUidSet (token, ic.Folder.UidValidity, out minValue, out maxValue, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); + var uids = ImapEngine.ParseUidSet (token, ic.Folder!.UidValidity, out minValue, out maxValue, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); if (!hasCount) results.Count = uids.Count; @@ -688,17 +689,17 @@ static async Task ParseESearchResultsAsync (ImapEngine engine, ImapCommand ic, S var min = ImapEngine.ParseNumber (token, true, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - results.Min = new UniqueId (ic.Folder.UidValidity, min); + results.Min = new UniqueId (ic.Folder!.UidValidity, min); } else if (atom.Equals ("MAX", StringComparison.OrdinalIgnoreCase)) { ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ESEARCH", token); var max = ImapEngine.ParseNumber (token, true, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - results.Max = new UniqueId (ic.Folder.UidValidity, max); + results.Max = new UniqueId (ic.Folder!.UidValidity, max); } else if (atom.Equals ("ALL", StringComparison.OrdinalIgnoreCase)) { ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ESEARCH", token); - var uids = ImapEngine.ParseUidSet (token, ic.Folder.UidValidity, out minValue, out maxValue, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); + var uids = ImapEngine.ParseUidSet (token, ic.Folder!.UidValidity, out minValue, out maxValue, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); if (!hasCount) results.Count = uids.Count; @@ -720,7 +721,7 @@ static async Task ParseESearchResultsAsync (ImapEngine engine, ImapCommand ic, S static Task UntaggedESearchHandler (ImapEngine engine, ImapCommand ic, int index, bool doAsync) { - var results = (SearchResults) ic.UserData; + var results = (SearchResults) ic.UserData!; if (doAsync) return ParseESearchResultsAsync (engine, ic, results); @@ -747,7 +748,7 @@ static void ParseSearchResults (ImapEngine engine, ImapCommand ic, SearchResults token = engine.ReadToken (ic.CancellationToken); uid = ImapEngine.ParseNumber (token, true, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "SEARCH", token); - uids.Add (new UniqueId (ic.Folder.UidValidity, uid)); + uids.Add (new UniqueId (ic.Folder!.UidValidity, uid)); min = Math.Min (min, uid); max = Math.Max (max, uid); } while (true); @@ -778,8 +779,8 @@ static void ParseSearchResults (ImapEngine engine, ImapCommand ic, SearchResults results.UniqueIds = uids; results.Count = uids.Count; if (uids.Count > 0) { - results.Min = new UniqueId (ic.Folder.UidValidity, min); - results.Max = new UniqueId (ic.Folder.UidValidity, max); + results.Min = new UniqueId (ic.Folder!.UidValidity, min); + results.Max = new UniqueId (ic.Folder!.UidValidity, max); } } @@ -800,7 +801,7 @@ static async Task ParseSearchResultsAsync (ImapEngine engine, ImapCommand ic, Se token = await engine.ReadTokenAsync (ic.CancellationToken).ConfigureAwait (false); uid = ImapEngine.ParseNumber (token, true, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "SEARCH", token); - uids.Add (new UniqueId (ic.Folder.UidValidity, uid)); + uids.Add (new UniqueId (ic.Folder!.UidValidity, uid)); min = Math.Min (min, uid); max = Math.Max (max, uid); } while (true); @@ -831,14 +832,14 @@ static async Task ParseSearchResultsAsync (ImapEngine engine, ImapCommand ic, Se results.UniqueIds = uids; results.Count = uids.Count; if (uids.Count > 0) { - results.Min = new UniqueId (ic.Folder.UidValidity, min); - results.Max = new UniqueId (ic.Folder.UidValidity, max); + results.Min = new UniqueId (ic.Folder!.UidValidity, min); + results.Max = new UniqueId (ic.Folder!.UidValidity, max); } } static Task UntaggedSearchHandler (ImapEngine engine, ImapCommand ic, int index, bool doAsync) { - var results = (SearchResults) ic.UserData; + var results = (SearchResults) ic.UserData!; if (doAsync) return ParseSearchResultsAsync (engine, ic, results); @@ -882,7 +883,7 @@ SearchResults ProcessSearchResponse (ImapCommand ic) ic.ThrowIfNotOk ("SEARCH"); - return (SearchResults) ic.UserData; + return (SearchResults) ic.UserData!; } /// @@ -987,7 +988,7 @@ public virtual async Task SearchAsync (string query, Cancellation return ProcessSearchResponse (ic); } - ImapCommand QueueSearchCommand (SearchOptions options, SearchQuery query, CancellationToken cancellationToken, out string charset) + ImapCommand QueueSearchCommand (SearchOptions options, SearchQuery query, CancellationToken cancellationToken, out string? charset) { if (query == null) throw new ArgumentNullException (nameof (query)); @@ -1046,7 +1047,7 @@ ImapCommand QueueSearchCommand (SearchOptions options, SearchQuery query, Cancel return ic; } - bool TryProcessSearchResponse (ImapCommand ic, string charset, bool retry, out SearchResults results) + bool TryProcessSearchResponse (ImapCommand ic, string? charset, bool retry, [NotNullWhen (true)] out SearchResults? results) { ProcessResponseCodes (ic, null); @@ -1059,14 +1060,14 @@ bool TryProcessSearchResponse (ImapCommand ic, string charset, bool retry, out S throw ImapCommandException.Create ("SEARCH", ic); } - results = (SearchResults) ic.UserData; + results = (SearchResults) ic.UserData!; return true; } SearchResults Search (SearchOptions options, SearchQuery query, bool retry, CancellationToken cancellationToken) { - var ic = QueueSearchCommand (options, query, cancellationToken, out string charset); + var ic = QueueSearchCommand (options, query, cancellationToken, out string? charset); Engine.Run (ic); @@ -1126,7 +1127,7 @@ public override SearchResults Search (SearchOptions options, SearchQuery query, async Task SearchAsync (SearchOptions options, SearchQuery query, bool retry, CancellationToken cancellationToken) { - var ic = QueueSearchCommand (options, query, cancellationToken, out string charset); + var ic = QueueSearchCommand (options, query, cancellationToken, out string? charset); await Engine.RunAsync (ic).ConfigureAwait (false); @@ -1217,7 +1218,7 @@ SearchResults ProcessSortResponse (ImapCommand ic) ic.ThrowIfNotOk ("SORT"); - return (SearchResults) ic.UserData; + return (SearchResults) ic.UserData!; } /// @@ -1328,7 +1329,7 @@ public virtual async Task SortAsync (string query, CancellationTo return ProcessSortResponse (ic); } - ImapCommand QueueSortCommand (SearchQuery query, IList orderBy, CancellationToken cancellationToken, out string charset) + ImapCommand QueueSortCommand (SearchQuery query, IList orderBy, CancellationToken cancellationToken, out string? charset) { if (query == null) throw new ArgumentNullException (nameof (query)); @@ -1369,7 +1370,7 @@ ImapCommand QueueSortCommand (SearchQuery query, IList orderBy, Cancell return ic; } - bool TryProcessSortResponse (ImapCommand ic, string charset, bool retry, out IList results) + bool TryProcessSortResponse (ImapCommand ic, string? charset, bool retry, [NotNullWhen (true)] out IList? results) { ProcessResponseCodes (ic, null); @@ -1382,18 +1383,18 @@ bool TryProcessSortResponse (ImapCommand ic, string charset, bool retry, out ILi throw ImapCommandException.Create ("SORT", ic); } - results = ((SearchResults) ic.UserData).UniqueIds; + results = ((SearchResults) ic.UserData!).UniqueIds; return true; } IList Sort (SearchQuery query, IList orderBy, bool retry, CancellationToken cancellationToken) { - var ic = QueueSortCommand (query, orderBy, cancellationToken, out string charset); + var ic = QueueSortCommand (query, orderBy, cancellationToken, out string? charset); Engine.Run (ic); - if (TryProcessSortResponse (ic, charset, retry, out IList results)) + if (TryProcessSortResponse (ic, charset, retry, out IList? results)) return results; return Sort (query, orderBy, false, cancellationToken); @@ -1454,11 +1455,11 @@ public override IList Sort (SearchQuery query, IList orderBy, async Task> SortAsync (SearchQuery query, IList orderBy, bool retry, CancellationToken cancellationToken) { - var ic = QueueSortCommand (query, orderBy, cancellationToken, out string charset); + var ic = QueueSortCommand (query, orderBy, cancellationToken, out string? charset); await Engine.RunAsync (ic).ConfigureAwait (false); - if (TryProcessSortResponse (ic, charset, retry, out IList results)) + if (TryProcessSortResponse (ic, charset, retry, out IList? results)) return results; return await SortAsync (query, orderBy, false, cancellationToken).ConfigureAwait (false); @@ -1517,7 +1518,7 @@ public override Task> SortAsync (SearchQuery query, IList orderBy, CancellationToken cancellationToken, out string charset) + ImapCommand QueueSortCommand (SearchOptions options, SearchQuery query, IList orderBy, CancellationToken cancellationToken, out string? charset) { if (query == null) throw new ArgumentNullException (nameof (query)); @@ -1577,7 +1578,7 @@ ImapCommand QueueSortCommand (SearchOptions options, SearchQuery query, IList orderBy, bool retry, CancellationToken cancellationToken) { - var ic = QueueSortCommand (options, query, orderBy, cancellationToken, out string charset); + var ic = QueueSortCommand (options, query, orderBy, cancellationToken, out string? charset); Engine.Run (ic); - if (TryProcessSortResponse (ic, charset, retry, out SearchResults results)) + if (TryProcessSortResponse (ic, charset, retry, out SearchResults? results)) return results; return Sort (options, query, orderBy, false, cancellationToken); @@ -1662,11 +1663,11 @@ public override SearchResults Sort (SearchOptions options, SearchQuery query, IL async Task SortAsync (SearchOptions options, SearchQuery query, IList orderBy, bool retry, CancellationToken cancellationToken) { - var ic = QueueSortCommand (options, query, orderBy, cancellationToken, out string charset); + var ic = QueueSortCommand (options, query, orderBy, cancellationToken, out string? charset); await Engine.RunAsync (ic).ConfigureAwait (false); - if (TryProcessSortResponse (ic, charset, retry, out SearchResults results)) + if (TryProcessSortResponse (ic, charset, retry, out SearchResults? results)) return results; return await SortAsync (options, query, orderBy, false, cancellationToken).ConfigureAwait (false); @@ -1725,7 +1726,7 @@ public override Task SortAsync (SearchOptions options, SearchQuer return SortAsync (options, query, orderBy, true, cancellationToken); } - ImapCommand QueueThreadCommand (ThreadingAlgorithm algorithm, SearchQuery query, CancellationToken cancellationToken, out string charset) + ImapCommand QueueThreadCommand (ThreadingAlgorithm algorithm, SearchQuery query, CancellationToken cancellationToken, out string? charset) { if ((Engine.Capabilities & ImapCapabilities.Thread) == 0) throw new NotSupportedException ("The IMAP server does not support the THREAD extension."); @@ -1754,7 +1755,7 @@ ImapCommand QueueThreadCommand (ThreadingAlgorithm algorithm, SearchQuery query, return ic; } - bool TryProcessThreadResponse (ImapCommand ic, string charset, bool retry, out IList threads) + bool TryProcessThreadResponse (ImapCommand ic, string? charset, bool retry, [NotNullWhen (true)] out IList? threads) { ProcessResponseCodes (ic, null); @@ -1767,18 +1768,18 @@ bool TryProcessThreadResponse (ImapCommand ic, string charset, bool retry, out I throw ImapCommandException.Create ("THREAD", ic); } - threads = (IList) ic.UserData ?? Array.Empty (); + threads = (IList) ic.UserData! ?? Array.Empty (); return true; } IList Thread (ThreadingAlgorithm algorithm, SearchQuery query, bool retry, CancellationToken cancellationToken) { - var ic = QueueThreadCommand (algorithm, query, cancellationToken, out string charset); + var ic = QueueThreadCommand (algorithm, query, cancellationToken, out string? charset); Engine.Run (ic); - if (TryProcessThreadResponse (ic, charset, retry, out IList threads)) + if (TryProcessThreadResponse (ic, charset, retry, out IList? threads)) return threads; return Thread (algorithm, query, false, cancellationToken); @@ -1837,11 +1838,11 @@ public override IList Thread (ThreadingAlgorithm algorithm, Searc async Task> ThreadAsync (ThreadingAlgorithm algorithm, SearchQuery query, bool retry, CancellationToken cancellationToken) { - var ic = QueueThreadCommand (algorithm, query, cancellationToken, out string charset); + var ic = QueueThreadCommand (algorithm, query, cancellationToken, out string? charset); await Engine.RunAsync (ic).ConfigureAwait (false); - if (TryProcessThreadResponse (ic, charset, retry, out IList threads)) + if (TryProcessThreadResponse (ic, charset, retry, out IList? threads)) return threads; return await ThreadAsync (algorithm, query, false, cancellationToken).ConfigureAwait (false); @@ -1898,7 +1899,7 @@ public override Task> ThreadAsync (ThreadingAlgorithm algor return ThreadAsync (algorithm, query, true, cancellationToken); } - ImapCommand QueueThreadCommand (IList uids, ThreadingAlgorithm algorithm, SearchQuery query, CancellationToken cancellationToken, out string charset) + ImapCommand? QueueThreadCommand (IList uids, ThreadingAlgorithm algorithm, SearchQuery query, CancellationToken cancellationToken, out string? charset) { if (uids == null) throw new ArgumentNullException (nameof (uids)); @@ -1938,14 +1939,14 @@ ImapCommand QueueThreadCommand (IList uids, ThreadingAlgorithm algorit IList Thread (IList uids, ThreadingAlgorithm algorithm, SearchQuery query, bool retry, CancellationToken cancellationToken) { - var ic = QueueThreadCommand (uids, algorithm, query, cancellationToken, out string charset); + var ic = QueueThreadCommand (uids, algorithm, query, cancellationToken, out string? charset); if (ic == null) return Array.Empty (); Engine.Run (ic); - if (TryProcessThreadResponse (ic, charset, retry, out IList threads)) + if (TryProcessThreadResponse (ic, charset!, retry, out IList? threads)) return threads; return Thread (uids, algorithm, query, false, cancellationToken); @@ -2012,14 +2013,14 @@ public override IList Thread (IList uids, ThreadingAlgo async Task> ThreadAsync (IList uids, ThreadingAlgorithm algorithm, SearchQuery query, bool retry, CancellationToken cancellationToken) { - var ic = QueueThreadCommand (uids, algorithm, query, cancellationToken, out string charset); + var ic = QueueThreadCommand (uids, algorithm, query, cancellationToken, out string? charset); if (ic == null) return Array.Empty (); await Engine.RunAsync (ic).ConfigureAwait (false); - if (TryProcessThreadResponse (ic, charset, retry, out IList threads)) + if (TryProcessThreadResponse (ic, charset!, retry, out IList? threads)) return threads; return await ThreadAsync (uids, algorithm, query, false, cancellationToken).ConfigureAwait (false); diff --git a/MailKit/Net/Imap/ImapImplementation.cs b/MailKit/Net/Imap/ImapImplementation.cs index 90df432116..557f652e72 100644 --- a/MailKit/Net/Imap/ImapImplementation.cs +++ b/MailKit/Net/Imap/ImapImplementation.cs @@ -50,10 +50,10 @@ public class ImapImplementation /// public ImapImplementation () { - Properties = new Dictionary (); + Properties = new Dictionary (); } - string GetProperty (string property) + string? GetProperty (string property) { Properties.TryGetValue (property, out var value); @@ -70,7 +70,7 @@ string GetProperty (string property) /// /// /// The properties. - public Dictionary Properties { + public Dictionary Properties { get; private set; } @@ -84,7 +84,7 @@ public Dictionary Properties { /// /// /// The program name. - public string Name { + public string? Name { get { return GetProperty ("name"); } set { Properties["name"] = value; } } @@ -99,7 +99,7 @@ public string Name { /// /// /// The program version. - public string Version { + public string? Version { get { return GetProperty ("version"); } set { Properties["version"] = value; } } @@ -111,7 +111,7 @@ public string Version { /// Gets or sets the name of the operating system. /// /// The name of the operation system. - public string OS { + public string? OS { get { return GetProperty ("os"); } set { Properties["os"] = value; } } @@ -123,7 +123,7 @@ public string OS { /// Gets or sets the version of the operating system. /// /// The version of the operation system. - public string OSVersion { + public string? OSVersion { get { return GetProperty ("os-version"); } set { Properties["os-version"] = value; } } @@ -135,7 +135,7 @@ public string OSVersion { /// Gets or sets the name of the vendor. /// /// The name of the vendor. - public string Vendor { + public string? Vendor { get { return GetProperty ("vendor"); } set { Properties["vendor"] = value; } } @@ -147,7 +147,7 @@ public string Vendor { /// Gets or sets the support URL. /// /// The support URL. - public string SupportUrl { + public string? SupportUrl { get { return GetProperty ("support-url"); } set { Properties["support-url"] = value; } } @@ -159,7 +159,7 @@ public string SupportUrl { /// Gets or sets the postal address of the vendor. /// /// The postal address. - public string Address { + public string? Address { get { return GetProperty ("address"); } set { Properties["address"] = value; } } @@ -171,7 +171,7 @@ public string Address { /// Gets or sets the release date of the program. /// /// The release date. - public string ReleaseDate { + public string? ReleaseDate { get { return GetProperty ("date"); } set { Properties["date"] = value; } } @@ -183,7 +183,7 @@ public string ReleaseDate { /// Gets or sets the command used to start the program. /// /// The command used to start the program. - public string Command { + public string? Command { get { return GetProperty ("command"); } set { Properties["command"] = value; } } @@ -195,7 +195,7 @@ public string Command { /// Gets or sets the command-line arguments used to start the program. /// /// The command-line arguments used to start the program. - public string Arguments { + public string? Arguments { get { return GetProperty ("arguments"); } set { Properties["arguments"] = value; } } @@ -207,7 +207,7 @@ public string Arguments { /// Get or set the environment variables available to the program. /// /// The environment variables. - public string Environment { + public string? Environment { get { return GetProperty ("environment"); } set { Properties["environment"] = value; } } diff --git a/MailKit/Net/Imap/ImapUtils.cs b/MailKit/Net/Imap/ImapUtils.cs index c2ffa5b923..3e9ccfd628 100644 --- a/MailKit/Net/Imap/ImapUtils.cs +++ b/MailKit/Net/Imap/ImapUtils.cs @@ -36,6 +36,7 @@ using MimeKit; using MimeKit.Utils; +using System.Diagnostics.CodeAnalysis; namespace MailKit.Net.Imap { /// @@ -571,7 +572,7 @@ static void AddFolderAttribute (ref FolderAttributes attrs, string atom) attrs |= FolderAttributes.Flagged; } - static void AddFolder (ImapEngine engine, List list, ImapFolder folder, string encodedName, char delim, FolderAttributes attrs, bool isLsub, bool returnsSubscribed) + static void AddFolder (ImapEngine engine, List? list, ImapFolder? folder, string encodedName, char delim, FolderAttributes attrs, bool isLsub, bool returnsSubscribed) { if (folder != null || engine.TryGetCachedFolder (encodedName, out folder)) { if ((attrs & FolderAttributes.NonExistent) != 0) { @@ -608,12 +609,12 @@ static void AddFolder (ImapEngine engine, List list, ImapFolder fold list?.Add (folder); } - static void ProcessListExtensionProperty (ImapEngine engine, ref ImapFolder folder, string encodedName, char delim, FolderAttributes attrs, string property, string value) + static void ProcessListExtensionProperty (ImapEngine engine, ref ImapFolder? folder, string encodedName, char delim, FolderAttributes attrs, string property, string? value) { - if (property.Equals ("OLDNAME", StringComparison.OrdinalIgnoreCase)) { + if (property.Equals ("OLDNAME", StringComparison.OrdinalIgnoreCase) && value != null) { var oldEncodedName = value.TrimEnd (delim); - if (engine.FolderCache.TryGetValue (oldEncodedName, out ImapFolder oldFolder)) { + if (engine.FolderCache.TryGetValue (oldEncodedName, out ImapFolder? oldFolder)) { var args = new ImapFolderConstructorArgs (engine, encodedName, attrs, delim); engine.FolderCache.Remove (oldEncodedName); @@ -645,12 +646,12 @@ static char ParseFolderSeparator (ImapToken token, string format) /// if it is an LSUB response; otherwise, . /// if the LIST response is expected to return \Subscribed flags; otherwise, . /// The cancellation token. - public static void ParseFolderList (ImapEngine engine, List list, bool isLsub, bool returnsSubscribed, CancellationToken cancellationToken) + public static void ParseFolderList (ImapEngine engine, List? list, bool isLsub, bool returnsSubscribed, CancellationToken cancellationToken) { var format = string.Format (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, isLsub ? "LSUB" : "LIST", "{0}"); var token = engine.ReadToken (cancellationToken); var attrs = FolderAttributes.None; - ImapFolder folder = null; + ImapFolder? folder = null; string encodedName; char delim; @@ -731,12 +732,12 @@ public static void ParseFolderList (ImapEngine engine, List list, bo /// if it is an LSUB response; otherwise, . /// if the LIST response is expected to return \Subscribed flags; otherwise, . /// The cancellation token. - public static async Task ParseFolderListAsync (ImapEngine engine, List list, bool isLsub, bool returnsSubscribed, CancellationToken cancellationToken) + public static async Task ParseFolderListAsync (ImapEngine engine, List? list, bool isLsub, bool returnsSubscribed, CancellationToken cancellationToken) { var format = string.Format (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, isLsub ? "LSUB" : "LIST", "{0}"); var token = await engine.ReadTokenAsync (cancellationToken).ConfigureAwait (false); var attrs = FolderAttributes.None; - ImapFolder folder = null; + ImapFolder? folder = null; string encodedName; char delim; @@ -817,7 +818,7 @@ public static async Task ParseFolderListAsync (ImapEngine engine, ListWhether or not asynchronous IO methods should be used. public static Task UntaggedListHandler (ImapEngine engine, ImapCommand ic, int index, bool doAsync) { - var list = (List) ic.UserData; + var list = (List) ic.UserData!; if (doAsync) return ParseFolderListAsync (engine, list, ic.Lsub, ic.ListReturnsSubscribed, ic.CancellationToken); @@ -894,7 +895,7 @@ public static async Task ParseMetadataAsync (ImapEngine engine, MetadataCollecti /// Whether or not asynchronous IO methods should be used. public static Task UntaggedMetadataHandler (ImapEngine engine, ImapCommand ic, int index, bool doAsync) { - var metadata = (MetadataCollection) ic.UserData; + var metadata = (MetadataCollection) ic.UserData!; if (doAsync) return ParseMetadataAsync (engine, metadata, ic.CancellationToken); @@ -934,7 +935,7 @@ internal static async ValueTask ReadStringTokenAsync (ImapEngine engine, } } - internal static string ReadNStringToken (ImapEngine engine, string format, bool rfc2047, CancellationToken cancellationToken) + internal static string? ReadNStringToken (ImapEngine engine, string format, bool rfc2047, CancellationToken cancellationToken) { var token = engine.ReadToken (cancellationToken); string value; @@ -959,7 +960,7 @@ internal static string ReadNStringToken (ImapEngine engine, string format, bool return value; } - internal static async ValueTask ReadNStringTokenAsync (ImapEngine engine, string format, bool rfc2047, CancellationToken cancellationToken) + internal static async ValueTask ReadNStringTokenAsync (ImapEngine engine, string format, bool rfc2047, CancellationToken cancellationToken) { var token = await engine.ReadTokenAsync (cancellationToken).ConfigureAwait (false); string value; @@ -1089,7 +1090,7 @@ static async Task ParseParameterListAsync (StringBuilder builder, ImapEngine eng //static readonly string[] MediaTypes = new string[] { "text", "application", "audio", "image", "message", "multipart", "video" }; - static bool IsMediaTypeWithDefaultSubtype (string type, out string subtype) + static bool IsMediaTypeWithDefaultSubtype (string type, [NotNullWhen (true)] out string? subtype) { if (type.Equals ("text", StringComparison.OrdinalIgnoreCase)) { subtype = "plain"; @@ -1122,7 +1123,7 @@ static ContentType ParseContentType (ImapEngine engine, string format, Cancellat { var type = ReadNStringToken (engine, format, false, cancellationToken); var token = engine.PeekToken (cancellationToken); - string subtype; + string? subtype; if (token.Type == ImapTokenType.OpenParen || token.Type == ImapTokenType.Nil) { // Note: work around broken IMAP server implementations... @@ -1179,7 +1180,7 @@ static async Task ParseContentTypeAsync (ImapEngine engine, string { var type = await ReadNStringTokenAsync (engine, format, false, cancellationToken).ConfigureAwait (false); var token = await engine.PeekTokenAsync (cancellationToken).ConfigureAwait (false); - string subtype; + string? subtype; if (token.Type == ImapTokenType.OpenParen || token.Type == ImapTokenType.Nil) { // Note: work around broken IMAP server implementations... @@ -1232,7 +1233,7 @@ static async Task ParseContentTypeAsync (ImapEngine engine, string return contentType; } - static ContentDisposition ParseContentDisposition (ImapEngine engine, string format, CancellationToken cancellationToken) + static ContentDisposition? ParseContentDisposition (ImapEngine engine, string format, CancellationToken cancellationToken) { // body-fld-dsp = "(" string SP body-fld-param ")" / nil var token = engine.ReadToken (cancellationToken); @@ -1260,7 +1261,7 @@ static ContentDisposition ParseContentDisposition (ImapEngine engine, string for if (string.IsNullOrEmpty (dsp)) builder.Append (ContentDisposition.Attachment); else - builder.Append (dsp.Trim ('"')); + builder.Append (dsp!.Trim ('"')); token = engine.ReadToken (cancellationToken); @@ -1283,7 +1284,7 @@ static ContentDisposition ParseContentDisposition (ImapEngine engine, string for return disposition; } - static async Task ParseContentDispositionAsync (ImapEngine engine, string format, CancellationToken cancellationToken) + static async Task ParseContentDispositionAsync (ImapEngine engine, string format, CancellationToken cancellationToken) { // body-fld-dsp = "(" string SP body-fld-param ")" / nil var token = await engine.ReadTokenAsync (cancellationToken).ConfigureAwait (false); @@ -1311,7 +1312,7 @@ static async Task ParseContentDispositionAsync (ImapEngine e if (string.IsNullOrEmpty (dsp)) builder.Append (ContentDisposition.Attachment); else - builder.Append (dsp.Trim ('"')); + builder.Append (dsp!.Trim ('"')); token = await engine.ReadTokenAsync (cancellationToken).ConfigureAwait (false); @@ -1334,11 +1335,11 @@ static async Task ParseContentDispositionAsync (ImapEngine e return disposition; } - static string[] ParseContentLanguage (ImapEngine engine, string format, CancellationToken cancellationToken) + static string[]? ParseContentLanguage (ImapEngine engine, string format, CancellationToken cancellationToken) { var token = engine.ReadToken (cancellationToken); var languages = new List (); - string language; + string? language; switch (token.Type) { case ImapTokenType.Literal: @@ -1378,11 +1379,11 @@ static string[] ParseContentLanguage (ImapEngine engine, string format, Cancella return languages.ToArray (); } - static async Task ParseContentLanguageAsync (ImapEngine engine, string format, CancellationToken cancellationToken) + static async Task ParseContentLanguageAsync (ImapEngine engine, string format, CancellationToken cancellationToken) { var token = await engine.ReadTokenAsync (cancellationToken).ConfigureAwait (false); var languages = new List (); - string language; + string? language; switch (token.Type) { case ImapTokenType.Literal: @@ -1422,7 +1423,7 @@ static async Task ParseContentLanguageAsync (ImapEngine engine, string return languages.ToArray (); } - static Uri ParseContentLocation (string location) + static Uri? ParseContentLocation (string? location) { if (string.IsNullOrWhiteSpace (location)) return null; @@ -1436,14 +1437,14 @@ static Uri ParseContentLocation (string location) return null; } - static Uri ParseContentLocation (ImapEngine engine, string format, CancellationToken cancellationToken) + static Uri? ParseContentLocation (ImapEngine engine, string format, CancellationToken cancellationToken) { var location = ReadNStringToken (engine, format, false, cancellationToken); return ParseContentLocation (location); } - static async Task ParseContentLocationAsync (ImapEngine engine, string format, CancellationToken cancellationToken) + static async Task ParseContentLocationAsync (ImapEngine engine, string format, CancellationToken cancellationToken) { var location = await ReadNStringTokenAsync (engine, format, false, cancellationToken).ConfigureAwait (false); @@ -1866,7 +1867,7 @@ static async Task ShouldParseMultipartAsync (ImapEngine engine, Cancellati } } - public static BodyPart ParseBody (ImapEngine engine, string format, string path, CancellationToken cancellationToken) + public static BodyPart? ParseBody (ImapEngine engine, string format, string path, CancellationToken cancellationToken) { var token = engine.ReadToken (cancellationToken); @@ -1975,7 +1976,7 @@ public static BodyPart ParseBody (ImapEngine engine, string format, string path, return body; } - public static async Task ParseBodyAsync (ImapEngine engine, string format, string path, CancellationToken cancellationToken) + public static async Task ParseBodyAsync (ImapEngine engine, string format, string path, CancellationToken cancellationToken) { var token = await engine.ReadTokenAsync (cancellationToken).ConfigureAwait (false); @@ -2126,7 +2127,7 @@ public MailboxAddress ToMailboxAddress (ImapEngine engine) var mailbox = Mailbox; var domain = Domain; - string name = null; + string? name = null; string address; if (Name != null) @@ -2412,7 +2413,7 @@ public static Envelope ParseEnvelope (ImapEngine engine, CancellationToken cance { string format = string.Format (ImapEngine.GenericItemSyntaxErrorFormat, "ENVELOPE", "{0}"); var token = engine.ReadToken (cancellationToken); - string nstring; + string? nstring; ImapEngine.AssertToken (token, ImapTokenType.OpenParen, format, token); @@ -2466,7 +2467,7 @@ public static async Task ParseEnvelopeAsync (ImapEngine engine, Cancel { string format = string.Format (ImapEngine.GenericItemSyntaxErrorFormat, "ENVELOPE", "{0}"); var token = await engine.ReadTokenAsync (cancellationToken).ConfigureAwait (false); - string nstring; + string? nstring; ImapEngine.AssertToken (token, ImapTokenType.OpenParen, format, token); @@ -2972,9 +2973,9 @@ public static Task UntaggedThreadHandler (ImapEngine engine, ImapCommand ic, int ic.UserData = threads; if (doAsync) - return ParseThreadsAsync (engine, ic.Folder.UidValidity, threads, ic.CancellationToken); + return ParseThreadsAsync (engine, ic.Folder!.UidValidity, threads, ic.CancellationToken); - ParseThreads (engine, ic.Folder.UidValidity, threads, ic.CancellationToken); + ParseThreads (engine, ic.Folder!.UidValidity, threads, ic.CancellationToken); return Task.CompletedTask; } From 10d17b6f7714aa5a437b8f2417f544148cbfd3f2 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Fri, 20 Feb 2026 12:11:07 -0500 Subject: [PATCH 05/44] Updated SaslMechanism API for nullability --- MailKit/Security/SaslMechanism.cs | 36 +++--- MailKit/Security/SaslMechanismAnonymous.cs | 2 +- MailKit/Security/SaslMechanismCramMd5.cs | 6 +- MailKit/Security/SaslMechanismDigestMd5.cs | 117 +++++++++++-------- MailKit/Security/SaslMechanismGssapi.cs | 2 +- MailKit/Security/SaslMechanismLogin.cs | 4 +- MailKit/Security/SaslMechanismNtlm.cs | 9 +- MailKit/Security/SaslMechanismOAuth2.cs | 2 +- MailKit/Security/SaslMechanismOAuthBearer.cs | 6 +- MailKit/Security/SaslMechanismPlain.cs | 4 +- MailKit/Security/SaslMechanismScramBase.cs | 33 +++--- 11 files changed, 125 insertions(+), 96 deletions(-) diff --git a/MailKit/Security/SaslMechanism.cs b/MailKit/Security/SaslMechanism.cs index 216efdb8ff..cf84253767 100644 --- a/MailKit/Security/SaslMechanism.cs +++ b/MailKit/Security/SaslMechanism.cs @@ -31,6 +31,7 @@ using System.Threading.Tasks; using System.Collections.Generic; using System.Security.Cryptography; +using System.Diagnostics.CodeAnalysis; using System.Security.Authentication.ExtendedProtection; using MailKit.Net; @@ -264,7 +265,7 @@ public virtual bool NegotiatedSecurityLayer { /// Gets or sets the channel-binding context. /// /// The channel-binding context. - internal IChannelBindingContext ChannelBindingContext { + internal IChannelBindingContext? ChannelBindingContext { get; set; } @@ -275,7 +276,7 @@ internal IChannelBindingContext ChannelBindingContext { /// Gets or sets the URI of the service. /// /// The URI of the service. - internal Uri Uri { + internal Uri? Uri { get; set; } @@ -289,7 +290,7 @@ internal Uri Uri { /// The kind of channel-binding desired. /// A buffer containing the channel-binding. /// if the channel-binding token was acquired; otherwise, . - protected bool TryGetChannelBinding (ChannelBindingKind kind, out ChannelBinding channelBinding) + protected bool TryGetChannelBinding (ChannelBindingKind kind, [NotNullWhen (true)] out ChannelBinding? channelBinding) { if (ChannelBindingContext == null) { channelBinding = null; @@ -309,7 +310,7 @@ protected bool TryGetChannelBinding (ChannelBindingKind kind, out ChannelBinding /// The kind of channel-binding desired. /// A buffer containing the channel-binding token. /// if the channel-binding token was acquired; otherwise, . - protected bool TryGetChannelBindingToken (ChannelBindingKind kind, out byte[] token) + protected bool TryGetChannelBindingToken (ChannelBindingKind kind, [NotNullWhen (true)] out byte[]? token) { if (ChannelBindingContext == null) { token = null; @@ -319,9 +320,9 @@ protected bool TryGetChannelBindingToken (ChannelBindingKind kind, out byte[] to return ChannelBindingContext.TryGetChannelBindingToken (kind, out token); } - static byte[] Base64Decode (string token, out int length) + static byte[]? Base64Decode (string? token, out int length) { - byte[] decoded = null; + byte[]? decoded = null; length = 0; @@ -336,7 +337,7 @@ static byte[] Base64Decode (string token, out int length) return decoded; } - static string Base64Encode (byte[] challenge) + static string Base64Encode (byte[]? challenge) { if (challenge == null || challenge.Length == 0) return string.Empty; @@ -364,7 +365,7 @@ static string Base64Encode (byte[] challenge) /// /// An error has occurred while parsing the server's challenge token. /// - protected abstract byte[] Challenge (byte[] token, int startIndex, int length, CancellationToken cancellationToken); + protected abstract byte[]? Challenge (byte[]? token, int startIndex, int length, CancellationToken cancellationToken); /// /// Decode the base64-encoded server challenge and return the next challenge response encoded in base64. @@ -384,11 +385,14 @@ static string Base64Encode (byte[] challenge) /// /// An error has occurred while parsing the server's challenge token. /// - public string Challenge (string token, CancellationToken cancellationToken = default) + public string Challenge (string? token, CancellationToken cancellationToken = default) { + if (Uri == null) + throw new InvalidOperationException (); + cancellationToken.ThrowIfCancellationRequested (); - byte[] decoded = Base64Decode (token?.Trim (), out int length); + byte[]? decoded = Base64Decode (token?.Trim (), out int length); var challenge = Challenge (decoded, 0, length, cancellationToken); @@ -415,7 +419,7 @@ public string Challenge (string token, CancellationToken cancellationToken = def /// /// An error has occurred while parsing the server's challenge token. /// - protected virtual Task ChallengeAsync (byte[] token, int startIndex, int length, CancellationToken cancellationToken) + protected virtual Task ChallengeAsync (byte[]? token, int startIndex, int length, CancellationToken cancellationToken) { return Task.FromResult (Challenge (token, startIndex, length, cancellationToken)); } @@ -438,11 +442,11 @@ protected virtual Task ChallengeAsync (byte[] token, int startIndex, int /// /// An error has occurred while parsing the server's challenge token. /// - public async Task ChallengeAsync (string token, CancellationToken cancellationToken = default) + public async Task ChallengeAsync (string? token, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested (); - byte[] decoded = Base64Decode (token?.Trim (), out int length); + byte[]? decoded = Base64Decode (token?.Trim (), out int length); var challenge = await ChallengeAsync (decoded, 0, length, cancellationToken).ConfigureAwait (false); @@ -517,8 +521,10 @@ public static bool IsSupported (string mechanism) /// -or- /// is . /// - public static SaslMechanism Create (string mechanism, Encoding encoding, NetworkCredential credentials) + public static SaslMechanism? Create (string mechanism, Encoding encoding, NetworkCredential credentials) { + // FIXME: This API should throw NotSupportedException rather than returning null if the mechanism is not supported. + if (mechanism == null) throw new ArgumentNullException (nameof (mechanism)); @@ -568,7 +574,7 @@ public static SaslMechanism Create (string mechanism, Encoding encoding, Network /// -or- /// is . /// - public static SaslMechanism Create (string mechanism, NetworkCredential credentials) + public static SaslMechanism? Create (string mechanism, NetworkCredential credentials) { return Create (mechanism, Encoding.UTF8, credentials); } diff --git a/MailKit/Security/SaslMechanismAnonymous.cs b/MailKit/Security/SaslMechanismAnonymous.cs index 1eadbb6ed6..d8831bd6bb 100644 --- a/MailKit/Security/SaslMechanismAnonymous.cs +++ b/MailKit/Security/SaslMechanismAnonymous.cs @@ -155,7 +155,7 @@ public override bool SupportsInitialResponse { /// /// An error has occurred while parsing the server's challenge token. /// - protected override byte[] Challenge (byte[] token, int startIndex, int length, CancellationToken cancellationToken) + protected override byte[]? Challenge (byte[]? token, int startIndex, int length, CancellationToken cancellationToken) { if (IsAuthenticated) return null; diff --git a/MailKit/Security/SaslMechanismCramMd5.cs b/MailKit/Security/SaslMechanismCramMd5.cs index b0eec4bb09..288e6f5e97 100644 --- a/MailKit/Security/SaslMechanismCramMd5.cs +++ b/MailKit/Security/SaslMechanismCramMd5.cs @@ -106,7 +106,7 @@ public override string MechanismName { /// /// An error has occurred while parsing the server's challenge token. /// - protected override byte[] Challenge (byte[] token, int startIndex, int length, CancellationToken cancellationToken) + protected override byte[]? Challenge (byte[]? token, int startIndex, int length, CancellationToken cancellationToken) { if (token == null) throw new NotSupportedException ("CRAM-MD5 does not support SASL-IR."); @@ -140,13 +140,13 @@ protected override byte[] Challenge (byte[] token, int startIndex, int length, C using (var md5 = MD5.Create ()) { md5.TransformBlock (ipad, 0, ipad.Length, null, 0); md5.TransformFinalBlock (token, startIndex, length); - digest = md5.Hash; + digest = md5.Hash!; } using (var md5 = MD5.Create ()) { md5.TransformBlock (opad, 0, opad.Length, null, 0); md5.TransformFinalBlock (digest, 0, digest.Length); - digest = md5.Hash; + digest = md5.Hash!; } var buffer = new byte[userName.Length + 1 + (digest.Length * 2)]; diff --git a/MailKit/Security/SaslMechanismDigestMd5.cs b/MailKit/Security/SaslMechanismDigestMd5.cs index 51da3eb2ef..7bb6bdcde3 100644 --- a/MailKit/Security/SaslMechanismDigestMd5.cs +++ b/MailKit/Security/SaslMechanismDigestMd5.cs @@ -31,6 +31,7 @@ using System.Globalization; using System.Collections.Generic; using System.Security.Cryptography; +using System.Diagnostics.CodeAnalysis; using MimeKit.Utils; @@ -50,10 +51,10 @@ enum LoginState { Final } - DigestChallenge challenge; - DigestResponse response; - internal string cnonce; - Encoding encoding; + DigestChallenge? challenge; + DigestResponse? response; + internal string? cnonce; + Encoding? encoding; LoginState state; /// @@ -95,7 +96,7 @@ public SaslMechanismDigestMd5 (string userName, string password) : base (userNam /// for all accesses. This is separate from the user name used for authentication. /// /// The authorization identifier. - public string AuthorizationId { + public string? AuthorizationId { get; set; } @@ -130,16 +131,16 @@ public override string MechanismName { /// /// An error has occurred while parsing the server's challenge token. /// - protected override byte[] Challenge (byte[] token, int startIndex, int length, CancellationToken cancellationToken) + protected override byte[]? Challenge (byte[]? token, int startIndex, int length, CancellationToken cancellationToken) { - if (token == null) - throw new NotSupportedException ("DIGEST-MD5 does not support SASL-IR."); - if (IsAuthenticated) return null; switch (state) { case LoginState.Auth: + if (token == null) + throw new NotSupportedException ("DIGEST-MD5 does not support SASL-IR."); + if (token.Length > 2048) throw new SaslException (MechanismName, SaslErrorCode.ChallengeTooLong, "Server challenge too long."); @@ -147,16 +148,16 @@ protected override byte[] Challenge (byte[] token, int startIndex, int length, C encoding = challenge.Charset != null ? Encoding.UTF8 : TextEncodings.Latin1; cnonce ??= GenerateEntropy (15); - response = new DigestResponse (challenge, encoding, Uri.Scheme, Uri.DnsSafeHost, AuthorizationId, Credentials.UserName, Credentials.Password, cnonce); + response = new DigestResponse (challenge, encoding, Uri!.Scheme, Uri!.DnsSafeHost, AuthorizationId, Credentials.UserName, Credentials.Password, cnonce); state = LoginState.Final; return response.Encode (encoding); case LoginState.Final: - if (token.Length == 0) + if (token == null || token.Length == 0) throw new SaslException (MechanismName, SaslErrorCode.MissingChallenge, "Server response did not contain any authentication data."); - var text = encoding.GetString (token, startIndex, length); - string key, value; + var text = encoding!.GetString (token, startIndex, length); + string? key, value; if (!DigestChallenge.TryParseKeyValuePair (text, out key, out value)) throw new SaslException (MechanismName, SaslErrorCode.IncompleteChallenge, "Server response contained incomplete authentication data."); @@ -164,7 +165,7 @@ protected override byte[] Challenge (byte[] token, int startIndex, int length, C if (!key.Equals ("rspauth", StringComparison.OrdinalIgnoreCase)) throw new SaslException (MechanismName, SaslErrorCode.InvalidChallenge, "Server response contained invalid data."); - var expected = response.ComputeHash (encoding, Credentials.Password, false); + var expected = response!.ComputeHash (encoding, Credentials.Password, false); if (value != expected) throw new SaslException (MechanismName, SaslErrorCode.IncorrectHash, "Server response did not contain the expected hash."); @@ -193,19 +194,25 @@ public override void Reset () class DigestChallenge { - public string[] Realms { get; private set; } - public string Nonce { get; private set; } + public string[]? Realms { get; private set; } + public string? Nonce { get; private set; } public HashSet Qop { get; private set; } public bool? Stale { get; private set; } public int? MaxBuf { get; private set; } - public string Charset { get; private set; } - public string Algorithm { get; private set; } + public string? Charset { get; private set; } + public string? Algorithm { get; private set; } public HashSet Ciphers { get; private set; } - DigestChallenge () + DigestChallenge (string nonce, string? algorithm, string? charset, string[]? ciphers, string[]? realms, string[]? qop, bool? stale, int? maxbuf) { - Ciphers = new HashSet (StringComparer.Ordinal); - Qop = new HashSet (StringComparer.Ordinal); + Ciphers = ciphers != null ? new HashSet (ciphers, StringComparer.Ordinal) : new HashSet (StringComparer.Ordinal); + Qop = qop != null ? new HashSet (qop, StringComparer.Ordinal) : new HashSet (StringComparer.Ordinal); + Algorithm = algorithm; + Charset = charset; + MaxBuf = maxbuf; + Realms = realms; + Nonce = nonce; + Stale = stale; } static bool SkipWhiteSpace (string text, ref int index) @@ -228,7 +235,7 @@ static string GetKey (string text, ref int index) return text.Substring (startIndex, index - startIndex); } - static bool TryParseQuoted (string text, ref int index, out string value) + static bool TryParseQuoted (string text, ref int index, [NotNullWhen (true)] out string? value) { var builder = new StringBuilder (); bool escaped = false; @@ -266,7 +273,7 @@ static bool TryParseQuoted (string text, ref int index, out string value) return true; } - static bool TryParseValue (string text, ref int index, out string value) + static bool TryParseValue (string text, ref int index, [NotNullWhen (true)] out string? value) { if (text[index] == '"') return TryParseQuoted (text, ref index, out value); @@ -281,7 +288,7 @@ static bool TryParseValue (string text, ref int index, out string value) return true; } - static bool TryParseKeyValuePair (string text, ref int index, out string key, out string value) + static bool TryParseKeyValuePair (string text, ref int index, [NotNullWhen (true)] out string? key, [NotNullWhen (true)] out string? value) { value = null; @@ -301,7 +308,7 @@ static bool TryParseKeyValuePair (string text, ref int index, out string key, ou return TryParseValue (text, ref index, out value); } - public static bool TryParseKeyValuePair (string text, out string key, out string value) + public static bool TryParseKeyValuePair (string text, [NotNullWhen (true)] out string? key, [NotNullWhen (true)] out string? value) { int index = 0; @@ -319,9 +326,13 @@ public static bool TryParseKeyValuePair (string text, out string key, out string public static DigestChallenge Parse (string token) { - var challenge = new DigestChallenge (); + string[]? realms = null, qop = null, ciphers = null; + string? algorithm = null; + string? charset = null; + string? nonce = null; + bool? stale = null; + int maxbuf = -1; int index = 0; - int maxbuf; SkipWhiteSpace (token, ref index); @@ -331,42 +342,43 @@ public static DigestChallenge Parse (string token) switch (key.ToLowerInvariant ()) { case "realm": - challenge.Realms = value.Split (Comma, StringSplitOptions.RemoveEmptyEntries); + if (realms != null) + throw new SaslException ("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format ("Invalid SASL challenge from the server: {0}", token)); + realms = value.Split (Comma, StringSplitOptions.RemoveEmptyEntries); break; case "nonce": - if (challenge.Nonce != null) + if (nonce != null) throw new SaslException ("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format ("Invalid SASL challenge from the server: {0}", token)); - challenge.Nonce = value; + nonce = value; break; case "qop": - foreach (var qop in value.Split (Comma, StringSplitOptions.RemoveEmptyEntries)) - challenge.Qop.Add (qop.Trim ()); + if (qop != null) + throw new SaslException ("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format ("Invalid SASL challenge from the server: {0}", token)); + qop = value.Split (Comma, StringSplitOptions.RemoveEmptyEntries); break; case "stale": - if (challenge.Stale.HasValue) + if (stale.HasValue) throw new SaslException ("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format ("Invalid SASL challenge from the server: {0}", token)); - challenge.Stale = value.Equals ("true", StringComparison.OrdinalIgnoreCase); + stale = value.Equals ("true", StringComparison.OrdinalIgnoreCase); break; case "maxbuf": - if (challenge.MaxBuf.HasValue || !int.TryParse (value, NumberStyles.None, CultureInfo.InvariantCulture, out maxbuf)) + if (maxbuf != -1 || !int.TryParse (value, NumberStyles.None, CultureInfo.InvariantCulture, out maxbuf)) throw new SaslException ("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format ("Invalid SASL challenge from the server: {0}", token)); - challenge.MaxBuf = maxbuf; break; case "charset": - if (challenge.Charset != null || !value.Equals ("utf-8", StringComparison.OrdinalIgnoreCase)) + if (charset != null || !value.Equals ("utf-8", StringComparison.OrdinalIgnoreCase)) throw new SaslException ("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format ("Invalid SASL challenge from the server: {0}", token)); - challenge.Charset = "utf-8"; + charset = "utf-8"; break; case "algorithm": - if (challenge.Algorithm != null) + if (algorithm != null) throw new SaslException ("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format ("Invalid SASL challenge from the server: {0}", token)); - challenge.Algorithm = value; + algorithm = value; break; case "cipher": - if (challenge.Ciphers.Count > 0) + if (ciphers != null) throw new SaslException ("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format ("Invalid SASL challenge from the server: {0}", token)); - foreach (var cipher in value.Split (Comma, StringSplitOptions.RemoveEmptyEntries)) - challenge.Ciphers.Add (cipher.Trim ()); + ciphers = value.Split (Comma, StringSplitOptions.RemoveEmptyEntries); break; } @@ -378,7 +390,10 @@ public static DigestChallenge Parse (string token) } } - return challenge; + if (nonce == null) + throw new SaslException ("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format ("Invalid SASL challenge from the server: {0}", token)); + + return new DigestChallenge (nonce, algorithm, charset, ciphers, realms, qop, stale, maxbuf != -1 ? maxbuf : null); } } @@ -386,19 +401,19 @@ class DigestResponse { public string UserName { get; private set; } public string Realm { get; private set; } - public string Nonce { get; private set; } + public string? Nonce { get; private set; } public string CNonce { get; private set; } public int Nc { get; private set; } public string Qop { get; private set; } public string DigestUri { get; private set; } public string Response { get; private set; } public int? MaxBuf { get; private set; } - public string Charset { get; private set; } - public string Algorithm { get; private set; } - public string Cipher { get; private set; } - public string AuthZid { get; private set; } + public string? Charset { get; private set; } + public string? Algorithm { get; private set; } + public string? Cipher { get; private set; } + public string? AuthZid { get; private set; } - public DigestResponse (DigestChallenge challenge, Encoding encoding, string protocol, string hostName, string authzid, string userName, string password, string cnonce) + public DigestResponse (DigestChallenge challenge, Encoding encoding, string protocol, string hostName, string? authzid, string userName, string password, string cnonce) { UserName = userName; @@ -452,7 +467,7 @@ public string ComputeHash (Encoding encoding, string password, bool client) text += ":" + AuthZid; buf = encoding.GetBytes (text); md5.TransformFinalBlock (buf, 0, buf.Length); - a1 = HexEncode (md5.Hash); + a1 = HexEncode (md5.Hash!); } // compute A2 diff --git a/MailKit/Security/SaslMechanismGssapi.cs b/MailKit/Security/SaslMechanismGssapi.cs index 25fec39429..6a7111802a 100644 --- a/MailKit/Security/SaslMechanismGssapi.cs +++ b/MailKit/Security/SaslMechanismGssapi.cs @@ -147,7 +147,7 @@ protected override NegotiateAuthenticationClientOptions CreateClientOptions () // Provide a default TargetName (the base implementation already sets the // TargetName to the ServicePrincipalName if the value was provided). - options.TargetName ??= $"SMTPSVC/{Uri.Host}"; + options.TargetName ??= $"SMTPSVC/{Uri!.Host}"; return options; } diff --git a/MailKit/Security/SaslMechanismLogin.cs b/MailKit/Security/SaslMechanismLogin.cs index f7ed450b0e..36af22afea 100644 --- a/MailKit/Security/SaslMechanismLogin.cs +++ b/MailKit/Security/SaslMechanismLogin.cs @@ -169,12 +169,12 @@ public override bool SupportsInitialResponse { /// /// An error has occurred while parsing the server's challenge token. /// - protected override byte[] Challenge (byte[] token, int startIndex, int length, CancellationToken cancellationToken) + protected override byte[]? Challenge (byte[]? token, int startIndex, int length, CancellationToken cancellationToken) { if (IsAuthenticated) return null; - byte[] challenge = null; + byte[]? challenge = null; switch (state) { case LoginState.UserName: diff --git a/MailKit/Security/SaslMechanismNtlm.cs b/MailKit/Security/SaslMechanismNtlm.cs index 7ece2251f6..705df44cf0 100644 --- a/MailKit/Security/SaslMechanismNtlm.cs +++ b/MailKit/Security/SaslMechanismNtlm.cs @@ -261,14 +261,14 @@ public bool IsUnverifiedServicePrincipalName { /// /// An error has occurred while parsing the server's challenge token. /// - protected override byte[] Challenge (byte[] token, int startIndex, int length, CancellationToken cancellationToken) + protected override byte[]? Challenge (byte[]? token, int startIndex, int length, CancellationToken cancellationToken) { if (IsAuthenticated) return null; string userName = Credentials.UserName; string domain = Credentials.Domain; - NtlmMessageBase message = null; + NtlmMessageBase? message = null; if (string.IsNullOrEmpty (domain)) { int index; @@ -293,6 +293,9 @@ protected override byte[] Challenge (byte[] token, int startIndex, int length, C state = LoginState.Challenge; break; case LoginState.Challenge: + if (token == null) + throw new SaslException (MechanismName, SaslErrorCode.MissingChallenge, "Server response did not contain any authentication data."); + var password = Credentials.Password; message = GetChallengeResponse (domain, userName, password, token, startIndex, length); IsAuthenticated = true; @@ -309,7 +312,7 @@ NtlmAuthenticateMessage GetChallengeResponse (string domain, string userName, st ClientChallenge = Nonce, Timestamp = Timestamp }; - byte[] channelBindingToken = null; + byte[]? channelBindingToken = null; if (AllowChannelBinding && challenge.TargetInfo != null) { // Only bother with attempting to channel-bind if the CHALLENGE_MESSAGE's TargetInfo is not NULL. diff --git a/MailKit/Security/SaslMechanismOAuth2.cs b/MailKit/Security/SaslMechanismOAuth2.cs index d62352456c..4305b70a4f 100644 --- a/MailKit/Security/SaslMechanismOAuth2.cs +++ b/MailKit/Security/SaslMechanismOAuth2.cs @@ -124,7 +124,7 @@ public override bool SupportsInitialResponse { /// /// An error has occurred while parsing the server's challenge token. /// - protected override byte[] Challenge (byte[] token, int startIndex, int length, CancellationToken cancellationToken) + protected override byte[]? Challenge (byte[]? token, int startIndex, int length, CancellationToken cancellationToken) { if (IsAuthenticated) return null; diff --git a/MailKit/Security/SaslMechanismOAuthBearer.cs b/MailKit/Security/SaslMechanismOAuthBearer.cs index 7a80f3079c..5b36a09f89 100644 --- a/MailKit/Security/SaslMechanismOAuthBearer.cs +++ b/MailKit/Security/SaslMechanismOAuthBearer.cs @@ -153,14 +153,14 @@ static int CalculateBufferSize (byte[] authzid, byte[] host, string port, string /// /// An error has occurred while parsing the server's challenge token. /// - protected override byte[] Challenge (byte[] token, int startIndex, int length, CancellationToken cancellationToken) + protected override byte[]? Challenge (byte[]? token, int startIndex, int length, CancellationToken cancellationToken) { if (IsAuthenticated) return ErrorResponse; var authzid = Encoding.UTF8.GetBytes (Credentials.UserName); - var port = Uri.Port.ToString (CultureInfo.InvariantCulture); - var host = Encoding.UTF8.GetBytes (Uri.Host); + var port = Uri!.Port.ToString (CultureInfo.InvariantCulture); + var host = Encoding.UTF8.GetBytes (Uri!.Host); var authToken = Credentials.Password; var buf = new byte[CalculateBufferSize (authzid, host, port, authToken)]; diff --git a/MailKit/Security/SaslMechanismPlain.cs b/MailKit/Security/SaslMechanismPlain.cs index 797f8fda14..793ae36725 100644 --- a/MailKit/Security/SaslMechanismPlain.cs +++ b/MailKit/Security/SaslMechanismPlain.cs @@ -127,7 +127,7 @@ public SaslMechanismPlain (string userName, string password) : this (Encoding.UT /// for all accesses. This is separate from the user name used for authentication. /// /// The authorization identifier. - public string AuthorizationId { + public string? AuthorizationId { get; set; } @@ -175,7 +175,7 @@ public override bool SupportsInitialResponse { /// /// An error has occurred while parsing the server's challenge token. /// - protected override byte[] Challenge (byte[] token, int startIndex, int length, CancellationToken cancellationToken) + protected override byte[]? Challenge (byte[]? token, int startIndex, int length, CancellationToken cancellationToken) { if (IsAuthenticated) return null; diff --git a/MailKit/Security/SaslMechanismScramBase.cs b/MailKit/Security/SaslMechanismScramBase.cs index fb77fb807a..3db45490f0 100644 --- a/MailKit/Security/SaslMechanismScramBase.cs +++ b/MailKit/Security/SaslMechanismScramBase.cs @@ -50,10 +50,10 @@ enum LoginState { ChannelBindingKind channelBindingKind; bool negotiatedChannelBinding; - byte[] channelBindingToken; - internal string cnonce; - string client, server; - byte[] salted, auth; + byte[]? channelBindingToken; + internal string? cnonce; + string? client, server; + byte[]? salted, auth; LoginState state; /// @@ -95,7 +95,7 @@ protected SaslMechanismScramBase (string userName, string password) : base (user /// for all accesses. This is separate from the user name used for authentication. /// /// The authorization identifier. - public string AuthorizationId { + public string? AuthorizationId { get; set; } @@ -263,7 +263,7 @@ static string GetChannelBindingName (ChannelBindingKind kind) return kind == ChannelBindingKind.Endpoint ? "tls-server-end-point" : "tls-unique"; } - static string GetChannelBindingInput (ChannelBindingKind kind, string authzid) + static string GetChannelBindingInput (ChannelBindingKind kind, string? authzid) { string flag; @@ -273,8 +273,7 @@ static string GetChannelBindingInput (ChannelBindingKind kind, string authzid) flag = "n"; } - if (string.IsNullOrEmpty (authzid)) - authzid = string.Empty; + authzid ??= string.Empty; return flag + "," + Normalize (authzid) + ","; } @@ -299,7 +298,7 @@ static string GetChannelBindingInput (ChannelBindingKind kind, string authzid) /// /// An error has occurred while parsing the server's challenge token. /// - protected override byte[] Challenge (byte[] token, int startIndex, int length, CancellationToken cancellationToken) + protected override byte[]? Challenge (byte[]? token, int startIndex, int length, CancellationToken cancellationToken) { if (IsAuthenticated) return null; @@ -334,9 +333,12 @@ protected override byte[] Challenge (byte[] token, int startIndex, int length, C state = LoginState.Final; break; case LoginState.Final: + if (token == null) + throw new SaslException (MechanismName, SaslErrorCode.MissingChallenge, "Server response did not contain any authentication data."); + server = Encoding.UTF8.GetString (token, startIndex, length); var tokens = ParseServerChallenge (server); - string salt, nonce, iterations; + string? salt, nonce, iterations; int count; if (!tokens.TryGetValue ('s', out salt)) @@ -348,7 +350,7 @@ protected override byte[] Challenge (byte[] token, int startIndex, int length, C if (!tokens.TryGetValue ('i', out iterations)) throw new SaslException (MechanismName, SaslErrorCode.IncompleteChallenge, "Challenge did not contain an iteration count."); - if (!nonce.StartsWith (cnonce, StringComparison.Ordinal)) + if (!nonce.StartsWith (cnonce!, StringComparison.Ordinal)) throw new SaslException (MechanismName, SaslErrorCode.InvalidChallenge, "Challenge contained an invalid nonce."); if (!int.TryParse (iterations, NumberStyles.None, CultureInfo.InvariantCulture, out count) || count < 1) @@ -362,7 +364,7 @@ protected override byte[] Challenge (byte[] token, int startIndex, int length, C var inputBuffer = Encoding.ASCII.GetBytes (input); string base64; - if (SupportsChannelBinding && channelBindingKind != ChannelBindingKind.Unknown) { + if (SupportsChannelBinding && channelBindingToken != null) { var binding = new byte[inputBuffer.Length + channelBindingToken.Length]; Buffer.BlockCopy (inputBuffer, 0, binding, 0, inputBuffer.Length); @@ -389,14 +391,17 @@ protected override byte[] Challenge (byte[] token, int startIndex, int length, C state = LoginState.Validate; break; case LoginState.Validate: + if (token == null) + throw new SaslException (MechanismName, SaslErrorCode.MissingChallenge, "Server response did not contain any authentication data."); + var challenge = Encoding.UTF8.GetString (token, startIndex, length); if (!challenge.StartsWith ("v=", StringComparison.Ordinal)) throw new SaslException (MechanismName, SaslErrorCode.InvalidChallenge, "Challenge did not start with a signature."); signature = Convert.FromBase64String (challenge.Substring (2)); - var serverKey = HMAC (salted, Encoding.ASCII.GetBytes ("Server Key")); - var calculated = HMAC (serverKey, auth); + var serverKey = HMAC (salted!, Encoding.ASCII.GetBytes ("Server Key")); + var calculated = HMAC (serverKey, auth!); if (signature.Length != calculated.Length) throw new SaslException (MechanismName, SaslErrorCode.IncorrectHash, "Challenge contained a signature with an invalid length."); From 46e1c38fac84caf27d01004dde68cc2468c74b03 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Fri, 20 Feb 2026 13:20:32 -0500 Subject: [PATCH 06/44] Updated SocketUtils for nullability --- MailKit/Net/SocketUtils.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MailKit/Net/SocketUtils.cs b/MailKit/Net/SocketUtils.cs index 95ac932a3d..f225b320a7 100644 --- a/MailKit/Net/SocketUtils.cs +++ b/MailKit/Net/SocketUtils.cs @@ -103,12 +103,12 @@ public void OnEndConnect (IAsyncResult ar) static void OnEndConnect (IAsyncResult ar) { - var state = (SocketConnectState) ar.AsyncState; + var state = (SocketConnectState) ar.AsyncState!; state.OnEndConnect (ar); } - public static Socket Connect (string host, int port, IPEndPoint localEndPoint, CancellationToken cancellationToken) + public static Socket Connect (string host, int port, IPEndPoint? localEndPoint, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested (); @@ -171,7 +171,7 @@ public static Socket Connect (string host, int port, IPEndPoint localEndPoint, C throw new IOException (string.Format ("Failed to resolve host: {0}", host)); } - public static async Task ConnectAsync (string host, int port, IPEndPoint localEndPoint, CancellationToken cancellationToken) + public static async Task ConnectAsync (string host, int port, IPEndPoint? localEndPoint, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested (); @@ -218,7 +218,7 @@ public static async Task ConnectAsync (string host, int port, IPEndPoint throw new IOException (string.Format ("Failed to resolve host: {0}", host)); } - public static Socket Connect (string host, int port, IPEndPoint localEndPoint, int timeout, CancellationToken cancellationToken) + public static Socket Connect (string host, int port, IPEndPoint? localEndPoint, int timeout, CancellationToken cancellationToken) { using (var ts = new CancellationTokenSource (timeout)) { using (var linked = CancellationTokenSource.CreateLinkedTokenSource (cancellationToken, ts.Token)) { @@ -233,7 +233,7 @@ public static Socket Connect (string host, int port, IPEndPoint localEndPoint, i } } - public static async Task ConnectAsync (string host, int port, IPEndPoint localEndPoint, int timeout, CancellationToken cancellationToken) + public static async Task ConnectAsync (string host, int port, IPEndPoint? localEndPoint, int timeout, CancellationToken cancellationToken) { using (var ts = new CancellationTokenSource (timeout)) { using (var linked = CancellationTokenSource.CreateLinkedTokenSource (cancellationToken, ts.Token)) { From 0b698d3244303a2951f4c703bd3f9e1cdcde6bde Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Fri, 20 Feb 2026 13:21:22 -0500 Subject: [PATCH 07/44] Updated SslHandshakeException for nullability --- MailKit/Security/SslHandshakeException.cs | 44 ++++++++++++++--------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/MailKit/Security/SslHandshakeException.cs b/MailKit/Security/SslHandshakeException.cs index 81d19e6b52..948b238030 100644 --- a/MailKit/Security/SslHandshakeException.cs +++ b/MailKit/Security/SslHandshakeException.cs @@ -135,7 +135,7 @@ public SslHandshakeException () : base (DefaultMessage) /// Gets the server's SSL certificate, if it is available. /// /// The server's SSL certificate. - public X509Certificate ServerCertificate { + public X509Certificate? ServerCertificate { get; private set; } @@ -146,7 +146,7 @@ public X509Certificate ServerCertificate { /// Gets the certificate for the Root Certificate Authority, if it is available. /// /// The Root Certificate Authority certificate. - public X509Certificate RootCertificateAuthority { + public X509Certificate? RootCertificateAuthority { get; private set; } @@ -184,11 +184,11 @@ public override void GetObjectData (SerializationInfo info, StreamingContext con } #endif - internal static SslHandshakeException Create (ref SslCertificateValidationInfo validationInfo, Exception ex, bool starttls, string protocol, string host, int port, int sslPort, params int[] standardPorts) + internal static SslHandshakeException Create (ref SslCertificateValidationInfo? validationInfo, Exception ex, bool starttls, string protocol, string host, int port, int sslPort, params int[] standardPorts) { var message = new StringBuilder (DefaultMessage); - X509Certificate2 certificate = null; - X509Certificate2 root = null; + X509Certificate2? certificate = null; + X509Certificate2? root = null; if (ex is AggregateException aggregate) { aggregate = aggregate.Flatten (); @@ -214,11 +214,13 @@ internal static SslHandshakeException Create (ref SslCertificateValidationInfo v #endif } + if (validationInfo.Certificate != null) { #if NET10_0_OR_GREATER - certificate = X509CertificateLoader.LoadCertificate (validationInfo.Certificate.RawData); + certificate = X509CertificateLoader.LoadCertificate (validationInfo.Certificate.RawData); #else - certificate = new X509Certificate2 (validationInfo.Certificate.RawData); + certificate = new X509Certificate2 (validationInfo.Certificate.RawData); #endif + } if ((validationInfo.SslPolicyErrors & SslPolicyErrors.RemoteCertificateNotAvailable) != 0) { message.AppendLine ("The SSL certificate for the server was not available."); @@ -300,11 +302,14 @@ internal static SslHandshakeException Create (ref SslCertificateValidationInfo v } // Adapted from Sebastian Krysmanski's https://github.com/skrysmanski/AppMotor/blob/main/src/AppMotor.Core/Certificates/SanExtensionHelpers.cs under the MIT license - static IReadOnlyCollection GetDnsNames (X509Certificate2 certificate) + static IReadOnlyCollection GetDnsNames (X509Certificate2? certificate) { const string subjectAlternativeNameOid = "2.5.29.17"; var dnsNames = new SortedSet (); + if (certificate == null) + return dnsNames; + var dnsNameInfo = certificate.GetNameInfo (X509NameType.DnsName, forIssuer: false); if (dnsNameInfo != null) dnsNames.Add (dnsNameInfo); @@ -369,29 +374,34 @@ sealed class SslCertificateValidationInfo : IDisposable public readonly List ChainElements; public readonly X509ChainStatus[] ChainStatus; public readonly SslPolicyErrors SslPolicyErrors; - public readonly X509Certificate2 Certificate; - public readonly string Host; + public readonly X509Certificate2? Certificate; + public readonly string? Host; - public SslCertificateValidationInfo (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) + public SslCertificateValidationInfo (object? sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors) { #if NET10_0_OR_GREATER - Certificate = X509CertificateLoader.LoadCertificate (certificate.Export (X509ContentType.Cert)); + Certificate = certificate != null ? X509CertificateLoader.LoadCertificate (certificate.Export (X509ContentType.Cert)) : null; #else - Certificate = new X509Certificate2 (certificate.Export (X509ContentType.Cert)); + Certificate = certificate != null ? new X509Certificate2 (certificate.Export (X509ContentType.Cert)) : null; #endif ChainElements = new List (); SslPolicyErrors = sslPolicyErrors; - ChainStatus = chain.ChainStatus; Host = sender as string; // Note: we need to copy the ChainElements because the chain will be destroyed - foreach (var element in chain.ChainElements) - ChainElements.Add (new SslChainElement (element)); + if (chain != null) { + ChainStatus = chain.ChainStatus; + + foreach (var element in chain.ChainElements) + ChainElements.Add (new SslChainElement (element)); + } else { + ChainStatus = Array.Empty (); + } } public void Dispose () { - Certificate.Dispose (); + Certificate?.Dispose (); foreach (var element in ChainElements) element.Dispose (); } From 7b66f95b7a63d1b3794a5453445bd9d7c23f0adb Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Fri, 20 Feb 2026 13:26:46 -0500 Subject: [PATCH 08/44] Updated ProxyClients for nullability --- MailKit/Net/Proxy/HttpProxyClient.cs | 2 +- MailKit/Net/Proxy/HttpsProxyClient.cs | 10 +++++----- MailKit/Net/Proxy/IProxyClient.cs | 4 ++-- MailKit/Net/Proxy/ProxyClient.cs | 10 +++++----- MailKit/Net/Proxy/Socks4Client.cs | 8 +++++--- MailKit/Net/Proxy/Socks5Client.cs | 16 ++++++++-------- MailKit/Net/Proxy/WebProxyClient.cs | 4 ++-- 7 files changed, 28 insertions(+), 26 deletions(-) diff --git a/MailKit/Net/Proxy/HttpProxyClient.cs b/MailKit/Net/Proxy/HttpProxyClient.cs index dba43e432f..e389532a45 100644 --- a/MailKit/Net/Proxy/HttpProxyClient.cs +++ b/MailKit/Net/Proxy/HttpProxyClient.cs @@ -95,7 +95,7 @@ public HttpProxyClient (string host, int port, NetworkCredential credentials) : { } - internal static byte[] GetConnectCommand (string host, int port, NetworkCredential proxyCredentials) + internal static byte[] GetConnectCommand (string host, int port, NetworkCredential? proxyCredentials) { var builder = new StringBuilder (); diff --git a/MailKit/Net/Proxy/HttpsProxyClient.cs b/MailKit/Net/Proxy/HttpsProxyClient.cs index 4f4b5322e2..76065ac2d2 100644 --- a/MailKit/Net/Proxy/HttpsProxyClient.cs +++ b/MailKit/Net/Proxy/HttpsProxyClient.cs @@ -134,7 +134,7 @@ public SslProtocols SslProtocols { /// changing this setting. /// /// The cipher algorithms allowed for use when negotiating SSL or TLS encryption. - public CipherSuitesPolicy SslCipherSuitesPolicy { + public CipherSuitesPolicy? SslCipherSuitesPolicy { get; set; } #endif @@ -149,7 +149,7 @@ public CipherSuitesPolicy SslCipherSuitesPolicy { /// Connect methods. /// /// The client SSL certificates. - public X509CertificateCollection ClientCertificates { + public X509CertificateCollection? ClientCertificates { get; set; } @@ -182,14 +182,14 @@ public bool CheckCertificateRevocation { /// Connect methods. /// /// The server certificate validation callback function. - public RemoteCertificateValidationCallback ServerCertificateValidationCallback { + public RemoteCertificateValidationCallback? ServerCertificateValidationCallback { get; set; } // Note: This is used by SslHandshakeException to build the exception message. - SslCertificateValidationInfo sslValidationInfo; + SslCertificateValidationInfo? sslValidationInfo; - bool ValidateRemoteCertificate (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) + bool ValidateRemoteCertificate (object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors) { bool valid; diff --git a/MailKit/Net/Proxy/IProxyClient.cs b/MailKit/Net/Proxy/IProxyClient.cs index c9e68d4499..95721b64f3 100644 --- a/MailKit/Net/Proxy/IProxyClient.cs +++ b/MailKit/Net/Proxy/IProxyClient.cs @@ -52,7 +52,7 @@ public interface IProxyClient /// Gets the credentials to use when authenticating with the proxy server. /// /// The proxy credentials. - NetworkCredential ProxyCredentials { get; } + NetworkCredential? ProxyCredentials { get; } /// /// Get the proxy host. @@ -79,7 +79,7 @@ public interface IProxyClient /// Gets or sets the local IP end point to use when connecting to a remote host. /// /// The local IP end point or to use the default end point. - IPEndPoint LocalEndPoint { get; set; } + IPEndPoint? LocalEndPoint { get; set; } /// /// Connect to the target host. diff --git a/MailKit/Net/Proxy/ProxyClient.cs b/MailKit/Net/Proxy/ProxyClient.cs index a72105c312..b447e71960 100644 --- a/MailKit/Net/Proxy/ProxyClient.cs +++ b/MailKit/Net/Proxy/ProxyClient.cs @@ -50,7 +50,7 @@ namespace MailKit.Net.Proxy public abstract class ProxyClient : IProxyClient { #if NET6_0_OR_GREATER - static IProxyClient systemProxy; + static IProxyClient? systemProxy; /// /// Get a client for the default system proxy. @@ -139,7 +139,7 @@ protected ProxyClient (string host, int port, NetworkCredential credentials) : t /// Gets the credentials to use when authenticating with the proxy server. /// /// The proxy credentials. - public NetworkCredential ProxyCredentials { + public NetworkCredential? ProxyCredentials { get; private set; } @@ -172,7 +172,7 @@ public int ProxyPort { /// Gets or sets the local IP end point to use when connecting to a remote host. /// /// The local IP end point or to use the default end point. - public IPEndPoint LocalEndPoint { + public IPEndPoint? LocalEndPoint { get; set; } @@ -196,9 +196,9 @@ static void ValidateArguments (string host, int port, int timeout) throw new ArgumentOutOfRangeException (nameof (timeout)); } - static void AsyncOperationCompleted (object sender, SocketAsyncEventArgs args) + static void AsyncOperationCompleted (object? sender, SocketAsyncEventArgs args) { - var tcs = (TaskCompletionSource) args.UserToken; + var tcs = (TaskCompletionSource) args.UserToken!; if (args.SocketError == SocketError.Success) { tcs.TrySetResult (true); diff --git a/MailKit/Net/Proxy/Socks4Client.cs b/MailKit/Net/Proxy/Socks4Client.cs index 52a66592d6..d2e01de762 100644 --- a/MailKit/Net/Proxy/Socks4Client.cs +++ b/MailKit/Net/Proxy/Socks4Client.cs @@ -164,7 +164,7 @@ static async Task ResolveAsync (string host, CancellationToken cancel return Resolve (host, ipAddresses); } - byte[] GetConnectCommand (byte[] domain, byte[] addr, int port) + byte[] GetConnectCommand (byte[]? domain, byte[] addr, int port) { // +----+-----+----------+----------+----------+-------+--------------+-------+ // |VER | CMD | DST.PORT | DST.ADDR | USERID | NULL | DST.DOMAIN | NULL | @@ -225,7 +225,8 @@ byte[] GetConnectCommand (byte[] domain, byte[] addr, int port) /// public override Stream Connect (string host, int port, CancellationToken cancellationToken = default) { - byte[] addr, domain = null; + byte[]? domain = null; + byte[] addr; ValidateArguments (host, port); @@ -310,7 +311,8 @@ public override Stream Connect (string host, int port, CancellationToken cancell /// public override async Task ConnectAsync (string host, int port, CancellationToken cancellationToken = default) { - byte[] addr, domain = null; + byte[]? domain = null; + byte[] addr; ValidateArguments (host, port); diff --git a/MailKit/Net/Proxy/Socks5Client.cs b/MailKit/Net/Proxy/Socks5Client.cs index 7a0f8ff38e..e31204ce74 100644 --- a/MailKit/Net/Proxy/Socks5Client.cs +++ b/MailKit/Net/Proxy/Socks5Client.cs @@ -156,7 +156,7 @@ internal static string GetFailureReason (byte reply) } } - internal static Socks5AddressType GetAddressType (string host, out IPAddress ip) + internal static Socks5AddressType GetAddressType (string host, out IPAddress? ip) { if (!IPAddress.TryParse (host, out ip)) return Socks5AddressType.Domain; @@ -238,7 +238,7 @@ async Task NegotiateAuthMethodAsync (Socket socket, Cancellati byte[] GetAuthenticateCommand () { - var user = Encoding.UTF8.GetBytes (ProxyCredentials.UserName); + var user = Encoding.UTF8.GetBytes (ProxyCredentials!.UserName); if (user.Length > 255) throw new AuthenticationException ("User name too long."); @@ -299,7 +299,7 @@ async Task AuthenticateAsync (Socket socket, CancellationToken cancellationToken throw new AuthenticationException ("Failed to authenticate with SOCKS5 proxy server."); } - byte[] GetConnectCommand (Socks5AddressType addrType, byte[] domain, IPAddress ip, int port, out int n) + byte[] GetConnectCommand (Socks5AddressType addrType, byte[]? domain, IPAddress? ip, int port, out int n) { // +----+-----+-------+------+----------+----------+ // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | @@ -317,17 +317,17 @@ byte[] GetConnectCommand (Socks5AddressType addrType, byte[] domain, IPAddress i buffer[n++] = (byte) addrType; switch (addrType) { case Socks5AddressType.Domain: - buffer[n++] = (byte) domain.Length; + buffer[n++] = (byte) domain!.Length; Buffer.BlockCopy (domain, 0, buffer, n, domain.Length); n += domain.Length; break; case Socks5AddressType.IPv6: - addr = ip.GetAddressBytes (); + addr = ip!.GetAddressBytes (); Buffer.BlockCopy (addr, 0, buffer, n, addr.Length); n += 16; break; case Socks5AddressType.IPv4: - addr = ip.GetAddressBytes (); + addr = ip!.GetAddressBytes (); Buffer.BlockCopy (addr, 0, buffer, n, addr.Length); n += 4; break; @@ -396,7 +396,7 @@ public override Stream Connect (string host, int port, CancellationToken cancell var socket = SocketUtils.Connect (ProxyHost, ProxyPort, LocalEndPoint, cancellationToken); var addrType = GetAddressType (host, out var ip); - byte[] domain = null; + byte[]? domain = null; if (addrType == Socks5AddressType.Domain) domain = Encoding.UTF8.GetBytes (host); @@ -494,7 +494,7 @@ public override async Task ConnectAsync (string host, int port, Cancella var socket = await SocketUtils.ConnectAsync (ProxyHost, ProxyPort, LocalEndPoint, cancellationToken).ConfigureAwait (false); var addrType = GetAddressType (host, out var ip); - byte[] domain = null; + byte[]? domain = null; if (addrType == Socks5AddressType.Domain) domain = Encoding.UTF8.GetBytes (host); diff --git a/MailKit/Net/Proxy/WebProxyClient.cs b/MailKit/Net/Proxy/WebProxyClient.cs index e1c3adfbb9..f191f6448d 100644 --- a/MailKit/Net/Proxy/WebProxyClient.cs +++ b/MailKit/Net/Proxy/WebProxyClient.cs @@ -76,7 +76,7 @@ static Uri GetTargetUri (string host, int port) return new Uri ($"{scheme}://{host}:{port}"); } - static NetworkCredential GetNetworkCredential (ICredentials credentials, Uri uri) + static NetworkCredential? GetNetworkCredential (ICredentials? credentials, Uri uri) { if (credentials == null) return null; @@ -87,7 +87,7 @@ static NetworkCredential GetNetworkCredential (ICredentials credentials, Uri uri return credentials.GetCredential (uri, "Basic"); } - internal static ProxyClient GetProxyClient (Uri proxyUri, ICredentials credentials) + internal static ProxyClient GetProxyClient (Uri proxyUri, ICredentials? credentials) { var credential = GetNetworkCredential (credentials, proxyUri); From 2f43f60ef54fe0bb5eb16d69db5625759c337094 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Fri, 20 Feb 2026 14:35:34 -0500 Subject: [PATCH 09/44] Updated Smtp and Pop3 clients for nullability --- MailKit/IMailService.cs | 16 +-- MailKit/IMailSpool.cs | 24 ++-- MailKit/IMailTransport.cs | 18 +-- MailKit/IProtocolLogger.cs | 2 +- MailKit/MailService.cs | 20 ++-- MailKit/MailSpool.cs | 24 ++-- MailKit/MailTransport.cs | 18 +-- MailKit/Net/IChannelBindingContext.cs | 5 +- MailKit/Net/NetworkOperation.cs | 8 +- MailKit/Net/NetworkStream.cs | 14 +-- MailKit/Net/Pop3/AsyncPop3Client.cs | 45 ++++---- MailKit/Net/Pop3/IPop3Client.cs | 2 +- MailKit/Net/Pop3/Pop3Client.cs | 126 ++++++++++---------- MailKit/Net/Pop3/Pop3Command.cs | 12 +- MailKit/Net/Pop3/Pop3Engine.cs | 42 +++---- MailKit/Net/Smtp/AsyncSmtpClient.cs | 60 +++++----- MailKit/Net/Smtp/ISmtpClient.cs | 2 +- MailKit/Net/Smtp/SmtpClient.cs | 140 ++++++++++++----------- MailKit/Net/Smtp/SmtpCommandException.cs | 2 +- MailKit/Net/Smtp/SmtpStream.cs | 2 +- MailKit/Net/SslStream.cs | 13 ++- MailKit/NullProtocolLogger.cs | 2 +- MailKit/ProgressStream.cs | 2 +- MailKit/ProtocolLogger.cs | 19 ++- 24 files changed, 313 insertions(+), 305 deletions(-) diff --git a/MailKit/IMailService.cs b/MailKit/IMailService.cs index 529942fe74..f3455fd36f 100644 --- a/MailKit/IMailService.cs +++ b/MailKit/IMailService.cs @@ -88,7 +88,7 @@ public interface IMailService : IDisposable /// ConnectAsync methods. /// /// The cipher algorithms allowed for use when negotiating SSL or TLS encryption. - CipherSuitesPolicy SslCipherSuitesPolicy { get; set; } + CipherSuitesPolicy? SslCipherSuitesPolicy { get; set; } /// /// Get the negotiated SSL or TLS cipher suite. @@ -111,7 +111,7 @@ public interface IMailService : IDisposable /// ConnectAsync methods. /// /// The client SSL certificates. - X509CertificateCollection ClientCertificates { get; set; } + X509CertificateCollection? ClientCertificates { get; set; } /// /// Get or set whether connecting via SSL/TLS should check certificate revocation. @@ -144,7 +144,7 @@ public interface IMailService : IDisposable /// /// /// The server certificate validation callback function. - RemoteCertificateValidationCallback ServerCertificateValidationCallback { get; set; } + RemoteCertificateValidationCallback? ServerCertificateValidationCallback { get; set; } /// /// Get or set the local IP end point to use when connecting to a remote host. @@ -153,7 +153,7 @@ public interface IMailService : IDisposable /// Gets or sets the local IP end point to use when connecting to a remote host. /// /// The local IP end point or to use the default end point. - IPEndPoint LocalEndPoint { get; set; } + IPEndPoint? LocalEndPoint { get; set; } /// /// Get or set the proxy client to use when connecting to a remote host. @@ -166,7 +166,7 @@ public interface IMailService : IDisposable /// /// /// The proxy client. - IProxyClient ProxyClient { get; set; } + IProxyClient? ProxyClient { get; set; } /// /// Get the authentication mechanisms supported by the message service. @@ -1230,7 +1230,7 @@ public interface IMailService : IDisposable /// The event is raised when the client /// successfully connects to the mail server. /// - event EventHandler Connected; + event EventHandler? Connected; /// /// Occurs when the client has been disconnected. @@ -1239,7 +1239,7 @@ public interface IMailService : IDisposable /// The event is raised whenever the client /// has been disconnected. /// - event EventHandler Disconnected; + event EventHandler? Disconnected; /// /// Occurs when the client has been successfully authenticated. @@ -1248,6 +1248,6 @@ public interface IMailService : IDisposable /// The event is raised whenever the client /// has been authenticated. /// - event EventHandler Authenticated; + event EventHandler? Authenticated; } } diff --git a/MailKit/IMailSpool.cs b/MailKit/IMailSpool.cs index 974d642843..04ee57b555 100644 --- a/MailKit/IMailSpool.cs +++ b/MailKit/IMailSpool.cs @@ -252,7 +252,7 @@ public interface IMailSpool : IMailService, IEnumerable /// The index of the message. /// The cancellation token. /// The progress reporting mechanism. - MimeMessage GetMessage (int index, CancellationToken cancellationToken = default, ITransferProgress progress = null); + MimeMessage GetMessage (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the message at the specified index. @@ -264,7 +264,7 @@ public interface IMailSpool : IMailService, IEnumerable /// The index of the message. /// The cancellation token. /// The progress reporting mechanism. - Task GetMessageAsync (int index, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetMessageAsync (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the messages at the specified indexes. @@ -276,7 +276,7 @@ public interface IMailSpool : IMailService, IEnumerable /// The indexes of the messages. /// The cancellation token. /// The progress reporting mechanism. - IList GetMessages (IList indexes, CancellationToken cancellationToken = default, ITransferProgress progress = null); + IList GetMessages (IList indexes, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the messages at the specified indexes. @@ -288,7 +288,7 @@ public interface IMailSpool : IMailService, IEnumerable /// The indexes of the messages. /// The cancellation token. /// The progress reporting mechanism. - Task> GetMessagesAsync (IList indexes, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task> GetMessagesAsync (IList indexes, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the messages within the specified range. @@ -301,7 +301,7 @@ public interface IMailSpool : IMailService, IEnumerable /// The number of messages to get. /// The cancellation token. /// The progress reporting mechanism. - IList GetMessages (int startIndex, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + IList GetMessages (int startIndex, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the messages within the specified range. @@ -314,7 +314,7 @@ public interface IMailSpool : IMailService, IEnumerable /// The number of messages to get. /// The cancellation token. /// The progress reporting mechanism. - Task> GetMessagesAsync (int startIndex, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task> GetMessagesAsync (int startIndex, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the message or header stream at the specified index. @@ -327,7 +327,7 @@ public interface IMailSpool : IMailService, IEnumerable /// if only the headers should be retrieved; otherwise, . /// The cancellation token. /// The progress reporting mechanism. - Stream GetStream (int index, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Stream GetStream (int index, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the message or header stream at the specified index. @@ -340,7 +340,7 @@ public interface IMailSpool : IMailService, IEnumerable /// if only the headers should be retrieved; otherwise, . /// The cancellation token. /// The progress reporting mechanism. - Task GetStreamAsync (int index, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetStreamAsync (int index, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the message or header streams at the specified index. @@ -353,7 +353,7 @@ public interface IMailSpool : IMailService, IEnumerable /// if only the headers should be retrieved; otherwise, . /// The cancellation token. /// The progress reporting mechanism. - IList GetStreams (IList indexes, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress progress = null); + IList GetStreams (IList indexes, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the message or header streams at the specified indexes. @@ -366,7 +366,7 @@ public interface IMailSpool : IMailService, IEnumerable /// if only the headers should be retrieved; otherwise, . /// The cancellation token. /// The progress reporting mechanism. - Task> GetStreamsAsync (IList indexes, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task> GetStreamsAsync (IList indexes, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the message or header streams within the specified range. @@ -380,7 +380,7 @@ public interface IMailSpool : IMailService, IEnumerable /// if only the headers should be retrieved; otherwise, . /// The cancellation token. /// The progress reporting mechanism. - IList GetStreams (int startIndex, int count, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress progress = null); + IList GetStreams (int startIndex, int count, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the message or header streams within the specified range. @@ -394,7 +394,7 @@ public interface IMailSpool : IMailService, IEnumerable /// if only the headers should be retrieved; otherwise, . /// The cancellation token. /// The progress reporting mechanism. - Task> GetStreamsAsync (int startIndex, int count, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task> GetStreamsAsync (int startIndex, int count, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Mark the specified message for deletion. diff --git a/MailKit/IMailTransport.cs b/MailKit/IMailTransport.cs index f3778b3c94..e96e464dcc 100644 --- a/MailKit/IMailTransport.cs +++ b/MailKit/IMailTransport.cs @@ -57,7 +57,7 @@ public interface IMailTransport : IMailService /// The message. /// The cancellation token. /// The progress reporting mechanism. - string Send (MimeMessage message, CancellationToken cancellationToken = default, ITransferProgress progress = null); + string Send (MimeMessage message, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously send the specified message. @@ -75,7 +75,7 @@ public interface IMailTransport : IMailService /// The message. /// The cancellation token. /// The progress reporting mechanism. - Task SendAsync (MimeMessage message, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task SendAsync (MimeMessage message, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Send the specified message using the supplied sender and recipients. @@ -89,7 +89,7 @@ public interface IMailTransport : IMailService /// The mailbox addresses that should receive the message. /// The cancellation token. /// The progress reporting mechanism. - string Send (MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default, ITransferProgress progress = null); + string Send (MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously send the specified message using the supplied sender and recipients. @@ -103,7 +103,7 @@ public interface IMailTransport : IMailService /// The mailbox addresses that should receive the message. /// The cancellation token. /// The progress reporting mechanism. - Task SendAsync (MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task SendAsync (MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Send the specified message. @@ -122,7 +122,7 @@ public interface IMailTransport : IMailService /// The message. /// The cancellation token. /// The progress reporting mechanism. - string Send (FormatOptions options, MimeMessage message, CancellationToken cancellationToken = default, ITransferProgress progress = null); + string Send (FormatOptions options, MimeMessage message, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously send the specified message. @@ -141,7 +141,7 @@ public interface IMailTransport : IMailService /// The message. /// The cancellation token. /// The progress reporting mechanism. - Task SendAsync (FormatOptions options, MimeMessage message, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task SendAsync (FormatOptions options, MimeMessage message, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Send the specified message using the supplied sender and recipients. @@ -156,7 +156,7 @@ public interface IMailTransport : IMailService /// The mailbox addresses that should receive the message. /// The cancellation token. /// The progress reporting mechanism. - string Send (FormatOptions options, MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default, ITransferProgress progress = null); + string Send (FormatOptions options, MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously send the specified message using the supplied sender and recipients. @@ -171,7 +171,7 @@ public interface IMailTransport : IMailService /// The mailbox addresses that should receive the message. /// The cancellation token. /// The progress reporting mechanism. - Task SendAsync (FormatOptions options, MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task SendAsync (FormatOptions options, MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Occurs when a message is successfully sent via the transport. @@ -179,6 +179,6 @@ public interface IMailTransport : IMailService /// /// The event will be emitted each time a message is successfully sent. /// - event EventHandler MessageSent; + event EventHandler? MessageSent; } } diff --git a/MailKit/IProtocolLogger.cs b/MailKit/IProtocolLogger.cs index 05cba6c2c1..8e7782f0fb 100644 --- a/MailKit/IProtocolLogger.cs +++ b/MailKit/IProtocolLogger.cs @@ -45,7 +45,7 @@ public interface IProtocolLogger : IDisposable /// Gets or sets the authentication secret detector. /// /// The authentication secret detector. - IAuthenticationSecretDetector AuthenticationSecretDetector { get; set; } + IAuthenticationSecretDetector? AuthenticationSecretDetector { get; set; } /// /// Logs a connection to the specified URI. diff --git a/MailKit/MailService.cs b/MailKit/MailService.cs index 4a99320f41..d81cb5be24 100644 --- a/MailKit/MailService.cs +++ b/MailKit/MailService.cs @@ -165,7 +165,7 @@ public SslProtocols SslProtocols { /// ConnectAsync methods. /// /// The cipher algorithms allowed for use when negotiating SSL or TLS encryption. - public CipherSuitesPolicy SslCipherSuitesPolicy { + public CipherSuitesPolicy? SslCipherSuitesPolicy { get; set; } @@ -192,7 +192,7 @@ public abstract TlsCipherSuite? SslCipherSuite { /// ConnectAsync methods. /// /// The client SSL certificates. - public X509CertificateCollection ClientCertificates { + public X509CertificateCollection? ClientCertificates { get; set; } @@ -229,7 +229,7 @@ public bool CheckCertificateRevocation { /// /// /// The server certificate validation callback function. - public RemoteCertificateValidationCallback ServerCertificateValidationCallback { + public RemoteCertificateValidationCallback? ServerCertificateValidationCallback { get; set; } @@ -240,7 +240,7 @@ public RemoteCertificateValidationCallback ServerCertificateValidationCallback { /// Gets or sets the local IP end point to use when connecting to the remote host. /// /// The local IP end point or to use the default end point. - public IPEndPoint LocalEndPoint { + public IPEndPoint? LocalEndPoint { get; set; } @@ -255,7 +255,7 @@ public IPEndPoint LocalEndPoint { /// /// /// The proxy client. - public IProxyClient ProxyClient { + public IProxyClient? ProxyClient { get; set; } @@ -454,7 +454,7 @@ public abstract int Timeout { /// The server's SSL certificate. /// The server's SSL certificate chain. /// The SSL policy errors. - protected static bool DefaultServerCertificateValidationCallback (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) + protected static bool DefaultServerCertificateValidationCallback (object? sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors) { return sslPolicyErrors == SslPolicyErrors.None; } @@ -791,7 +791,7 @@ internal SecureSocketOptions GetSecureSocketOptions (Uri uri) if (!protocol.Equals (Protocol, StringComparison.OrdinalIgnoreCase)) throw new ArgumentException ("Unknown URI scheme.", nameof (uri)); - if (query.TryGetValue ("starttls", out string value)) { + if (query.TryGetValue ("starttls", out string? value)) { if (IsAny (value, "always", "true", "yes")) return SecureSocketOptions.StartTls; @@ -1617,7 +1617,7 @@ public Task AuthenticateAsync (string userName, string password, CancellationTok /// The event is raised when the client /// successfully connects to the mail server. /// - public event EventHandler Connected; + public event EventHandler? Connected; /// /// Raise the connected event. @@ -1640,7 +1640,7 @@ protected virtual void OnConnected (string host, int port, SecureSocketOptions o /// The event is raised whenever the client /// gets disconnected. /// - public event EventHandler Disconnected; + public event EventHandler? Disconnected; /// /// Raise the disconnected event. @@ -1664,7 +1664,7 @@ protected virtual void OnDisconnected (string host, int port, SecureSocketOption /// The event is raised whenever the client /// has been authenticated. /// - public event EventHandler Authenticated; + public event EventHandler? Authenticated; /// /// Raise the authenticated event. diff --git a/MailKit/MailSpool.cs b/MailKit/MailSpool.cs index aa05fae451..ae58ae5598 100644 --- a/MailKit/MailSpool.cs +++ b/MailKit/MailSpool.cs @@ -712,7 +712,7 @@ public abstract bool SupportsUids { /// /// A protocol error occurred. /// - public abstract MimeMessage GetMessage (int index, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract MimeMessage GetMessage (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the message at the specified index. @@ -748,7 +748,7 @@ public abstract bool SupportsUids { /// /// A protocol error occurred. /// - public abstract Task GetMessageAsync (int index, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Task GetMessageAsync (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the messages at the specified indexes. @@ -789,7 +789,7 @@ public abstract bool SupportsUids { /// /// A protocol error occurred. /// - public abstract IList GetMessages (IList indexes, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract IList GetMessages (IList indexes, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the messages at the specified indexes. @@ -830,7 +830,7 @@ public abstract bool SupportsUids { /// /// A protocol error occurred. /// - public abstract Task> GetMessagesAsync (IList indexes, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Task> GetMessagesAsync (IList indexes, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the messages within the specified range. @@ -871,7 +871,7 @@ public abstract bool SupportsUids { /// /// A protocol error occurred. /// - public abstract IList GetMessages (int startIndex, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract IList GetMessages (int startIndex, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the messages within the specified range. @@ -909,7 +909,7 @@ public abstract bool SupportsUids { /// /// A protocol error occurred. /// - public abstract Task> GetMessagesAsync (int startIndex, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Task> GetMessagesAsync (int startIndex, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the message or header stream at the specified index. @@ -946,7 +946,7 @@ public abstract bool SupportsUids { /// /// A protocol error occurred. /// - public abstract Stream GetStream (int index, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Stream GetStream (int index, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the message or header stream at the specified index. @@ -983,7 +983,7 @@ public abstract bool SupportsUids { /// /// A protocol error occurred. /// - public abstract Task GetStreamAsync (int index, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Task GetStreamAsync (int index, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the message or header streams at the specified indexes. @@ -1028,7 +1028,7 @@ public abstract bool SupportsUids { /// /// A protocol error occurred. /// - public abstract IList GetStreams (IList indexes, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract IList GetStreams (IList indexes, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the message or header streams at the specified indexes. @@ -1070,7 +1070,7 @@ public abstract bool SupportsUids { /// /// A protocol error occurred. /// - public abstract Task> GetStreamsAsync (IList indexes, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Task> GetStreamsAsync (IList indexes, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the message or header streams within the specified range. @@ -1112,7 +1112,7 @@ public abstract bool SupportsUids { /// /// A protocol error occurred. /// - public abstract IList GetStreams (int startIndex, int count, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract IList GetStreams (int startIndex, int count, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the message or header streams within the specified range. @@ -1151,7 +1151,7 @@ public abstract bool SupportsUids { /// /// A protocol error occurred. /// - public abstract Task> GetStreamsAsync (int startIndex, int count, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Task> GetStreamsAsync (int startIndex, int count, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Mark the specified message for deletion. diff --git a/MailKit/MailTransport.cs b/MailKit/MailTransport.cs index 946b74072e..39422a5b73 100644 --- a/MailKit/MailTransport.cs +++ b/MailKit/MailTransport.cs @@ -115,7 +115,7 @@ protected MailTransport (IProtocolLogger protocolLogger) : base (protocolLogger) /// /// A protocol exception occurred. /// - public virtual string Send (MimeMessage message, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual string Send (MimeMessage message, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return Send (DefaultOptions, message, cancellationToken, progress); } @@ -165,7 +165,7 @@ public virtual string Send (MimeMessage message, CancellationToken cancellationT /// /// A protocol exception occurred. /// - public virtual Task SendAsync (MimeMessage message, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual Task SendAsync (MimeMessage message, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return SendAsync (DefaultOptions, message, cancellationToken, progress); } @@ -215,7 +215,7 @@ public virtual Task SendAsync (MimeMessage message, CancellationToken ca /// /// A protocol exception occurred. /// - public virtual string Send (MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual string Send (MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return Send (DefaultOptions, message, sender, recipients, cancellationToken, progress); } @@ -265,7 +265,7 @@ public virtual string Send (MimeMessage message, MailboxAddress sender, IEnumera /// /// A protocol exception occurred. /// - public virtual Task SendAsync (MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public virtual Task SendAsync (MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return SendAsync (DefaultOptions, message, sender, recipients, cancellationToken, progress); } @@ -324,7 +324,7 @@ public virtual Task SendAsync (MimeMessage message, MailboxAddress sende /// /// A protocol exception occurred. /// - public abstract string Send (FormatOptions options, MimeMessage message, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract string Send (FormatOptions options, MimeMessage message, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously send the specified message. @@ -377,7 +377,7 @@ public virtual Task SendAsync (MimeMessage message, MailboxAddress sende /// /// A protocol exception occurred. /// - public abstract Task SendAsync (FormatOptions options, MimeMessage message, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Task SendAsync (FormatOptions options, MimeMessage message, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Send the specified message using the supplied sender and recipients. @@ -430,7 +430,7 @@ public virtual Task SendAsync (MimeMessage message, MailboxAddress sende /// /// A protocol exception occurred. /// - public abstract string Send (FormatOptions options, MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract string Send (FormatOptions options, MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously send the specified message using the supplied sender and recipients. @@ -483,7 +483,7 @@ public virtual Task SendAsync (MimeMessage message, MailboxAddress sende /// /// A protocol exception occurred. /// - public abstract Task SendAsync (FormatOptions options, MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default, ITransferProgress progress = null); + public abstract Task SendAsync (FormatOptions options, MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Occurs when a message is successfully sent via the transport. @@ -491,7 +491,7 @@ public virtual Task SendAsync (MimeMessage message, MailboxAddress sende /// /// The event will be emitted each time a message is successfully sent. /// - public event EventHandler MessageSent; + public event EventHandler? MessageSent; /// /// Raise the message sent event. diff --git a/MailKit/Net/IChannelBindingContext.cs b/MailKit/Net/IChannelBindingContext.cs index 19b30727b1..a34d703604 100644 --- a/MailKit/Net/IChannelBindingContext.cs +++ b/MailKit/Net/IChannelBindingContext.cs @@ -24,6 +24,7 @@ // THE SOFTWARE. // +using System.Diagnostics.CodeAnalysis; using System.Security.Authentication.ExtendedProtection; namespace MailKit.Net { @@ -44,7 +45,7 @@ interface IChannelBindingContext /// The kind of channel-binding desired. /// The channel-binding. /// if the channel-binding token was acquired; otherwise, . - bool TryGetChannelBinding (ChannelBindingKind kind, out ChannelBinding channelBinding); + bool TryGetChannelBinding (ChannelBindingKind kind, [NotNullWhen (true)] out ChannelBinding? channelBinding); /// /// Try to get a channel-binding token. @@ -55,6 +56,6 @@ interface IChannelBindingContext /// The kind of channel-binding desired. /// The channel-binding token. /// if the channel-binding token was acquired; otherwise, . - bool TryGetChannelBindingToken (ChannelBindingKind kind, out byte[] token); + bool TryGetChannelBindingToken (ChannelBindingKind kind, [NotNullWhen (true)] out byte[]? token); } } diff --git a/MailKit/Net/NetworkOperation.cs b/MailKit/Net/NetworkOperation.cs index cc44befca7..92f09de102 100644 --- a/MailKit/Net/NetworkOperation.cs +++ b/MailKit/Net/NetworkOperation.cs @@ -51,12 +51,12 @@ class NetworkOperation : IDisposable readonly NetworkOperationKind kind; readonly ClientMetrics metrics; - readonly Activity activity; + readonly Activity? activity; readonly long startTimestamp; readonly Uri uri; - Exception ex; + Exception? ex; - NetworkOperation (NetworkOperationKind kind, Uri uri, Activity activity, ClientMetrics metrics) + NetworkOperation (NetworkOperationKind kind, Uri uri, Activity? activity, ClientMetrics metrics) { this.kind = kind; this.uri = uri; @@ -72,7 +72,7 @@ class NetworkOperation : IDisposable startTimestamp = Stopwatch.GetTimestamp (); } #else - Exception ex; + Exception? ex; NetworkOperation () { diff --git a/MailKit/Net/NetworkStream.cs b/MailKit/Net/NetworkStream.cs index 21fe030fa9..d44af996a7 100644 --- a/MailKit/Net/NetworkStream.cs +++ b/MailKit/Net/NetworkStream.cs @@ -34,8 +34,8 @@ namespace MailKit.Net { class NetworkStream : Stream { - SocketAsyncEventArgs send; - SocketAsyncEventArgs recv; + SocketAsyncEventArgs? send; + SocketAsyncEventArgs? recv; bool ownsSocket; bool connected; @@ -115,9 +115,9 @@ public override int WriteTimeout { } } - void AsyncOperationCompleted (object sender, SocketAsyncEventArgs args) + void AsyncOperationCompleted (object? sender, SocketAsyncEventArgs args) { - var tcs = (TaskCompletionSource) args.UserToken; + var tcs = (TaskCompletionSource) args.UserToken!; if (args.SocketError == SocketError.Success) { tcs.TrySetResult (true); @@ -178,7 +178,7 @@ public override async Task ReadAsync (byte[] buffer, int offset, int count, using (var timeout = new CancellationTokenSource (readTimeout)) { using (var linked = CancellationTokenSource.CreateLinkedTokenSource (cancellationToken, timeout.Token)) { using (var registration = linked.Token.Register (() => tcs.TrySetCanceled (), false)) { - recv.SetBuffer (buffer, offset, count); + recv!.SetBuffer (buffer, offset, count); recv.UserToken = tcs; if (!Socket.ReceiveAsync (recv)) @@ -224,7 +224,7 @@ public override async Task WriteAsync (byte[] buffer, int offset, int count, Can using (var timeout = new CancellationTokenSource (writeTimeout)) { using (var linked = CancellationTokenSource.CreateLinkedTokenSource (cancellationToken, timeout.Token)) { using (var registration = linked.Token.Register (() => tcs.TrySetCanceled (), false)) { - send.SetBuffer (buffer, offset, count); + send!.SetBuffer (buffer, offset, count); send.UserToken = tcs; if (!Socket.SendAsync (send)) @@ -267,7 +267,7 @@ public override void SetLength (long value) throw new NotSupportedException (); } - public static NetworkStream Get (Stream stream) + public static NetworkStream? Get (Stream stream) { #if !MAILKIT_LITE if (stream is CompressedStream compressed) diff --git a/MailKit/Net/Pop3/AsyncPop3Client.cs b/MailKit/Net/Pop3/AsyncPop3Client.cs index 5d030d369d..8895d15009 100644 --- a/MailKit/Net/Pop3/AsyncPop3Client.cs +++ b/MailKit/Net/Pop3/AsyncPop3Client.cs @@ -144,7 +144,7 @@ public override async Task AuthenticateAsync (SaslMechanism mechanism, Cancellat using var operation = engine.StartNetworkOperation (NetworkOperationKind.Authenticate); try { - var saslUri = new Uri ("pop://" + engine.Uri.Host); + var saslUri = new Uri ("pop://" + engine.Uri!.Host); var ctx = GetSaslAuthContext (mechanism, saslUri); var pc = await ctx.AuthenticateAsync (cancellationToken).ConfigureAwait (false); @@ -154,7 +154,7 @@ public override async Task AuthenticateAsync (SaslMechanism mechanism, Cancellat pc.ThrowIfError (); - await OnAuthenticatedAsync (ctx.AuthMessage, cancellationToken).ConfigureAwait (false); + await OnAuthenticatedAsync (ctx.AuthMessage!, cancellationToken).ConfigureAwait (false); } catch (Exception ex) { operation.SetError (ex); throw; @@ -226,12 +226,13 @@ public override async Task AuthenticateAsync (Encoding encoding, ICredentials cr using var operation = engine.StartNetworkOperation (NetworkOperationKind.Authenticate); try { - var saslUri = new Uri ("pop://" + engine.Uri.Host); - string userName, password, message = null; - NetworkCredential cred; + var saslUri = new Uri ("pop://" + engine.Uri!.Host); + string userName, password; + NetworkCredential? cred; + string? message = null; - if ((engine.Capabilities & Pop3Capabilities.Apop) != 0) { - var apop = GetApopCommand (encoding, credentials, saslUri); + if ((engine.Capabilities & Pop3Capabilities.Apop) != 0 && (cred = credentials.GetCredential (saslUri, "APOP")) != null) { + var apop = GetApopCommand (encoding, cred); detector.IsAuthenticating = true; @@ -251,11 +252,11 @@ public override async Task AuthenticateAsync (Encoding encoding, ICredentials cr if ((engine.Capabilities & Pop3Capabilities.Sasl) != 0) { foreach (var authmech in SaslMechanism.Rank (engine.AuthenticationMechanisms)) { - SaslMechanism sasl; + SaslMechanism? sasl; cred = credentials.GetCredential (saslUri, authmech); - if ((sasl = SaslMechanism.Create (authmech, encoding, cred)) == null) + if (cred == null || (sasl = SaslMechanism.Create (authmech, encoding, cred)) == null) continue; cancellationToken.ThrowIfCancellationRequested (); @@ -269,13 +270,15 @@ public override async Task AuthenticateAsync (Encoding encoding, ICredentials cr pc.ThrowIfError (); - await OnAuthenticatedAsync (ctx.AuthMessage, cancellationToken).ConfigureAwait (false); + await OnAuthenticatedAsync (ctx.AuthMessage!, cancellationToken).ConfigureAwait (false); return; } } // fall back to the classic USER & PASS commands... - cred = credentials.GetCredential (saslUri, "DEFAULT"); + if ((cred = credentials.GetCredential (saslUri, "DEFAULT")) == null) + throw new AuthenticationException ("No credentials could be found for the POP3 server."); + userName = utf8 ? SaslMechanism.SaslPrep (cred.UserName) : cred.UserName; password = utf8 ? SaslMechanism.SaslPrep (cred.Password) : cred.Password; detector.IsAuthenticating = true; @@ -310,7 +313,7 @@ async Task PostConnectAsync (Stream stream, string host, int port, SecureSocketO probed = ProbedCapabilities.None; try { - ProtocolLogger.LogConnect (engine.Uri); + ProtocolLogger.LogConnect (engine.Uri!); } catch { stream.Dispose (); secure = false; @@ -332,7 +335,7 @@ async Task PostConnectAsync (Stream stream, string host, int port, SecureSocketO try { var tls = new SslStream (stream, false, ValidateRemoteCertificate); - engine.Stream.Stream = tls; + engine.Stream!.Stream = tls; await SslHandshakeAsync (tls, host, cancellationToken).ConfigureAwait (false); } catch (Exception ex) { @@ -790,7 +793,7 @@ public async Task EnableUTF8Async (CancellationToken cancellationToken = default static async Task ReadLangResponseAsync (Pop3Engine engine, Pop3Command pc, CancellationToken cancellationToken) { - var langs = (List) pc.UserData; + var langs = (List) pc.UserData!; do { var response = await engine.ReadLineAsync (cancellationToken).ConfigureAwait (false); @@ -1039,7 +1042,7 @@ public override async Task GetMessageSizeAsync (int index, CancellationToke await engine.RunAsync (true, cancellationToken).ConfigureAwait (false); - return (int) pc.UserData; + return (int) pc.UserData!; } static async Task ReadListAllResponseAsync (Pop3Engine engine, Pop3Command pc, CancellationToken cancellationToken) @@ -1279,7 +1282,7 @@ public override Task> GetMessageHeadersAsync (int startIndex, /// /// A POP3 protocol error occurred. /// - public override Task GetMessageAsync (int index, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override Task GetMessageAsync (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { CheckCanDownload (index); @@ -1332,7 +1335,7 @@ public override Task GetMessageAsync (int index, CancellationToken /// /// A POP3 protocol error occurred. /// - public override Task> GetMessagesAsync (IList indexes, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override Task> GetMessagesAsync (IList indexes, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (!CheckCanDownload (indexes)) return Task.FromResult ((IList) Array.Empty ()); @@ -1388,7 +1391,7 @@ public override Task> GetMessagesAsync (IList indexes, C /// /// A POP3 protocol error occurred. /// - public override Task> GetMessagesAsync (int startIndex, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override Task> GetMessagesAsync (int startIndex, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (!CheckCanDownload (startIndex, count)) return Task.FromResult ((IList) Array.Empty ()); @@ -1433,7 +1436,7 @@ public override Task> GetMessagesAsync (int startIndex, int c /// /// A POP3 protocol error occurred. /// - public override Task GetStreamAsync (int index, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override Task GetStreamAsync (int index, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { CheckCanDownload (index); @@ -1487,7 +1490,7 @@ public override Task GetStreamAsync (int index, bool headersOnly = false /// /// A POP3 protocol error occurred. /// - public override Task> GetStreamsAsync (IList indexes, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override Task> GetStreamsAsync (IList indexes, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (!CheckCanDownload (indexes)) return Task.FromResult ((IList) Array.Empty ()); @@ -1541,7 +1544,7 @@ public override Task> GetStreamsAsync (IList indexes, bool he /// /// A POP3 protocol error occurred. /// - public override Task> GetStreamsAsync (int startIndex, int count, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override Task> GetStreamsAsync (int startIndex, int count, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (!CheckCanDownload (startIndex, count)) return Task.FromResult ((IList) Array.Empty ()); diff --git a/MailKit/Net/Pop3/IPop3Client.cs b/MailKit/Net/Pop3/IPop3Client.cs index b00664a9a1..199d0aca25 100644 --- a/MailKit/Net/Pop3/IPop3Client.cs +++ b/MailKit/Net/Pop3/IPop3Client.cs @@ -79,7 +79,7 @@ public interface IPop3Client : IMailSpool /// information details provided by the server. /// /// The implementation details. - string Implementation { get; } + string? Implementation { get; } /// /// Gets the minimum delay, in milliseconds, between logins. diff --git a/MailKit/Net/Pop3/Pop3Client.cs b/MailKit/Net/Pop3/Pop3Client.cs index bfd697394e..0174c5040c 100644 --- a/MailKit/Net/Pop3/Pop3Client.cs +++ b/MailKit/Net/Pop3/Pop3Client.cs @@ -75,7 +75,7 @@ enum ProbedCapabilities : byte { readonly Pop3AuthenticationSecretDetector detector = new Pop3AuthenticationSecretDetector (); readonly MimeParser parser = new MimeParser (Stream.Null); readonly Pop3Engine engine; - SslCertificateValidationInfo sslValidationInfo; + SslCertificateValidationInfo? sslValidationInfo; ProbedCapabilities probed; bool disposed, disconnecting, secure, utf8; int timeout = 2 * 60 * 1000; @@ -193,7 +193,7 @@ public int ExpirePolicy { /// information details provided by the server. /// /// The implementation details. - public string Implementation { + public string? Implementation { get { return engine.Implementation; } } @@ -257,7 +257,7 @@ void CheckAuthenticated () throw new ServiceNotAuthenticatedException ("The Pop3Client has not been authenticated."); } - bool ValidateRemoteCertificate (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) + bool ValidateRemoteCertificate (object? sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors) { bool valid; @@ -265,13 +265,13 @@ bool ValidateRemoteCertificate (object sender, X509Certificate certificate, X509 sslValidationInfo = null; if (ServerCertificateValidationCallback != null) { - valid = ServerCertificateValidationCallback (engine.Uri.Host, certificate, chain, sslPolicyErrors); + valid = ServerCertificateValidationCallback (engine.Uri!.Host, certificate, chain, sslPolicyErrors); #if NETFRAMEWORK } else if (ServicePointManager.ServerCertificateValidationCallback != null) { - valid = ServicePointManager.ServerCertificateValidationCallback (engine.Uri.Host, certificate, chain, sslPolicyErrors); + valid = ServicePointManager.ServerCertificateValidationCallback (engine.Uri!.Host, certificate, chain, sslPolicyErrors); #endif } else { - valid = DefaultServerCertificateValidationCallback (engine.Uri.Host, certificate, chain, sslPolicyErrors); + valid = DefaultServerCertificateValidationCallback (engine.Uri!.Host, certificate, chain, sslPolicyErrors); } if (!valid) { @@ -365,7 +365,7 @@ public override HashSet AuthenticationMechanisms { public override int Timeout { get { return timeout; } set { - if (IsConnected && engine.Stream.CanTimeout) { + if (IsConnected && engine.Stream!.CanTimeout) { engine.Stream.WriteTimeout = value; engine.Stream.ReadTimeout = value; } @@ -414,7 +414,7 @@ public override bool IsSecure { /// /// if the connection is encrypted; otherwise, . public override bool IsEncrypted { - get { return IsSecure && (engine.Stream.Stream is SslStream sslStream) && sslStream.IsEncrypted; } + get { return IsSecure && (engine.Stream!.Stream is SslStream sslStream) && sslStream.IsEncrypted; } } /// @@ -425,7 +425,7 @@ public override bool IsEncrypted { /// /// if the connection is signed; otherwise, . public override bool IsSigned { - get { return IsSecure && (engine.Stream.Stream is SslStream sslStream) && sslStream.IsSigned; } + get { return IsSecure && (engine.Stream!.Stream is SslStream sslStream) && sslStream.IsSigned; } } /// @@ -440,7 +440,7 @@ public override bool IsSigned { /// The negotiated SSL or TLS protocol version. public override SslProtocols SslProtocol { get { - if (IsSecure && (engine.Stream.Stream is SslStream sslStream)) + if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) return sslStream.SslProtocol; return SslProtocols.None; @@ -462,7 +462,7 @@ public override SslProtocols SslProtocol { #endif public override CipherAlgorithmType? SslCipherAlgorithm { get { - if (IsSecure && (engine.Stream.Stream is SslStream sslStream)) + if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) return sslStream.CipherAlgorithm; return null; @@ -484,7 +484,7 @@ public override CipherAlgorithmType? SslCipherAlgorithm { #endif public override int? SslCipherStrength { get { - if (IsSecure && (engine.Stream.Stream is SslStream sslStream)) + if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) return sslStream.CipherStrength; return null; @@ -501,7 +501,7 @@ public override int? SslCipherStrength { /// The negotiated SSL or TLS cipher suite. public override TlsCipherSuite? SslCipherSuite { get { - if (IsSecure && (engine.Stream.Stream is SslStream sslStream)) + if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) return sslStream.NegotiatedCipherSuite; return null; @@ -524,7 +524,7 @@ public override TlsCipherSuite? SslCipherSuite { #endif public override HashAlgorithmType? SslHashAlgorithm { get { - if (IsSecure && (engine.Stream.Stream is SslStream sslStream)) + if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) return sslStream.HashAlgorithm; return null; @@ -546,7 +546,7 @@ public override HashAlgorithmType? SslHashAlgorithm { #endif public override int? SslHashStrength { get { - if (IsSecure && (engine.Stream.Stream is SslStream sslStream)) + if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) return sslStream.HashStrength; return null; @@ -568,7 +568,7 @@ public override int? SslHashStrength { #endif public override ExchangeAlgorithmType? SslKeyExchangeAlgorithm { get { - if (IsSecure && (engine.Stream.Stream is SslStream sslStream)) + if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) return sslStream.KeyExchangeAlgorithm; return null; @@ -590,7 +590,7 @@ public override ExchangeAlgorithmType? SslKeyExchangeAlgorithm { #endif public override int? SslKeyExchangeStrength { get { - if (IsSecure && (engine.Stream.Stream is SslStream sslStream)) + if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) return sslStream.KeyExchangeStrength; return null; @@ -669,7 +669,7 @@ public SaslAuthContext (Pop3Client client, SaslMechanism mechanism) this.client = client; } - public string AuthMessage { + public string? AuthMessage { get; private set; } @@ -683,7 +683,7 @@ void OnDataReceived (Pop3Engine pop3, Pop3Command pc, string text, CancellationT var challenge = mechanism.Challenge (text, cancellationToken); var buf = Encoding.ASCII.GetBytes (challenge + "\r\n"); - pop3.Stream.Write (buf, 0, buf.Length, cancellationToken); + pop3.Stream!.Write (buf, 0, buf.Length, cancellationToken); pop3.Stream.Flush (cancellationToken); var response = pop3.ReadLine (cancellationToken).TrimEnd (); @@ -703,7 +703,7 @@ async Task OnDataReceivedAsync (Pop3Engine pop3, Pop3Command pc, string text, Ca var challenge = await mechanism.ChallengeAsync (text, cancellationToken).ConfigureAwait (false); var buf = Encoding.ASCII.GetBytes (challenge + "\r\n"); - await pop3.Stream.WriteAsync (buf, 0, buf.Length, cancellationToken).ConfigureAwait (false); + await pop3.Stream!.WriteAsync (buf, 0, buf.Length, cancellationToken).ConfigureAwait (false); await pop3.Stream.FlushAsync (cancellationToken).ConfigureAwait (false); var response = (await pop3.ReadLineAsync (cancellationToken).ConfigureAwait (false)).TrimEnd (); @@ -781,7 +781,7 @@ void CheckCanAuthenticate (SaslMechanism mechanism, CancellationToken cancellati SaslAuthContext GetSaslAuthContext (SaslMechanism mechanism, Uri saslUri) { - mechanism.ChannelBindingContext = engine.Stream.Stream as IChannelBindingContext; + mechanism.ChannelBindingContext = engine.Stream!.Stream as IChannelBindingContext; mechanism.Uri = saslUri; return new SaslAuthContext (this, mechanism); @@ -845,7 +845,7 @@ public override void Authenticate (SaslMechanism mechanism, CancellationToken ca using var operation = engine.StartNetworkOperation (NetworkOperationKind.Authenticate); try { - var saslUri = new Uri ("pop://" + engine.Uri.Host); + var saslUri = new Uri ("pop://" + engine.Uri!.Host); var ctx = GetSaslAuthContext (mechanism, saslUri); var pc = ctx.Authenticate (cancellationToken); @@ -855,7 +855,7 @@ public override void Authenticate (SaslMechanism mechanism, CancellationToken ca pc.ThrowIfError (); - OnAuthenticated (ctx.AuthMessage, cancellationToken); + OnAuthenticated (ctx.AuthMessage!, cancellationToken); } catch (Exception ex) { operation.SetError (ex); throw; @@ -879,9 +879,8 @@ void CheckCanAuthenticate (Encoding encoding, ICredentials credentials, Cancella CheckDisposed (); } - string GetApopCommand (Encoding encoding, ICredentials credentials, Uri saslUri) + string GetApopCommand (Encoding encoding, NetworkCredential cred) { - var cred = credentials.GetCredential (saslUri, "APOP"); var userName = utf8 ? SaslMechanism.SaslPrep (cred.UserName) : cred.UserName; var password = utf8 ? SaslMechanism.SaslPrep (cred.Password) : cred.Password; var challenge = engine.ApopToken + password; @@ -961,12 +960,13 @@ public override void Authenticate (Encoding encoding, ICredentials credentials, using var operation = engine.StartNetworkOperation (NetworkOperationKind.Authenticate); try { - var saslUri = new Uri ("pop://" + engine.Uri.Host); - string userName, password, message = null; - NetworkCredential cred; + var saslUri = new Uri ("pop://" + engine.Uri!.Host); + string userName, password; + NetworkCredential? cred; + string? message = null; - if ((engine.Capabilities & Pop3Capabilities.Apop) != 0) { - var apop = GetApopCommand (encoding, credentials, saslUri); + if ((engine.Capabilities & Pop3Capabilities.Apop) != 0 && (cred = credentials.GetCredential (saslUri, "APOP")) != null) { + var apop = GetApopCommand (encoding, cred); detector.IsAuthenticating = true; @@ -986,11 +986,11 @@ public override void Authenticate (Encoding encoding, ICredentials credentials, if ((engine.Capabilities & Pop3Capabilities.Sasl) != 0) { foreach (var authmech in SaslMechanism.Rank (engine.AuthenticationMechanisms)) { - SaslMechanism sasl; + SaslMechanism? sasl; cred = credentials.GetCredential (saslUri, authmech); - if ((sasl = SaslMechanism.Create (authmech, encoding, cred)) == null) + if (cred == null || (sasl = SaslMechanism.Create (authmech, encoding, cred)) == null) continue; cancellationToken.ThrowIfCancellationRequested (); @@ -1004,13 +1004,15 @@ public override void Authenticate (Encoding encoding, ICredentials credentials, pc.ThrowIfError (); - OnAuthenticated (ctx.AuthMessage, cancellationToken); + OnAuthenticated (ctx.AuthMessage!, cancellationToken); return; } } // fall back to the classic USER & PASS commands... - cred = credentials.GetCredential (saslUri, "DEFAULT"); + if ((cred = credentials.GetCredential (saslUri, "DEFAULT")) == null) + throw new AuthenticationException ("No credentials could be found for the POP3 server."); + userName = utf8 ? SaslMechanism.SaslPrep (cred.UserName) : cred.UserName; password = utf8 ? SaslMechanism.SaslPrep (cred.Password) : cred.Password; detector.IsAuthenticating = true; @@ -1105,7 +1107,7 @@ void PostConnect (Stream stream, string host, int port, SecureSocketOptions opti probed = ProbedCapabilities.None; try { - ProtocolLogger.LogConnect (engine.Uri); + ProtocolLogger.LogConnect (engine.Uri!); } catch { stream.Dispose (); secure = false; @@ -1127,7 +1129,7 @@ void PostConnect (Stream stream, string host, int port, SecureSocketOptions opti try { var tls = new SslStream (stream, false, ValidateRemoteCertificate); - engine.Stream.Stream = tls; + engine.Stream!.Stream = tls; SslHandshake (tls, host, cancellationToken); } catch (Exception ex) { @@ -1555,11 +1557,11 @@ public override void NoOp (CancellationToken cancellationToken = default) SendCommand (cancellationToken, "NOOP\r\n"); } - void OnEngineDisconnected (object sender, EventArgs e) + void OnEngineDisconnected (object? sender, EventArgs e) { var options = SecureSocketOptions.None; bool requested = disconnecting; - string host = null; + string? host = null; int port = 0; if (engine.Uri != null) { @@ -1636,7 +1638,7 @@ public void EnableUTF8 (CancellationToken cancellationToken = default) static void ReadLangResponse (Pop3Engine engine, Pop3Command pc, CancellationToken cancellationToken) { - var langs = (List) pc.UserData; + var langs = (List) pc.UserData!; do { var response = engine.ReadLine (cancellationToken); @@ -1890,7 +1892,7 @@ T OnUidlComplete (Pop3Command pc) engine.Capabilities |= Pop3Capabilities.UIDL; - return (T) pc.UserData; + return (T) pc.UserData!; } /// @@ -1944,7 +1946,7 @@ public override string GetMessageUid (int index, CancellationToken cancellationT static void ParseUidlAllResponse (Pop3Command pc, string response) { var tokens = response.Split (Space, StringSplitOptions.RemoveEmptyEntries); - var uids = (List) pc.UserData; + var uids = (List) pc.UserData!; if (tokens.Length < 2) { pc.Exception = CreatePop3ParseException ("Pop3 server returned an incomplete response to the UIDL command."); @@ -2129,13 +2131,13 @@ public override int GetMessageSize (int index, CancellationToken cancellationTok engine.Run (true, cancellationToken); - return (int) pc.UserData; + return (int) pc.UserData!; } static void ParseListAllResponse (Pop3Command pc, string response) { var tokens = response.Split (Space, StringSplitOptions.RemoveEmptyEntries); - var sizes = (List) pc.UserData; + var sizes = (List) pc.UserData!; if (tokens.Length < 2) { pc.Exception = CreatePop3ParseException ("Pop3 server returned an incomplete response to the LIST command: {0}", response); @@ -2236,13 +2238,13 @@ public override IList GetMessageSizes (CancellationToken cancellationToken abstract class DownloadContext { - readonly ITransferProgress progress; + readonly ITransferProgress? progress; readonly Pop3Client client; - T[] downloaded; + T[]? downloaded; long nread; int idx; - protected DownloadContext (Pop3Client client, ITransferProgress progress) + protected DownloadContext (Pop3Client client, ITransferProgress? progress) { this.progress = progress; this.client = client; @@ -2269,34 +2271,34 @@ protected void Update (int n) void OnDataReceived (Pop3Engine engine, Pop3Command pc, CancellationToken cancellationToken) { try { - engine.Stream.Mode = Pop3StreamMode.Data; + engine.Stream!.Mode = Pop3StreamMode.Data; var item = Parse (engine.Stream, cancellationToken); - downloaded[idx++] = item; + downloaded![idx++] = item; } catch (FormatException ex) { pc.Exception = CreatePop3ParseException (ex, "Failed to parse data."); - engine.Stream.CopyTo (Stream.Null, 4096); + engine.Stream!.CopyTo (Stream.Null, 4096); } finally { - engine.Stream.Mode = Pop3StreamMode.Line; + engine.Stream!.Mode = Pop3StreamMode.Line; } } async Task OnDataReceivedAsync (Pop3Engine engine, Pop3Command pc, CancellationToken cancellationToken) { try { - engine.Stream.Mode = Pop3StreamMode.Data; + engine.Stream!.Mode = Pop3StreamMode.Data; var item = await ParseAsync (engine.Stream, cancellationToken).ConfigureAwait (false); - downloaded[idx++] = item; + downloaded![idx++] = item; } catch (FormatException ex) { pc.Exception = CreatePop3ParseException (ex, "Failed to parse data."); - await engine.Stream.CopyToAsync (Stream.Null, 4096, cancellationToken).ConfigureAwait (false); + await engine.Stream!.CopyToAsync (Stream.Null, 4096, cancellationToken).ConfigureAwait (false); } finally { - engine.Stream.Mode = Pop3StreamMode.Line; + engine.Stream!.Mode = Pop3StreamMode.Line; } } @@ -2440,7 +2442,7 @@ class DownloadStreamContext : DownloadContext { const int BufferSize = 4096; - public DownloadStreamContext (Pop3Client client, ITransferProgress progress = null) : base (client, progress) + public DownloadStreamContext (Pop3Client client, ITransferProgress? progress = null) : base (client, progress) { } @@ -2529,7 +2531,7 @@ class DownloadMessageContext : DownloadContext { readonly MimeParser parser; - public DownloadMessageContext (Pop3Client client, MimeParser parser, ITransferProgress progress = null) : base (client, progress) + public DownloadMessageContext (Pop3Client client, MimeParser parser, ITransferProgress? progress = null) : base (client, progress) { this.parser = parser; } @@ -2782,7 +2784,7 @@ public override IList GetMessageHeaders (int startIndex, int count, /// /// A POP3 protocol error occurred. /// - public override MimeMessage GetMessage (int index, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override MimeMessage GetMessage (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { CheckCanDownload (index); @@ -2835,7 +2837,7 @@ public override MimeMessage GetMessage (int index, CancellationToken cancellatio /// /// A POP3 protocol error occurred. /// - public override IList GetMessages (IList indexes, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override IList GetMessages (IList indexes, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (!CheckCanDownload (indexes)) return Array.Empty (); @@ -2891,7 +2893,7 @@ public override IList GetMessages (IList indexes, Cancellation /// /// A POP3 protocol error occurred. /// - public override IList GetMessages (int startIndex, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override IList GetMessages (int startIndex, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (!CheckCanDownload (startIndex, count)) return Array.Empty (); @@ -2936,7 +2938,7 @@ public override IList GetMessages (int startIndex, int count, Cance /// /// A POP3 protocol error occurred. /// - public override Stream GetStream (int index, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override Stream GetStream (int index, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { CheckCanDownload (index); @@ -2990,7 +2992,7 @@ public override Stream GetStream (int index, bool headersOnly = false, Cancellat /// /// A POP3 protocol error occurred. /// - public override IList GetStreams (IList indexes, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override IList GetStreams (IList indexes, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (!CheckCanDownload (indexes)) return Array.Empty (); @@ -3044,7 +3046,7 @@ public override IList GetStreams (IList indexes, bool headersOnly = /// /// A POP3 protocol error occurred. /// - public override IList GetStreams (int startIndex, int count, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override IList GetStreams (int startIndex, int count, bool headersOnly = false, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (!CheckCanDownload (startIndex, count)) return Array.Empty (); diff --git a/MailKit/Net/Pop3/Pop3Command.cs b/MailKit/Net/Pop3/Pop3Command.cs index b47fc15f34..7c13254805 100644 --- a/MailKit/Net/Pop3/Pop3Command.cs +++ b/MailKit/Net/Pop3/Pop3Command.cs @@ -52,18 +52,18 @@ enum Pop3CommandStatus { class Pop3Command { - public Pop3CommandHandler Handler { get; private set; } + public Pop3CommandHandler? Handler { get; private set; } public Encoding Encoding { get; private set; } public string Command { get; private set; } // output public Pop3CommandStatus Status { get; internal set; } - public ProtocolException Exception { get; set; } - public string StatusText { get; set; } + public ProtocolException? Exception { get; set; } + public string? StatusText { get; set; } - public object UserData { get; set; } + public object? UserData { get; set; } - public Pop3Command (Pop3CommandHandler handler, Encoding encoding, string format, params object[] args) + public Pop3Command (Pop3CommandHandler? handler, Encoding encoding, string format, params object[] args) { Command = string.Format (CultureInfo.InvariantCulture, format, args); Encoding = encoding; @@ -76,7 +76,7 @@ static Exception CreatePop3Exception (Pop3Command pc) var message = string.Format ("POP3 server did not respond with a +OK response to the {0} command.", command); if (pc.Status == Pop3CommandStatus.Error) - return new Pop3CommandException (message, pc.StatusText); + return new Pop3CommandException (message, pc.StatusText!); return new Pop3ProtocolException (message); } diff --git a/MailKit/Net/Pop3/Pop3Engine.cs b/MailKit/Net/Pop3/Pop3Engine.cs index 654188ebd2..432cabf336 100644 --- a/MailKit/Net/Pop3/Pop3Engine.cs +++ b/MailKit/Net/Pop3/Pop3Engine.cs @@ -64,7 +64,7 @@ class Pop3Engine #endif readonly List queue; long clientConnectedTimestamp; - Pop3Stream stream; + Pop3Stream? stream; /// /// Initializes a new instance of the class. @@ -88,7 +88,7 @@ public Pop3Engine () /// Gets the URI of the POP3 server. /// /// The URI of the POP3 server. - public Uri Uri { + public Uri? Uri { get; internal set; } @@ -123,7 +123,7 @@ public Pop3Capabilities Capabilities { /// Gets the underlying POP3 stream. /// /// The pop3 stream. - public Pop3Stream Stream { + public Pop3Stream? Stream { get { return stream; } } @@ -156,7 +156,7 @@ public bool IsConnected { /// Gets the APOP authentication token. /// /// The APOP authentication token. - public string ApopToken { + public string? ApopToken { get; private set; } @@ -178,7 +178,7 @@ public int ExpirePolicy { /// Gets the implementation details of the server. /// /// The implementation details. - public string Implementation { + public string? Implementation { get; private set; } @@ -232,7 +232,7 @@ void ParseGreeting (string greeting) } if (token != "+OK") { - stream.Dispose (); + stream!.Dispose (); stream = null; throw new Pop3ProtocolException (string.Format ("Unexpected greeting from server: {0}", greeting)); @@ -251,12 +251,12 @@ void ParseGreeting (string greeting) State = Pop3EngineState.Connected; } - public NetworkOperation StartNetworkOperation (NetworkOperationKind kind, Uri uri = null) + public NetworkOperation StartNetworkOperation (NetworkOperationKind kind, Uri? uri = null) { #if NET6_0_OR_GREATER - return NetworkOperation.Start (kind, uri ?? Uri, Telemetry.Pop3Client.ActivitySource, metrics); + return NetworkOperation.Start (kind, uri ?? Uri!, Telemetry.Pop3Client.ActivitySource, metrics); #else - return NetworkOperation.Start (kind, uri ?? Uri); + return NetworkOperation.Start (kind, uri ?? Uri!); #endif } @@ -296,17 +296,17 @@ public async Task ConnectAsync (Pop3Stream pop3, CancellationToken cancellationT ParseGreeting (greeting); } - public event EventHandler Disconnected; + public event EventHandler? Disconnected; void OnDisconnected () { Disconnected?.Invoke (this, EventArgs.Empty); } - void RecordClientDisconnected (Exception ex) + void RecordClientDisconnected (Exception? ex) { #if NET6_0_OR_GREATER - metrics?.RecordClientDisconnected (clientConnectedTimestamp, Uri, ex); + metrics?.RecordClientDisconnected (clientConnectedTimestamp, Uri!, ex); #endif clientConnectedTimestamp = 0; } @@ -318,7 +318,7 @@ void RecordClientDisconnected (Exception ex) /// Disconnects the . /// /// The exception that is causing the disconnection. - public void Disconnect (Exception ex) + public void Disconnect (Exception? ex) { RecordClientDisconnected (ex); @@ -355,7 +355,7 @@ public string ReadLine (CancellationToken cancellationToken) bool complete; do { - complete = stream.ReadLine (builder, cancellationToken); + complete = stream!.ReadLine (builder, cancellationToken); } while (!complete); // FIXME: All callers expect CRLF to be trimmed, but many also want all trailing whitespace trimmed. @@ -387,7 +387,7 @@ public async Task ReadLineAsync (CancellationToken cancellationToken) bool complete; do { - complete = await stream.ReadLineAsync (builder, cancellationToken).ConfigureAwait (false); + complete = await stream!.ReadLineAsync (builder, cancellationToken).ConfigureAwait (false); } while (!complete); // FIXME: All callers expect CRLF to be trimmed, but many also want all trailing whitespace trimmed. @@ -527,10 +527,10 @@ public void Run (bool throwOnError, CancellationToken cancellationToken) pc.Status = Pop3CommandStatus.Active; - stream.QueueCommand (pc.Encoding, pc.Command, cancellationToken); + stream!.QueueCommand (pc.Encoding, pc.Command, cancellationToken); } - stream.Flush (cancellationToken); + stream!.Flush (cancellationToken); for (int i = 0; i < queue.Count; i++) ReadResponse (queue[i], cancellationToken); @@ -560,10 +560,10 @@ public async Task RunAsync (bool throwOnError, CancellationToken cancellationTok pc.Status = Pop3CommandStatus.Active; - await stream.QueueCommandAsync (pc.Encoding, pc.Command, cancellationToken).ConfigureAwait (false); + await stream!.QueueCommandAsync (pc.Encoding, pc.Command, cancellationToken).ConfigureAwait (false); } - await stream.FlushAsync (cancellationToken).ConfigureAwait (false); + await stream!.FlushAsync (cancellationToken).ConfigureAwait (false); for (int i = 0; i < queue.Count; i++) await ReadResponseAsync (queue[i], cancellationToken).ConfigureAwait (false); @@ -575,14 +575,14 @@ public async Task RunAsync (bool throwOnError, CancellationToken cancellationTok } } - public Pop3Command QueueCommand (Pop3CommandHandler handler, Encoding encoding, string format, params object[] args) + public Pop3Command QueueCommand (Pop3CommandHandler? handler, Encoding encoding, string format, params object[] args) { var pc = new Pop3Command (handler, encoding, format, args); queue.Add (pc); return pc; } - public Pop3Command QueueCommand (Pop3CommandHandler handler, string format, params object[] args) + public Pop3Command QueueCommand (Pop3CommandHandler? handler, string format, params object[] args) { return QueueCommand (handler, Encoding.ASCII, format, args); } diff --git a/MailKit/Net/Smtp/AsyncSmtpClient.cs b/MailKit/Net/Smtp/AsyncSmtpClient.cs index d4db9013d3..5d86d82298 100644 --- a/MailKit/Net/Smtp/AsyncSmtpClient.cs +++ b/MailKit/Net/Smtp/AsyncSmtpClient.cs @@ -46,7 +46,7 @@ public partial class SmtpClient { async Task QueueCommandAsync (SmtpCommand type, string command, CancellationToken cancellationToken) { - await Stream.QueueCommandAsync (command, cancellationToken).ConfigureAwait (false); + await Stream!.QueueCommandAsync (command, cancellationToken).ConfigureAwait (false); queued.Add (type); } @@ -54,14 +54,14 @@ async Task FlushCommandQueueAsync (MimeMessage message, MailboxAdd { try { // Note: Queued commands are buffered by the stream - await Stream.FlushAsync (cancellationToken).ConfigureAwait (false); + await Stream!.FlushAsync (cancellationToken).ConfigureAwait (false); } catch { queued.Clear (); throw; } var responses = new List (queued.Count); - Exception rex = null; + Exception? rex = null; // Note: We need to read all responses from the server before we can process // them in case any of them have any errors so that we can RSET the state. @@ -85,9 +85,9 @@ async Task FlushCommandQueueAsync (MimeMessage message, MailboxAdd async Task SendCommandInternalAsync (string command, CancellationToken cancellationToken) { try { - return await Stream.SendCommandAsync (command, cancellationToken).ConfigureAwait (false); + return await Stream!.SendCommandAsync (command, cancellationToken).ConfigureAwait (false); } catch { - Disconnect (uri.Host, uri.Port, GetSecureSocketOptions (uri), false); + Disconnect (uri!.Host, uri.Port, GetSecureSocketOptions (uri), false); throw; } } @@ -141,7 +141,7 @@ Task SendEhloAsync (bool connecting, string helo, CancellationToke var command = CreateEhloCommand (helo); if (connecting) - return Stream.SendCommandAsync (command, cancellationToken); + return Stream!.SendCommandAsync (command, cancellationToken); return SendCommandInternalAsync (command, cancellationToken); } @@ -212,7 +212,7 @@ public override async Task AuthenticateAsync (SaslMechanism mechanism, Cancellat using var operation = StartNetworkOperation (NetworkOperationKind.Authenticate); try { - SaslException saslException = null; + SaslException? saslException = null; SmtpResponse response; string challenge; string command; @@ -331,11 +331,11 @@ public override async Task AuthenticateAsync (Encoding encoding, ICredentials cr using var operation = StartNetworkOperation (NetworkOperationKind.Authenticate); try { - var saslUri = new Uri ($"smtp://{uri.Host}"); - AuthenticationException authException = null; - SaslException saslException; + var saslUri = new Uri ($"smtp://{uri!.Host}"); + AuthenticationException? authException = null; + SaslException? saslException; SmtpResponse response; - SaslMechanism sasl; + SaslMechanism? sasl; bool tried = false; string challenge; string command; @@ -343,10 +343,10 @@ public override async Task AuthenticateAsync (Encoding encoding, ICredentials cr foreach (var authmech in SaslMechanism.Rank (AuthenticationMechanisms)) { var cred = credentials.GetCredential (uri, authmech); - if ((sasl = SaslMechanism.Create (authmech, encoding, cred)) == null) + if (cred == null || (sasl = SaslMechanism.Create (authmech, encoding, cred)) == null) continue; - sasl.ChannelBindingContext = Stream.Stream as IChannelBindingContext; + sasl.ChannelBindingContext = Stream!.Stream as IChannelBindingContext; sasl.Uri = saslUri; tried = true; @@ -432,7 +432,7 @@ async Task PostConnectAsync (Stream stream, string host, int port, SecureSocketO clientConnectedTimestamp = Stopwatch.GetTimestamp (); try { - ProtocolLogger.LogConnect (uri); + ProtocolLogger.LogConnect (uri!); } catch { stream.Dispose (); secure = false; @@ -801,7 +801,7 @@ public override async Task DisconnectAsync (bool quit, CancellationToken cancell if (quit) { try { - await Stream.SendCommandAsync ("QUIT\r\n", cancellationToken).ConfigureAwait (false); + await Stream!.SendCommandAsync ("QUIT\r\n", cancellationToken).ConfigureAwait (false); } catch (OperationCanceledException) { } catch (SmtpProtocolException) { } catch (SmtpCommandException) { @@ -809,7 +809,7 @@ public override async Task DisconnectAsync (bool quit, CancellationToken cancell } } - Disconnect (uri.Host, uri.Port, GetSecureSocketOptions (uri), true); + Disconnect (uri!.Host, uri.Port, GetSecureSocketOptions (uri), true); } /// @@ -884,7 +884,7 @@ async Task MailFromAsync (FormatOptions options, MimeMessage message, MailboxAdd return; } - var response = await Stream.SendCommandAsync (command, cancellationToken).ConfigureAwait (false); + var response = await Stream!.SendCommandAsync (command, cancellationToken).ConfigureAwait (false); ParseMailFromResponse (message, mailbox, response); } @@ -898,16 +898,16 @@ async Task RcptToAsync (FormatOptions options, MimeMessage message, Mailbo return false; } - var response = await Stream.SendCommandAsync (command, cancellationToken).ConfigureAwait (false); + var response = await Stream!.SendCommandAsync (command, cancellationToken).ConfigureAwait (false); return ParseRcptToResponse (message, mailbox, response); } - async Task BdatAsync (FormatOptions options, MimeMessage message, long size, CancellationToken cancellationToken, ITransferProgress progress) + async Task BdatAsync (FormatOptions options, MimeMessage message, long size, CancellationToken cancellationToken, ITransferProgress? progress) { var command = string.Format (CultureInfo.InvariantCulture, "BDAT {0} LAST\r\n", size); - await Stream.QueueCommandAsync (command, cancellationToken).ConfigureAwait (false); + await Stream!.QueueCommandAsync (command, cancellationToken).ConfigureAwait (false); if (progress != null) { var ctx = new SendContext (progress, size); @@ -926,12 +926,12 @@ async Task BdatAsync (FormatOptions options, MimeMessage message, long s return ParseBdatResponse (message, response); } - async Task MessageDataAsync (FormatOptions options, MimeMessage message, long size, CancellationToken cancellationToken, ITransferProgress progress) + async Task MessageDataAsync (FormatOptions options, MimeMessage message, long size, CancellationToken cancellationToken, ITransferProgress? progress) { if (progress != null) { var ctx = new SendContext (progress, size); - using (var stream = new ProgressStream (Stream, ctx.Update)) { + using (var stream = new ProgressStream (Stream!, ctx.Update)) { using (var filtered = new FilteredStream (stream)) { filtered.Add (new SmtpDataFilter ()); @@ -940,7 +940,7 @@ async Task MessageDataAsync (FormatOptions options, MimeMessage message, } } } else { - using (var filtered = new FilteredStream (Stream)) { + using (var filtered = new FilteredStream (Stream!)) { filtered.Add (new SmtpDataFilter ()); await message.WriteToAsync (options, filtered, cancellationToken).ConfigureAwait (false); @@ -948,7 +948,7 @@ async Task MessageDataAsync (FormatOptions options, MimeMessage message, } } - await Stream.WriteAsync (EndData, 0, EndData.Length, cancellationToken).ConfigureAwait (false); + await Stream!.WriteAsync (EndData, 0, EndData.Length, cancellationToken).ConfigureAwait (false); await Stream.FlushAsync (cancellationToken).ConfigureAwait (false); var response = await Stream.ReadResponseAsync (cancellationToken).ConfigureAwait (false); @@ -968,10 +968,10 @@ async Task ResetAsync (CancellationToken cancellationToken) } if (response.StatusCode != SmtpStatusCode.Ok) - Disconnect (uri.Host, uri.Port, GetSecureSocketOptions (uri), false); + Disconnect (uri!.Host, uri.Port, GetSecureSocketOptions (uri), false); } - async Task SendAsync (FormatOptions options, MimeMessage message, MailboxAddress sender, IList recipients, CancellationToken cancellationToken, ITransferProgress progress) + async Task SendAsync (FormatOptions options, MimeMessage message, MailboxAddress sender, IList recipients, CancellationToken cancellationToken, ITransferProgress? progress) { var format = Prepare (options, message, sender, recipients, out var extensions); var pipeline = (capabilities & SmtpCapabilities.Pipelining) != 0; @@ -1017,7 +1017,7 @@ async Task SendAsync (FormatOptions options, MimeMessage message, Mailbo if (bdat) return await BdatAsync (format, message, size, cancellationToken, progress).ConfigureAwait (false); - var dataResponse = await Stream.SendCommandAsync ("DATA\r\n", cancellationToken).ConfigureAwait (false); + var dataResponse = await Stream!.SendCommandAsync ("DATA\r\n", cancellationToken).ConfigureAwait (false); ParseDataResponse (dataResponse); @@ -1037,7 +1037,7 @@ async Task SendAsync (FormatOptions options, MimeMessage message, Mailbo } catch (Exception ex) { operation.SetError (ex); - Disconnect (uri.Host, uri.Port, GetSecureSocketOptions (uri), false); + Disconnect (uri!.Host, uri.Port, GetSecureSocketOptions (uri), false); throw; } } @@ -1096,7 +1096,7 @@ async Task SendAsync (FormatOptions options, MimeMessage message, Mailbo /// /// An SMTP protocol exception occurred. /// - public override Task SendAsync (FormatOptions options, MimeMessage message, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override Task SendAsync (FormatOptions options, MimeMessage message, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { ValidateArguments (options, message, out var sender, out var recipients); @@ -1154,7 +1154,7 @@ public override Task SendAsync (FormatOptions options, MimeMessage messa /// /// An SMTP protocol exception occurred. /// - public override Task SendAsync (FormatOptions options, MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override Task SendAsync (FormatOptions options, MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var rcpts = ValidateArguments (options, message, sender, recipients); diff --git a/MailKit/Net/Smtp/ISmtpClient.cs b/MailKit/Net/Smtp/ISmtpClient.cs index fd0780cdea..6ae947025e 100644 --- a/MailKit/Net/Smtp/ISmtpClient.cs +++ b/MailKit/Net/Smtp/ISmtpClient.cs @@ -63,7 +63,7 @@ public interface ISmtpClient : IMailTransport /// used instead. /// /// The local domain. - string LocalDomain { get; set; } + string? LocalDomain { get; set; } /// /// Get the maximum message size supported by the server. diff --git a/MailKit/Net/Smtp/SmtpClient.cs b/MailKit/Net/Smtp/SmtpClient.cs index bf52ce01b4..46b6c184fd 100644 --- a/MailKit/Net/Smtp/SmtpClient.cs +++ b/MailKit/Net/Smtp/SmtpClient.cs @@ -86,7 +86,7 @@ enum SmtpCommand { readonly HashSet authenticationMechanisms = new HashSet (StringComparer.Ordinal); readonly SmtpAuthenticationSecretDetector detector = new SmtpAuthenticationSecretDetector (); readonly List queued = new List (); - SslCertificateValidationInfo sslValidationInfo; + SslCertificateValidationInfo? sslValidationInfo; #if NET6_0_OR_GREATER readonly ClientMetrics metrics; #endif @@ -97,14 +97,14 @@ enum SmtpCommand { bool connected; bool disposed; bool secure; - Uri uri; + Uri? uri; - internal static string GetSafeHostName (string hostName) + internal static string? GetSafeHostName (string? hostName) { var idn = new IdnMapping (); if (!string.IsNullOrEmpty (hostName)) { - hostName = hostName.Replace ('_', '-'); + hostName = hostName!.Replace ('_', '-'); try { return idn.GetAscii (hostName); @@ -214,7 +214,7 @@ public SmtpClient (IProtocolLogger protocolLogger, IMeterFactory meterFactory) : /// Gets the underlying SMTP stream. /// /// The SMTP stream. - SmtpStream Stream { + SmtpStream? Stream { get; set; } @@ -275,7 +275,7 @@ public SmtpCapabilities Capabilities { /// used instead. /// /// The local domain. - public string LocalDomain { + public string? LocalDomain { get; set; } @@ -366,7 +366,7 @@ public override HashSet AuthenticationMechanisms { public override int Timeout { get { return timeout; } set { - if (IsConnected && Stream.CanTimeout) { + if (IsConnected && Stream!.CanTimeout) { Stream.WriteTimeout = value; Stream.ReadTimeout = value; } @@ -415,7 +415,7 @@ public override bool IsSecure { /// /// if the connection is encrypted; otherwise, . public override bool IsEncrypted { - get { return IsSecure && (Stream.Stream is SslStream sslStream) && sslStream.IsEncrypted; } + get { return IsSecure && (Stream!.Stream is SslStream sslStream) && sslStream.IsEncrypted; } } /// @@ -426,7 +426,7 @@ public override bool IsEncrypted { /// /// if the connection is signed; otherwise, . public override bool IsSigned { - get { return IsSecure && (Stream.Stream is SslStream sslStream) && sslStream.IsSigned; } + get { return IsSecure && (Stream!.Stream is SslStream sslStream) && sslStream.IsSigned; } } /// @@ -441,7 +441,7 @@ public override bool IsSigned { /// The negotiated SSL or TLS protocol version. public override SslProtocols SslProtocol { get { - if (IsSecure && (Stream.Stream is SslStream sslStream)) + if (IsSecure && (Stream!.Stream is SslStream sslStream)) return sslStream.SslProtocol; return SslProtocols.None; @@ -463,7 +463,7 @@ public override SslProtocols SslProtocol { #endif public override CipherAlgorithmType? SslCipherAlgorithm { get { - if (IsSecure && (Stream.Stream is SslStream sslStream)) + if (IsSecure && (Stream!.Stream is SslStream sslStream)) return sslStream.CipherAlgorithm; return null; @@ -485,7 +485,7 @@ public override CipherAlgorithmType? SslCipherAlgorithm { #endif public override int? SslCipherStrength { get { - if (IsSecure && (Stream.Stream is SslStream sslStream)) + if (IsSecure && (Stream!.Stream is SslStream sslStream)) return sslStream.CipherStrength; return null; @@ -502,7 +502,7 @@ public override int? SslCipherStrength { /// The negotiated SSL or TLS cipher suite. public override TlsCipherSuite? SslCipherSuite { get { - if (IsSecure && (Stream.Stream is SslStream sslStream)) + if (IsSecure && (Stream!.Stream is SslStream sslStream)) return sslStream.NegotiatedCipherSuite; return null; @@ -525,7 +525,7 @@ public override TlsCipherSuite? SslCipherSuite { #endif public override HashAlgorithmType? SslHashAlgorithm { get { - if (IsSecure && (Stream.Stream is SslStream sslStream)) + if (IsSecure && (Stream!.Stream is SslStream sslStream)) return sslStream.HashAlgorithm; return null; @@ -547,7 +547,7 @@ public override HashAlgorithmType? SslHashAlgorithm { #endif public override int? SslHashStrength { get { - if (IsSecure && (Stream.Stream is SslStream sslStream)) + if (IsSecure && (Stream!.Stream is SslStream sslStream)) return sslStream.HashStrength; return null; @@ -569,7 +569,7 @@ public override int? SslHashStrength { #endif public override ExchangeAlgorithmType? SslKeyExchangeAlgorithm { get { - if (IsSecure && (Stream.Stream is SslStream sslStream)) + if (IsSecure && (Stream!.Stream is SslStream sslStream)) return sslStream.KeyExchangeAlgorithm; return null; @@ -591,7 +591,7 @@ public override ExchangeAlgorithmType? SslKeyExchangeAlgorithm { #endif public override int? SslKeyExchangeStrength { get { - if (IsSecure && (Stream.Stream is SslStream sslStream)) + if (IsSecure && (Stream!.Stream is SslStream sslStream)) return sslStream.KeyExchangeStrength; return null; @@ -615,13 +615,13 @@ public override bool IsAuthenticated { NetworkOperation StartNetworkOperation (NetworkOperationKind kind) { #if NET6_0_OR_GREATER - return NetworkOperation.Start (kind, uri, Telemetry.SmtpClient.ActivitySource, metrics); + return NetworkOperation.Start (kind, uri!, Telemetry.SmtpClient.ActivitySource, metrics); #else - return NetworkOperation.Start (kind, uri); + return NetworkOperation.Start (kind, uri!); #endif } - bool ValidateRemoteCertificate (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) + bool ValidateRemoteCertificate (object? sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors) { bool valid; @@ -629,13 +629,13 @@ bool ValidateRemoteCertificate (object sender, X509Certificate certificate, X509 sslValidationInfo = null; if (ServerCertificateValidationCallback != null) { - valid = ServerCertificateValidationCallback (uri.Host, certificate, chain, sslPolicyErrors); + valid = ServerCertificateValidationCallback (uri!.Host, certificate, chain, sslPolicyErrors); #if NETFRAMEWORK } else if (ServicePointManager.ServerCertificateValidationCallback != null) { - valid = ServicePointManager.ServerCertificateValidationCallback (uri.Host, certificate, chain, sslPolicyErrors); + valid = ServicePointManager.ServerCertificateValidationCallback (uri!.Host, certificate, chain, sslPolicyErrors); #endif } else { - valid = DefaultServerCertificateValidationCallback (uri.Host, certificate, chain, sslPolicyErrors); + valid = DefaultServerCertificateValidationCallback (uri!.Host, certificate, chain, sslPolicyErrors); } if (!valid) { @@ -661,25 +661,25 @@ protected virtual void OnNoRecipientsAccepted (MimeMessage message) void QueueCommand (SmtpCommand type, string command, CancellationToken cancellationToken) { - Stream.QueueCommand (command, cancellationToken); + Stream!.QueueCommand (command, cancellationToken); queued.Add (type); } struct QueueResults { public readonly int RecipientsAccepted; - public Exception FirstException; + public Exception? FirstException; - public QueueResults (int recipientsAccepted, Exception firstException) + public QueueResults (int recipientsAccepted, Exception? firstException) { RecipientsAccepted = recipientsAccepted; FirstException = firstException; } } - QueueResults ParseCommandQueueResponses (MimeMessage message, MailboxAddress sender, IList recipients, List responses, Exception readResponseException) + QueueResults ParseCommandQueueResponses (MimeMessage message, MailboxAddress sender, IList recipients, List responses, Exception? readResponseException) { - Exception firstException = null; + Exception? firstException = null; int recipientsAccepted = 0; int rcpt = 0; @@ -715,14 +715,14 @@ QueueResults FlushCommandQueue (MimeMessage message, MailboxAddress sender, ILis { try { // Note: Queued commands are buffered by the stream - Stream.Flush (cancellationToken); + Stream!.Flush (cancellationToken); } catch { queued.Clear (); throw; } var responses = new List (queued.Count); - Exception rex = null; + Exception? rex = null; // Note: We need to read all responses from the server before we can process // them in case any of them have any errors so that we can RSET the state. @@ -746,9 +746,9 @@ QueueResults FlushCommandQueue (MimeMessage message, MailboxAddress sender, ILis SmtpResponse SendCommandInternal (string command, CancellationToken cancellationToken) { try { - return Stream.SendCommand (command, cancellationToken); + return Stream!.SendCommand (command, cancellationToken); } catch { - Disconnect (uri.Host, uri.Port, GetSecureSocketOptions (uri), false); + Disconnect (uri!.Host, uri.Port, GetSecureSocketOptions (uri), false); throw; } } @@ -951,7 +951,7 @@ string CreateEhloCommand (string helo) return string.Format ("{0} [{1}]\r\n", helo, ip); } else { - domain = LocalDomain; + domain = LocalDomain!; } } else { domain = DefaultLocalDomain; @@ -965,7 +965,7 @@ SmtpResponse SendEhlo (bool connecting, string helo, CancellationToken cancellat var command = CreateEhloCommand (helo); if (connecting) - return Stream.SendCommand (command, cancellationToken); + return Stream!.SendCommand (command, cancellationToken); return SendCommandInternal (command, cancellationToken); } @@ -1001,8 +1001,8 @@ void ValidateArguments (SaslMechanism mechanism) if ((capabilities & SmtpCapabilities.Authentication) == 0) throw new NotSupportedException ("The SMTP server does not support authentication."); - mechanism.ChannelBindingContext = Stream.Stream as IChannelBindingContext; - mechanism.Uri = new Uri ($"smtp://{uri.Host}"); + mechanism.ChannelBindingContext = Stream!.Stream as IChannelBindingContext; + mechanism.Uri = new Uri ($"smtp://{uri!.Host}"); } /// @@ -1055,7 +1055,7 @@ public override void Authenticate (SaslMechanism mechanism, CancellationToken ca using var operation = StartNetworkOperation (NetworkOperationKind.Authenticate); try { - SaslException saslException = null; + SaslException? saslException = null; SmtpResponse response; string challenge; string command; @@ -1193,11 +1193,11 @@ public override void Authenticate (Encoding encoding, ICredentials credentials, using var operation = StartNetworkOperation (NetworkOperationKind.Authenticate); try { - var saslUri = new Uri ($"smtp://{uri.Host}"); - AuthenticationException authException = null; - SaslException saslException; + var saslUri = new Uri ($"smtp://{uri!.Host}"); + AuthenticationException? authException = null; + SaslException? saslException; SmtpResponse response; - SaslMechanism sasl; + SaslMechanism? sasl; bool tried = false; string challenge; string command; @@ -1205,10 +1205,10 @@ public override void Authenticate (Encoding encoding, ICredentials credentials, foreach (var authmech in SaslMechanism.Rank (AuthenticationMechanisms)) { var cred = credentials.GetCredential (uri, authmech); - if ((sasl = SaslMechanism.Create (authmech, encoding, cred)) == null) + if (cred == null || (sasl = SaslMechanism.Create (authmech, encoding, cred)) == null) continue; - sasl.ChannelBindingContext = Stream.Stream as IChannelBindingContext; + sasl.ChannelBindingContext = Stream!.Stream as IChannelBindingContext; sasl.Uri = saslUri; tried = true; @@ -1329,10 +1329,10 @@ void SslHandshake (SslStream ssl, string host, CancellationToken cancellationTok #endif } - void RecordClientDisconnected (Exception ex) + void RecordClientDisconnected (Exception? ex) { #if NET6_0_OR_GREATER - metrics?.RecordClientDisconnected (clientConnectedTimestamp, uri, ex); + metrics?.RecordClientDisconnected (clientConnectedTimestamp, uri!, ex); #endif clientConnectedTimestamp = 0; } @@ -1342,7 +1342,7 @@ void PostConnect (Stream stream, string host, int port, SecureSocketOptions opti clientConnectedTimestamp = Stopwatch.GetTimestamp (); try { - ProtocolLogger.LogConnect (uri); + ProtocolLogger.LogConnect (uri!); } catch { stream.Dispose (); secure = false; @@ -1743,7 +1743,7 @@ public override void Disconnect (bool quit, CancellationToken cancellationToken if (quit) { try { - Stream.SendCommand ("QUIT\r\n", cancellationToken); + Stream!.SendCommand ("QUIT\r\n", cancellationToken); } catch (OperationCanceledException) { } catch (SmtpProtocolException) { } catch (SmtpCommandException) { @@ -1751,7 +1751,7 @@ public override void Disconnect (bool quit, CancellationToken cancellationToken } } - Disconnect (uri.Host, uri.Port, GetSecureSocketOptions (uri), true); + Disconnect (uri!.Host, uri.Port, GetSecureSocketOptions (uri), true); } /// @@ -1790,7 +1790,7 @@ public override void NoOp (CancellationToken cancellationToken = default) throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); } - void Disconnect (string host, int port, SecureSocketOptions options, bool requested) + void Disconnect (string? host, int port, SecureSocketOptions options, bool requested) { // Note: if the uri is null, then the user manually disconnected already. if (uri != null) @@ -1816,7 +1816,7 @@ void Disconnect (string host, int port, SecureSocketOptions options, bool reques #region IMailTransport implementation - static MailboxAddress GetMessageSender (MimeMessage message) + static MailboxAddress? GetMessageSender (MimeMessage message) { if (message.ResentSender != null) return message.ResentSender; @@ -1899,7 +1899,7 @@ protected virtual void OnSenderNotAccepted (MimeMessage message, MailboxAddress /// /// The envelope identifier. /// The message. - protected virtual string GetEnvelopeId (MimeMessage message) + protected virtual string? GetEnvelopeId (MimeMessage message) { return null; } @@ -1995,7 +1995,7 @@ string CreateMailFromCommand (FormatOptions options, MimeMessage message, Mailbo if (!string.IsNullOrEmpty (envid)) { builder.Append (" ENVID="); - AppendHexEncoded (builder, envid); + AppendHexEncoded (builder, envid!); } switch (DeliveryStatusNotificationType) { @@ -2043,7 +2043,7 @@ void MailFrom (FormatOptions options, MimeMessage message, MailboxAddress mailbo return; } - var response = Stream.SendCommand (command, cancellationToken); + var response = Stream!.SendCommand (command, cancellationToken); ParseMailFromResponse (message, mailbox, response); } @@ -2184,7 +2184,7 @@ bool RcptTo (FormatOptions options, MimeMessage message, MailboxAddress mailbox, return false; } - var response = Stream.SendCommand (command, cancellationToken); + var response = Stream!.SendCommand (command, cancellationToken); return ParseRcptToResponse (message, mailbox, response); } @@ -2225,11 +2225,11 @@ string ParseBdatResponse (MimeMessage message, SmtpResponse response) } } - string Bdat (FormatOptions options, MimeMessage message, long size, CancellationToken cancellationToken, ITransferProgress progress) + string Bdat (FormatOptions options, MimeMessage message, long size, CancellationToken cancellationToken, ITransferProgress? progress) { var command = string.Format (CultureInfo.InvariantCulture, "BDAT {0} LAST\r\n", size); - Stream.QueueCommand (command, cancellationToken); + Stream!.QueueCommand (command, cancellationToken); if (progress != null) { var ctx = new SendContext (progress, size); @@ -2267,12 +2267,12 @@ string ParseMessageDataResponse (MimeMessage message, SmtpResponse response) } } - string MessageData (FormatOptions options, MimeMessage message, long size, CancellationToken cancellationToken, ITransferProgress progress) + string MessageData (FormatOptions options, MimeMessage message, long size, CancellationToken cancellationToken, ITransferProgress? progress) { if (progress != null) { var ctx = new SendContext (progress, size); - using (var stream = new ProgressStream (Stream, ctx.Update)) { + using (var stream = new ProgressStream (Stream!, ctx.Update)) { using (var filtered = new FilteredStream (stream)) { filtered.Add (new SmtpDataFilter ()); @@ -2281,7 +2281,7 @@ string MessageData (FormatOptions options, MimeMessage message, long size, Cance } } } else { - using (var filtered = new FilteredStream (Stream)) { + using (var filtered = new FilteredStream (Stream!)) { filtered.Add (new SmtpDataFilter ()); message.WriteTo (options, filtered, cancellationToken); @@ -2289,7 +2289,7 @@ string MessageData (FormatOptions options, MimeMessage message, long size, Cance } } - Stream.Write (EndData, 0, EndData.Length, cancellationToken); + Stream!.Write (EndData, 0, EndData.Length, cancellationToken); Stream.Flush (cancellationToken); var response = Stream.ReadResponse (cancellationToken); @@ -2309,7 +2309,7 @@ void Reset (CancellationToken cancellationToken) } if (response.StatusCode != SmtpStatusCode.Ok) - Disconnect (uri.Host, uri.Port, GetSecureSocketOptions (uri), false); + Disconnect (uri!.Host, uri.Port, GetSecureSocketOptions (uri), false); } /// @@ -2430,7 +2430,7 @@ bool UseBdatCommand (SmtpExtensions extensions) return (extensions & SmtpExtensions.BinaryMime) != 0 || (PreferSendAsBinaryData && (Capabilities & (SmtpCapabilities.BinaryMime | SmtpCapabilities.Chunking)) != 0); } - string Send (FormatOptions options, MimeMessage message, MailboxAddress sender, IList recipients, CancellationToken cancellationToken, ITransferProgress progress) + string Send (FormatOptions options, MimeMessage message, MailboxAddress sender, IList recipients, CancellationToken cancellationToken, ITransferProgress? progress) { var format = Prepare (options, message, sender, recipients, out var extensions); var pipeline = (capabilities & SmtpCapabilities.Pipelining) != 0; @@ -2476,7 +2476,7 @@ string Send (FormatOptions options, MimeMessage message, MailboxAddress sender, if (bdat) return Bdat (format, message, size, cancellationToken, progress); - var dataResponse = Stream.SendCommand ("DATA\r\n", cancellationToken); + var dataResponse = Stream!.SendCommand ("DATA\r\n", cancellationToken); ParseDataResponse (dataResponse); @@ -2496,7 +2496,7 @@ string Send (FormatOptions options, MimeMessage message, MailboxAddress sender, } catch (Exception ex) { operation.SetError (ex); - Disconnect (uri.Host, uri.Port, GetSecureSocketOptions (uri), false); + Disconnect (uri!.Host, uri.Port, GetSecureSocketOptions (uri), false); throw; } } @@ -2509,12 +2509,14 @@ static void ValidateArguments (FormatOptions options, MimeMessage message, out M if (message == null) throw new ArgumentNullException (nameof (message)); - recipients = GetMessageRecipients (message); - sender = GetMessageSender (message); + var mailbox = GetMessageSender (message); - if (sender == null) + if (mailbox == null) throw new InvalidOperationException ("No sender has been specified."); + sender = mailbox; + recipients = GetMessageRecipients (message); + if (recipients.Count == 0) throw new InvalidOperationException ("No recipients have been specified."); } @@ -2573,7 +2575,7 @@ static void ValidateArguments (FormatOptions options, MimeMessage message, out M /// /// An SMTP protocol exception occurred. /// - public override string Send (FormatOptions options, MimeMessage message, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override string Send (FormatOptions options, MimeMessage message, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { ValidateArguments (options, message, out var sender, out var recipients); @@ -2656,7 +2658,7 @@ static List ValidateArguments (FormatOptions options, MimeMessag /// /// An SMTP protocol exception occurred. /// - public override string Send (FormatOptions options, MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public override string Send (FormatOptions options, MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var rcpts = ValidateArguments (options, message, sender, recipients); diff --git a/MailKit/Net/Smtp/SmtpCommandException.cs b/MailKit/Net/Smtp/SmtpCommandException.cs index 2d0a44a4fd..0019a9fd51 100644 --- a/MailKit/Net/Smtp/SmtpCommandException.cs +++ b/MailKit/Net/Smtp/SmtpCommandException.cs @@ -200,7 +200,7 @@ public SmtpErrorCode ErrorCode { /// /// /// The mailbox. - public MailboxAddress Mailbox { + public MailboxAddress? Mailbox { get; private set; } diff --git a/MailKit/Net/Smtp/SmtpStream.cs b/MailKit/Net/Smtp/SmtpStream.cs index b354be4e06..7fa6e1c0db 100644 --- a/MailKit/Net/Smtp/SmtpStream.cs +++ b/MailKit/Net/Smtp/SmtpStream.cs @@ -56,7 +56,7 @@ class SmtpStream : Stream, ICancellableStream readonly IProtocolLogger logger; int inputIndex, inputEnd; - string lastResponse; + string? lastResponse; bool disposed; /// diff --git a/MailKit/Net/SslStream.cs b/MailKit/Net/SslStream.cs index af746fd47e..4b283314de 100644 --- a/MailKit/Net/SslStream.cs +++ b/MailKit/Net/SslStream.cs @@ -27,14 +27,15 @@ using System; using System.IO; using System.Net.Security; +using System.Diagnostics.CodeAnalysis; using System.Security.Authentication.ExtendedProtection; namespace MailKit.Net { class SslStream : System.Net.Security.SslStream, IChannelBindingContext { - ChannelBinding tlsServerEndPoint; - ChannelBinding tlsUnique; + ChannelBinding? tlsServerEndPoint; + ChannelBinding? tlsUnique; public SslStream (Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback userCertificateValidationCallback) : base (innerStream, leaveInnerStreamOpen, userCertificateValidationCallback) { @@ -44,9 +45,9 @@ public SslStream (Stream innerStream, bool leaveInnerStreamOpen, RemoteCertifica get { return base.InnerStream; } } - ChannelBinding GetChannelBinding (ChannelBindingKind kind) + ChannelBinding? GetChannelBinding (ChannelBindingKind kind) { - ChannelBinding channelBinding; + ChannelBinding? channelBinding; try { // Note: Documentation for TransportContext.GetChannelBinding() states that it will return null if the @@ -72,7 +73,7 @@ ChannelBinding GetChannelBinding (ChannelBindingKind kind) /// The kind of channel-binding desired. /// The channel-binding. /// if the channel-binding token was acquired; otherwise, . - public bool TryGetChannelBinding (ChannelBindingKind kind, out ChannelBinding channelBinding) + public bool TryGetChannelBinding (ChannelBindingKind kind, [NotNullWhen (true)] out ChannelBinding? channelBinding) { int identifierLength; @@ -102,7 +103,7 @@ public bool TryGetChannelBinding (ChannelBindingKind kind, out ChannelBinding ch /// The kind of channel-binding desired. /// The channel-binding token. /// if the channel-binding token was acquired; otherwise, . - public bool TryGetChannelBindingToken (ChannelBindingKind kind, out byte[] token) + public bool TryGetChannelBindingToken (ChannelBindingKind kind, [NotNullWhen (true)] out byte[]? token) { token = null; diff --git a/MailKit/NullProtocolLogger.cs b/MailKit/NullProtocolLogger.cs index 57efe0484f..39b98e137a 100644 --- a/MailKit/NullProtocolLogger.cs +++ b/MailKit/NullProtocolLogger.cs @@ -57,7 +57,7 @@ public NullProtocolLogger () /// Gets or sets the authentication secret detector. /// /// The authentication secret detector. - public IAuthenticationSecretDetector AuthenticationSecretDetector { get; set; } + public IAuthenticationSecretDetector? AuthenticationSecretDetector { get; set; } /// /// Logs a connection to the specified URI. diff --git a/MailKit/ProgressStream.cs b/MailKit/ProgressStream.cs index 763be4359f..e2b65d027e 100644 --- a/MailKit/ProgressStream.cs +++ b/MailKit/ProgressStream.cs @@ -34,7 +34,7 @@ namespace MailKit { class ProgressStream : Stream, ICancellableStream { - readonly ICancellableStream cancellable; + readonly ICancellableStream? cancellable; public ProgressStream (Stream source, Action update) { diff --git a/MailKit/ProtocolLogger.cs b/MailKit/ProtocolLogger.cs index 3b8c7ddd74..b282cf975c 100644 --- a/MailKit/ProtocolLogger.cs +++ b/MailKit/ProtocolLogger.cs @@ -55,12 +55,6 @@ public class ProtocolLogger : IProtocolLogger bool clientMidline; bool serverMidline; - ProtocolLogger () - { - TimestampFormat = DefaultTimestampFormat; - RedactSecrets = true; - } - /// /// Initializes a new instance of the class. /// @@ -72,9 +66,11 @@ public class ProtocolLogger : IProtocolLogger /// /// The file name. /// if the file should be appended to; otherwise, . Defaults to . - public ProtocolLogger (string fileName, bool append = true) : this () + public ProtocolLogger (string fileName, bool append = true) { stream = File.Open (fileName, append ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.Read); + TimestampFormat = DefaultTimestampFormat; + RedactSecrets = true; } /// @@ -85,11 +81,14 @@ public ProtocolLogger (string fileName, bool append = true) : this () /// /// The stream. /// if the stream should be left open after the protocol logger is disposed. - public ProtocolLogger (Stream stream, bool leaveOpen = false) : this () + public ProtocolLogger (Stream stream, bool leaveOpen = false) { if (stream == null) throw new ArgumentNullException (nameof (stream)); + TimestampFormat = DefaultTimestampFormat; + RedactSecrets = true; + this.leaveOpen = leaveOpen; this.stream = stream; } @@ -212,7 +211,7 @@ public string TimestampFormat { /// Gets or sets the authentication secret detector. /// /// The authentication secret detector. - public IAuthenticationSecretDetector AuthenticationSecretDetector { get; set; } + public IAuthenticationSecretDetector? AuthenticationSecretDetector { get; set; } static void ValidateArguments (byte[] buffer, int offset, int count) { @@ -255,7 +254,7 @@ void Log (byte[] prefix, ref bool midline, byte[] buffer, int offset, int count, midline = true; } - if (isClient && RedactSecrets) { + if (isClient && RedactSecrets && AuthenticationSecretDetector != null) { var secrets = AuthenticationSecretDetector.DetectSecrets (buffer, start, index - start); foreach (var secret in secrets) { From 808eed5e5d158db5a9202397d8cc2ef1d1a339f5 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Fri, 20 Feb 2026 17:12:57 -0500 Subject: [PATCH 10/44] Lots of IMAP nullability fixes --- MailKit/AppendRequest.cs | 2 +- MailKit/FetchRequest.cs | 2 +- MailKit/IAppendRequest.cs | 2 +- MailKit/IFetchRequest.cs | 2 +- MailKit/IMailFolderAppendExtensions.cs | 72 ++-- MailKit/IMailFolderStoreExtensions.cs | 2 +- MailKit/IReplaceRequest.cs | 7 +- MailKit/MailFolder.cs | 44 +-- MailKit/MailStore.cs | 6 +- MailKit/Net/Imap/AsyncImapClient.cs | 24 +- MailKit/Net/Imap/IImapFolder.cs | 28 +- MailKit/Net/Imap/ImapClient.cs | 81 +++-- MailKit/Net/Imap/ImapCommand.cs | 29 +- MailKit/Net/Imap/ImapCommandException.cs | 7 +- MailKit/Net/Imap/ImapEngine.cs | 81 ++--- MailKit/Net/Imap/ImapFolder.cs | 32 +- MailKit/Net/Imap/ImapFolderAnnotations.cs | 33 +- MailKit/Net/Imap/ImapFolderConstructorArgs.cs | 6 +- MailKit/Net/Imap/ImapFolderFetch.cs | 339 ++++++++---------- MailKit/Net/Imap/ImapFolderFlags.cs | 45 +-- MailKit/Net/Imap/ImapFolderSearch.cs | 4 +- MailKit/Net/Imap/ImapIdleContext.cs | 2 +- MailKit/ReplaceRequest.cs | 2 +- 23 files changed, 395 insertions(+), 457 deletions(-) diff --git a/MailKit/AppendRequest.cs b/MailKit/AppendRequest.cs index a3e6d4916b..4e88b8d1a3 100644 --- a/MailKit/AppendRequest.cs +++ b/MailKit/AppendRequest.cs @@ -203,7 +203,7 @@ public IList Annotations { /// Gets or sets the transfer progress reporting mechanism. /// /// The transfer progress mechanism. - public ITransferProgress TransferProgress { + public ITransferProgress? TransferProgress { get; set; } } diff --git a/MailKit/FetchRequest.cs b/MailKit/FetchRequest.cs index 69469c3297..087304ec37 100644 --- a/MailKit/FetchRequest.cs +++ b/MailKit/FetchRequest.cs @@ -134,7 +134,7 @@ public FetchRequest (MessageSummaryItems items, IEnumerable headers) : t /// Gets the set of headers that will be fetched. /// /// The set of headers to be fetched. - public HeaderSet Headers { get; set; } + public HeaderSet? Headers { get; set; } #if ENABLE_LAZY_PREVIEW_API /// diff --git a/MailKit/IAppendRequest.cs b/MailKit/IAppendRequest.cs index 8a83176d42..c63ffa0676 100644 --- a/MailKit/IAppendRequest.cs +++ b/MailKit/IAppendRequest.cs @@ -95,6 +95,6 @@ public interface IAppendRequest /// Gets or sets the transfer progress reporting mechanism. /// /// The transfer progress mechanism. - ITransferProgress TransferProgress { get; set; } + ITransferProgress? TransferProgress { get; set; } } } diff --git a/MailKit/IFetchRequest.cs b/MailKit/IFetchRequest.cs index 8c33010b16..30e984d4f0 100644 --- a/MailKit/IFetchRequest.cs +++ b/MailKit/IFetchRequest.cs @@ -69,7 +69,7 @@ public interface IFetchRequest /// Gets the set of headers that will be fetched. /// /// The set of headers to be fetched. - HeaderSet Headers { get; } + HeaderSet? Headers { get; } #if ENABLE_LAZY_PREVIEW_API /// diff --git a/MailKit/IMailFolderAppendExtensions.cs b/MailKit/IMailFolderAppendExtensions.cs index 2d6a949a73..37a648f382 100644 --- a/MailKit/IMailFolderAppendExtensions.cs +++ b/MailKit/IMailFolderAppendExtensions.cs @@ -75,7 +75,7 @@ public static partial class IMailFolderExtensions /// /// The command failed. /// - public static UniqueId? Append (this IMailFolder folder, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static UniqueId? Append (this IMailFolder folder, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return Append (folder, FormatOptions.Default, message, flags, cancellationToken, progress); } @@ -119,7 +119,7 @@ public static partial class IMailFolderExtensions /// /// The command failed. /// - public static Task AppendAsync (this IMailFolder folder, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static Task AppendAsync (this IMailFolder folder, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return AppendAsync (folder, FormatOptions.Default, message, flags, cancellationToken, progress); } @@ -164,7 +164,7 @@ public static partial class IMailFolderExtensions /// /// The command failed. /// - public static UniqueId? Append (this IMailFolder folder, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static UniqueId? Append (this IMailFolder folder, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return Append (folder, FormatOptions.Default, message, flags, date, cancellationToken, progress); } @@ -209,7 +209,7 @@ public static partial class IMailFolderExtensions /// /// The command failed. /// - public static Task AppendAsync (this IMailFolder folder, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static Task AppendAsync (this IMailFolder folder, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return AppendAsync (folder, FormatOptions.Default, message, flags, date, cancellationToken, progress); } @@ -258,7 +258,7 @@ public static partial class IMailFolderExtensions /// /// The command failed. /// - public static UniqueId? Append (this IMailFolder folder, MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static UniqueId? Append (this IMailFolder folder, MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return Append (folder, FormatOptions.Default, message, flags, date, annotations, cancellationToken, progress); } @@ -307,7 +307,7 @@ public static partial class IMailFolderExtensions /// /// The command failed. /// - public static Task AppendAsync (this IMailFolder folder, MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static Task AppendAsync (this IMailFolder folder, MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return AppendAsync (folder, FormatOptions.Default, message, flags, date, annotations, cancellationToken, progress); } @@ -360,7 +360,7 @@ public static partial class IMailFolderExtensions /// /// The command failed. /// - public static UniqueId? Append (this IMailFolder folder, FormatOptions options, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static UniqueId? Append (this IMailFolder folder, FormatOptions options, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var request = new AppendRequest (message, flags) { TransferProgress = progress @@ -417,7 +417,7 @@ public static partial class IMailFolderExtensions /// /// The command failed. /// - public static Task AppendAsync (this IMailFolder folder, FormatOptions options, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static Task AppendAsync (this IMailFolder folder, FormatOptions options, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var request = new AppendRequest (message, flags) { TransferProgress = progress @@ -475,7 +475,7 @@ public static partial class IMailFolderExtensions /// /// The command failed. /// - public static UniqueId? Append (this IMailFolder folder, FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static UniqueId? Append (this IMailFolder folder, FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var request = new AppendRequest (message, flags, date) { TransferProgress = progress @@ -533,7 +533,7 @@ public static partial class IMailFolderExtensions /// /// The command failed. /// - public static Task AppendAsync (this IMailFolder folder, FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static Task AppendAsync (this IMailFolder folder, FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var request = new AppendRequest (message, flags, date) { TransferProgress = progress @@ -592,7 +592,7 @@ public static partial class IMailFolderExtensions /// /// The command failed. /// - public static UniqueId? Append (this IMailFolder folder, FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static UniqueId? Append (this IMailFolder folder, FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var request = new AppendRequest (message, flags) { TransferProgress = progress, @@ -653,7 +653,7 @@ public static partial class IMailFolderExtensions /// /// The command failed. /// - public static Task AppendAsync (this IMailFolder folder, FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static Task AppendAsync (this IMailFolder folder, FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var request = new AppendRequest (message, flags) { TransferProgress = progress, @@ -710,7 +710,7 @@ public static partial class IMailFolderExtensions /// /// The command failed. /// - public static IList Append (this IMailFolder folder, IList messages, IList flags, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static IList Append (this IMailFolder folder, IList messages, IList flags, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return Append (folder, FormatOptions.Default, messages, flags, cancellationToken, progress); } @@ -761,7 +761,7 @@ public static IList Append (this IMailFolder folder, IList /// The command failed. /// - public static Task> AppendAsync (this IMailFolder folder, IList messages, IList flags, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static Task> AppendAsync (this IMailFolder folder, IList messages, IList flags, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return AppendAsync (folder, FormatOptions.Default, messages, flags, cancellationToken, progress); } @@ -815,7 +815,7 @@ public static Task> AppendAsync (this IMailFolder folder, IList< /// /// The command failed. /// - public static IList Append (this IMailFolder folder, IList messages, IList flags, IList dates, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static IList Append (this IMailFolder folder, IList messages, IList flags, IList dates, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return Append (folder, FormatOptions.Default, messages, flags, dates, cancellationToken, progress); } @@ -869,7 +869,7 @@ public static IList Append (this IMailFolder folder, IList /// The command failed. /// - public static Task> AppendAsync (this IMailFolder folder, IList messages, IList flags, IList dates, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static Task> AppendAsync (this IMailFolder folder, IList messages, IList flags, IList dates, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return AppendAsync (folder, FormatOptions.Default, messages, flags, dates, cancellationToken, progress); } @@ -929,7 +929,7 @@ public static Task> AppendAsync (this IMailFolder folder, IList< /// /// The command failed. /// - public static IList Append (this IMailFolder folder, FormatOptions options, IList messages, IList flags, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static IList Append (this IMailFolder folder, FormatOptions options, IList messages, IList flags, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (options == null) throw new ArgumentNullException (nameof (options)); @@ -1013,7 +1013,7 @@ public static IList Append (this IMailFolder folder, FormatOptions opt /// /// The command failed. /// - public static Task> AppendAsync (this IMailFolder folder, FormatOptions options, IList messages, IList flags, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static Task> AppendAsync (this IMailFolder folder, FormatOptions options, IList messages, IList flags, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (options == null) throw new ArgumentNullException (nameof (options)); @@ -1100,7 +1100,7 @@ public static Task> AppendAsync (this IMailFolder folder, Format /// /// The command failed. /// - public static IList Append (this IMailFolder folder, FormatOptions options, IList messages, IList flags, IList dates, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static IList Append (this IMailFolder folder, FormatOptions options, IList messages, IList flags, IList dates, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (options == null) throw new ArgumentNullException (nameof (options)); @@ -1193,7 +1193,7 @@ public static IList Append (this IMailFolder folder, FormatOptions opt /// /// The command failed. /// - public static Task> AppendAsync (this IMailFolder folder, FormatOptions options, IList messages, IList flags, IList dates, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static Task> AppendAsync (this IMailFolder folder, FormatOptions options, IList messages, IList flags, IList dates, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { if (options == null) throw new ArgumentNullException (nameof (options)); @@ -1281,7 +1281,7 @@ public static Task> AppendAsync (this IMailFolder folder, Format /// /// The server replied with a NO or BAD response. /// - public static UniqueId? Replace (this IMailFolder folder, UniqueId uid, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static UniqueId? Replace (this IMailFolder folder, UniqueId uid, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return Replace (folder, FormatOptions.Default, uid, message, flags, cancellationToken, progress); } @@ -1335,7 +1335,7 @@ public static Task> AppendAsync (this IMailFolder folder, Format /// /// The server replied with a NO or BAD response. /// - public static Task ReplaceAsync (this IMailFolder folder, UniqueId uid, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static Task ReplaceAsync (this IMailFolder folder, UniqueId uid, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return ReplaceAsync (folder, FormatOptions.Default, uid, message, flags, cancellationToken, progress); } @@ -1387,7 +1387,7 @@ public static Task> AppendAsync (this IMailFolder folder, Format /// /// The server replied with a NO or BAD response. /// - public static UniqueId? Replace (this IMailFolder folder, UniqueId uid, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static UniqueId? Replace (this IMailFolder folder, UniqueId uid, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return Replace (folder, FormatOptions.Default, uid, message, flags, date, cancellationToken, progress); } @@ -1439,7 +1439,7 @@ public static Task> AppendAsync (this IMailFolder folder, Format /// /// The server replied with a NO or BAD response. /// - public static Task ReplaceAsync (this IMailFolder folder, UniqueId uid, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static Task ReplaceAsync (this IMailFolder folder, UniqueId uid, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return ReplaceAsync (folder, FormatOptions.Default, uid, message, flags, date, cancellationToken, progress); } @@ -1499,7 +1499,7 @@ public static Task> AppendAsync (this IMailFolder folder, Format /// /// The server replied with a NO or BAD response. /// - public static UniqueId? Replace (this IMailFolder folder, FormatOptions options, UniqueId uid, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static UniqueId? Replace (this IMailFolder folder, FormatOptions options, UniqueId uid, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var request = new ReplaceRequest (message, flags) { TransferProgress = progress @@ -1563,7 +1563,7 @@ public static Task> AppendAsync (this IMailFolder folder, Format /// /// The server replied with a NO or BAD response. /// - public static Task ReplaceAsync (this IMailFolder folder, FormatOptions options, UniqueId uid, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static Task ReplaceAsync (this IMailFolder folder, FormatOptions options, UniqueId uid, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var request = new ReplaceRequest (message, flags) { TransferProgress = progress @@ -1628,7 +1628,7 @@ public static Task> AppendAsync (this IMailFolder folder, Format /// /// The server replied with a NO or BAD response. /// - public static UniqueId? Replace (this IMailFolder folder, FormatOptions options, UniqueId uid, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static UniqueId? Replace (this IMailFolder folder, FormatOptions options, UniqueId uid, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var request = new ReplaceRequest (message, flags, date) { TransferProgress = progress @@ -1693,7 +1693,7 @@ public static Task> AppendAsync (this IMailFolder folder, Format /// /// The server replied with a NO or BAD response. /// - public static Task ReplaceAsync (this IMailFolder folder, FormatOptions options, UniqueId uid, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static Task ReplaceAsync (this IMailFolder folder, FormatOptions options, UniqueId uid, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var request = new ReplaceRequest (message, flags, date) { TransferProgress = progress @@ -1751,7 +1751,7 @@ public static Task> AppendAsync (this IMailFolder folder, Format /// /// The server replied with a NO or BAD response. /// - public static UniqueId? Replace (this IMailFolder folder, int index, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static UniqueId? Replace (this IMailFolder folder, int index, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return Replace (folder, FormatOptions.Default, index, message, flags, cancellationToken, progress); } @@ -1805,7 +1805,7 @@ public static Task> AppendAsync (this IMailFolder folder, Format /// /// The server replied with a NO or BAD response. /// - public static Task ReplaceAsync (this IMailFolder folder, int index, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static Task ReplaceAsync (this IMailFolder folder, int index, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return ReplaceAsync (folder, FormatOptions.Default, index, message, flags, cancellationToken, progress); } @@ -1857,7 +1857,7 @@ public static Task> AppendAsync (this IMailFolder folder, Format /// /// The server replied with a NO or BAD response. /// - public static UniqueId? Replace (this IMailFolder folder, int index, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static UniqueId? Replace (this IMailFolder folder, int index, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return Replace (folder, FormatOptions.Default, index, message, flags, date, cancellationToken, progress); } @@ -1909,7 +1909,7 @@ public static Task> AppendAsync (this IMailFolder folder, Format /// /// The server replied with a NO or BAD response. /// - public static Task ReplaceAsync (this IMailFolder folder, int index, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static Task ReplaceAsync (this IMailFolder folder, int index, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { return ReplaceAsync (folder, FormatOptions.Default, index, message, flags, date, cancellationToken, progress); } @@ -1969,7 +1969,7 @@ public static Task> AppendAsync (this IMailFolder folder, Format /// /// The server replied with a NO or BAD response. /// - public static UniqueId? Replace (this IMailFolder folder, FormatOptions options, int index, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static UniqueId? Replace (this IMailFolder folder, FormatOptions options, int index, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var request = new ReplaceRequest (message, flags) { TransferProgress = progress @@ -2033,7 +2033,7 @@ public static Task> AppendAsync (this IMailFolder folder, Format /// /// The server replied with a NO or BAD response. /// - public static Task ReplaceAsync (this IMailFolder folder, FormatOptions options, int index, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static Task ReplaceAsync (this IMailFolder folder, FormatOptions options, int index, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var request = new ReplaceRequest (message, flags) { TransferProgress = progress @@ -2098,7 +2098,7 @@ public static Task> AppendAsync (this IMailFolder folder, Format /// /// The server replied with a NO or BAD response. /// - public static UniqueId? Replace (this IMailFolder folder, FormatOptions options, int index, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static UniqueId? Replace (this IMailFolder folder, FormatOptions options, int index, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var request = new ReplaceRequest (message, flags, date) { TransferProgress = progress @@ -2163,7 +2163,7 @@ public static Task> AppendAsync (this IMailFolder folder, Format /// /// The server replied with a NO or BAD response. /// - public static Task ReplaceAsync (this IMailFolder folder, FormatOptions options, int index, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress progress = null) + public static Task ReplaceAsync (this IMailFolder folder, FormatOptions options, int index, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var request = new ReplaceRequest (message, flags, date) { TransferProgress = progress diff --git a/MailKit/IMailFolderStoreExtensions.cs b/MailKit/IMailFolderStoreExtensions.cs index 6350e04d0f..f25522cf38 100644 --- a/MailKit/IMailFolderStoreExtensions.cs +++ b/MailKit/IMailFolderStoreExtensions.cs @@ -34,7 +34,7 @@ public static partial class IMailFolderExtensions { #region Store Flags Extensions - static StoreFlagsRequest GetStoreFlagsRequest (StoreAction action, bool silent, MessageFlags flags, HashSet keywords = null, ulong? modseq = null) + static StoreFlagsRequest GetStoreFlagsRequest (StoreAction action, bool silent, MessageFlags flags, HashSet? keywords = null, ulong? modseq = null) { if (keywords != null) { return new StoreFlagsRequest (action, flags, keywords) { diff --git a/MailKit/IReplaceRequest.cs b/MailKit/IReplaceRequest.cs index 1220b72358..d96f5aaf55 100644 --- a/MailKit/IReplaceRequest.cs +++ b/MailKit/IReplaceRequest.cs @@ -24,11 +24,6 @@ // THE SOFTWARE. // -using System; -using System.Collections.Generic; - -using MimeKit; - namespace MailKit { /// /// A request for replacing a message in a folder. @@ -47,6 +42,6 @@ public interface IReplaceRequest : IAppendRequest /// appended to the original folder. /// /// The destination folder. - IMailFolder Destination { get; set; } + IMailFolder? Destination { get; set; } } } diff --git a/MailKit/MailFolder.cs b/MailKit/MailFolder.cs index d20089b74e..e10ea0352e 100644 --- a/MailKit/MailFolder.cs +++ b/MailKit/MailFolder.cs @@ -9349,7 +9349,7 @@ public virtual Task SortAsync (SearchOptions options, IList /// The event is emitted when the folder is opened. /// - public event EventHandler Opened; + public event EventHandler? Opened; /// /// Raise the opened event. @@ -9368,7 +9368,7 @@ protected virtual void OnOpened () /// /// The event is emitted when the folder is closed. /// - public event EventHandler Closed; + public event EventHandler? Closed; /// /// Raise the closed event. @@ -9396,7 +9396,7 @@ internal protected virtual void OnClosed () /// /// The event is emitted when the folder is deleted. /// - public event EventHandler Deleted; + public event EventHandler? Deleted; /// /// Raise the deleted event. @@ -9415,7 +9415,7 @@ protected virtual void OnDeleted () /// /// The event is emitted when the folder is renamed. /// - public event EventHandler Renamed; + public event EventHandler? Renamed; /// /// Raise the renamed event. @@ -9458,7 +9458,7 @@ void OnParentFolderRenamed (object? sender, FolderRenamedEventArgs e) /// /// The event is emitted when the folder is subscribed. /// - public event EventHandler Subscribed; + public event EventHandler? Subscribed; /// /// Raise the subscribed event. @@ -9477,7 +9477,7 @@ protected virtual void OnSubscribed () /// /// The event is emitted when the folder is unsubscribed. /// - public event EventHandler Unsubscribed; + public event EventHandler? Unsubscribed; /// /// Raise the unsubscribed event. @@ -9499,7 +9499,7 @@ protected virtual void OnUnsubscribed () /// /// /// - public event EventHandler MessageExpunged; + public event EventHandler? MessageExpunged; /// /// Raise the message expunged event. @@ -9519,7 +9519,7 @@ protected virtual void OnMessageExpunged (MessageEventArgs args) /// /// The event is emitted when messages vanish from the folder. /// - public event EventHandler MessagesVanished; + public event EventHandler? MessagesVanished; /// /// Raise the messages vanished event. @@ -9542,7 +9542,7 @@ protected virtual void OnMessagesVanished (MessagesVanishedEventArgs args) /// /// /// - public event EventHandler MessageFlagsChanged; + public event EventHandler? MessageFlagsChanged; /// /// Raise the message flags changed event. @@ -9562,7 +9562,7 @@ protected virtual void OnMessageFlagsChanged (MessageFlagsChangedEventArgs args) /// /// The event is emitted when the labels for a message are changed. /// - public event EventHandler MessageLabelsChanged; + public event EventHandler? MessageLabelsChanged; /// /// Raise the message labels changed event. @@ -9582,7 +9582,7 @@ protected virtual void OnMessageLabelsChanged (MessageLabelsChangedEventArgs arg /// /// The event is emitted when the annotations for a message are changed. /// - public event EventHandler AnnotationsChanged; + public event EventHandler? AnnotationsChanged; /// /// Raise the message annotations changed event. @@ -9617,7 +9617,7 @@ protected virtual void OnAnnotationsChanged (AnnotationsChangedEventArgs args) /// property to determine which f properties have /// been populated. /// - public event EventHandler MessageSummaryFetched; + public event EventHandler? MessageSummaryFetched; /// /// Raise the message summary fetched event. @@ -9652,7 +9652,7 @@ protected virtual void OnMessageSummaryFetched (IMessageSummary message) /// /// The event is emitted when metadata changes. /// - public event EventHandler MetadataChanged; + public event EventHandler? MetadataChanged; /// /// Raise the metadata changed event. @@ -9672,7 +9672,7 @@ internal protected virtual void OnMetadataChanged (Metadata metadata) /// /// The event is emitted when the mod-sequence for a message is changed. /// - public event EventHandler ModSeqChanged; + public event EventHandler? ModSeqChanged; /// /// Raise the message mod-sequence changed event. @@ -9692,7 +9692,7 @@ protected virtual void OnModSeqChanged (ModSeqChangedEventArgs args) /// /// The event is emitted whenever the value changes. /// - public event EventHandler HighestModSeqChanged; + public event EventHandler? HighestModSeqChanged; /// /// Raise the highest mod-sequence changed event. @@ -9711,7 +9711,7 @@ protected virtual void OnHighestModSeqChanged () /// /// The event is emitted whenever the value changes. /// - public event EventHandler UidNextChanged; + public event EventHandler? UidNextChanged; /// /// Raise the next UID changed event. @@ -9730,7 +9730,7 @@ protected virtual void OnUidNextChanged () /// /// The event is emitted whenever the value changes. /// - public event EventHandler UidValidityChanged; + public event EventHandler? UidValidityChanged; /// /// Raise the uid validity changed event. @@ -9749,7 +9749,7 @@ protected virtual void OnUidValidityChanged () /// /// The event is emitted whenever the value changes. /// - public event EventHandler IdChanged; + public event EventHandler? IdChanged; /// /// Raise the ID changed event. @@ -9768,7 +9768,7 @@ protected virtual void OnIdChanged () /// /// The event is emitted whenever the value changes. /// - public event EventHandler SizeChanged; + public event EventHandler? SizeChanged; /// /// Raise the size changed event. @@ -9790,7 +9790,7 @@ protected virtual void OnSizeChanged () /// /// /// - public event EventHandler CountChanged; + public event EventHandler? CountChanged; /// /// Raise the message count changed event. @@ -9809,7 +9809,7 @@ protected virtual void OnCountChanged () /// /// The event is emitted whenever the value changes. /// - public event EventHandler RecentChanged; + public event EventHandler? RecentChanged; /// /// Raise the recent message count changed event. @@ -9828,7 +9828,7 @@ protected virtual void OnRecentChanged () /// /// The event is emitted whenever the value changes. /// - public event EventHandler UnreadChanged; + public event EventHandler? UnreadChanged; /// /// Raise the unread message count changed event. diff --git a/MailKit/MailStore.cs b/MailKit/MailStore.cs index d344174bef..328b226a19 100644 --- a/MailKit/MailStore.cs +++ b/MailKit/MailStore.cs @@ -803,7 +803,7 @@ public virtual Task GetMetadataAsync (IEnumerable event is raised whenever the mail server sends an /// alert message. /// - public event EventHandler Alert; + public event EventHandler? Alert; /// /// Raise the alert event. @@ -826,7 +826,7 @@ protected virtual void OnAlert (string message) /// /// The event is emitted when a new folder is created. /// - public event EventHandler FolderCreated; + public event EventHandler? FolderCreated; /// /// Raise the folder created event. @@ -846,7 +846,7 @@ protected virtual void OnFolderCreated (IMailFolder folder) /// /// The event is emitted when metadata changes. /// - public event EventHandler MetadataChanged; + public event EventHandler? MetadataChanged; /// /// Raise the metadata changed event. diff --git a/MailKit/Net/Imap/AsyncImapClient.cs b/MailKit/Net/Imap/AsyncImapClient.cs index 858b77d9d5..073b3bf7d1 100644 --- a/MailKit/Net/Imap/AsyncImapClient.cs +++ b/MailKit/Net/Imap/AsyncImapClient.cs @@ -290,7 +290,7 @@ public override async Task AuthenticateAsync (SaslMechanism mechanism, Cancellat CheckCanAuthenticate (mechanism, cancellationToken); int capabilitiesVersion = engine.CapabilitiesVersion; - ImapCommand ic = null; + ImapCommand? ic = null; ConfigureSaslMechanism (mechanism); @@ -308,7 +308,7 @@ public override async Task AuthenticateAsync (SaslMechanism mechanism, Cancellat string challenge = await mechanism.ChallengeAsync (text, cmd.CancellationToken).ConfigureAwait (false); var buf = Encoding.ASCII.GetBytes (challenge + "\r\n"); - await imap.Stream.WriteAsync (buf, 0, buf.Length, cmd.CancellationToken).ConfigureAwait (false); + await imap.Stream!.WriteAsync (buf, 0, buf.Length, cmd.CancellationToken).ConfigureAwait (false); await imap.Stream.FlushAsync (cmd.CancellationToken).ConfigureAwait (false); }; @@ -397,16 +397,16 @@ public override async Task AuthenticateAsync (Encoding encoding, ICredentials cr try { int capabilitiesVersion = engine.CapabilitiesVersion; - var uri = new Uri ("imap://" + engine.Uri.Host); - NetworkCredential cred; - ImapCommand ic = null; - SaslMechanism sasl; + var uri = new Uri ("imap://" + engine.Uri!.Host); + NetworkCredential? cred; + ImapCommand? ic = null; + SaslMechanism? sasl; string id; foreach (var authmech in SaslMechanism.Rank (engine.AuthenticationMechanisms)) { cred = credentials.GetCredential (uri, authmech); - if ((sasl = SaslMechanism.Create (authmech, encoding, cred)) == null) + if (cred == null || (sasl = SaslMechanism.Create (authmech, encoding, cred)) == null) continue; ConfigureSaslMechanism (sasl, uri); @@ -428,7 +428,7 @@ public override async Task AuthenticateAsync (Encoding encoding, ICredentials cr string challenge = await sasl.ChallengeAsync (text, cmd.CancellationToken).ConfigureAwait (false); var buf = Encoding.ASCII.GetBytes (challenge + "\r\n"); - await imap.Stream.WriteAsync (buf, 0, buf.Length, cmd.CancellationToken).ConfigureAwait (false); + await imap.Stream!.WriteAsync (buf, 0, buf.Length, cmd.CancellationToken).ConfigureAwait (false); await imap.Stream.FlushAsync (cmd.CancellationToken).ConfigureAwait (false); }; @@ -449,7 +449,6 @@ public override async Task AuthenticateAsync (Encoding encoding, ICredentials cr engine.State = ImapEngineState.Authenticated; - cred = credentials.GetCredential (uri, sasl.MechanismName); id = GetSessionIdentifier (cred.UserName); if (id != identifier) { engine.FolderCache.Clear (); @@ -468,7 +467,8 @@ public override async Task AuthenticateAsync (Encoding encoding, ICredentials cr CheckCanLogin (ic); // fall back to the classic LOGIN command... - cred = credentials.GetCredential (uri, "DEFAULT"); + if ((cred = credentials.GetCredential (uri, "DEFAULT")) == null) + throw new AuthenticationException ("No credentials could be found for the IMAP server."); ic = engine.QueueCommand (cancellationToken, null, "LOGIN %S %S\r\n", cred.UserName, cred.Password); @@ -515,7 +515,7 @@ async Task SslHandshakeAsync (SslStream ssl, string host, CancellationToken canc async Task PostConnectAsync (Stream stream, string host, int port, SecureSocketOptions options, bool starttls, CancellationToken cancellationToken) { try { - ProtocolLogger.LogConnect (engine.Uri); + ProtocolLogger.LogConnect (engine.Uri!); } catch { stream.Dispose (); secure = false; @@ -548,7 +548,7 @@ async Task PostConnectAsync (Stream stream, string host, int port, SecureSocketO if (ic.Response == ImapCommandResponse.Ok) { try { var tls = new SslStream (stream, false, ValidateRemoteCertificate); - engine.Stream.Stream = tls; + engine.Stream!.Stream = tls; await SslHandshakeAsync (tls, host, cancellationToken).ConfigureAwait (false); } catch (Exception ex) { diff --git a/MailKit/Net/Imap/IImapFolder.cs b/MailKit/Net/Imap/IImapFolder.cs index 506bd19d63..126025b01b 100644 --- a/MailKit/Net/Imap/IImapFolder.cs +++ b/MailKit/Net/Imap/IImapFolder.cs @@ -91,7 +91,7 @@ public interface IImapFolder : IMailFolder /// /// The server replied with a NO or BAD response. /// - HeaderList GetHeaders (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress progress = null); + HeaderList GetHeaders (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the specified body part headers. @@ -137,7 +137,7 @@ public interface IImapFolder : IMailFolder /// /// The server replied with a NO or BAD response. /// - Task GetHeadersAsync (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetHeadersAsync (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the specified body part headers. @@ -183,7 +183,7 @@ public interface IImapFolder : IMailFolder /// /// The server replied with a NO or BAD response. /// - HeaderList GetHeaders (int index, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress progress = null); + HeaderList GetHeaders (int index, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the specified body part headers. @@ -229,7 +229,7 @@ public interface IImapFolder : IMailFolder /// /// The server replied with a NO or BAD response. /// - Task GetHeadersAsync (int index, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetHeadersAsync (int index, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the specified body part. @@ -277,7 +277,7 @@ public interface IImapFolder : IMailFolder /// /// The server replied with a NO or BAD response. /// - MimeEntity GetBodyPart (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress progress = null); + MimeEntity GetBodyPart (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the specified body part. @@ -325,7 +325,7 @@ public interface IImapFolder : IMailFolder /// /// The server replied with a NO or BAD response. /// - Task GetBodyPartAsync (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetBodyPartAsync (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the specified body part. @@ -373,7 +373,7 @@ public interface IImapFolder : IMailFolder /// /// The server replied with a NO or BAD response. /// - MimeEntity GetBodyPart (int index, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress progress = null); + MimeEntity GetBodyPart (int index, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the specified body part. @@ -421,7 +421,7 @@ public interface IImapFolder : IMailFolder /// /// The server replied with a NO or BAD response. /// - Task GetBodyPartAsync (int index, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetBodyPartAsync (int index, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the streams for the specified messages. @@ -465,7 +465,7 @@ public interface IImapFolder : IMailFolder /// /// The server replied with a NO or BAD response. /// - void GetStreams (IList uids, ImapFetchStreamCallback callback, CancellationToken cancellationToken = default, ITransferProgress progress = null); + void GetStreams (IList uids, ImapFetchStreamCallback callback, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the streams for the specified messages. @@ -510,7 +510,7 @@ public interface IImapFolder : IMailFolder /// /// The server replied with a NO or BAD response. /// - Task GetStreamsAsync (IList uids, ImapFetchStreamAsyncCallback callback, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetStreamsAsync (IList uids, ImapFetchStreamAsyncCallback callback, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the streams for the specified messages. @@ -554,7 +554,7 @@ public interface IImapFolder : IMailFolder /// /// The server replied with a NO or BAD response. /// - void GetStreams (IList indexes, ImapFetchStreamCallback callback, CancellationToken cancellationToken = default, ITransferProgress progress = null); + void GetStreams (IList indexes, ImapFetchStreamCallback callback, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the streams for the specified messages. @@ -599,7 +599,7 @@ public interface IImapFolder : IMailFolder /// /// The server replied with a NO or BAD response. /// - Task GetStreamsAsync (IList indexes, ImapFetchStreamAsyncCallback callback, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetStreamsAsync (IList indexes, ImapFetchStreamAsyncCallback callback, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Get the streams for the specified messages. @@ -644,7 +644,7 @@ public interface IImapFolder : IMailFolder /// /// The server replied with a NO or BAD response. /// - void GetStreams (int min, int max, ImapFetchStreamCallback callback, CancellationToken cancellationToken = default, ITransferProgress progress = null); + void GetStreams (int min, int max, ImapFetchStreamCallback callback, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Asynchronously get the streams for the specified messages. @@ -690,7 +690,7 @@ public interface IImapFolder : IMailFolder /// /// The server replied with a NO or BAD response. /// - Task GetStreamsAsync (int min, int max, ImapFetchStreamAsyncCallback callback, CancellationToken cancellationToken = default, ITransferProgress progress = null); + Task GetStreamsAsync (int min, int max, ImapFetchStreamAsyncCallback callback, CancellationToken cancellationToken = default, ITransferProgress? progress = null); /// /// Search the folder for messages matching the specified query. diff --git a/MailKit/Net/Imap/ImapClient.cs b/MailKit/Net/Imap/ImapClient.cs index 10e045f3ff..63f785fed9 100644 --- a/MailKit/Net/Imap/ImapClient.cs +++ b/MailKit/Net/Imap/ImapClient.cs @@ -68,9 +68,9 @@ public partial class ImapClient : MailStore, IImapClient readonly ImapAuthenticationSecretDetector detector = new ImapAuthenticationSecretDetector (); readonly ImapEngine engine; - SslCertificateValidationInfo sslValidationInfo; + SslCertificateValidationInfo? sslValidationInfo; int timeout = 2 * 60 * 1000; - string identifier; + string? identifier; bool disconnecting; bool connecting; bool disposed; @@ -259,7 +259,7 @@ protected virtual ImapFolder CreateImapFolder (ImapFolderConstructorArgs args) return folder; } - bool ValidateRemoteCertificate (object? sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) + bool ValidateRemoteCertificate (object? sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors) { bool valid; @@ -267,13 +267,13 @@ bool ValidateRemoteCertificate (object? sender, X509Certificate certificate, X50 sslValidationInfo = null; if (ServerCertificateValidationCallback != null) { - valid = ServerCertificateValidationCallback (engine.Uri.Host, certificate, chain, sslPolicyErrors); + valid = ServerCertificateValidationCallback (engine.Uri!.Host, certificate, chain, sslPolicyErrors); #if NETFRAMEWORK } else if (ServicePointManager.ServerCertificateValidationCallback != null) { - valid = ServicePointManager.ServerCertificateValidationCallback (engine.Uri.Host, certificate, chain, sslPolicyErrors); + valid = ServicePointManager.ServerCertificateValidationCallback (engine.Uri!.Host, certificate, chain, sslPolicyErrors); #endif } else { - valid = DefaultServerCertificateValidationCallback (engine.Uri.Host, certificate, chain, sslPolicyErrors); + valid = DefaultServerCertificateValidationCallback (engine.Uri!.Host, certificate, chain, sslPolicyErrors); } if (!valid) { @@ -314,7 +314,7 @@ void ProcessCompressResponse (ImapCommand ic) throw ImapCommandException.Create ("COMPRESS", ic); } - engine.Stream.Stream = new CompressedStream (engine.Stream.Stream); + engine.Stream!.Stream = new CompressedStream (engine.Stream.Stream); #endif } @@ -361,7 +361,7 @@ public void Compress (CancellationToken cancellationToken = default) ProcessCompressResponse (ic); } - bool TryQueueEnableQuickResyncCommand (CancellationToken cancellationToken, out ImapCommand ic) + bool TryQueueEnableQuickResyncCommand (CancellationToken cancellationToken, [NotNullWhen (true)] out ImapCommand? ic) { CheckDisposed (); CheckConnected (); @@ -450,7 +450,7 @@ public override void EnableQuickResync (CancellationToken cancellationToken = de ProcessEnableResponse (ic); } - bool TryQueueEnableUTF8Command (CancellationToken cancellationToken, out ImapCommand ic) + bool TryQueueEnableUTF8Command (CancellationToken cancellationToken, [NotNullWhen (true)] out ImapCommand? ic) { CheckDisposed (); CheckConnected (); @@ -558,7 +558,7 @@ static ImapImplementation ProcessIdentifyResponse (ImapCommand ic) { ic.ThrowIfNotOk ("ID"); - return (ImapImplementation) ic.UserData; + return (ImapImplementation) ic.UserData!; } /// @@ -666,7 +666,7 @@ public override HashSet ThreadingAlgorithms { public override int Timeout { get { return timeout; } set { - if (IsConnected && engine.Stream.CanTimeout) { + if (IsConnected && engine.Stream!.CanTimeout) { engine.Stream.WriteTimeout = value; engine.Stream.ReadTimeout = value; } @@ -712,7 +712,7 @@ public override bool IsSecure { /// /// if the connection is encrypted; otherwise, . public override bool IsEncrypted { - get { return IsSecure && (engine.Stream.Stream is SslStream sslStream) && sslStream.IsEncrypted; } + get { return IsSecure && (engine.Stream!.Stream is SslStream sslStream) && sslStream.IsEncrypted; } } /// @@ -723,7 +723,7 @@ public override bool IsEncrypted { /// /// if the connection is signed; otherwise, . public override bool IsSigned { - get { return IsSecure && (engine.Stream.Stream is SslStream sslStream) && sslStream.IsSigned; } + get { return IsSecure && (engine.Stream!.Stream is SslStream sslStream) && sslStream.IsSigned; } } /// @@ -738,7 +738,7 @@ public override bool IsSigned { /// The negotiated SSL or TLS protocol version. public override SslProtocols SslProtocol { get { - if (IsSecure && (engine.Stream.Stream is SslStream sslStream)) + if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) return sslStream.SslProtocol; return SslProtocols.None; @@ -760,7 +760,7 @@ public override SslProtocols SslProtocol { #endif public override CipherAlgorithmType? SslCipherAlgorithm { get { - if (IsSecure && (engine.Stream.Stream is SslStream sslStream)) + if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) return sslStream.CipherAlgorithm; return null; @@ -782,7 +782,7 @@ public override CipherAlgorithmType? SslCipherAlgorithm { #endif public override int? SslCipherStrength { get { - if (IsSecure && (engine.Stream.Stream is SslStream sslStream)) + if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) return sslStream.CipherStrength; return null; @@ -799,7 +799,7 @@ public override int? SslCipherStrength { /// The negotiated SSL or TLS cipher suite. public override TlsCipherSuite? SslCipherSuite { get { - if (IsSecure && (engine.Stream.Stream is SslStream sslStream)) + if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) return sslStream.NegotiatedCipherSuite; return null; @@ -822,7 +822,7 @@ public override TlsCipherSuite? SslCipherSuite { #endif public override HashAlgorithmType? SslHashAlgorithm { get { - if (IsSecure && (engine.Stream.Stream is SslStream sslStream)) + if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) return sslStream.HashAlgorithm; return null; @@ -844,7 +844,7 @@ public override HashAlgorithmType? SslHashAlgorithm { #endif public override int? SslHashStrength { get { - if (IsSecure && (engine.Stream.Stream is SslStream sslStream)) + if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) return sslStream.HashStrength; return null; @@ -866,7 +866,7 @@ public override int? SslHashStrength { #endif public override ExchangeAlgorithmType? SslKeyExchangeAlgorithm { get { - if (IsSecure && (engine.Stream.Stream is SslStream sslStream)) + if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) return sslStream.KeyExchangeAlgorithm; return null; @@ -888,7 +888,7 @@ public override ExchangeAlgorithmType? SslKeyExchangeAlgorithm { #endif public override int? SslKeyExchangeStrength { get { - if (IsSecure && (engine.Stream.Stream is SslStream sslStream)) + if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) return sslStream.KeyExchangeStrength; return null; @@ -1034,7 +1034,7 @@ string GetSessionIdentifier (string userName) var builder = new StringBuilder (); var uri = engine.Uri; - builder.Append (uri.Scheme); + builder.Append (uri!.Scheme); builder.Append ("://"); EscapeUserName (builder, userName); builder.Append ('@'); @@ -1068,13 +1068,13 @@ void CheckCanAuthenticate (SaslMechanism mechanism, CancellationToken cancellati void ConfigureSaslMechanism (SaslMechanism mechanism, Uri uri) { - mechanism.ChannelBindingContext = engine.Stream.Stream as IChannelBindingContext; + mechanism.ChannelBindingContext = engine.Stream!.Stream as IChannelBindingContext; mechanism.Uri = uri; } void ConfigureSaslMechanism (SaslMechanism mechanism) { - var uri = new Uri ("imap://" + engine.Uri.Host); + var uri = new Uri ("imap://" + engine.Uri!.Host); ConfigureSaslMechanism (mechanism, uri); } @@ -1142,7 +1142,7 @@ public override void Authenticate (SaslMechanism mechanism, CancellationToken ca CheckCanAuthenticate (mechanism, cancellationToken); int capabilitiesVersion = engine.CapabilitiesVersion; - ImapCommand ic = null; + ImapCommand? ic = null; ConfigureSaslMechanism (mechanism); @@ -1161,7 +1161,7 @@ public override void Authenticate (SaslMechanism mechanism, CancellationToken ca string challenge = mechanism.Challenge (text, cmd.CancellationToken); var buf = Encoding.ASCII.GetBytes (challenge + "\r\n"); - imap.Stream.Write (buf, 0, buf.Length, cmd.CancellationToken); + imap.Stream!.Write (buf, 0, buf.Length, cmd.CancellationToken); imap.Stream.Flush (cmd.CancellationToken); return Task.CompletedTask; @@ -1207,7 +1207,7 @@ void CheckCanAuthenticate (Encoding encoding, ICredentials credentials) throw new InvalidOperationException ("The ImapClient is already authenticated."); } - void CheckCanLogin (ImapCommand ic) + void CheckCanLogin (ImapCommand? ic) { if ((Capabilities & ImapCapabilities.LoginDisabled) != 0) { if (ic == null) @@ -1276,17 +1276,16 @@ public override void Authenticate (Encoding encoding, ICredentials credentials, try { int capabilitiesVersion = engine.CapabilitiesVersion; - var uri = new Uri ("imap://" + engine.Uri.Host); + var uri = new Uri ("imap://" + engine.Uri!.Host); NetworkCredential? cred; ImapCommand? ic = null; - SaslMechanism sasl; + SaslMechanism? sasl; string id; foreach (var authmech in SaslMechanism.Rank (engine.AuthenticationMechanisms)) { - if ((cred = credentials.GetCredential (uri, authmech)) == null) - continue; + cred = credentials.GetCredential (uri, authmech); - if ((sasl = SaslMechanism.Create (authmech, encoding, cred)) == null) + if (cred == null || (sasl = SaslMechanism.Create (authmech, encoding, cred)) == null) continue; ConfigureSaslMechanism (sasl, uri); @@ -1309,7 +1308,7 @@ public override void Authenticate (Encoding encoding, ICredentials credentials, var buf = Encoding.ASCII.GetBytes (challenge + "\r\n"); - imap.Stream.Write (buf, 0, buf.Length, cmd.CancellationToken); + imap.Stream!.Write (buf, 0, buf.Length, cmd.CancellationToken); imap.Stream.Flush (cmd.CancellationToken); return Task.CompletedTask; @@ -1332,7 +1331,6 @@ public override void Authenticate (Encoding encoding, ICredentials credentials, engine.State = ImapEngineState.Authenticated; - cred = credentials.GetCredential (uri, sasl.MechanismName); id = GetSessionIdentifier (cred.UserName); if (id != identifier) { engine.FolderCache.Clear (); @@ -1351,7 +1349,8 @@ public override void Authenticate (Encoding encoding, ICredentials credentials, CheckCanLogin (ic); // fall back to the classic LOGIN command... - cred = credentials.GetCredential (uri, "DEFAULT"); + if ((cred = credentials.GetCredential (uri, "DEFAULT")) == null) + throw new AuthenticationException ("No credentials could be found for the IMAP server."); ic = engine.QueueCommand (cancellationToken, null, "LOGIN %S %S\r\n", cred.UserName, cred.Password); @@ -1458,7 +1457,7 @@ void SslHandshake (SslStream ssl, string host, CancellationToken cancellationTok void PostConnect (Stream stream, string host, int port, SecureSocketOptions options, bool starttls, CancellationToken cancellationToken) { try { - ProtocolLogger.LogConnect (engine.Uri); + ProtocolLogger.LogConnect (engine.Uri!); } catch { stream.Dispose (); secure = false; @@ -1491,7 +1490,7 @@ void PostConnect (Stream stream, string host, int port, SecureSocketOptions opti if (ic.Response == ImapCommandResponse.Ok) { try { var tls = new SslStream (stream, false, ValidateRemoteCertificate); - engine.Stream.Stream = tls; + engine.Stream!.Stream = tls; SslHandshake (tls, host, cancellationToken); } catch (Exception ex) { @@ -2476,7 +2475,7 @@ ImapCommand QueueGetMetadataCommand (MetadataTag tag, CancellationToken cancella { ic.ThrowIfNotOk ("GETMETADATA"); - var metadata = (MetadataCollection) ic.UserData; + var metadata = (MetadataCollection) ic.UserData!; string? value = null; for (int i = 0; i < metadata.Count; i++) { @@ -2609,7 +2608,7 @@ MetadataCollection ProcessGetMetadataResponse (ImapCommand ic, MetadataOptions o options.LongEntries = metadata.Value; } - return engine.FilterMetadata ((MetadataCollection) ic.UserData, string.Empty); + return engine.FilterMetadata ((MetadataCollection) ic.UserData!, string.Empty); } /// @@ -2782,7 +2781,7 @@ void OnEngineWebAlert (object? sender, WebAlertEventArgs e) /// The event is raised whenever the Google Mail server sends a /// WEBALERT message. /// - public event EventHandler WebAlert; + public event EventHandler? WebAlert; /// /// Raise the web alert event. @@ -2813,7 +2812,7 @@ void OnEngineDisconnected (object? sender, EventArgs e) disconnecting = false; secure = false; - OnDisconnected (uri.Host, uri.Port, GetSecureSocketOptions (uri), requested); + OnDisconnected (uri!.Host, uri.Port, GetSecureSocketOptions (uri), requested); } /// diff --git a/MailKit/Net/Imap/ImapCommand.cs b/MailKit/Net/Imap/ImapCommand.cs index 5952caf3d9..808c6c1c9f 100644 --- a/MailKit/Net/Imap/ImapCommand.cs +++ b/MailKit/Net/Imap/ImapCommand.cs @@ -30,7 +30,6 @@ using System.Globalization; using System.Threading.Tasks; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using MimeKit; using MimeKit.Utils; @@ -548,21 +547,22 @@ public bool Step () var buf = Encoding.ASCII.GetBytes (Tag + " "); - Engine.Stream.Write (buf, 0, buf.Length, CancellationToken); + Engine.Stream!.Write (buf, 0, buf.Length, CancellationToken); } do { - var command = parts[current].Command; + var part = parts[current]; + var command = part.Command; - Engine.Stream.Write (command, 0, command.Length, CancellationToken); + Engine.Stream!.Write (command, 0, command.Length, CancellationToken); // if the server doesn't support LITERAL+ (or LITERAL-), we'll need to wait // for a "+" response before writing out the any literals... - if (parts[current].WaitForContinuation) + if (part.WaitForContinuation || part.Literal == null) break; // otherwise, we can write out any and all literal tokens we have... - parts[current].Literal.WriteTo (Engine.Stream, CancellationToken); + part.Literal.WriteTo (Engine.Stream, CancellationToken); if (current + 1 >= parts.Count) break; @@ -598,7 +598,7 @@ public bool Step () // if we've got a Literal pending, the '+' means we can send it now... if (!supportsLiteralPlus && parts[current].Literal != null) { - parts[current].Literal.WriteTo (Engine.Stream, CancellationToken); + parts[current].Literal!.WriteTo (Engine.Stream, CancellationToken); break; } @@ -689,21 +689,22 @@ public async Task StepAsync () var buf = Encoding.ASCII.GetBytes (Tag + " "); - await Engine.Stream.WriteAsync (buf, 0, buf.Length, CancellationToken).ConfigureAwait (false); + await Engine.Stream!.WriteAsync (buf, 0, buf.Length, CancellationToken).ConfigureAwait (false); } do { - var command = parts[current].Command; + var part = parts[current]; + var command = part.Command; - await Engine.Stream.WriteAsync (command, 0, command.Length, CancellationToken).ConfigureAwait (false); + await Engine.Stream!.WriteAsync (command, 0, command.Length, CancellationToken).ConfigureAwait (false); // if the server doesn't support LITERAL+ (or LITERAL-), we'll need to wait // for a "+" response before writing out the any literals... - if (parts[current].WaitForContinuation) + if (part.WaitForContinuation || part.Literal == null) break; // otherwise, we can write out any and all literal tokens we have... - await parts[current].Literal.WriteToAsync (Engine.Stream, CancellationToken).ConfigureAwait (false); + await part.Literal.WriteToAsync (Engine.Stream, CancellationToken).ConfigureAwait (false); if (current + 1 >= parts.Count) break; @@ -739,7 +740,7 @@ public async Task StepAsync () // if we've got a Literal pending, the '+' means we can send it now... if (!supportsLiteralPlus && parts[current].Literal != null) { - await parts[current].Literal.WriteToAsync (Engine.Stream, CancellationToken).ConfigureAwait (false); + await parts[current].Literal!.WriteToAsync (Engine.Stream, CancellationToken).ConfigureAwait (false); break; } @@ -812,7 +813,7 @@ public async Task StepAsync () /// /// The type of response-code. /// The response-code if it exists; otherwise, . - public ImapResponseCode GetResponseCode (ImapResponseCodeType type) + public ImapResponseCode? GetResponseCode (ImapResponseCodeType type) { for (int i = 0; i < RespCodes.Count; i++) { if (RespCodes[i].Type == type) diff --git a/MailKit/Net/Imap/ImapCommandException.cs b/MailKit/Net/Imap/ImapCommandException.cs index 857511a122..b7f9bb468b 100644 --- a/MailKit/Net/Imap/ImapCommandException.cs +++ b/MailKit/Net/Imap/ImapCommandException.cs @@ -77,7 +77,8 @@ protected ImapCommandException (SerializationInfo info, StreamingContext context internal static ImapCommandException Create (string command, ImapCommand ic) { var result = ic.Response.ToString ().ToUpperInvariant (); - string message, reason = null; + string? reason = null; + string message; if (string.IsNullOrEmpty (ic.ResponseText)) { for (int i = ic.RespCodes.Count - 1; i >= 0; i--) { @@ -86,10 +87,10 @@ internal static ImapCommandException Create (string command, ImapCommand ic) break; } } - } else { - reason = ic.ResponseText; } + reason ??= ic.ResponseText ?? string.Empty; + if (!string.IsNullOrEmpty (reason)) message = string.Format ("The IMAP server replied to the '{0}' command with a '{1}' response: {2}", command, result, reason); else diff --git a/MailKit/Net/Imap/ImapEngine.cs b/MailKit/Net/Imap/ImapEngine.cs index aefa8a938d..62126f2bfb 100644 --- a/MailKit/Net/Imap/ImapEngine.cs +++ b/MailKit/Net/Imap/ImapEngine.cs @@ -160,7 +160,7 @@ class ImapEngine : IDisposable long clientConnectedTimestamp; internal char TagPrefix; ImapCommand? current; - MimeParser parser; + MimeParser? parser; internal int Tag; bool disposed; @@ -362,7 +362,7 @@ public bool UTF8Enabled { /// Gets the URI of the IMAP server. /// /// The URI of the IMAP server. - public Uri Uri { + public Uri? Uri { get; internal set; } @@ -373,7 +373,7 @@ public Uri Uri { /// Gets the underlying IMAP stream. /// /// The IMAP stream. - public ImapStream Stream { + public ImapStream? Stream { get; private set; } @@ -627,9 +627,9 @@ internal void SetStream (ImapStream stream) public NetworkOperation StartNetworkOperation (NetworkOperationKind kind, Uri? uri = null) { #if NET6_0_OR_GREATER - return NetworkOperation.Start (kind, uri ?? Uri, Telemetry.ImapClient.ActivitySource, metrics); + return NetworkOperation.Start (kind, uri ?? Uri!, Telemetry.ImapClient.ActivitySource, metrics); #else - return NetworkOperation.Start (kind, uri ?? Uri); + return NetworkOperation.Start (kind, uri ?? Uri!); #endif } @@ -835,7 +835,7 @@ public async Task ConnectAsync (ImapStream stream, CancellationToken cancellatio void RecordClientDisconnected (Exception? ex) { #if NET6_0_OR_GREATER - metrics?.RecordClientDisconnected (clientConnectedTimestamp, Uri, ex); + metrics?.RecordClientDisconnected (clientConnectedTimestamp, Uri!, ex); #endif clientConnectedTimestamp = 0; } @@ -893,7 +893,7 @@ public string ReadLine (CancellationToken cancellationToken) bool complete; do { - complete = Stream.ReadLine (builder, cancellationToken); + complete = Stream!.ReadLine (builder, cancellationToken); } while (!complete); // FIXME: All callers expect CRLF to be trimmed, but many also want all trailing whitespace trimmed. @@ -926,7 +926,7 @@ public async Task ReadLineAsync (CancellationToken cancellationToken) bool complete; do { - complete = await Stream.ReadLineAsync (builder, cancellationToken).ConfigureAwait (false); + complete = await Stream!.ReadLineAsync (builder, cancellationToken).ConfigureAwait (false); } while (!complete); // FIXME: All callers expect CRLF to be trimmed, but many also want all trailing whitespace trimmed. @@ -955,7 +955,7 @@ public async Task ReadLineAsync (CancellationToken cancellationToken) /// public ImapToken ReadToken (CancellationToken cancellationToken) { - return Stream.ReadToken (cancellationToken); + return Stream!.ReadToken (cancellationToken); } /// @@ -977,7 +977,7 @@ public ImapToken ReadToken (CancellationToken cancellationToken) /// public ValueTask ReadTokenAsync (CancellationToken cancellationToken) { - return Stream.ReadTokenAsync (cancellationToken); + return Stream!.ReadTokenAsync (cancellationToken); } /// @@ -1000,7 +1000,7 @@ public ValueTask ReadTokenAsync (CancellationToken cancellationToken) /// public ImapToken ReadToken (string specials, CancellationToken cancellationToken) { - return Stream.ReadToken (specials, cancellationToken); + return Stream!.ReadToken (specials, cancellationToken); } /// @@ -1023,7 +1023,7 @@ public ImapToken ReadToken (string specials, CancellationToken cancellationToken /// public ValueTask ReadTokenAsync (string specials, CancellationToken cancellationToken) { - return Stream.ReadTokenAsync (specials, cancellationToken); + return Stream!.ReadTokenAsync (specials, cancellationToken); } /// @@ -1046,7 +1046,7 @@ public ValueTask ReadTokenAsync (string specials, CancellationToken c /// public ImapToken PeekToken (string specials, CancellationToken cancellationToken) { - var token = Stream.ReadToken (specials, cancellationToken); + var token = Stream!.ReadToken (specials, cancellationToken); Stream.UngetToken (token); @@ -1073,7 +1073,7 @@ public ImapToken PeekToken (string specials, CancellationToken cancellationToken /// public async ValueTask PeekTokenAsync (string specials, CancellationToken cancellationToken) { - var token = await Stream.ReadTokenAsync (specials, cancellationToken).ConfigureAwait (false); + var token = await Stream!.ReadTokenAsync (specials, cancellationToken).ConfigureAwait (false); Stream.UngetToken (token); @@ -1099,7 +1099,7 @@ public async ValueTask PeekTokenAsync (string specials, CancellationT /// public ImapToken PeekToken (CancellationToken cancellationToken) { - var token = Stream.ReadToken (cancellationToken); + var token = Stream!.ReadToken (cancellationToken); Stream.UngetToken (token); @@ -1125,7 +1125,7 @@ public ImapToken PeekToken (CancellationToken cancellationToken) /// public async ValueTask PeekTokenAsync (CancellationToken cancellationToken) { - var token = await Stream.ReadTokenAsync (cancellationToken).ConfigureAwait (false); + var token = await Stream!.ReadTokenAsync (cancellationToken).ConfigureAwait (false); Stream.UngetToken (token); @@ -1148,7 +1148,7 @@ public async ValueTask PeekTokenAsync (CancellationToken cancellation /// public string ReadLiteral (CancellationToken cancellationToken) { - if (Stream.Mode != ImapStreamMode.Literal) + if (Stream!.Mode != ImapStreamMode.Literal) throw new InvalidOperationException (); int literalLength = Stream.LiteralLength; @@ -1188,7 +1188,7 @@ public string ReadLiteral (CancellationToken cancellationToken) /// public async Task ReadLiteralAsync (CancellationToken cancellationToken) { - if (Stream.Mode != ImapStreamMode.Literal) + if (Stream!.Mode != ImapStreamMode.Literal) throw new InvalidOperationException (); int literalLength = Stream.LiteralLength; @@ -1225,7 +1225,7 @@ void SkipLine (CancellationToken cancellationToken) try { do { - nread = Stream.Read (buf, 0, BufferSize, cancellationToken); + nread = Stream!.Read (buf, 0, BufferSize, cancellationToken); } while (nread > 0); } finally { ArrayPool.Shared.Return (buf); @@ -1247,7 +1247,7 @@ async Task SkipLineAsync (CancellationToken cancellationToken) try { do { - nread = await Stream.ReadAsync (buf, 0, BufferSize, cancellationToken).ConfigureAwait (false); + nread = await Stream!.ReadAsync (buf, 0, BufferSize, cancellationToken).ConfigureAwait (false); } while (nread > 0); } finally { ArrayPool.Shared.Return (buf); @@ -1486,7 +1486,7 @@ void UpdateCapabilities (ImapTokenType sentinel, CancellationToken cancellationT AssertToken (token, sentinel, GenericItemSyntaxErrorFormat, "CAPABILITIES", token); // unget the sentinel - Stream.UngetToken (token); + Stream!.UngetToken (token); StandardizeCapabilities (); } @@ -1513,7 +1513,7 @@ async Task UpdateCapabilitiesAsync (ImapTokenType sentinel, CancellationToken ca AssertToken (token, sentinel, GenericItemSyntaxErrorFormat, "CAPABILITIES", token); // unget the sentinel - Stream.UngetToken (token); + Stream!.UngetToken (token); StandardizeCapabilities (); } @@ -1923,14 +1923,14 @@ public ImapResponseCode ParseResponseCode (bool isTagged, CancellationToken canc } break; case ImapResponseCodeType.Capability: - Stream.UngetToken (token); + Stream!.UngetToken (token); UpdateCapabilities (ImapTokenType.CloseBracket, cancellationToken); token = ReadToken (cancellationToken); break; case ImapResponseCodeType.PermanentFlags: var perm = (PermanentFlagsResponseCode) code; - Stream.UngetToken (token); + Stream!.UngetToken (token); perm.Flags = ImapUtils.ParseFlagsList (this, "PERMANENTFLAGS", perm.Keywords, cancellationToken); token = ReadToken (cancellationToken); break; @@ -2020,7 +2020,7 @@ public ImapResponseCode ParseResponseCode (bool isTagged, CancellationToken canc copy.SrcUidSet = ParseUidSet (token, validity, out _, out _, GenericResponseCodeSyntaxErrorFormat, "COPYUID", token); } else { copy.SrcUidSet = new UniqueIdSet (); - Stream.UngetToken (token); + Stream!.UngetToken (token); } token = ReadToken (cancellationToken); @@ -2029,7 +2029,7 @@ public ImapResponseCode ParseResponseCode (bool isTagged, CancellationToken canc copy.DestUidSet = ParseUidSet (token, copy.UidValidity, out _, out _, GenericResponseCodeSyntaxErrorFormat, "COPYUID", token); } else { copy.DestUidSet = new UniqueIdSet (); - Stream.UngetToken (token); + Stream!.UngetToken (token); } token = ReadToken (cancellationToken); @@ -2249,14 +2249,14 @@ public async ValueTask ParseResponseCodeAsync (bool isTagged, } break; case ImapResponseCodeType.Capability: - Stream.UngetToken (token); + Stream!.UngetToken (token); await UpdateCapabilitiesAsync (ImapTokenType.CloseBracket, cancellationToken).ConfigureAwait (false); token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); break; case ImapResponseCodeType.PermanentFlags: var perm = (PermanentFlagsResponseCode) code; - Stream.UngetToken (token); + Stream!.UngetToken (token); perm.Flags = await ImapUtils.ParseFlagsListAsync (this, "PERMANENTFLAGS", perm.Keywords, cancellationToken).ConfigureAwait (false); token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); break; @@ -2346,7 +2346,7 @@ public async ValueTask ParseResponseCodeAsync (bool isTagged, copy.SrcUidSet = ParseUidSet (token, validity, out _, out _, GenericResponseCodeSyntaxErrorFormat, "COPYUID", token); } else { copy.SrcUidSet = new UniqueIdSet (); - Stream.UngetToken (token); + Stream!.UngetToken (token); } token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); @@ -2355,7 +2355,7 @@ public async ValueTask ParseResponseCodeAsync (bool isTagged, copy.DestUidSet = ParseUidSet (token, copy.UidValidity, out _, out _, GenericResponseCodeSyntaxErrorFormat, "COPYUID", token); } else { copy.DestUidSet = new UniqueIdSet (); - Stream.UngetToken (token); + Stream!.UngetToken (token); } token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); @@ -2759,11 +2759,11 @@ internal void ProcessUntaggedResponse (CancellationToken cancellationToken) // See https://github.com/jstedfast/MailKit/issues/115#issuecomment-313684616 for details. if (token.Type == ImapTokenType.OpenBracket) { // unget the '[' token and then pretend that we got an "OK" - Stream.UngetToken (token); + Stream!.UngetToken (token); atom = "OK"; } else if (token.Type != ImapTokenType.Atom) { // if we get anything else here, just ignore it? - Stream.UngetToken (token); + Stream!.UngetToken (token); SkipLine (cancellationToken); return; } else { @@ -2912,11 +2912,11 @@ internal async Task ProcessUntaggedResponseAsync (CancellationToken cancellation // See https://github.com/jstedfast/MailKit/issues/115#issuecomment-313684616 for details. if (token.Type == ImapTokenType.OpenBracket) { // unget the '[' token and then pretend that we got an "OK" - Stream.UngetToken (token); + Stream!.UngetToken (token); atom = "OK"; } else if (token.Type != ImapTokenType.Atom) { // if we get anything else here, just ignore it? - Stream.UngetToken (token); + Stream!.UngetToken (token); await SkipLineAsync (cancellationToken).ConfigureAwait (false); return; } else { @@ -4159,6 +4159,7 @@ public static bool IsValidMailboxName (string mailboxName, char delim) return mailboxName.Length > 0; } + [MemberNotNull (nameof (parser))] void InitializeParser (Stream stream, bool persistent) { if (parser == null) @@ -4212,7 +4213,7 @@ public Task ParseEntityAsync (Stream stream, bool persistent, Cancel /// /// Occurs when the engine receives an alert message from the server. /// - public event EventHandler Alert; + public event EventHandler? Alert; internal void OnAlert (string message) { @@ -4222,7 +4223,7 @@ internal void OnAlert (string message) /// /// Occurs when the engine receives a webalert message from the server. /// - public event EventHandler WebAlert; + public event EventHandler? WebAlert; internal void OnWebAlert (Uri uri, string message) { @@ -4232,7 +4233,7 @@ internal void OnWebAlert (Uri uri, string message) /// /// Occurs when the engine receives a notification that a folder has been created. /// - public event EventHandler FolderCreated; + public event EventHandler? FolderCreated; internal void OnFolderCreated (IMailFolder folder) { @@ -4242,7 +4243,7 @@ internal void OnFolderCreated (IMailFolder folder) /// /// Occurs when the engine receives a notification that metadata has changed. /// - public event EventHandler MetadataChanged; + public event EventHandler? MetadataChanged; internal void OnMetadataChanged (Metadata metadata) { @@ -4252,7 +4253,7 @@ internal void OnMetadataChanged (Metadata metadata) /// /// Occurs when the engine receives a notification overflow message from the server. /// - public event EventHandler NotificationOverflow; + public event EventHandler? NotificationOverflow; internal void OnNotificationOverflow () { @@ -4262,7 +4263,7 @@ internal void OnNotificationOverflow () NotificationOverflow?.Invoke (this, EventArgs.Empty); } - public event EventHandler Disconnected; + public event EventHandler? Disconnected; void OnDisconnected () { diff --git a/MailKit/Net/Imap/ImapFolder.cs b/MailKit/Net/Imap/ImapFolder.cs index 70314cedff..726ad175d3 100644 --- a/MailKit/Net/Imap/ImapFolder.cs +++ b/MailKit/Net/Imap/ImapFolder.cs @@ -857,14 +857,14 @@ ImapCommand QueueCreateCommand (string name, bool isMessageFolder, CancellationT return Engine.QueueCommand (cancellationToken, null, "CREATE %S\r\n", createName); } - MailboxIdResponseCode ProcessCreateResponse (ImapCommand ic) + MailboxIdResponseCode? ProcessCreateResponse (ImapCommand ic) { ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok && ic.GetResponseCode (ImapResponseCodeType.AlreadyExists) == null) throw ImapCommandException.Create ("CREATE", ic); - return (MailboxIdResponseCode) ic.GetResponseCode (ImapResponseCodeType.MailboxId); + return ic.GetResponseCode (ImapResponseCodeType.MailboxId) as MailboxIdResponseCode; } IMailFolder? Create (ImapCommand ic, string encodedName, bool specialUse, CancellationToken cancellationToken) @@ -3373,8 +3373,8 @@ MetadataCollection ProcessGetMetadataResponse (ImapCommand ic, MetadataOptions o ic.ThrowIfNotOk ("GETMETADATA"); - var metadata = (MetadataResponseCode) ic.GetResponseCode (ImapResponseCodeType.Metadata); - if (metadata != null && metadata.SubType == MetadataResponseCodeSubType.LongEntries) + var rc = ic.GetResponseCode (ImapResponseCodeType.Metadata); + if (rc is MetadataResponseCode metadata && metadata.SubType == MetadataResponseCodeSubType.LongEntries) options.LongEntries = metadata.Value; return Engine.FilterMetadata ((MetadataCollection) ic.UserData!, EncodedName); @@ -4492,9 +4492,9 @@ ImapCommand QueueAppendCommand (FormatOptions options, IAppendRequest request, C ic.ThrowIfNotOk ("APPEND"); - var append = (AppendUidResponseCode) ic.GetResponseCode (ImapResponseCodeType.AppendUid); + var rc = ic.GetResponseCode (ImapResponseCodeType.AppendUid); - if (append != null) + if (rc is AppendUidResponseCode append) return append.UidSet[0]; return null; @@ -4692,9 +4692,9 @@ IList ProcessMultiAppendResponse (ImapCommand ic) ic.ThrowIfNotOk ("APPEND"); - var append = (AppendUidResponseCode) ic.GetResponseCode (ImapResponseCodeType.AppendUid); + var rc = ic.GetResponseCode (ImapResponseCodeType.AppendUid); - if (append != null) + if (rc is AppendUidResponseCode append) return append.UidSet; return Array.Empty (); @@ -4937,9 +4937,9 @@ ImapCommand QueueReplaceCommand (FormatOptions options, UniqueId uid, IReplaceRe ic.ThrowIfNotOk ("REPLACE"); - var append = (AppendUidResponseCode) ic.GetResponseCode (ImapResponseCodeType.AppendUid); + var rc = ic.GetResponseCode (ImapResponseCodeType.AppendUid); - if (append != null) + if (rc is AppendUidResponseCode append) return append.UidSet[0]; return null; @@ -5361,9 +5361,9 @@ void ValidateArguments (IList uids, IMailFolder destination) static void GetCopiedUids (ImapCommand ic, ref UniqueIdSet? src, ref UniqueIdSet? dest) { - var copy = (CopyUidResponseCode) ic.GetResponseCode (ImapResponseCodeType.CopyUid); + var rc = ic.GetResponseCode (ImapResponseCodeType.CopyUid); - if (copy != null) { + if (rc is CopyUidResponseCode copy) { if (dest == null) { dest = copy.DestUidSet; src = copy.SrcUidSet; @@ -6133,7 +6133,7 @@ void OnFetchAsyncCompleted (MessageSummary message) OnMessageFlagsChanged (args); } - if ((message.Fields & MessageSummaryItems.GMailLabels) != 0) { + if (message.GMailLabels != null) { var args = new MessageLabelsChangedEventArgs (index, message.GMailLabels) { ModSeq = message.ModSeq, UniqueId = uid @@ -6142,7 +6142,7 @@ void OnFetchAsyncCompleted (MessageSummary message) OnMessageLabelsChanged (args); } - if ((message.Fields & MessageSummaryItems.Annotations) != 0) { + if (message.Annotations != null) { var args = new AnnotationsChangedEventArgs (index, message.Annotations) { ModSeq = message.ModSeq, UniqueId = uid @@ -6151,8 +6151,8 @@ void OnFetchAsyncCompleted (MessageSummary message) OnAnnotationsChanged (args); } - if ((message.Fields & MessageSummaryItems.ModSeq) != 0) { - var args = new ModSeqChangedEventArgs (index, message.ModSeq!.Value) { + if (message.ModSeq.HasValue) { + var args = new ModSeqChangedEventArgs (index, message.ModSeq.Value) { UniqueId = uid }; diff --git a/MailKit/Net/Imap/ImapFolderAnnotations.cs b/MailKit/Net/Imap/ImapFolderAnnotations.cs index 94c5bb64b8..3fa7a10b4f 100644 --- a/MailKit/Net/Imap/ImapFolderAnnotations.cs +++ b/MailKit/Net/Imap/ImapFolderAnnotations.cs @@ -30,6 +30,7 @@ using System.Globalization; using System.Threading.Tasks; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace MailKit.Net.Imap { @@ -249,7 +250,7 @@ public override async Task StoreAsync (IList uids, IList a /// public override IList Store (IList uids, ulong modseq, IList annotations, CancellationToken cancellationToken = default) { - UniqueIdSet unmodified = null; + UniqueIdSet? unmodified = null; foreach (var ic in QueueStoreCommands (uids, modseq, annotations, cancellationToken)) { Engine.Run (ic); @@ -318,7 +319,7 @@ public override IList Store (IList uids, ulong modseq, IList /// public override async Task> StoreAsync (IList uids, ulong modseq, IList annotations, CancellationToken cancellationToken = default) { - UniqueIdSet unmodified = null; + UniqueIdSet? unmodified = null; foreach (var ic in QueueStoreCommands (uids, modseq, annotations, cancellationToken)) { await Engine.RunAsync (ic).ConfigureAwait (false); @@ -334,7 +335,7 @@ public override async Task> StoreAsync (IList uids, ul return unmodified; } - ImapCommand QueueStoreCommand (IList indexes, ulong? modseq, IList annotations, CancellationToken cancellationToken) + bool TryQueueStoreCommand (IList indexes, ulong? modseq, IList annotations, CancellationToken cancellationToken, [NotNullWhen (true)] out ImapCommand? ic) { if (indexes == null) throw new ArgumentNullException (nameof (indexes)); @@ -350,8 +351,10 @@ ImapCommand QueueStoreCommand (IList indexes, ulong? modseq, IList (); @@ -368,7 +371,9 @@ ImapCommand QueueStoreCommand (IList indexes, ulong? modseq, IList @@ -420,9 +425,7 @@ ImapCommand QueueStoreCommand (IList indexes, ulong? modseq, IList public override void Store (IList indexes, IList annotations, CancellationToken cancellationToken = default) { - var ic = QueueStoreCommand (indexes, null, annotations, cancellationToken); - - if (ic == null) + if (!TryQueueStoreCommand (indexes, null, annotations, cancellationToken, out var ic)) return; Engine.Run (ic); @@ -480,9 +483,7 @@ public override void Store (IList indexes, IList annotations, C /// public override async Task StoreAsync (IList indexes, IList annotations, CancellationToken cancellationToken = default) { - var ic = QueueStoreCommand (indexes, null, annotations, cancellationToken); - - if (ic == null) + if (!TryQueueStoreCommand (indexes, null, annotations, cancellationToken, out var ic)) return; await Engine.RunAsync (ic).ConfigureAwait (false); @@ -543,9 +544,7 @@ public override async Task StoreAsync (IList indexes, IList ann /// public override IList Store (IList indexes, ulong modseq, IList annotations, CancellationToken cancellationToken = default) { - var ic = QueueStoreCommand (indexes, modseq, annotations, cancellationToken); - - if (ic == null) + if (!TryQueueStoreCommand (indexes, modseq, annotations, cancellationToken, out var ic)) return Array.Empty (); Engine.Run (ic); @@ -608,9 +607,7 @@ public override IList Store (IList indexes, ulong modseq, IList public override async Task> StoreAsync (IList indexes, ulong modseq, IList annotations, CancellationToken cancellationToken = default) { - var ic = QueueStoreCommand (indexes, modseq, annotations, cancellationToken); - - if (ic == null) + if (!TryQueueStoreCommand (indexes, modseq, annotations, cancellationToken, out var ic)) return Array.Empty (); await Engine.RunAsync (ic).ConfigureAwait (false); diff --git a/MailKit/Net/Imap/ImapFolderConstructorArgs.cs b/MailKit/Net/Imap/ImapFolderConstructorArgs.cs index 16b191ae12..c20402959a 100644 --- a/MailKit/Net/Imap/ImapFolderConstructorArgs.cs +++ b/MailKit/Net/Imap/ImapFolderConstructorArgs.cs @@ -47,7 +47,7 @@ public sealed class ImapFolderConstructorArgs /// The encoded name. /// The attributes. /// The directory separator. - internal ImapFolderConstructorArgs (ImapEngine engine, string encodedName, FolderAttributes attributes, char delim) : this () + internal ImapFolderConstructorArgs (ImapEngine engine, string encodedName, FolderAttributes attributes, char delim) { FullName = engine.DecodeMailboxName (encodedName); Name = GetBaseName (FullName, delim); @@ -57,10 +57,6 @@ internal ImapFolderConstructorArgs (ImapEngine engine, string encodedName, Folde Engine = engine; } - ImapFolderConstructorArgs () - { - } - /// /// Get the folder attributes. /// diff --git a/MailKit/Net/Imap/ImapFolderFetch.cs b/MailKit/Net/Imap/ImapFolderFetch.cs index c30247dfee..893e067db8 100644 --- a/MailKit/Net/Imap/ImapFolderFetch.cs +++ b/MailKit/Net/Imap/ImapFolderFetch.cs @@ -131,7 +131,7 @@ static void ReadLiteralData (ImapEngine engine, CancellationToken cancellationTo try { do { - nread = engine.Stream.Read (buf, 0, BufferSize, cancellationToken); + nread = engine.Stream!.Read (buf, 0, BufferSize, cancellationToken); } while (nread > 0); } finally { ArrayPool.Shared.Return (buf); @@ -145,7 +145,7 @@ static async Task ReadLiteralDataAsync (ImapEngine engine, CancellationToken can try { do { - nread = await engine.Stream.ReadAsync (buf, 0, BufferSize, cancellationToken).ConfigureAwait (false); + nread = await engine.Stream!.ReadAsync (buf, 0, BufferSize, cancellationToken).ConfigureAwait (false); } while (nread > 0); } finally { ArrayPool.Shared.Return (buf); @@ -306,7 +306,7 @@ void ParseSummaryItems (ImapEngine engine, MessageSummary message, FetchSummaryI break; // the header field names will generally be atoms or qstrings but may also be literals - engine.Stream.UngetToken (token); + engine.Stream!.UngetToken (token); var field = ImapUtils.ReadStringToken (engine, format, cancellationToken); @@ -335,7 +335,7 @@ void ParseSummaryItems (ImapEngine engine, MessageSummary message, FetchSummaryI ImapEngine.AssertToken (token, ImapTokenType.Literal, format, token); try { - message.Headers = engine.ParseHeaders (engine.Stream, cancellationToken); + message.Headers = engine.ParseHeaders (engine.Stream!, cancellationToken); } catch (FormatException) { message.Headers = new HeaderList (); } @@ -550,7 +550,7 @@ async Task ParseSummaryItemsAsync (ImapEngine engine, MessageSummary message, Fe break; // the header field names will generally be atoms or qstrings but may also be literals - engine.Stream.UngetToken (token); + engine.Stream!.UngetToken (token); var field = await ImapUtils.ReadStringTokenAsync (engine, format, cancellationToken).ConfigureAwait (false); @@ -579,7 +579,7 @@ async Task ParseSummaryItemsAsync (ImapEngine engine, MessageSummary message, Fe ImapEngine.AssertToken (token, ImapTokenType.Literal, format, token); try { - message.Headers = await engine.ParseHeadersAsync (engine.Stream, cancellationToken).ConfigureAwait (false); + message.Headers = await engine.ParseHeadersAsync (engine.Stream!, cancellationToken).ConfigureAwait (false); } catch (FormatException) { message.Headers = new HeaderList (); } @@ -722,7 +722,7 @@ async Task ParseSummaryItemsAsync (ImapEngine engine, MessageSummary message, Fe Task UntaggedFetchSummaryItemsHandler (ImapEngine engine, ImapCommand ic, int index, bool doAsync) { - var ctx = (FetchSummaryContext) ic.UserData; + var ctx = (FetchSummaryContext) ic.UserData!; if (!ctx.TryGetValue (index, out var message)) { message = new MessageSummary (this, index); @@ -990,7 +990,7 @@ void FetchPreviewText (FetchSummaryContext sctx, Dictionary { foreach (var pair in bodies) { var ic = QueueFetchPreviewTextCommand (sctx, pair, octets, cancellationToken); - var ctx = (FetchPreviewTextContext) ic.UserData; + var ctx = (FetchPreviewTextContext) ic.UserData!; try { Engine.Run (ic); @@ -1006,7 +1006,7 @@ async Task FetchPreviewTextAsync (FetchSummaryContext sctx, Dictionary Fetch (IList indexes, IFetchRequest return Array.Empty (); var ic = QueueFetchCommand (indexes, request, cancellationToken, out bool previewText); - var ctx = (FetchSummaryContext) ic.UserData; + var ctx = (FetchSummaryContext) ic.UserData!; Engine.Run (ic); @@ -1461,7 +1461,7 @@ public override async Task> FetchAsync (IList indexe return Array.Empty (); var ic = QueueFetchCommand (indexes, request, cancellationToken, out bool previewText); - var ctx = (FetchSummaryContext) ic.UserData; + var ctx = (FetchSummaryContext) ic.UserData!; await Engine.RunAsync (ic).ConfigureAwait (false); @@ -1586,7 +1586,7 @@ public override IList Fetch (int min, int max, IFetchRequest re return Array.Empty (); var ic = QueueFetchCommand (min, max, request, cancellationToken, out bool previewText); - var ctx = (FetchSummaryContext) ic.UserData; + var ctx = (FetchSummaryContext) ic.UserData!; Engine.Run (ic); @@ -1657,7 +1657,7 @@ public override async Task> FetchAsync (int min, int max, return Array.Empty (); var ic = QueueFetchCommand (min, max, request, cancellationToken, out bool previewText); - var ctx = (FetchSummaryContext) ic.UserData; + var ctx = (FetchSummaryContext) ic.UserData!; await Engine.RunAsync (ic).ConfigureAwait (false); @@ -1900,7 +1900,7 @@ public bool TryGetSection (UniqueId uid, string specifier, [NotNullWhen (true)] return false; } - public bool TryGetSection (int index, string specifier, [NotNullWhen (true)] out Section section, bool remove = false) + public bool TryGetSection (int index, string specifier, [NotNullWhen (true)] out Section? section, bool remove = false) { for (int i = 0; i < Sections.Count; i++) { var item = Sections[i]; @@ -1998,7 +1998,7 @@ void FetchStream (ImapEngine engine, ImapCommand ic, int index) break; // the header field names will generally be atoms or qstrings but may also be literals - engine.Stream.UngetToken (token); + engine.Stream!.UngetToken (token); var field = ImapUtils.ReadStringToken (engine, ImapEngine.FetchBodySyntaxErrorFormat, ic.CancellationToken); @@ -2047,7 +2047,7 @@ void FetchStream (ImapEngine engine, ImapCommand ic, int index) try { do { - n = engine.Stream.Read (buf, 0, BufferSize, ic.CancellationToken); + n = engine.Stream!.Read (buf, 0, BufferSize, ic.CancellationToken); if (n > 0) { stream.Write (buf, 0, n); @@ -2095,7 +2095,7 @@ void FetchStream (ImapEngine engine, ImapCommand ic, int index) // See https://github.com/jstedfast/MailKit/issues/1708 for details. // // Unget the ')' token and pretend we got a NIL token. - engine.Stream.UngetToken (token); + engine.Stream!.UngetToken (token); goto case ImapTokenType.Nil; default: throw ImapEngine.UnexpectedToken (ImapEngine.GenericItemSyntaxErrorFormat, atom, token); @@ -2256,7 +2256,7 @@ async Task FetchStreamAsync (ImapEngine engine, ImapCommand ic, int index) break; // the header field names will generally be atoms or qstrings but may also be literals - engine.Stream.UngetToken (token); + engine.Stream!.UngetToken (token); var field = await ImapUtils.ReadStringTokenAsync (engine, ImapEngine.FetchBodySyntaxErrorFormat, ic.CancellationToken).ConfigureAwait (false); @@ -2305,7 +2305,7 @@ async Task FetchStreamAsync (ImapEngine engine, ImapCommand ic, int index) try { do { - n = await engine.Stream.ReadAsync (buf, 0, BufferSize, ic.CancellationToken).ConfigureAwait (false); + n = await engine.Stream!.ReadAsync (buf, 0, BufferSize, ic.CancellationToken).ConfigureAwait (false); if (n > 0) { stream.Write (buf, 0, n); @@ -2353,7 +2353,7 @@ async Task FetchStreamAsync (ImapEngine engine, ImapCommand ic, int index) // See https://github.com/jstedfast/MailKit/issues/1708 for details. // // Unget the ')' token and pretend we got a NIL token. - engine.Stream.UngetToken (token); + engine.Stream!.UngetToken (token); goto case ImapTokenType.Nil; default: throw ImapEngine.UnexpectedToken (ImapEngine.GenericItemSyntaxErrorFormat, atom, token); @@ -2502,12 +2502,14 @@ ImapCommand QueueGetHeadersCommand (UniqueId uid, CancellationToken cancellation return ic; } - void ProcessGetHeadersResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId uid, out Section section) + Stream ProcessGetHeadersResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId uid) { ProcessFetchResponse (ic); - if (!ctx.TryGetSection (uid, "HEADER", out section, true)) + if (!ctx.TryGetSection (uid, "HEADER", out var section, true)) throw new MessageNotFoundException ("The IMAP server did not return the requested message headers."); + + return section.Stream; } /// @@ -2553,17 +2555,16 @@ void ProcessGetHeadersResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId public override HeaderList GetHeaders (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetHeadersCommand (uid, cancellationToken, progress, out var ctx); - Section section; try { Engine.Run (ic); - ProcessGetHeadersResponse (ic, ctx, uid, out section); + var stream = ProcessGetHeadersResponse (ic, ctx, uid); + + return ParseHeaders (stream, cancellationToken); } finally { ctx.Dispose (); } - - return ParseHeaders (section.Stream, cancellationToken); } /// @@ -2609,17 +2610,16 @@ public override HeaderList GetHeaders (UniqueId uid, CancellationToken cancellat public override async Task GetHeadersAsync (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetHeadersCommand (uid, cancellationToken, progress, out var ctx); - Section section; try { await Engine.RunAsync (ic).ConfigureAwait (false); - ProcessGetHeadersResponse (ic, ctx, uid, out section); + var stream = ProcessGetHeadersResponse (ic, ctx, uid); + + return await ParseHeadersAsync (stream, cancellationToken).ConfigureAwait (false); } finally { ctx.Dispose (); } - - return await ParseHeadersAsync (section.Stream, cancellationToken).ConfigureAwait (false); } ImapCommand QueueGetHeadersCommand (UniqueId uid, string partSpecifier, CancellationToken cancellationToken, ITransferProgress? progress, out FetchStreamContext ctx, out string[] tags) @@ -2642,14 +2642,14 @@ ImapCommand QueueGetHeadersCommand (UniqueId uid, string partSpecifier, Cancella return ic; } - void ProcessGetHeadersResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId uid, string[] tags, out Section section) + Stream ProcessGetHeadersResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId uid, string[] tags) { ProcessFetchResponse (ic); - if (!ctx.TryGetSection (uid, tags[0], out var sect, true)) + if (!ctx.TryGetSection (uid, tags[0], out var section, true)) throw new MessageNotFoundException ("The IMAP server did not return the requested body part headers."); - section = sect; + return section.Stream; } /// @@ -2699,17 +2699,16 @@ void ProcessGetHeadersResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId public virtual HeaderList GetHeaders (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetHeadersCommand (uid, partSpecifier, cancellationToken, progress, out var ctx, out var tags); - Section section; try { Engine.Run (ic); - ProcessGetHeadersResponse (ic, ctx, uid, tags, out section); + var stream = ProcessGetHeadersResponse (ic, ctx, uid, tags); + + return ParseHeaders (stream, cancellationToken); } finally { ctx.Dispose (); } - - return ParseHeaders (section.Stream, cancellationToken); } /// @@ -2759,17 +2758,16 @@ public virtual HeaderList GetHeaders (UniqueId uid, string partSpecifier, Cancel public virtual async Task GetHeadersAsync (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetHeadersCommand (uid, partSpecifier, cancellationToken, progress, out var ctx, out var tags); - Section section; try { await Engine.RunAsync (ic).ConfigureAwait (false); - ProcessGetHeadersResponse (ic, ctx, uid, tags, out section); + var stream = ProcessGetHeadersResponse (ic, ctx, uid, tags); + + return await ParseHeadersAsync (stream, cancellationToken).ConfigureAwait (false); } finally { ctx.Dispose (); } - - return await ParseHeadersAsync (section.Stream, cancellationToken).ConfigureAwait (false); } /// @@ -2898,12 +2896,14 @@ ImapCommand QueueGetHeadersCommand (int index, CancellationToken cancellationTok return ic; } - void ProcessGetHeadersResponse (ImapCommand ic, FetchStreamContext ctx, int index, out Section section) + Stream ProcessGetHeadersResponse (ImapCommand ic, FetchStreamContext ctx, int index) { ProcessFetchResponse (ic); - if (!ctx.TryGetSection (index, "HEADER", out section, true)) + if (!ctx.TryGetSection (index, "HEADER", out var section, true)) throw new MessageNotFoundException ("The IMAP server did not return the requested message headers."); + + return section.Stream; } /// @@ -2949,17 +2949,16 @@ void ProcessGetHeadersResponse (ImapCommand ic, FetchStreamContext ctx, int inde public override HeaderList GetHeaders (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetHeadersCommand (index, cancellationToken, progress, out var ctx); - Section section; try { Engine.Run (ic); - ProcessGetHeadersResponse (ic, ctx, index, out section); + var stream = ProcessGetHeadersResponse (ic, ctx, index); + + return ParseHeaders (stream, cancellationToken); } finally { ctx.Dispose (); } - - return ParseHeaders (section.Stream, cancellationToken); } /// @@ -3005,17 +3004,16 @@ public override HeaderList GetHeaders (int index, CancellationToken cancellation public override async Task GetHeadersAsync (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetHeadersCommand (index, cancellationToken, progress, out var ctx); - Section section; try { await Engine.RunAsync (ic).ConfigureAwait (false); - ProcessGetHeadersResponse (ic, ctx, index, out section); + var stream = ProcessGetHeadersResponse (ic, ctx, index); + + return await ParseHeadersAsync (stream, cancellationToken).ConfigureAwait (false); } finally { ctx.Dispose (); } - - return await ParseHeadersAsync (section.Stream, cancellationToken).ConfigureAwait (false); } ImapCommand QueueGetHeadersCommand (int index, string partSpecifier, CancellationToken cancellationToken, ITransferProgress? progress, out FetchStreamContext ctx, out string[] tags) @@ -3038,12 +3036,14 @@ ImapCommand QueueGetHeadersCommand (int index, string partSpecifier, Cancellatio return ic; } - void ProcessGetHeadersResponse (ImapCommand ic, FetchStreamContext ctx, int index, string[] tags, out Section section) + Stream ProcessGetHeadersResponse (ImapCommand ic, FetchStreamContext ctx, int index, string[] tags) { ProcessFetchResponse (ic); - if (!ctx.TryGetSection (index, tags[0], out section, true)) + if (!ctx.TryGetSection (index, tags[0], out var section, true)) throw new MessageNotFoundException ("The IMAP server did not return the requested body part headers."); + + return section.Stream; } /// @@ -3093,17 +3093,16 @@ void ProcessGetHeadersResponse (ImapCommand ic, FetchStreamContext ctx, int inde public virtual HeaderList GetHeaders (int index, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetHeadersCommand (index, partSpecifier, cancellationToken, progress, out var ctx, out var tags); - Section section; try { Engine.Run (ic); - ProcessGetHeadersResponse (ic, ctx, index, tags, out section); + var stream = ProcessGetHeadersResponse (ic, ctx, index, tags); + + return ParseHeaders (stream, cancellationToken); } finally { ctx.Dispose (); } - - return ParseHeaders (section.Stream, cancellationToken); } /// @@ -3153,17 +3152,16 @@ public virtual HeaderList GetHeaders (int index, string partSpecifier, Cancellat public virtual async Task GetHeadersAsync (int index, string partSpecifier, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetHeadersCommand (index, partSpecifier, cancellationToken, progress, out var ctx, out var tags); - Section section; try { await Engine.RunAsync (ic).ConfigureAwait (false); - ProcessGetHeadersResponse (ic, ctx, index, tags, out section); + var stream = ProcessGetHeadersResponse (ic, ctx, index, tags); + + return await ParseHeadersAsync (stream, cancellationToken).ConfigureAwait (false); } finally { ctx.Dispose (); } - - return await ParseHeadersAsync (section.Stream, cancellationToken).ConfigureAwait (false); } /// @@ -3276,7 +3274,7 @@ public override Task GetHeadersAsync (int index, BodyPart part, Canc return GetHeadersAsync (index, part.PartSpecifier, cancellationToken, progress); } - ImapCommand QueueGetMessageCommand (UniqueId uid, CancellationToken cancellationToken, ITransferProgress progress, out FetchStreamContext ctx) + ImapCommand QueueGetMessageCommand (UniqueId uid, CancellationToken cancellationToken, ITransferProgress? progress, out FetchStreamContext ctx) { if (!uid.IsValid) throw new ArgumentException ("The uid is invalid.", nameof (uid)); @@ -3292,12 +3290,14 @@ ImapCommand QueueGetMessageCommand (UniqueId uid, CancellationToken cancellation return ic; } - void ProcessGetMessageResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId uid, out Section section) + Stream ProcessGetMessageResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId uid) { ProcessFetchResponse (ic); - if (!ctx.TryGetSection (uid, string.Empty, out section, true)) + if (!ctx.TryGetSection (uid, string.Empty, out var section, true)) throw new MessageNotFoundException ("The IMAP server did not return the requested message."); + + return section.Stream; } /// @@ -3343,17 +3343,16 @@ void ProcessGetMessageResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId public override MimeMessage GetMessage (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetMessageCommand (uid, cancellationToken, progress, out var ctx); - Section section; try { Engine.Run (ic); - ProcessGetMessageResponse (ic, ctx, uid, out section); + var stream = ProcessGetMessageResponse (ic, ctx, uid); + + return ParseMessage (stream, cancellationToken); } finally { ctx.Dispose (); } - - return ParseMessage (section.Stream, cancellationToken); } /// @@ -3399,20 +3398,19 @@ public override MimeMessage GetMessage (UniqueId uid, CancellationToken cancella public override async Task GetMessageAsync (UniqueId uid, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetMessageCommand (uid, cancellationToken, progress, out var ctx); - Section section; try { await Engine.RunAsync (ic).ConfigureAwait (false); - ProcessGetMessageResponse (ic, ctx, uid, out section); + var stream = ProcessGetMessageResponse (ic, ctx, uid); + + return await ParseMessageAsync (stream, cancellationToken).ConfigureAwait (false); } finally { ctx.Dispose (); } - - return await ParseMessageAsync (section.Stream, cancellationToken).ConfigureAwait (false); } - ImapCommand QueueGetMessageCommand (int index, CancellationToken cancellationToken, ITransferProgress progress, out FetchStreamContext ctx) + ImapCommand QueueGetMessageCommand (int index, CancellationToken cancellationToken, ITransferProgress? progress, out FetchStreamContext ctx) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); @@ -3428,12 +3426,14 @@ ImapCommand QueueGetMessageCommand (int index, CancellationToken cancellationTok return ic; } - void ProcessGetMessageResponse (ImapCommand ic, FetchStreamContext ctx, int index, out Section section) + Stream ProcessGetMessageResponse (ImapCommand ic, FetchStreamContext ctx, int index) { ProcessFetchResponse (ic); - if (!ctx.TryGetSection (index, string.Empty, out section, true)) + if (!ctx.TryGetSection (index, string.Empty, out var section, true)) throw new MessageNotFoundException ("The IMAP server did not return the requested message."); + + return section.Stream; } /// @@ -3479,17 +3479,16 @@ void ProcessGetMessageResponse (ImapCommand ic, FetchStreamContext ctx, int inde public override MimeMessage GetMessage (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetMessageCommand (index, cancellationToken, progress, out var ctx); - Section section; try { Engine.Run (ic); - ProcessGetMessageResponse (ic, ctx, index, out section); + var stream = ProcessGetMessageResponse (ic, ctx, index); + + return ParseMessage (stream, cancellationToken); } finally { ctx.Dispose (); } - - return ParseMessage (section.Stream, cancellationToken); } /// @@ -3535,20 +3534,19 @@ public override MimeMessage GetMessage (int index, CancellationToken cancellatio public override async Task GetMessageAsync (int index, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetMessageCommand (index, cancellationToken, progress, out var ctx); - Section section; try { await Engine.RunAsync (ic).ConfigureAwait (false); - ProcessGetMessageResponse (ic, ctx, index, out section); + var stream = ProcessGetMessageResponse (ic, ctx, index); + + return await ParseMessageAsync (stream, cancellationToken).ConfigureAwait (false); } finally { ctx.Dispose (); } - - return await ParseMessageAsync (section.Stream, cancellationToken).ConfigureAwait (false); } - ImapCommand QueueGetBodyPartCommand (UniqueId uid, string partSpecifier, CancellationToken cancellationToken, ITransferProgress progress, out FetchStreamContext ctx, out string[] tags) + ImapCommand QueueGetBodyPartCommand (UniqueId uid, string partSpecifier, CancellationToken cancellationToken, ITransferProgress? progress, out FetchStreamContext ctx, out string[] tags) { if (!uid.IsValid) throw new ArgumentException ("The uid is invalid.", nameof (uid)); @@ -3568,7 +3566,7 @@ ImapCommand QueueGetBodyPartCommand (UniqueId uid, string partSpecifier, Cancell return ic; } - void ProcessGetBodyPartResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId uid, string[] tags, out ChainedStream? chained, out bool dispose) + void ProcessGetBodyPartResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId uid, string[] tags, out ChainedStream chained, out bool dispose) { ProcessFetchResponse (ic); @@ -3587,7 +3585,6 @@ void ProcessGetBodyPartResponse (ImapCommand ic, FetchStreamContext ctx, UniqueI } } catch { chained.Dispose (); - chained = null; throw; } } @@ -3860,7 +3857,7 @@ public override Task GetBodyPartAsync (UniqueId uid, BodyPart part, return GetBodyPartAsync (uid, part.PartSpecifier, cancellationToken, progress); } - ImapCommand QueueGetBodyPartCommand (int index, string partSpecifier, CancellationToken cancellationToken, ITransferProgress progress, out FetchStreamContext ctx, out string[] tags) + ImapCommand QueueGetBodyPartCommand (int index, string partSpecifier, CancellationToken cancellationToken, ITransferProgress? progress, out FetchStreamContext ctx, out string[] tags) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); @@ -3881,7 +3878,7 @@ ImapCommand QueueGetBodyPartCommand (int index, string partSpecifier, Cancellati return ic; } - void ProcessGetBodyPartResponse (ImapCommand ic, FetchStreamContext ctx, int index, string[] tags, out ChainedStream? chained, out bool dispose) + void ProcessGetBodyPartResponse (ImapCommand ic, FetchStreamContext ctx, int index, string[] tags, out ChainedStream chained, out bool dispose) { ProcessFetchResponse (ic); @@ -3900,7 +3897,6 @@ void ProcessGetBodyPartResponse (ImapCommand ic, FetchStreamContext ctx, int ind } } catch { chained.Dispose (); - chained = null; throw; } } @@ -4151,7 +4147,7 @@ public override Task GetBodyPartAsync (int index, BodyPart part, Can return GetBodyPartAsync (index, part.PartSpecifier, cancellationToken, progress); } - ImapCommand? QueueGetStreamCommand (UniqueId uid, int offset, int count, CancellationToken cancellationToken, ITransferProgress? progress, out FetchStreamContext? ctx) + bool TryQueueGetStreamCommand (UniqueId uid, int offset, int count, CancellationToken cancellationToken, ITransferProgress? progress, [NotNullWhen (true)] out ImapCommand? ic, [NotNullWhen (true)] out FetchStreamContext? ctx) { if (!uid.IsValid) throw new ArgumentException ("The uid is invalid.", nameof (uid)); @@ -4166,26 +4162,27 @@ public override Task GetBodyPartAsync (int index, BodyPart part, Can if (count == 0) { ctx = null; - return null; + ic = null; + return false; } - var ic = new ImapCommand (Engine, cancellationToken, this, "UID FETCH %u (BODY.PEEK[]<%d.%d>)\r\n", uid.Id, offset, count); + ic = new ImapCommand (Engine, cancellationToken, this, "UID FETCH %u (BODY.PEEK[]<%d.%d>)\r\n", uid.Id, offset, count); ic.RegisterUntaggedHandler ("FETCH", FetchStreamHandler); ic.UserData = ctx = new FetchStreamContext (progress); Engine.QueueCommand (ic); - return ic; + return true; } - void ProcessGetStreamResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId uid, out Section section) + Stream ProcessGetStreamResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId uid) { ProcessFetchResponse (ic); - if (!ctx.TryGetSection (uid, string.Empty, out var sect, true)) + if (!ctx.TryGetSection (uid, string.Empty, out var section, true)) throw new MessageNotFoundException ("The IMAP server did not return the requested stream."); - section = sect; + return section.Stream; } /// @@ -4240,22 +4237,16 @@ void ProcessGetStreamResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId /// public override Stream GetStream (UniqueId uid, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { - var ic = QueueGetStreamCommand (uid, offset, count, cancellationToken, progress, out var ctx); - - if (ic == null) + if (!TryQueueGetStreamCommand (uid, offset, count, cancellationToken, progress, out var ic, out var ctx)) return new MemoryStream (); - Section section; - try { Engine.Run (ic); - ProcessGetStreamResponse (ic, ctx, uid, out section); + return ProcessGetStreamResponse (ic, ctx, uid); } finally { ctx.Dispose (); } - - return section.Stream; } /// @@ -4310,25 +4301,19 @@ public override Stream GetStream (UniqueId uid, int offset, int count, Cancellat /// public override async Task GetStreamAsync (UniqueId uid, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { - var ic = QueueGetStreamCommand (uid, offset, count, cancellationToken, progress, out var ctx); - - if (ic == null) + if (!TryQueueGetStreamCommand (uid, offset, count, cancellationToken, progress, out var ic, out var ctx)) return new MemoryStream (); - Section section; - try { await Engine.RunAsync (ic).ConfigureAwait (false); - ProcessGetStreamResponse (ic, ctx, uid, out section); + return ProcessGetStreamResponse (ic, ctx, uid); } finally { ctx.Dispose (); } - - return section.Stream; } - ImapCommand? QueueGetStreamCommand (int index, int offset, int count, CancellationToken cancellationToken, ITransferProgress progress, out FetchStreamContext? ctx) + bool TryQueueGetStreamCommand (int index, int offset, int count, CancellationToken cancellationToken, ITransferProgress? progress, [NotNullWhen (true)] out ImapCommand? ic, [NotNullWhen (true)] out FetchStreamContext? ctx) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); @@ -4343,24 +4328,27 @@ public override async Task GetStreamAsync (UniqueId uid, int offset, int if (count == 0) { ctx = null; - return null; + ic = null; + return false; } - var ic = new ImapCommand (Engine, cancellationToken, this, "FETCH %d (BODY.PEEK[]<%d.%d>)\r\n", index + 1, offset, count); + ic = new ImapCommand (Engine, cancellationToken, this, "FETCH %d (BODY.PEEK[]<%d.%d>)\r\n", index + 1, offset, count); ic.RegisterUntaggedHandler ("FETCH", FetchStreamHandler); ic.UserData = ctx = new FetchStreamContext (progress); Engine.QueueCommand (ic); - return ic; + return true; } - void ProcessGetStreamResponse (ImapCommand ic, FetchStreamContext ctx, int index, out Section section) + Stream ProcessGetStreamResponse (ImapCommand ic, FetchStreamContext ctx, int index) { ProcessFetchResponse (ic); - if (!ctx.TryGetSection (index, string.Empty, out section, true)) + if (!ctx.TryGetSection (index, string.Empty, out var section, true)) throw new MessageNotFoundException ("The IMAP server did not return the requested stream."); + + return section.Stream; } /// @@ -4414,22 +4402,16 @@ void ProcessGetStreamResponse (ImapCommand ic, FetchStreamContext ctx, int index /// public override Stream GetStream (int index, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { - var ic = QueueGetStreamCommand (index, offset, count, cancellationToken, progress, out var ctx); - - if (ic == null) + if (!TryQueueGetStreamCommand (index, offset, count, cancellationToken, progress, out var ic, out var ctx)) return new MemoryStream (); - Section section; - try { Engine.Run (ic); - ProcessGetStreamResponse (ic, ctx, index, out section); + return ProcessGetStreamResponse (ic, ctx, index); } finally { ctx.Dispose (); } - - return section.Stream; } /// @@ -4483,25 +4465,19 @@ public override Stream GetStream (int index, int offset, int count, Cancellation /// public override async Task GetStreamAsync (int index, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { - var ic = QueueGetStreamCommand (index, offset, count, cancellationToken, progress, out var ctx); - - if (ic == null) + if (!TryQueueGetStreamCommand (index, offset, count, cancellationToken, progress, out var ic, out var ctx)) return new MemoryStream (); - Section section; - try { await Engine.RunAsync (ic).ConfigureAwait (false); - ProcessGetStreamResponse (ic, ctx, index, out section); + return ProcessGetStreamResponse (ic, ctx, index); } finally { ctx.Dispose (); } - - return section.Stream; } - ImapCommand QueueGetStreamCommand (UniqueId uid, string section, CancellationToken cancellationToken, ITransferProgress progress, out FetchStreamContext ctx) + ImapCommand QueueGetStreamCommand (UniqueId uid, string section, CancellationToken cancellationToken, ITransferProgress? progress, out FetchStreamContext ctx) { if (!uid.IsValid) throw new ArgumentException ("The uid is invalid.", nameof (uid)); @@ -4521,12 +4497,14 @@ ImapCommand QueueGetStreamCommand (UniqueId uid, string section, CancellationTok return ic; } - void ProcessGetStreamResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId uid, string section, out Section s) + Stream ProcessGetStreamResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId uid, string section) { ProcessFetchResponse (ic); - if (!ctx.TryGetSection (uid, section, out s, true)) + if (!ctx.TryGetSection (uid, section, out var s, true)) throw new MessageNotFoundException ("The IMAP server did not return the requested stream."); + + return s.Stream; } /// @@ -4581,17 +4559,14 @@ void ProcessGetStreamResponse (ImapCommand ic, FetchStreamContext ctx, UniqueId public override Stream GetStream (UniqueId uid, string section, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetStreamCommand (uid, section, cancellationToken, progress, out var ctx); - Section s; try { Engine.Run (ic); - ProcessGetStreamResponse (ic, ctx, uid, section, out s); + return ProcessGetStreamResponse (ic, ctx, uid, section); } finally { ctx.Dispose (); } - - return s.Stream; } /// @@ -4646,20 +4621,17 @@ public override Stream GetStream (UniqueId uid, string section, CancellationToke public override async Task GetStreamAsync (UniqueId uid, string section, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetStreamCommand (uid, section, cancellationToken, progress, out var ctx); - Section s; try { await Engine.RunAsync (ic).ConfigureAwait (false); - ProcessGetStreamResponse (ic, ctx, uid, section, out s); + return ProcessGetStreamResponse (ic, ctx, uid, section); } finally { ctx.Dispose (); } - - return s.Stream; } - ImapCommand? QueueGetStreamCommand (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken, ITransferProgress progress, out FetchStreamContext? ctx) + bool TryQueueGetStreamCommand (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken, ITransferProgress? progress, [NotNullWhen (true)] out ImapCommand? ic, [NotNullWhen (true)] out FetchStreamContext? ctx) { if (!uid.IsValid) throw new ArgumentException ("The uid is invalid.", nameof (uid)); @@ -4677,18 +4649,20 @@ public override async Task GetStreamAsync (UniqueId uid, string section, if (count == 0) { ctx = null; - return null; + ic = null; + return false; } var range = string.Format (CultureInfo.InvariantCulture, "{0}.{1}", offset, count); var command = string.Format ("UID FETCH {0} (BODY.PEEK[{1}]<{2}>)\r\n", uid, section, range); - var ic = new ImapCommand (Engine, cancellationToken, this, command); + + ic = new ImapCommand (Engine, cancellationToken, this, command); ic.RegisterUntaggedHandler ("FETCH", FetchStreamHandler); ic.UserData = ctx = new FetchStreamContext (progress); Engine.QueueCommand (ic); - return ic; + return true; } /// @@ -4749,22 +4723,16 @@ public override async Task GetStreamAsync (UniqueId uid, string section, /// public override Stream GetStream (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { - var ic = QueueGetStreamCommand (uid, section, offset, count, cancellationToken, progress, out var ctx); - - if (ic == null) + if (!TryQueueGetStreamCommand (uid, section, offset, count, cancellationToken, progress, out var ic, out var ctx)) return new MemoryStream (); - Section s; - try { Engine.Run (ic); - ProcessGetStreamResponse (ic, ctx, uid, section, out s); + return ProcessGetStreamResponse (ic, ctx, uid, section); } finally { ctx.Dispose (); } - - return s.Stream; } /// @@ -4825,25 +4793,19 @@ public override Stream GetStream (UniqueId uid, string section, int offset, int /// public override async Task GetStreamAsync (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { - var ic = QueueGetStreamCommand (uid, section, offset, count, cancellationToken, progress, out var ctx); - - if (ic == null) + if (!TryQueueGetStreamCommand (uid, section, offset, count, cancellationToken, progress, out var ic, out var ctx)) return new MemoryStream (); - Section s; - try { await Engine.RunAsync (ic).ConfigureAwait (false); - ProcessGetStreamResponse (ic, ctx, uid, section, out s); + return ProcessGetStreamResponse (ic, ctx, uid, section); } finally { ctx.Dispose (); } - - return s.Stream; } - ImapCommand QueueGetStreamCommand (int index, string section, CancellationToken cancellationToken, ITransferProgress progress, out FetchStreamContext ctx) + ImapCommand QueueGetStreamCommand (int index, string section, CancellationToken cancellationToken, ITransferProgress? progress, out FetchStreamContext ctx) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); @@ -4864,12 +4826,14 @@ ImapCommand QueueGetStreamCommand (int index, string section, CancellationToken return ic; } - void ProcessGetStreamResponse (ImapCommand ic, FetchStreamContext ctx, int index, string section, out Section s) + Stream ProcessGetStreamResponse (ImapCommand ic, FetchStreamContext ctx, int index, string section) { ProcessFetchResponse (ic); - if (!ctx.TryGetSection (index, section, out s, true)) + if (!ctx.TryGetSection (index, section, out var sect, true)) throw new MessageNotFoundException ("The IMAP server did not return the requested stream."); + + return sect.Stream; } /// @@ -4921,17 +4885,14 @@ void ProcessGetStreamResponse (ImapCommand ic, FetchStreamContext ctx, int index public override Stream GetStream (int index, string section, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetStreamCommand (index, section, cancellationToken, progress, out var ctx); - Section s; try { Engine.Run (ic); - ProcessGetStreamResponse (ic, ctx, index, section, out s); + return ProcessGetStreamResponse (ic, ctx, index, section); } finally { ctx.Dispose (); } - - return s.Stream; } /// @@ -4983,20 +4944,17 @@ public override Stream GetStream (int index, string section, CancellationToken c public override async Task GetStreamAsync (int index, string section, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { var ic = QueueGetStreamCommand (index, section, cancellationToken, progress, out var ctx); - Section s; try { await Engine.RunAsync (ic).ConfigureAwait (false); - ProcessGetStreamResponse (ic, ctx, index, section, out s); + return ProcessGetStreamResponse (ic, ctx, index, section); } finally { ctx.Dispose (); } - - return s.Stream; } - ImapCommand? QueueGetStreamCommand (int index, string section, int offset, int count, CancellationToken cancellationToken, ITransferProgress progress, out FetchStreamContext? ctx) + bool TryQueueGetStreamCommand (int index, string section, int offset, int count, CancellationToken cancellationToken, ITransferProgress? progress, [NotNullWhen (true)] out ImapCommand? ic, [NotNullWhen (true)] out FetchStreamContext? ctx) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); @@ -5014,20 +4972,21 @@ public override async Task GetStreamAsync (int index, string section, Ca if (count == 0) { ctx = null; - return null; + ic = null; + return false; } var seqid = (index + 1).ToString (CultureInfo.InvariantCulture); var range = string.Format (CultureInfo.InvariantCulture, "{0}.{1}", offset, count); var command = string.Format ("FETCH {0} (BODY.PEEK[{1}]<{2}>)\r\n", seqid, section, range); - var ic = new ImapCommand (Engine, cancellationToken, this, command); + ic = new ImapCommand (Engine, cancellationToken, this, command); ic.RegisterUntaggedHandler ("FETCH", FetchStreamHandler); ic.UserData = ctx = new FetchStreamContext (progress); Engine.QueueCommand (ic); - return ic; + return true; } /// @@ -5087,22 +5046,16 @@ public override async Task GetStreamAsync (int index, string section, Ca /// public override Stream GetStream (int index, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { - var ic = QueueGetStreamCommand (index, section, offset, count, cancellationToken, progress, out var ctx); - - if (ic == null) + if (!TryQueueGetStreamCommand (index, section, offset, count, cancellationToken, progress, out var ic, out var ctx)) return new MemoryStream (); - Section s; - try { Engine.Run (ic); - ProcessGetStreamResponse (ic, ctx, index, section, out s); + return ProcessGetStreamResponse (ic, ctx, index, section); } finally { ctx.Dispose (); } - - return s.Stream; } /// @@ -5162,22 +5115,16 @@ public override Stream GetStream (int index, string section, int offset, int cou /// public override async Task GetStreamAsync (int index, string section, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress? progress = null) { - var ic = QueueGetStreamCommand (index, section, offset, count, cancellationToken, progress, out var ctx); - - if (ic == null) + if (!TryQueueGetStreamCommand (index, section, offset, count, cancellationToken, progress, out var ic, out var ctx)) return new MemoryStream (); - Section s; - try { await Engine.RunAsync (ic).ConfigureAwait (false); - ProcessGetStreamResponse (ic, ctx, index, section, out s); + return ProcessGetStreamResponse (ic, ctx, index, section); } finally { ctx.Dispose (); } - - return s.Stream; } class FetchStreamCallbackContext : FetchStreamContextBase diff --git a/MailKit/Net/Imap/ImapFolderFlags.cs b/MailKit/Net/Imap/ImapFolderFlags.cs index 778971461e..a2752514b8 100644 --- a/MailKit/Net/Imap/ImapFolderFlags.cs +++ b/MailKit/Net/Imap/ImapFolderFlags.cs @@ -31,6 +31,7 @@ using System.Globalization; using System.Threading.Tasks; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace MailKit.Net.Imap { @@ -252,7 +253,7 @@ public override async Task> StoreAsync (IList uids, IS return unmodified; } - ImapCommand? QueueStoreCommand (IList indexes, IStoreFlagsRequest request, CancellationToken cancellationToken) + bool TryQueueStoreCommand (IList indexes, IStoreFlagsRequest request, CancellationToken cancellationToken, [NotNullWhen (true)] out ImapCommand? ic) { if (indexes == null) throw new ArgumentNullException (nameof (indexes)); @@ -265,8 +266,10 @@ public override async Task> StoreAsync (IList uids, IS CheckState (true, true); + ic = null; + if (indexes.Count == 0) - return null; + return false; int numKeywords = request.Keywords != null ? request.Keywords.Count : 0; string action; @@ -274,13 +277,13 @@ public override async Task> StoreAsync (IList uids, IS switch (request.Action) { case StoreAction.Add: if ((request.Flags & SettableFlags) == 0 && numKeywords == 0) - return null; + return false; action = request.Silent ? "+FLAGS.SILENT" : "+FLAGS"; break; case StoreAction.Remove: if ((request.Flags & SettableFlags) == 0 && numKeywords == 0) - return null; + return false; action = request.Silent ? "-FLAGS.SILENT" : "-FLAGS"; break; @@ -305,7 +308,9 @@ public override async Task> StoreAsync (IList uids, IS ImapUtils.FormatFlagsList (command, request.Flags & PermanentFlags, request.Keywords != null ? request.Keywords.Count : 0); command.Append ("\r\n"); - return Engine.QueueCommand (cancellationToken, this, command.ToString (), keywordList); + ic = Engine.QueueCommand (cancellationToken, this, command.ToString (), keywordList); + + return true; } /// @@ -356,9 +361,7 @@ public override async Task> StoreAsync (IList uids, IS /// public override IList Store (IList indexes, IStoreFlagsRequest request, CancellationToken cancellationToken = default) { - var ic = QueueStoreCommand (indexes, request, cancellationToken); - - if (ic == null) + if (!TryQueueStoreCommand (indexes, request, cancellationToken, out var ic)) return Array.Empty (); Engine.Run (ic); @@ -416,9 +419,7 @@ public override IList Store (IList indexes, IStoreFlagsRequest request /// public override async Task> StoreAsync (IList indexes, IStoreFlagsRequest request, CancellationToken cancellationToken = default) { - var ic = QueueStoreCommand (indexes, request, cancellationToken); - - if (ic == null) + if (!TryQueueStoreCommand (indexes, request, cancellationToken, out var ic)) return Array.Empty (); await Engine.RunAsync (ic).ConfigureAwait (false); @@ -649,7 +650,7 @@ public override async Task> StoreAsync (IList uids, IS return unmodified; } - ImapCommand? QueueStoreCommand (IList indexes, IStoreLabelsRequest request, CancellationToken cancellationToken) + bool TryQueueStoreCommand (IList indexes, IStoreLabelsRequest request, CancellationToken cancellationToken, [NotNullWhen (true)] out ImapCommand? ic) { if (indexes == null) throw new ArgumentNullException (nameof (indexes)); @@ -662,21 +663,23 @@ public override async Task> StoreAsync (IList uids, IS CheckState (true, true); + ic = null; + if (indexes.Count == 0) - return null; + return false; string action; switch (request.Action) { case StoreAction.Add: if (request.Labels == null || request.Labels.Count == 0) - return null; + return false; action = request.Silent ? "+X-GM-LABELS.SILENT" : "+X-GM-LABELS"; break; case StoreAction.Remove: if (request.Labels == null || request.Labels.Count == 0) - return null; + return false; action = request.Silent ? "-X-GM-LABELS.SILENT" : "-X-GM-LABELS"; break; @@ -702,7 +705,9 @@ public override async Task> StoreAsync (IList uids, IS AppendLabelList (command, request.Labels, args); command.Append ("\r\n"); - return Engine.QueueCommand (cancellationToken, this, command.ToString (), args.ToArray ()); + ic = Engine.QueueCommand (cancellationToken, this, command.ToString (), args.ToArray ()); + + return true; } /// @@ -753,9 +758,7 @@ public override async Task> StoreAsync (IList uids, IS /// public override IList Store (IList indexes, IStoreLabelsRequest request, CancellationToken cancellationToken = default) { - var ic = QueueStoreCommand (indexes, request, cancellationToken); - - if (ic == null) + if (!TryQueueStoreCommand (indexes, request, cancellationToken, out var ic)) return Array.Empty (); Engine.Run (ic); @@ -813,9 +816,7 @@ public override IList Store (IList indexes, IStoreLabelsRequest reques /// public override async Task> StoreAsync (IList indexes, IStoreLabelsRequest request, CancellationToken cancellationToken = default) { - var ic = QueueStoreCommand (indexes, request, cancellationToken); - - if (ic == null) + if (!TryQueueStoreCommand (indexes, request, cancellationToken, out var ic)) return Array.Empty (); await Engine.RunAsync (ic).ConfigureAwait (false); diff --git a/MailKit/Net/Imap/ImapFolderSearch.cs b/MailKit/Net/Imap/ImapFolderSearch.cs index bbf8c6dcc1..64efee4ed4 100644 --- a/MailKit/Net/Imap/ImapFolderSearch.cs +++ b/MailKit/Net/Imap/ImapFolderSearch.cs @@ -509,7 +509,7 @@ static void ParseESearchResults (ImapEngine engine, ImapCommand ic, SearchResult if (token.Type == ImapTokenType.Eoln) { // unget the eoln token - engine.Stream.UngetToken (token); + engine.Stream!.UngetToken (token); break; } @@ -640,7 +640,7 @@ static async Task ParseESearchResultsAsync (ImapEngine engine, ImapCommand ic, S if (token.Type == ImapTokenType.Eoln) { // unget the eoln token - engine.Stream.UngetToken (token); + engine.Stream!.UngetToken (token); break; } diff --git a/MailKit/Net/Imap/ImapIdleContext.cs b/MailKit/Net/Imap/ImapIdleContext.cs index 7eec4ef265..d3e8090758 100644 --- a/MailKit/Net/Imap/ImapIdleContext.cs +++ b/MailKit/Net/Imap/ImapIdleContext.cs @@ -124,7 +124,7 @@ void IdleComplete () { if (Engine.State == ImapEngineState.Idle) { try { - Engine.Stream.Write (DoneCommand, 0, DoneCommand.Length, CancellationToken); + Engine.Stream!.Write (DoneCommand, 0, DoneCommand.Length, CancellationToken); Engine.Stream.Flush (CancellationToken); } catch { return; diff --git a/MailKit/ReplaceRequest.cs b/MailKit/ReplaceRequest.cs index b4baa9b4a8..b6b42d7e50 100644 --- a/MailKit/ReplaceRequest.cs +++ b/MailKit/ReplaceRequest.cs @@ -115,7 +115,7 @@ public ReplaceRequest (MimeMessage message, MessageFlags flags, IEnumerable /// /// The destination folder. - public IMailFolder Destination { + public IMailFolder? Destination { get; set; } } From 05d950e6897c5c2f1bcf3d019cc8bd4fa3ae6b88 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Fri, 20 Feb 2026 20:50:40 -0500 Subject: [PATCH 11/44] Fixed up NTLM for nullability --- MailKit/Security/Ntlm/MD4.cs | 39 ++++++------------- .../Security/Ntlm/NtlmAuthenticateMessage.cs | 33 ++++++++++------ MailKit/Security/Ntlm/NtlmMessageBase.cs | 2 +- MailKit/Security/Ntlm/NtlmUtils.cs | 7 ++-- MailKit/Security/SaslMechanismNtlm.cs | 8 ++-- 5 files changed, 42 insertions(+), 47 deletions(-) diff --git a/MailKit/Security/Ntlm/MD4.cs b/MailKit/Security/Ntlm/MD4.cs index e8f5c0bd30..7ae9aaddaf 100644 --- a/MailKit/Security/Ntlm/MD4.cs +++ b/MailKit/Security/Ntlm/MD4.cs @@ -47,12 +47,12 @@ sealed class MD4 : IDisposable const int S33 = 11; const int S34 = 15; + readonly byte[] buffered; + readonly uint[] state; + readonly uint[] count; + readonly uint[] x; + byte[]? hashValue; bool disposed; - byte[] hashValue; - byte[] buffered; - uint[] state; - uint[] count; - uint[] x; public MD4 () { @@ -155,7 +155,7 @@ static byte[] Padding (int length) return padding; } - return null; + return Array.Empty (); } // F, G and H are basic MD4 functions. @@ -379,26 +379,12 @@ public byte[] TransformFinalBlock (byte[] inputBuffer, int inputOffset, int inpu void Dispose (bool disposing) { - if (disposing) { - if (buffered != null) { - Array.Clear (buffered, 0, buffered.Length); - buffered = null; - } - - if (state != null) { - Array.Clear (state, 0, state.Length); - state = null; - } - - if (count != null) { - Array.Clear (count, 0, count.Length); - count = null; - } - - if (x != null) { - Array.Clear (x, 0, x.Length); - x = null; - } + if (disposing && !disposed) { + Array.Clear (buffered, 0, buffered.Length); + Array.Clear (state, 0, state.Length); + Array.Clear (count, 0, count.Length); + Array.Clear (x, 0, x.Length); + disposed = true; } } @@ -406,7 +392,6 @@ public void Dispose () { Dispose (true); GC.SuppressFinalize (this); - disposed = true; } } } diff --git a/MailKit/Security/Ntlm/NtlmAuthenticateMessage.cs b/MailKit/Security/Ntlm/NtlmAuthenticateMessage.cs index a797f0b7d1..2acb199936 100644 --- a/MailKit/Security/Ntlm/NtlmAuthenticateMessage.cs +++ b/MailKit/Security/Ntlm/NtlmAuthenticateMessage.cs @@ -28,14 +28,15 @@ using System; using System.Text; +using System.Diagnostics.CodeAnalysis; namespace MailKit.Security.Ntlm { class NtlmAuthenticateMessage : NtlmMessageBase { static readonly byte[] Z16 = new byte[16]; - readonly NtlmNegotiateMessage negotiate; - readonly NtlmChallengeMessage challenge; + readonly NtlmNegotiateMessage? negotiate; + readonly NtlmChallengeMessage? challenge; byte[] clientChallenge; public NtlmAuthenticateMessage (NtlmNegotiateMessage negotiate, NtlmChallengeMessage challenge, string userName, string password, string domain, string workstation) : base (3) @@ -64,6 +65,8 @@ public NtlmAuthenticateMessage (NtlmNegotiateMessage negotiate, NtlmChallengeMes } else if (challenge.TargetInfo != null) { // The server is not domain-joined, so the TargetName will be the machine name of the server. Domain = challenge.TargetInfo.DomainName; + } else { + Domain = string.Empty; } Workstation = workstation; @@ -95,16 +98,18 @@ public NtlmAuthenticateMessage (NtlmNegotiateMessage negotiate, NtlmChallengeMes OSVersion = negotiate.OSVersion ?? OSVersion; } + // Note: This .ctor is for debugging purposes only. It allows us to decode an NTLM AUTHENTICATE_MESSAGE without having to go through the entire NTLM authentication process. public NtlmAuthenticateMessage (byte[] message, int startIndex, int length) : base (3) { Decode (message, startIndex, length); + clientChallenge = Array.Empty (); challenge = null; + Password = string.Empty; } ~NtlmAuthenticateMessage () { - if (clientChallenge != null) - Array.Clear (clientChallenge, 0, clientChallenge.Length); + Array.Clear (clientChallenge, 0, clientChallenge.Length); if (LmChallengeResponse != null) Array.Clear (LmChallengeResponse, 0, LmChallengeResponse.Length); @@ -122,7 +127,7 @@ public NtlmAuthenticateMessage (byte[] message, int startIndex, int length) : ba /// /// This is only used for unit testing purposes. /// - internal byte[] ClientChallenge { + internal byte[]? ClientChallenge { get { return clientChallenge; } set { if (value == null) @@ -159,26 +164,27 @@ public string UserName { get; private set; } - public byte[] Mic { + public byte[]? Mic { get; private set; } - public byte[] LmChallengeResponse { + public byte[]? LmChallengeResponse { get; private set; } - public byte[] NtChallengeResponse { + public byte[]? NtChallengeResponse { get; private set; } - public byte[] ExportedSessionKey { + public byte[]? ExportedSessionKey { get; private set; } - public byte[] EncryptedRandomSessionKey { + public byte[]? EncryptedRandomSessionKey { get; private set; } + [MemberNotNull (nameof (LmChallengeResponse), nameof (NtChallengeResponse), nameof (Domain), nameof (UserName), nameof (Workstation), nameof (EncryptedRandomSessionKey))] void Decode (byte[] message, int startIndex, int length) { int payloadOffset = length; @@ -259,8 +265,11 @@ byte[] EncodeString (string text) return encoding.GetBytes (text); } - public void ComputeNtlmV2 (string targetName, bool unverifiedTargetName, byte[] channelBinding) + public void ComputeNtlmV2 (string? targetName, bool unverifiedTargetName, byte[]? channelBinding) { + if (challenge == null || negotiate == null) + return; + var targetInfo = new NtlmTargetInfo (); int avFlags = 0; @@ -420,7 +429,7 @@ public override byte[] Encode () message[62] = (byte)((uint) Flags >> 16); message[63] = (byte)((uint) Flags >> 24); - if ((challenge.Flags & NtlmFlags.NegotiateVersion) != 0 && OSVersion != null) { + if (challenge != null && (challenge.Flags & NtlmFlags.NegotiateVersion) != 0 && OSVersion != null) { message[64] = (byte) OSVersion.Major; message[65] = (byte) OSVersion.Minor; message[66] = (byte) OSVersion.Build; diff --git a/MailKit/Security/Ntlm/NtlmMessageBase.cs b/MailKit/Security/Ntlm/NtlmMessageBase.cs index afba8e311d..e9dd66bcaa 100644 --- a/MailKit/Security/Ntlm/NtlmMessageBase.cs +++ b/MailKit/Security/Ntlm/NtlmMessageBase.cs @@ -43,7 +43,7 @@ public NtlmFlags Flags { get; protected set; } - public Version OSVersion { + public Version? OSVersion { get; protected set; } diff --git a/MailKit/Security/Ntlm/NtlmUtils.cs b/MailKit/Security/Ntlm/NtlmUtils.cs index 0b92b1fccd..a2453e71c3 100644 --- a/MailKit/Security/Ntlm/NtlmUtils.cs +++ b/MailKit/Security/Ntlm/NtlmUtils.cs @@ -43,6 +43,7 @@ static class NtlmUtils //static readonly byte[] SealKeySuffix56 = new byte[] { 0xa0 }; static readonly byte[] Responserversion = new byte[] { 1 }; static readonly byte[] HiResponserversion = new byte[] { 1 }; + static readonly byte[] Z0 = Array.Empty (); static readonly byte[] Z24 = new byte[24]; static readonly byte[] Z6 = new byte[6]; static readonly byte[] Z4 = new byte[4]; @@ -104,7 +105,7 @@ public static byte[] HMACMD5 (byte[] key, params byte[][] values) md5.TransformFinalBlock (values[i], 0, values[i].Length); - return md5.Hash; + return md5.Hash!; } } @@ -204,13 +205,13 @@ static byte[] NTOWFv2 (string domain, string userName, string password) return responseKey; } - public static void ComputeNtlmV2 (NtlmChallengeMessage type2, string domain, string userName, string password, byte[] targetInfo, byte[] clientChallenge, long? time, out byte[] ntChallengeResponse, out byte[] lmChallengeResponse, out byte[] sessionBaseKey) + public static void ComputeNtlmV2 (NtlmChallengeMessage type2, string domain, string userName, string password, byte[] targetInfo, byte[] clientChallenge, long? time, out byte[]? ntChallengeResponse, out byte[] lmChallengeResponse, out byte[] sessionBaseKey) { if (userName.Length == 0 && password.Length == 0) { // Special case for anonymous authentication ntChallengeResponse = null; lmChallengeResponse = Z1; - sessionBaseKey = null; + sessionBaseKey = Z0; return; } diff --git a/MailKit/Security/SaslMechanismNtlm.cs b/MailKit/Security/SaslMechanismNtlm.cs index 705df44cf0..591eaacf54 100644 --- a/MailKit/Security/SaslMechanismNtlm.cs +++ b/MailKit/Security/SaslMechanismNtlm.cs @@ -62,7 +62,7 @@ enum LoginState { Challenge } - NtlmNegotiateMessage negotiate; + NtlmNegotiateMessage? negotiate; bool negotiatedChannelBinding; LoginState state; @@ -124,7 +124,7 @@ public SaslMechanismNtlm (string userName, string password) : base (userName, pa /// /// This is only used for unit testing purposes. /// - internal byte[] Nonce { + internal byte[]? Nonce { get; set; } @@ -226,7 +226,7 @@ public string Workstation { /// This value is optional. /// /// The service principal name (SPN) of the service that the client wishes to authenticate with. - public string ServicePrincipalName { + public string? ServicePrincipalName { get; set; } @@ -308,7 +308,7 @@ public bool IsUnverifiedServicePrincipalName { NtlmAuthenticateMessage GetChallengeResponse (string domain, string userName, string password, byte[] token, int startIndex, int length) { var challenge = new NtlmChallengeMessage (token, startIndex, length); - var authenticate = new NtlmAuthenticateMessage (negotiate, challenge, userName, password, domain, Workstation) { + var authenticate = new NtlmAuthenticateMessage (negotiate!, challenge, userName, password, domain, Workstation) { ClientChallenge = Nonce, Timestamp = Timestamp }; From 305177473b089a2812b988373fd4573fcb65662f Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Fri, 20 Feb 2026 21:16:59 -0500 Subject: [PATCH 12/44] Fixed MessageSorter and MessageThreader classes for nullability --- MailKit/MessageSorter.cs | 32 ++++++++-------- MailKit/MessageThread.cs | 4 +- MailKit/MessageThreader.cs | 77 +++++++++++++++++++++----------------- 3 files changed, 60 insertions(+), 53 deletions(-) diff --git a/MailKit/MessageSorter.cs b/MailKit/MessageSorter.cs index 01fc2fcd7e..58c3839cd6 100644 --- a/MailKit/MessageSorter.cs +++ b/MailKit/MessageSorter.cs @@ -93,7 +93,7 @@ static int CompareMailboxAddresses (InternetAddressList list1, InternetAddressLi return n1 ? 1 : (n2 ? -1 : 0); } - public int Compare (T x, T y) + public int Compare (T? x, T? y) { int cmp = 0; @@ -102,8 +102,8 @@ public int Compare (T x, T y) case OrderByType.Annotation: var annotation = (OrderByAnnotation) orderBy[i]; - var xannotation = x.Annotations?.FirstOrDefault (a => a.Entry == annotation.Entry); - var yannotation = y.Annotations?.FirstOrDefault (a => a.Entry == annotation.Entry); + var xannotation = x!.Annotations?.FirstOrDefault (a => a.Entry == annotation.Entry); + var yannotation = y!.Annotations?.FirstOrDefault (a => a.Entry == annotation.Entry); var xvalue = xannotation?.Properties[annotation.Attribute] ?? string.Empty; var yvalue = yannotation?.Properties[annotation.Attribute] ?? string.Empty; @@ -111,43 +111,43 @@ public int Compare (T x, T y) cmp = string.Compare (xvalue, yvalue, StringComparison.OrdinalIgnoreCase); break; case OrderByType.Arrival: - cmp = x.Index.CompareTo (y.Index); + cmp = x!.Index.CompareTo (y!.Index); break; case OrderByType.Cc: - cmp = CompareMailboxAddresses (x.Envelope.Cc, y.Envelope.Cc); + cmp = CompareMailboxAddresses (x!.Envelope!.Cc, y!.Envelope!.Cc); break; case OrderByType.Date: - cmp = x.Date.CompareTo (y.Date); + cmp = x!.Date.CompareTo (y!.Date); break; case OrderByType.DisplayFrom: - cmp = CompareDisplayNames (x.Envelope.From, y.Envelope.From); + cmp = CompareDisplayNames (x!.Envelope!.From, y!.Envelope!.From); break; case OrderByType.From: - cmp = CompareMailboxAddresses (x.Envelope.From, y.Envelope.From); + cmp = CompareMailboxAddresses (x!.Envelope!.From, y!.Envelope!.From); break; case OrderByType.ModSeq: - var xmodseq = x.ModSeq ?? 0; - var ymodseq = y.ModSeq ?? 0; + var xmodseq = x!.ModSeq ?? 0; + var ymodseq = y!.ModSeq ?? 0; cmp = xmodseq.CompareTo (ymodseq); break; case OrderByType.Size: - var xsize = x.Size ?? 0; - var ysize = y.Size ?? 0; + var xsize = x!.Size ?? 0; + var ysize = y!.Size ?? 0; cmp = xsize.CompareTo (ysize); break; case OrderByType.Subject: - var xsubject = x.Envelope.Subject ?? string.Empty; - var ysubject = y.Envelope.Subject ?? string.Empty; + var xsubject = x!.Envelope!.Subject ?? string.Empty; + var ysubject = y!.Envelope!.Subject ?? string.Empty; cmp = string.Compare (xsubject, ysubject, StringComparison.OrdinalIgnoreCase); break; case OrderByType.DisplayTo: - cmp = CompareDisplayNames (x.Envelope.To, y.Envelope.To); + cmp = CompareDisplayNames (x!.Envelope!.To, y!.Envelope!.To); break; case OrderByType.To: - cmp = CompareMailboxAddresses (x.Envelope.To, y.Envelope.To); + cmp = CompareMailboxAddresses (x!.Envelope!.To, y!.Envelope!.To); break; } diff --git a/MailKit/MessageThread.cs b/MailKit/MessageThread.cs index 3c409d0a2c..1c6168fba7 100644 --- a/MailKit/MessageThread.cs +++ b/MailKit/MessageThread.cs @@ -55,7 +55,7 @@ public MessageThread (UniqueId? uid) /// Creates a new message thread node. /// /// The message summary. - public MessageThread (IMessageSummary message) + public MessageThread (IMessageSummary? message) { Children = new List (); if (message != null && message.UniqueId.IsValid) @@ -76,7 +76,7 @@ public MessageThread (IMessageSummary message) /// methods will always be . /// /// The message summary. - public IMessageSummary Message { + public IMessageSummary? Message { get; private set; } diff --git a/MailKit/MessageThreader.cs b/MailKit/MessageThreader.cs index 41ec3cc30b..97eb3fa30c 100644 --- a/MailKit/MessageThreader.cs +++ b/MailKit/MessageThreader.cs @@ -27,6 +27,7 @@ using System; using System.Text; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using MimeKit; using MimeKit.Utils; @@ -51,14 +52,15 @@ public static class MessageThreader internal class ThreadableNode : IMessageSummary { public readonly List Children = new List (); - public IMessageSummary Message; - public ThreadableNode Parent; + public IMessageSummary? Message; + public ThreadableNode? Parent; - public ThreadableNode (IMessageSummary message) + public ThreadableNode (IMessageSummary? message) { Message = message; } + [MemberNotNullWhen (true, nameof (Parent))] public bool HasParent { get { return Parent != null; } } @@ -67,25 +69,25 @@ public bool HasChildren { get { return Children.Count > 0; } } - public IMailFolder Folder => null; + public IMailFolder? Folder => null; public MessageSummaryItems Fields { get { return MessageSummaryItems.UniqueId | MessageSummaryItems.Envelope | MessageSummaryItems.ModSeq | MessageSummaryItems.Size; } } - public BodyPart Body => null; + public BodyPart? Body => null; - public BodyPartText TextBody => null; + public BodyPartText? TextBody => null; - public BodyPartText HtmlBody => null; + public BodyPartText? HtmlBody => null; - public IEnumerable BodyParts => null; + public IEnumerable BodyParts => Array.Empty (); - public IEnumerable Attachments => null; + public IEnumerable Attachments => Array.Empty (); - public string PreviewText => null; + public string? PreviewText => null; - public Envelope Envelope { + public Envelope? Envelope { get { return Message != null ? Message.Envelope : Children[0].Envelope; } } @@ -103,13 +105,15 @@ public bool IsReply { public MessageFlags? Flags => null; - public IReadOnlySetOfStrings Keywords => null; + public IReadOnlySetOfStrings Keywords { + get { return new HashSet (); } + } - public IReadOnlyList Annotations { + public IReadOnlyList? Annotations { get { return Message != null ? Message.Annotations : Children[0].Annotations; } } - public HeaderList Headers => null; + public HeaderList? Headers => null; public DateTimeOffset? InternalDate => null; @@ -123,13 +127,13 @@ public ulong? ModSeq { get { return Message != null ? Message.ModSeq : Children[0].ModSeq; } } - public MessageIdList References { + public MessageIdList? References { get { return Message != null ? Message.References : Children[0].References; } } - public string EmailId => null; + public string? EmailId => null; - public string ThreadId => null; + public string? ThreadId => null; public UniqueId UniqueId { get { return Message != null ? Message.UniqueId : Children[0].UniqueId; } @@ -143,7 +147,7 @@ public int Index { public ulong? GMailThreadId => null; - public IList GMailLabels => null; + public IList? GMailLabels => null; } static Dictionary CreateIdTable (IEnumerable messages) @@ -159,7 +163,7 @@ static Dictionary CreateIdTable (IEnumerable CreateIdTable (IEnumerable (StringComparer.OrdinalIgnoreCase); - ThreadableNode match; + ThreadableNode? match; int count = 0; for (int i = 0; i < root.Children.Count; i++) { @@ -308,7 +315,7 @@ static void GroupBySubject (ThreadableNode root) // is not, make the current message a child of the message in the subject // table (a sibling of its children). match.Children.Add (current); - } else if (current.Message.IsReply && !match.Message.IsReply) { + } else if (current.IsReply && !match.IsReply) { // If the current message is a reply or forward and the message in the // subject table is not, make the current message a child of the message // in the subject table (a sibling of its children). From 7d2a63cbf0682d8935ba4125cba2f5fce95a3aec Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Fri, 20 Feb 2026 21:24:10 -0500 Subject: [PATCH 13/44] More NTLM fixes for nullability --- MailKit/Security/Ntlm/NtlmNegotiateMessage.cs | 20 ++++++++++--------- MailKit/Security/Ntlm/NtlmSingleHostData.cs | 2 ++ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/MailKit/Security/Ntlm/NtlmNegotiateMessage.cs b/MailKit/Security/Ntlm/NtlmNegotiateMessage.cs index a880d70dae..9bab1c588b 100644 --- a/MailKit/Security/Ntlm/NtlmNegotiateMessage.cs +++ b/MailKit/Security/Ntlm/NtlmNegotiateMessage.cs @@ -31,6 +31,7 @@ using System; using System.Text; +using System.Diagnostics.CodeAnalysis; namespace MailKit.Security.Ntlm { class NtlmNegotiateMessage : NtlmMessageBase @@ -38,9 +39,9 @@ class NtlmNegotiateMessage : NtlmMessageBase // System.Net.Mail seems to default to: NtlmFlags.Negotiate56 | NtlmFlags.NegotiateUnicode | NtlmFlags.NegotiateOem | NtlmFlags.RequestTarget | NtlmFlags.NegotiateNtlm | NtlmFlags.NegotiateAlwaysSign | NtlmFlags.NegotiateExtendedSessionSecurity | NtlmFlags.NegotiateVersion | NtlmFlags.Negotiate128 internal const NtlmFlags DefaultFlags = NtlmFlags.Negotiate56 | NtlmFlags.NegotiateUnicode | NtlmFlags.NegotiateOem | NtlmFlags.RequestTarget | NtlmFlags.NegotiateNtlm | NtlmFlags.NegotiateAlwaysSign | NtlmFlags.NegotiateExtendedSessionSecurity | NtlmFlags.Negotiate128; - byte[] cached; + byte[]? cached; - public NtlmNegotiateMessage (NtlmFlags flags, string domain, string workstation, Version osVersion = null) : base (1) + public NtlmNegotiateMessage (NtlmFlags flags, string? domain, string? workstation, Version? osVersion = null) : base (1) { Flags = flags & ~(NtlmFlags.NegotiateDomainSupplied | NtlmFlags.NegotiateWorkstationSupplied | NtlmFlags.NegotiateVersion); @@ -55,21 +56,21 @@ public NtlmNegotiateMessage (NtlmFlags flags, string domain, string workstation, } else { if (!string.IsNullOrEmpty (workstation)) { Flags |= NtlmFlags.NegotiateWorkstationSupplied; - Workstation = workstation.ToUpperInvariant (); + Workstation = workstation!.ToUpperInvariant (); } else { Workstation = string.Empty; } if (!string.IsNullOrEmpty (domain)) { Flags |= NtlmFlags.NegotiateDomainSupplied; - Domain = domain.ToUpperInvariant (); + Domain = domain!.ToUpperInvariant (); } else { Domain = string.Empty; } } } - public NtlmNegotiateMessage (string domain = null, string workstation = null, Version osVersion = null) : this (DefaultFlags, domain, workstation, osVersion) + public NtlmNegotiateMessage (string? domain = null, string? workstation = null, Version? osVersion = null) : this (DefaultFlags, domain, workstation, osVersion) { } @@ -89,6 +90,7 @@ public string Workstation { get; private set; } + [MemberNotNull (nameof (Domain), nameof (Workstation))] void Decode (byte[] message, int startIndex, int length) { ValidateArguments (message, startIndex, length); @@ -148,10 +150,10 @@ public override byte[] Encode () message[29] = (byte)(workstationOffset >> 8); if ((Flags & NtlmFlags.NegotiateVersion) != 0) { - message[32] = (byte) OSVersion.Major; - message[33] = (byte) OSVersion.Minor; - message[34] = (byte) OSVersion.Build; - message[35] = (byte)(OSVersion.Build >> 8); + message[32] = (byte) OSVersion!.Major; + message[33] = (byte) OSVersion!.Minor; + message[34] = (byte) OSVersion!.Build; + message[35] = (byte)(OSVersion!.Build >> 8); message[36] = 0x00; message[37] = 0x00; message[38] = 0x00; diff --git a/MailKit/Security/Ntlm/NtlmSingleHostData.cs b/MailKit/Security/Ntlm/NtlmSingleHostData.cs index 456f5947e1..325cbefc68 100644 --- a/MailKit/Security/Ntlm/NtlmSingleHostData.cs +++ b/MailKit/Security/Ntlm/NtlmSingleHostData.cs @@ -27,6 +27,7 @@ // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/b38c36ed-2804-4868-a9ff-8dd3182128e4 using System; +using System.Diagnostics.CodeAnalysis; namespace MailKit.Security.Ntlm { /// @@ -120,6 +121,7 @@ public int Size { get; private set; } + [MemberNotNull (nameof (CustomData), nameof (MachineId))] void Decode (byte[] buffer, int startIndex, int length) { if (buffer == null) From 7ec095ebc425a83ddd198ab0be0e3b242cd9d7be Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Fri, 20 Feb 2026 22:01:16 -0500 Subject: [PATCH 14/44] More IMAP nullability fixes --- MailKit/AnnotationEntry.cs | 24 ++--- MailKit/AnnotationsChangedEventArgs.cs | 11 --- MailKit/AppendRequest.cs | 4 +- MailKit/IAppendRequest.cs | 4 +- MailKit/MessageLabelsChangedEventArgs.cs | 14 --- MailKit/Metadata.cs | 1 + MailKit/ModSeqChangedEventArgs.cs | 13 +-- MailKit/Net/Imap/ImapFolder.cs | 15 +-- MailKit/Net/Imap/ImapFolderFetch.cs | 118 +++++++++-------------- MailKit/Net/Imap/ImapLiteral.cs | 8 +- UnitTests/EventArgsTests.cs | 13 --- 11 files changed, 79 insertions(+), 146 deletions(-) diff --git a/MailKit/AnnotationEntry.cs b/MailKit/AnnotationEntry.cs index 7ca14d792d..d1f2a14f23 100644 --- a/MailKit/AnnotationEntry.cs +++ b/MailKit/AnnotationEntry.cs @@ -172,8 +172,12 @@ static void ValidatePartSpecifier (string partSpecifier) throw new ArgumentException ("Invalid part-specifier.", nameof (partSpecifier)); } - AnnotationEntry () + AnnotationEntry (string? partSpecifier, string entry, string path, AnnotationScope scope) { + PartSpecifier = partSpecifier; + Entry = entry; + Path = path; + Scope = scope; } /// @@ -199,6 +203,7 @@ public AnnotationEntry (string path, AnnotationScope scope = AnnotationScope.Bot case AnnotationScope.Shared: Entry = path + ".shared"; break; default: Entry = path; break; } + PartSpecifier = null; Path = path; Scope = scope; @@ -233,6 +238,7 @@ public AnnotationEntry (string partSpecifier, string path, AnnotationScope scope case AnnotationScope.Shared: Entry = string.Format ("/{0}{1}.shared", partSpecifier, path); break; default: Entry = string.Format ("/{0}{1}", partSpecifier, path); break; } + PartSpecifier = partSpecifier; Path = path; Scope = scope; @@ -267,6 +273,7 @@ public AnnotationEntry (BodyPart part, string path, AnnotationScope scope = Anno case AnnotationScope.Shared: Entry = string.Format ("/{0}{1}.shared", part.PartSpecifier, path); break; default: Entry = string.Format ("/{0}{1}", part.PartSpecifier, path); break; } + PartSpecifier = part.PartSpecifier; Path = path; Scope = scope; @@ -290,7 +297,7 @@ public string Entry { /// Gets the part-specifier component of the annotation entry. /// /// The part-specifier. - public string PartSpecifier { + public string? PartSpecifier { get; private set; } @@ -327,7 +334,7 @@ public AnnotationScope Scope { /// The to compare with the current . /// if the specified is equal to the current /// ; otherwise, . - public bool Equals (AnnotationEntry other) + public bool Equals (AnnotationEntry? other) { return other?.Entry == Entry; } @@ -371,7 +378,7 @@ public bool Equals (AnnotationEntry other) /// The to compare with the current . /// if the specified is equal to the current /// ; otherwise, . - public override bool Equals (object obj) + public override bool Equals (object? obj) { return obj is AnnotationEntry entry && entry.Entry == Entry; } @@ -427,7 +434,7 @@ public static AnnotationEntry Parse (string entry) var scope = AnnotationScope.Both; int startIndex = 0, endIndex; - string partSpecifier = null; + string? partSpecifier = null; var component = 0; var pc = entry[0]; string path; @@ -497,12 +504,7 @@ public static AnnotationEntry Parse (string entry) path = entry.Substring (startIndex, endIndex - startIndex); - return new AnnotationEntry { - PartSpecifier = partSpecifier, - Entry = entry, - Path = path, - Scope = scope - }; + return new AnnotationEntry (partSpecifier, entry, path, scope); } internal static AnnotationEntry Create (string entry) diff --git a/MailKit/AnnotationsChangedEventArgs.cs b/MailKit/AnnotationsChangedEventArgs.cs index e50deb4d10..25144ad860 100644 --- a/MailKit/AnnotationsChangedEventArgs.cs +++ b/MailKit/AnnotationsChangedEventArgs.cs @@ -38,17 +38,6 @@ namespace MailKit { /// public class AnnotationsChangedEventArgs : MessageEventArgs { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The message index. - internal AnnotationsChangedEventArgs (int index) : base (index) - { - } - /// /// Initializes a new instance of the class. /// diff --git a/MailKit/AppendRequest.cs b/MailKit/AppendRequest.cs index 4e88b8d1a3..af3d9457a4 100644 --- a/MailKit/AppendRequest.cs +++ b/MailKit/AppendRequest.cs @@ -165,7 +165,7 @@ public MessageFlags Flags { /// Gets or sets the keywords that should be set on the message. /// /// The keywords. - public ISet Keywords { + public ISet? Keywords { get; set; } @@ -192,7 +192,7 @@ public DateTimeOffset? InternalDate { /// /// /// The list of annotations. - public IList Annotations { + public IList? Annotations { get; set; } diff --git a/MailKit/IAppendRequest.cs b/MailKit/IAppendRequest.cs index c63ffa0676..7b83ab0c77 100644 --- a/MailKit/IAppendRequest.cs +++ b/MailKit/IAppendRequest.cs @@ -63,7 +63,7 @@ public interface IAppendRequest /// Gets or sets the keywords that should be set on the message. /// /// The keywords. - ISet Keywords { get; set; } + ISet? Keywords { get; set; } /// /// Get or set the timestamp that should be used by folder as the . @@ -86,7 +86,7 @@ public interface IAppendRequest /// /// /// The list of annotations. - IList Annotations { get; set; } + IList? Annotations { get; set; } /// /// Get or set the transfer progress reporting mechanism. diff --git a/MailKit/MessageLabelsChangedEventArgs.cs b/MailKit/MessageLabelsChangedEventArgs.cs index 3a12ef7bbc..3eb0c31104 100644 --- a/MailKit/MessageLabelsChangedEventArgs.cs +++ b/MailKit/MessageLabelsChangedEventArgs.cs @@ -37,20 +37,6 @@ namespace MailKit { /// public class MessageLabelsChangedEventArgs : MessageEventArgs { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The message index. - /// - /// is out of range. - /// - internal MessageLabelsChangedEventArgs (int index) : base (index) - { - } - /// /// Initializes a new instance of the class. /// diff --git a/MailKit/Metadata.cs b/MailKit/Metadata.cs index 7d9529cfd9..521fc4ee53 100644 --- a/MailKit/Metadata.cs +++ b/MailKit/Metadata.cs @@ -45,6 +45,7 @@ public class Metadata /// The metadata value. public Metadata (MetadataTag tag, string value) { + EncodedName = string.Empty; Value = value; Tag = tag; } diff --git a/MailKit/ModSeqChangedEventArgs.cs b/MailKit/ModSeqChangedEventArgs.cs index bbf400402d..5451baa7e9 100644 --- a/MailKit/ModSeqChangedEventArgs.cs +++ b/MailKit/ModSeqChangedEventArgs.cs @@ -34,17 +34,6 @@ namespace MailKit /// public class ModSeqChangedEventArgs : MessageEventArgs { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The message index. - internal ModSeqChangedEventArgs (int index) : base (index) - { - } - /// /// Initializes a new instance of the class. /// @@ -80,7 +69,7 @@ public ModSeqChangedEventArgs (int index, UniqueId uid, ulong modseq) : base (in /// /// The mod-sequence value. public ulong ModSeq { - get; internal set; + get; private set; } } } diff --git a/MailKit/Net/Imap/ImapFolder.cs b/MailKit/Net/Imap/ImapFolder.cs index 726ad175d3..035bafd077 100644 --- a/MailKit/Net/Imap/ImapFolder.cs +++ b/MailKit/Net/Imap/ImapFolder.cs @@ -4624,7 +4624,8 @@ void ValidateArguments (FormatOptions options, IList requests) if (requests[i] == null) throw new ArgumentException ("One or more of the requests is null."); - if (requests[i].Annotations != null && requests[i].Annotations.Count > 0 && (Engine.Capabilities & ImapCapabilities.Annotate) == 0) + var annotations = requests[i].Annotations; + if (annotations != null && annotations.Count > 0 && (Engine.Capabilities & ImapCapabilities.Annotate) == 0) throw new NotSupportedException ("One ore more requests included annotations but the IMAP server does not support annotations."); } @@ -4640,7 +4641,8 @@ ImapCommand QueueMultiAppendCommand (FormatOptions options, IList 0) { - ImapUtils.FormatAnnotations (builder, requests[i].Annotations, list, false); + var annotations = requests[i].Annotations; + if (annotations != null && annotations.Count > 0) { + ImapUtils.FormatAnnotations (builder, annotations, list, false); if (builder[builder.Length - 1] != ' ') builder.Append (' '); diff --git a/MailKit/Net/Imap/ImapFolderFetch.cs b/MailKit/Net/Imap/ImapFolderFetch.cs index 893e067db8..e5b5bb7377 100644 --- a/MailKit/Net/Imap/ImapFolderFetch.cs +++ b/MailKit/Net/Imap/ImapFolderFetch.cs @@ -1934,18 +1934,15 @@ public override void SetUniqueId (int index, UniqueId uid, CancellationToken can void FetchStream (ImapEngine engine, ImapCommand ic, int index) { var token = engine.ReadToken (ic.CancellationToken); - var annotations = new AnnotationsChangedEventArgs (index); - var labels = new MessageLabelsChangedEventArgs (index); - var flags = new MessageFlagsChangedEventArgs (index); - var modSeq = new ModSeqChangedEventArgs (index); var ctx = (FetchStreamContextBase) ic.UserData!; var sectionBuilder = new StringBuilder (); - bool annotationsChanged = false; - bool modSeqChanged = false; - bool labelsChanged = false; - bool flagsChanged = false; + IList? annotations = null; + MessageFlags flags = MessageFlags.None; + HashSet? keywords = null; + IList? labels = null; long nread = 0, size = 0; UniqueId? uid = null; + ulong? modseq = null; Stream stream; string name; byte[] buf; @@ -1972,7 +1969,6 @@ void FetchStream (ImapEngine engine, ImapCommand ic, int index) var atom = (string) token.Value; int offset = 0, length; - ulong modseq; uint value; if (atom.Equals ("BODY", StringComparison.OrdinalIgnoreCase)) { @@ -2117,11 +2113,6 @@ void FetchStream (ImapEngine engine, ImapCommand ic, int index) uid = new UniqueId (UidValidity, value); ctx.SetUniqueId (index, uid.Value, ic.CancellationToken); - - annotations.UniqueId = uid.Value; - modSeq.UniqueId = uid.Value; - labels.UniqueId = uid.Value; - flags.UniqueId = uid.Value; } else if (atom.Equals ("MODSEQ", StringComparison.OrdinalIgnoreCase)) { token = engine.ReadToken (ic.CancellationToken); @@ -2136,15 +2127,11 @@ void FetchStream (ImapEngine engine, ImapCommand ic, int index) // If we get an invalid value, just ignore it. // // See https://github.com/jstedfast/MailKit/issues/1686 for details. - if (ImapEngine.TryParseNumber64 (token, out modseq)) { - if (modseq > HighestModSeq) - UpdateHighestModSeq (modseq); - - annotations.ModSeq = modseq; - modSeq.ModSeq = modseq; - labels.ModSeq = modseq; - flags.ModSeq = modseq; - modSeqChanged = true; + if (ImapEngine.TryParseNumber64 (token, out ulong n64)) { + if (n64 > HighestModSeq) + UpdateHighestModSeq (n64); + + modseq = n64; } token = engine.ReadToken (ic.CancellationToken); @@ -2153,18 +2140,16 @@ void FetchStream (ImapEngine engine, ImapCommand ic, int index) } else if (atom.Equals ("FLAGS", StringComparison.OrdinalIgnoreCase)) { // even though we didn't request this piece of information, the IMAP server // may send it if another client has recently modified the message flags. - flags.Flags = ImapUtils.ParseFlagsList (engine, atom, (HashSet) flags.Keywords, ic.CancellationToken); - flagsChanged = true; + keywords = new HashSet (StringComparer.Ordinal); + flags = ImapUtils.ParseFlagsList (engine, atom, keywords, ic.CancellationToken); } else if (atom.Equals ("X-GM-LABELS", StringComparison.OrdinalIgnoreCase)) { // even though we didn't request this piece of information, the IMAP server // may send it if another client has recently modified the message labels. - labels.Labels = ImapUtils.ParseLabelsList (engine, ic.CancellationToken); - labelsChanged = true; + labels = ImapUtils.ParseLabelsList (engine, ic.CancellationToken); } else if (atom.Equals ("ANNOTATION", StringComparison.OrdinalIgnoreCase)) { // even though we didn't request this piece of information, the IMAP server // may send it if another client has recently modified the message annotations. - annotations.Annotations = ImapUtils.ParseAnnotations (engine, ic.CancellationToken); - annotationsChanged = true; + annotations = ImapUtils.ParseAnnotations (engine, ic.CancellationToken); } else { // Unexpected or unknown token (such as XAOL.SPAM.REASON or XAOL-MSGID). Simply read 1 more token (the argument) and ignore. token = engine.ReadToken (ic.CancellationToken); @@ -2176,34 +2161,31 @@ void FetchStream (ImapEngine engine, ImapCommand ic, int index) ImapEngine.AssertToken (token, ImapTokenType.CloseParen, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "FETCH", token); - if (flagsChanged) - OnMessageFlagsChanged (flags); + if (keywords != null) + OnMessageFlagsChanged (new MessageFlagsChangedEventArgs (index, flags, keywords) { UniqueId = uid, ModSeq = modseq }); - if (labelsChanged) - OnMessageLabelsChanged (labels); + if (labels != null) + OnMessageLabelsChanged (new MessageLabelsChangedEventArgs (index, labels) { UniqueId = uid, ModSeq = modseq }); - if (annotationsChanged) - OnAnnotationsChanged (annotations); + if (annotations != null) + OnAnnotationsChanged (new AnnotationsChangedEventArgs (index, annotations) { UniqueId = uid, ModSeq = modseq }); - if (modSeqChanged) - OnModSeqChanged (modSeq); + if (modseq.HasValue) + OnModSeqChanged (new ModSeqChangedEventArgs (index, modseq.Value) { UniqueId = uid }); } async Task FetchStreamAsync (ImapEngine engine, ImapCommand ic, int index) { var token = await engine.ReadTokenAsync (ic.CancellationToken).ConfigureAwait (false); - var annotations = new AnnotationsChangedEventArgs (index); - var labels = new MessageLabelsChangedEventArgs (index); - var flags = new MessageFlagsChangedEventArgs (index); - var modSeq = new ModSeqChangedEventArgs (index); var ctx = (FetchStreamContextBase) ic.UserData!; var sectionBuilder = new StringBuilder (); - bool annotationsChanged = false; - bool modSeqChanged = false; - bool labelsChanged = false; - bool flagsChanged = false; + IList? annotations = null; + MessageFlags flags = MessageFlags.None; + HashSet? keywords = null; + IList? labels = null; long nread = 0, size = 0; UniqueId? uid = null; + ulong? modseq = null; Stream stream; string name; byte[] buf; @@ -2230,7 +2212,6 @@ async Task FetchStreamAsync (ImapEngine engine, ImapCommand ic, int index) var atom = (string) token.Value; int offset = 0, length; - ulong modseq; uint value; if (atom.Equals ("BODY", StringComparison.OrdinalIgnoreCase)) { @@ -2375,11 +2356,6 @@ async Task FetchStreamAsync (ImapEngine engine, ImapCommand ic, int index) uid = new UniqueId (UidValidity, value); await ctx.SetUniqueIdAsync (index, uid.Value, ic.CancellationToken).ConfigureAwait (false); - - annotations.UniqueId = uid.Value; - modSeq.UniqueId = uid.Value; - labels.UniqueId = uid.Value; - flags.UniqueId = uid.Value; } else if (atom.Equals ("MODSEQ", StringComparison.OrdinalIgnoreCase)) { token = await engine.ReadTokenAsync (ic.CancellationToken).ConfigureAwait (false); @@ -2394,15 +2370,11 @@ async Task FetchStreamAsync (ImapEngine engine, ImapCommand ic, int index) // If we get an invalid value, just ignore it. // // See https://github.com/jstedfast/MailKit/issues/1686 for details. - if (ImapEngine.TryParseNumber64 (token, out modseq)) { - if (modseq > HighestModSeq) - UpdateHighestModSeq (modseq); - - annotations.ModSeq = modseq; - modSeq.ModSeq = modseq; - labels.ModSeq = modseq; - flags.ModSeq = modseq; - modSeqChanged = true; + if (ImapEngine.TryParseNumber64 (token, out ulong n64)) { + if (n64 > HighestModSeq) + UpdateHighestModSeq (n64); + + modseq = n64; } token = await engine.ReadTokenAsync (ic.CancellationToken).ConfigureAwait (false); @@ -2411,18 +2383,16 @@ async Task FetchStreamAsync (ImapEngine engine, ImapCommand ic, int index) } else if (atom.Equals ("FLAGS", StringComparison.OrdinalIgnoreCase)) { // even though we didn't request this piece of information, the IMAP server // may send it if another client has recently modified the message flags. - flags.Flags = await ImapUtils.ParseFlagsListAsync (engine, atom, (HashSet) flags.Keywords, ic.CancellationToken).ConfigureAwait (false); - flagsChanged = true; + keywords = new HashSet (StringComparer.Ordinal); + flags = await ImapUtils.ParseFlagsListAsync (engine, atom, keywords, ic.CancellationToken).ConfigureAwait (false); } else if (atom.Equals ("X-GM-LABELS", StringComparison.OrdinalIgnoreCase)) { // even though we didn't request this piece of information, the IMAP server // may send it if another client has recently modified the message labels. - labels.Labels = await ImapUtils.ParseLabelsListAsync (engine, ic.CancellationToken).ConfigureAwait (false); - labelsChanged = true; + labels = await ImapUtils.ParseLabelsListAsync (engine, ic.CancellationToken).ConfigureAwait (false); } else if (atom.Equals ("ANNOTATION", StringComparison.OrdinalIgnoreCase)) { // even though we didn't request this piece of information, the IMAP server // may send it if another client has recently modified the message annotations. - annotations.Annotations = await ImapUtils.ParseAnnotationsAsync (engine, ic.CancellationToken).ConfigureAwait (false); - annotationsChanged = true; + annotations = await ImapUtils.ParseAnnotationsAsync (engine, ic.CancellationToken).ConfigureAwait (false); } else { // Unexpected or unknown token (such as XAOL.SPAM.REASON or XAOL-MSGID). Simply read 1 more token (the argument) and ignore. token = await engine.ReadTokenAsync (ic.CancellationToken).ConfigureAwait (false); @@ -2434,17 +2404,17 @@ async Task FetchStreamAsync (ImapEngine engine, ImapCommand ic, int index) ImapEngine.AssertToken (token, ImapTokenType.CloseParen, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "FETCH", token); - if (flagsChanged) - OnMessageFlagsChanged (flags); + if (keywords != null) + OnMessageFlagsChanged (new MessageFlagsChangedEventArgs (index, flags, keywords) { UniqueId = uid, ModSeq = modseq }); - if (labelsChanged) - OnMessageLabelsChanged (labels); + if (labels != null) + OnMessageLabelsChanged (new MessageLabelsChangedEventArgs (index, labels) { UniqueId = uid, ModSeq = modseq }); - if (annotationsChanged) - OnAnnotationsChanged (annotations); + if (annotations != null) + OnAnnotationsChanged (new AnnotationsChangedEventArgs (index, annotations) { UniqueId = uid, ModSeq = modseq }); - if (modSeqChanged) - OnModSeqChanged (modSeq); + if (modseq.HasValue) + OnModSeqChanged (new ModSeqChangedEventArgs (index, modseq.Value) { UniqueId = uid }); } Task FetchStreamHandler (ImapEngine engine, ImapCommand ic, int index, bool doAsync) diff --git a/MailKit/Net/Imap/ImapLiteral.cs b/MailKit/Net/Imap/ImapLiteral.cs index 752870f29a..fc4356a955 100644 --- a/MailKit/Net/Imap/ImapLiteral.cs +++ b/MailKit/Net/Imap/ImapLiteral.cs @@ -52,6 +52,10 @@ class ImapLiteral readonly FormatOptions format; readonly Action update; + static void DefaultUpdate (int value) + { + } + /// /// Initializes a new instance of the class. /// @@ -61,7 +65,7 @@ class ImapLiteral /// The formatting options. /// The message. /// The progress update action. - public ImapLiteral (FormatOptions options, MimeMessage message, Action action = null) + public ImapLiteral (FormatOptions options, MimeMessage message, Action action) { format = options.Clone (); format.NewLineFormat = NewLineFormat.Dos; @@ -85,6 +89,8 @@ public ImapLiteral (FormatOptions options, byte[] literal) format = options.Clone (); format.NewLineFormat = NewLineFormat.Dos; + update = DefaultUpdate; + Type = ImapLiteralType.String; Literal = literal; } diff --git a/UnitTests/EventArgsTests.cs b/UnitTests/EventArgsTests.cs index f1b9b6d36e..0d7f12135c 100644 --- a/UnitTests/EventArgsTests.cs +++ b/UnitTests/EventArgsTests.cs @@ -199,12 +199,6 @@ public void TestMessageLabelsChangedEventArgs () var uid = new UniqueId (5); ulong modseq = 724; - args = new MessageLabelsChangedEventArgs (0); - Assert.That (args.Labels, Is.Null); - Assert.That (args.UniqueId.HasValue, Is.False); - Assert.That (args.ModSeq.HasValue, Is.False); - Assert.That (args.Index, Is.EqualTo (0)); - args = new MessageLabelsChangedEventArgs (0, labels); Assert.That (args.Labels, Has.Count.EqualTo (labels.Length)); Assert.That (args.UniqueId.HasValue, Is.False); @@ -229,7 +223,6 @@ public void TestMessageLabelsChangedEventArgs () Assert.That (args.ModSeq, Is.EqualTo (modseq)); Assert.That (args.Index, Is.EqualTo (0)); - Assert.Throws (() => new MessageLabelsChangedEventArgs (-1)); Assert.Throws (() => new MessageLabelsChangedEventArgs (-1, labels)); Assert.Throws (() => new MessageLabelsChangedEventArgs (-1, labels, modseq)); Assert.Throws (() => new MessageLabelsChangedEventArgs (-1, uid, labels)); @@ -300,11 +293,6 @@ public void TestModSeqChangedEventArgs () ModSeqChangedEventArgs args; ulong modseq = 724; - args = new ModSeqChangedEventArgs (0); - Assert.That (args.UniqueId.HasValue, Is.False); - Assert.That (args.ModSeq, Is.EqualTo (0)); - Assert.That (args.Index, Is.EqualTo (0)); - args = new ModSeqChangedEventArgs (0, modseq); Assert.That (args.UniqueId.HasValue, Is.False); Assert.That (args.ModSeq, Is.EqualTo (modseq)); @@ -315,7 +303,6 @@ public void TestModSeqChangedEventArgs () Assert.That (args.ModSeq, Is.EqualTo (modseq)); Assert.That (args.Index, Is.EqualTo (0)); - Assert.Throws (() => new ModSeqChangedEventArgs (-1)); Assert.Throws (() => new ModSeqChangedEventArgs (-1, modseq)); Assert.Throws (() => new ModSeqChangedEventArgs (-1, UniqueId.MinValue, modseq)); } From 421527c2e55d75f9d490c58760bbc3fda47fcd41 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 21 Feb 2026 09:44:48 -0500 Subject: [PATCH 15/44] Fixed ByteArrayBuilder for nullability --- MailKit/ByteArrayBuilder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MailKit/ByteArrayBuilder.cs b/MailKit/ByteArrayBuilder.cs index f6cb842bf2..a22b7a3994 100644 --- a/MailKit/ByteArrayBuilder.cs +++ b/MailKit/ByteArrayBuilder.cs @@ -173,9 +173,9 @@ public bool TryParse (int startIndex, int endIndex, out int value) public void Dispose () { - if (buffer != null) { + if (length != -1) { ArrayPool.Shared.Return (buffer); - buffer = null; + length = -1; } } } From 863768385b19273e51c9e6afc82557f112f540c0 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 21 Feb 2026 10:19:11 -0500 Subject: [PATCH 16/44] More NTLM nullability fixes --- .../Security/Ntlm/NtlmAuthenticateMessage.cs | 16 +++---- MailKit/Security/Ntlm/NtlmChallengeMessage.cs | 24 ++++++----- MailKit/Security/Ntlm/NtlmNegotiateMessage.cs | 10 +++-- MailKit/Security/Ntlm/NtlmTargetInfo.cs | 42 +++++++++---------- MailKit/Security/Ntlm/RC4.cs | 11 +++-- .../Security/SaslMechanismNegotiateBase.cs | 14 +++---- UnitTests/Security/SaslMechanismNtlmTests.cs | 4 +- 7 files changed, 63 insertions(+), 58 deletions(-) diff --git a/MailKit/Security/Ntlm/NtlmAuthenticateMessage.cs b/MailKit/Security/Ntlm/NtlmAuthenticateMessage.cs index 2acb199936..4ecf124241 100644 --- a/MailKit/Security/Ntlm/NtlmAuthenticateMessage.cs +++ b/MailKit/Security/Ntlm/NtlmAuthenticateMessage.cs @@ -61,10 +61,10 @@ public NtlmAuthenticateMessage (NtlmNegotiateMessage negotiate, NtlmChallengeMes Domain = domain; } else if ((challenge.Flags & NtlmFlags.TargetTypeDomain) != 0) { // The server is domain-joined, so the TargetName will be the domain. - Domain = challenge.TargetName; + Domain = challenge.TargetName ?? string.Empty; } else if (challenge.TargetInfo != null) { // The server is not domain-joined, so the TargetName will be the machine name of the server. - Domain = challenge.TargetInfo.DomainName; + Domain = challenge.TargetInfo.DomainName ?? string.Empty; } else { Domain = string.Empty; } @@ -429,11 +429,13 @@ public override byte[] Encode () message[62] = (byte)((uint) Flags >> 16); message[63] = (byte)((uint) Flags >> 24); - if (challenge != null && (challenge.Flags & NtlmFlags.NegotiateVersion) != 0 && OSVersion != null) { - message[64] = (byte) OSVersion.Major; - message[65] = (byte) OSVersion.Minor; - message[66] = (byte) OSVersion.Build; - message[67] = (byte)(OSVersion.Build >> 8); + if (challenge != null && (challenge.Flags & NtlmFlags.NegotiateVersion) != 0) { + if (OSVersion != null) { + message[64] = (byte) OSVersion.Major; + message[65] = (byte) OSVersion.Minor; + message[66] = (byte) OSVersion.Build; + message[67] = (byte)(OSVersion.Build >> 8); + } message[68] = 0x00; message[69] = 0x00; message[70] = 0x00; diff --git a/MailKit/Security/Ntlm/NtlmChallengeMessage.cs b/MailKit/Security/Ntlm/NtlmChallengeMessage.cs index f7f56a6977..f281502485 100644 --- a/MailKit/Security/Ntlm/NtlmChallengeMessage.cs +++ b/MailKit/Security/Ntlm/NtlmChallengeMessage.cs @@ -34,16 +34,16 @@ class NtlmChallengeMessage : NtlmMessageBase { const NtlmFlags DefaultFlags = NtlmFlags.NegotiateNtlm | NtlmFlags.NegotiateUnicode /*| NtlmFlags.NegotiateAlwaysSign*/; byte[] serverChallenge; - byte[] cached; + byte[]? cached; - public NtlmChallengeMessage (NtlmFlags flags, Version osVersion = null) : base (2) + public NtlmChallengeMessage (NtlmFlags flags, Version? osVersion = null) : base (2) { serverChallenge = NtlmUtils.NONCE (8); OSVersion = osVersion; Flags = flags; } - public NtlmChallengeMessage (Version osVersion = null) : this (DefaultFlags, osVersion) + public NtlmChallengeMessage (Version? osVersion = null) : this (DefaultFlags, osVersion) { } @@ -76,15 +76,15 @@ public byte[] ServerChallenge { } } - public string TargetName { + public string? TargetName { get; set; } - public NtlmTargetInfo TargetInfo { + public NtlmTargetInfo? TargetInfo { get; set; } - public byte[] GetEncodedTargetInfo () + public byte[]? GetEncodedTargetInfo () { return TargetInfo?.Encode ((Flags & NtlmFlags.NegotiateUnicode) != 0); } @@ -134,7 +134,7 @@ public override byte[] Encode () var targetInfo = GetEncodedTargetInfo (); int targetNameOffset = 48; int targetInfoOffset = 56; - byte[] targetName = null; + byte[]? targetName = null; int size = 48; if (TargetName != null) { @@ -193,10 +193,12 @@ public override byte[] Encode () } if ((Flags & NtlmFlags.NegotiateVersion) != 0) { - message[48] = (byte) OSVersion.Major; - message[49] = (byte) OSVersion.Minor; - message[50] = (byte) OSVersion.Build; - message[51] = (byte)(OSVersion.Build >> 8); + if (OSVersion != null) { + message[48] = (byte) OSVersion.Major; + message[49] = (byte) OSVersion.Minor; + message[50] = (byte) OSVersion.Build; + message[51] = (byte)(OSVersion.Build >> 8); + } message[52] = 0x00; message[53] = 0x00; message[54] = 0x00; diff --git a/MailKit/Security/Ntlm/NtlmNegotiateMessage.cs b/MailKit/Security/Ntlm/NtlmNegotiateMessage.cs index 9bab1c588b..6ebb79abc4 100644 --- a/MailKit/Security/Ntlm/NtlmNegotiateMessage.cs +++ b/MailKit/Security/Ntlm/NtlmNegotiateMessage.cs @@ -150,10 +150,12 @@ public override byte[] Encode () message[29] = (byte)(workstationOffset >> 8); if ((Flags & NtlmFlags.NegotiateVersion) != 0) { - message[32] = (byte) OSVersion!.Major; - message[33] = (byte) OSVersion!.Minor; - message[34] = (byte) OSVersion!.Build; - message[35] = (byte)(OSVersion!.Build >> 8); + if (OSVersion != null) { + message[32] = (byte) OSVersion.Major; + message[33] = (byte) OSVersion.Minor; + message[34] = (byte) OSVersion.Build; + message[35] = (byte)(OSVersion.Build >> 8); + } message[36] = 0x00; message[37] = 0x00; message[38] = 0x00; diff --git a/MailKit/Security/Ntlm/NtlmTargetInfo.cs b/MailKit/Security/Ntlm/NtlmTargetInfo.cs index 3acfd87e7e..bdc266d355 100644 --- a/MailKit/Security/Ntlm/NtlmTargetInfo.cs +++ b/MailKit/Security/Ntlm/NtlmTargetInfo.cs @@ -88,7 +88,7 @@ public void CopyTo (NtlmTargetInfo targetInfo) } } - internal NtlmAttributeValuePair GetAvPair (NtlmAttribute attr) + internal NtlmAttributeValuePair? GetAvPair (NtlmAttribute attr) { for (int i = 0; i < attributes.Count; i++) { if (attributes[i].Attribute == attr) @@ -98,14 +98,14 @@ internal NtlmAttributeValuePair GetAvPair (NtlmAttribute attr) return null; } - string GetAvPairString (NtlmAttribute attr) + string? GetAvPairString (NtlmAttribute attr) { - return ((NtlmAttributeStringValuePair) GetAvPair (attr))?.Value; + return ((NtlmAttributeStringValuePair?) GetAvPair (attr))?.Value; } - void SetAvPairString (NtlmAttribute attr, string value) + void SetAvPairString (NtlmAttribute attr, string? value) { - var pair = (NtlmAttributeStringValuePair) GetAvPair (attr); + var pair = (NtlmAttributeStringValuePair?) GetAvPair (attr); if (pair == null) { if (value != null) @@ -117,14 +117,14 @@ void SetAvPairString (NtlmAttribute attr, string value) } } - byte[] GetAvPairByteArray (NtlmAttribute attr) + byte[]? GetAvPairByteArray (NtlmAttribute attr) { - return ((NtlmAttributeByteArrayValuePair) GetAvPair (attr))?.Value; + return ((NtlmAttributeByteArrayValuePair?) GetAvPair (attr))?.Value; } - void SetAvPairByteArray (NtlmAttribute attr, byte[] value) + void SetAvPairByteArray (NtlmAttribute attr, byte[]? value) { - var pair = (NtlmAttributeByteArrayValuePair) GetAvPair (attr); + var pair = (NtlmAttributeByteArrayValuePair?) GetAvPair (attr); if (pair == null) { if (value != null) @@ -143,7 +143,7 @@ void SetAvPairByteArray (NtlmAttribute attr, byte[] value) /// Gets or sets the server's NetBIOS computer name. /// /// The server's NetBIOS computer name if available; otherwise, . - public string ServerName { + public string? ServerName { get { return GetAvPairString (NtlmAttribute.ServerName); } set { SetAvPairString (NtlmAttribute.ServerName, value); } } @@ -155,7 +155,7 @@ public string ServerName { /// Gets or sets the server's NetBIOS domain name. /// /// The server's NetBIOS domain name if available; otherwise, . - public string DomainName { + public string? DomainName { get { return GetAvPairString (NtlmAttribute.DomainName); } set { SetAvPairString (NtlmAttribute.DomainName, value); } } @@ -167,7 +167,7 @@ public string DomainName { /// Gets or sets the fully qualified domain name (FQDN) of the server. /// /// The fully qualified domain name (FQDN) of the server if available; otherwise, . - public string DnsServerName { + public string? DnsServerName { get { return GetAvPairString (NtlmAttribute.DnsServerName); } set { SetAvPairString (NtlmAttribute.DnsServerName, value); } } @@ -179,7 +179,7 @@ public string DnsServerName { /// Gets or sets the fully qualified domain name (FQDN) of the domain. /// /// The fully qualified domain name (FQDN) of the domain if available; otherwise, . - public string DnsDomainName { + public string? DnsDomainName { get { return GetAvPairString (NtlmAttribute.DnsDomainName); } set { SetAvPairString (NtlmAttribute.DnsDomainName, value); } } @@ -191,7 +191,7 @@ public string DnsDomainName { /// Gets or sets the fully qualified domain name (FQDN) of the forest. /// /// The fully qualified domain name (FQDN) of the forest if available; otherwise, . - public string DnsTreeName { + public string? DnsTreeName { get { return GetAvPairString (NtlmAttribute.DnsTreeName); } set { SetAvPairString (NtlmAttribute.DnsTreeName, value); } } @@ -207,9 +207,9 @@ public string DnsTreeName { /// /// The 32-bit flags value if available; otherwise, . public int? Flags { - get { return ((NtlmAttributeFlagsValuePair) GetAvPair (NtlmAttribute.Flags))?.Value; } + get { return ((NtlmAttributeFlagsValuePair?) GetAvPair (NtlmAttribute.Flags))?.Value; } set { - var pair = (NtlmAttributeFlagsValuePair) GetAvPair (NtlmAttribute.Flags); + var pair = (NtlmAttributeFlagsValuePair?) GetAvPair (NtlmAttribute.Flags); if (pair == null) { if (value != null) @@ -233,9 +233,9 @@ public int? Flags { /// /// The local time of the server, if available; otherwise . public long? Timestamp { - get { return ((NtlmAttributeTimestampValuePair) GetAvPair (NtlmAttribute.Timestamp))?.Value; } + get { return ((NtlmAttributeTimestampValuePair?) GetAvPair (NtlmAttribute.Timestamp))?.Value; } set { - var pair = (NtlmAttributeTimestampValuePair) GetAvPair (NtlmAttribute.Timestamp); + var pair = (NtlmAttributeTimestampValuePair?) GetAvPair (NtlmAttribute.Timestamp); if (pair == null) { if (value != null) @@ -257,7 +257,7 @@ public long? Timestamp { /// The Value field contains a platform-specific blob, as well as a MachineID created at computer startup to identify the calling machine. /// /// The single host data structure, if available; otherwise, . - public byte[] SingleHost { + public byte[]? SingleHost { get { return GetAvPairByteArray (NtlmAttribute.SingleHost); } set { SetAvPairByteArray (NtlmAttribute.SingleHost, value); } } @@ -269,7 +269,7 @@ public byte[] SingleHost { /// Gets or sets the Service Principal Name (SPN) of the server. /// /// The Service Principal Name (SPN) of the server, if available; otherwise, . - public string TargetName { + public string? TargetName { get { return GetAvPairString (NtlmAttribute.TargetName); } set { SetAvPairString (NtlmAttribute.TargetName, value); } } @@ -281,7 +281,7 @@ public string TargetName { /// Gets or sets the channel binding hash. /// /// An MD5 hash of the channel binding data, if available; otherwise . - public byte[] ChannelBinding { + public byte[]? ChannelBinding { get { return GetAvPairByteArray (NtlmAttribute.ChannelBinding); } set { SetAvPairByteArray (NtlmAttribute.ChannelBinding, value); } } diff --git a/MailKit/Security/Ntlm/RC4.cs b/MailKit/Security/Ntlm/RC4.cs index fce4a64a19..7b0543bda6 100644 --- a/MailKit/Security/Ntlm/RC4.cs +++ b/MailKit/Security/Ntlm/RC4.cs @@ -34,7 +34,8 @@ namespace MailKit.Security.Ntlm { class RC4 : SymmetricAlgorithm, ICryptoTransform { - byte[] key, state; + byte[] state; + byte[]? key; byte x, y; bool disposed; @@ -85,12 +86,12 @@ public override byte[] Key { } } - public override ICryptoTransform CreateEncryptor (byte[] rgbKey, byte[] rgvIV) + public override ICryptoTransform CreateEncryptor (byte[] rgbKey, byte[]? rgvIV) { return new RC4 { Key = rgbKey }; } - public override ICryptoTransform CreateDecryptor (byte[] rgbKey, byte[] rgvIV) + public override ICryptoTransform CreateDecryptor (byte[] rgbKey, byte[]? rgvIV) { return new RC4 { Key = rgbKey }; } @@ -194,10 +195,8 @@ protected override void Dispose (bool disposing) Array.Clear (state, 0, state.Length); - if (disposing) { - state = null; + if (disposing) key = null; - } disposed = true; } diff --git a/MailKit/Security/SaslMechanismNegotiateBase.cs b/MailKit/Security/SaslMechanismNegotiateBase.cs index b60747feea..3010f920c7 100644 --- a/MailKit/Security/SaslMechanismNegotiateBase.cs +++ b/MailKit/Security/SaslMechanismNegotiateBase.cs @@ -46,7 +46,7 @@ public abstract class SaslMechanismNegotiateBase : SaslMechanism { static ReadOnlySpan SaslNoSecurityLayerToken => new byte[] { 1, 0, 0, 0 }; - NegotiateAuthentication negotiate; + NegotiateAuthentication? negotiate; bool negotiatedChannelBinding; bool requestedChannelBinding; bool negotiatedSecurityLayer; @@ -212,7 +212,7 @@ public override bool NegotiatedSecurityLayer { /// This value is optional. /// /// The service principal name (SPN) of the service that the client wishes to authenticate with. - public string ServicePrincipalName { + public string? ServicePrincipalName { get; set; } @@ -236,7 +236,7 @@ public string ServicePrincipalName { /// /// An error has occurred while parsing the server's challenge token. /// - protected override byte[] Challenge (byte[] token, int startIndex, int length, CancellationToken cancellationToken = default) + protected override byte[]? Challenge (byte[]? token, int startIndex, int length, CancellationToken cancellationToken = default) { if (!SupportsSecurityLayer && IsAuthenticated) return null; @@ -285,9 +285,9 @@ protected virtual NegotiateAuthenticationClientOptions CreateClientOptions () return options; } - byte[] GetChallengeResponse (ReadOnlySpan challenge) + byte[]? GetChallengeResponse (ReadOnlySpan challenge) { - var response = negotiate.GetOutgoingBlob (challenge, out NegotiateAuthenticationStatusCode statusCode); + var response = negotiate!.GetOutgoingBlob (challenge, out NegotiateAuthenticationStatusCode statusCode); switch (statusCode) { case NegotiateAuthenticationStatusCode.Completed: @@ -309,13 +309,13 @@ byte[] GetChallengeResponse (ReadOnlySpan challenge) // Returns null for failure. // // Cloned from: https://github.com/dotnet/runtime/blob/4631ecec883a90ae9c29c058eea4527f9f2cb473/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpNegotiateAuthenticationModule.cs#L107 - byte[] GetSecurityLayerNegotiationResponse (ReadOnlySpan challenge) + byte[]? GetSecurityLayerNegotiationResponse (ReadOnlySpan challenge) { NegotiateAuthenticationStatusCode statusCode; byte[] input = challenge.ToArray (); Span unwrapped; - statusCode = negotiate.UnwrapInPlace (input, out int unwrappedOffset, out int unwrappedLength, out _); + statusCode = negotiate!.UnwrapInPlace (input, out int unwrappedOffset, out int unwrappedLength, out _); if (statusCode != NegotiateAuthenticationStatusCode.Completed) return null; diff --git a/UnitTests/Security/SaslMechanismNtlmTests.cs b/UnitTests/Security/SaslMechanismNtlmTests.cs index 833b490a7b..8997f8346f 100644 --- a/UnitTests/Security/SaslMechanismNtlmTests.cs +++ b/UnitTests/Security/SaslMechanismNtlmTests.cs @@ -289,7 +289,7 @@ public void TestNtlmChallengeMessageDecode () var nonce = HexEncode (challenge.ServerChallenge); Assert.That (nonce, Is.EqualTo ("0123456789abcdef"), "The expected nonce does not match."); - var targetInfo = HexEncode (challenge.GetEncodedTargetInfo ()); + var targetInfo = HexEncode (challenge.GetEncodedTargetInfo () ?? Array.Empty ()); Assert.That (targetInfo, Is.EqualTo (expectedTargetInfo), "The expected TargetInfo does not match."); Assert.That (challenge.TargetInfo.DomainName, Is.EqualTo ("DOMAIN"), "The expected TargetInfo domain name does not match."); @@ -360,7 +360,7 @@ public void TestNtlmChallengeMessageDecodeWithOSVersion () var nonce = HexEncode (challenge.ServerChallenge); Assert.That (nonce, Is.EqualTo ("0123456789abcdef"), "The expected nonce does not match."); - var targetInfo = HexEncode (challenge.GetEncodedTargetInfo ()); + var targetInfo = HexEncode (challenge.GetEncodedTargetInfo () ?? Array.Empty ()); Assert.That (targetInfo, Is.EqualTo (expectedTargetInfo), "The expected TargetInfo does not match."); Assert.That (challenge.TargetInfo.DomainName, Is.EqualTo ("DOMAIN"), "The expected TargetInfo domain name does not match."); From 98a6f0979a1626d580daea85ba3ecc03525f56f9 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 21 Feb 2026 10:20:16 -0500 Subject: [PATCH 17/44] ImapResponseCode nullability fixes --- MailKit/Net/Imap/ImapEngine.cs | 4 +++- MailKit/Net/Imap/ImapFolder.cs | 30 +++++++++++----------------- MailKit/Net/Imap/ImapFolderFlags.cs | 4 ++-- MailKit/Net/Imap/ImapResponseCode.cs | 21 +++++++++---------- 4 files changed, 28 insertions(+), 31 deletions(-) diff --git a/MailKit/Net/Imap/ImapEngine.cs b/MailKit/Net/Imap/ImapEngine.cs index 62126f2bfb..ef304181f9 100644 --- a/MailKit/Net/Imap/ImapEngine.cs +++ b/MailKit/Net/Imap/ImapEngine.cs @@ -1716,7 +1716,9 @@ void ProcessResponseCodes (ImapCommand ic) OnAlert (code.Message); break; case ImapResponseCodeType.WebAlert: - OnWebAlert (((WebAlertResponseCode) code).WebUri, code.Message); + var webAlert = (WebAlertResponseCode) code; + if (webAlert.WebUri != null) + OnWebAlert (webAlert.WebUri, code.Message); break; case ImapResponseCodeType.NotificationOverflow: OnNotificationOverflow (); diff --git a/MailKit/Net/Imap/ImapFolder.cs b/MailKit/Net/Imap/ImapFolder.cs index 035bafd077..3a99ff3c6c 100644 --- a/MailKit/Net/Imap/ImapFolder.cs +++ b/MailKit/Net/Imap/ImapFolder.cs @@ -4492,12 +4492,9 @@ ImapCommand QueueAppendCommand (FormatOptions options, IAppendRequest request, C ic.ThrowIfNotOk ("APPEND"); - var rc = ic.GetResponseCode (ImapResponseCodeType.AppendUid); + var rc = ic.GetResponseCode (ImapResponseCodeType.AppendUid) as AppendUidResponseCode; - if (rc is AppendUidResponseCode append) - return append.UidSet[0]; - - return null; + return rc?.UidSet?[0]; } /// @@ -4695,10 +4692,10 @@ IList ProcessMultiAppendResponse (ImapCommand ic) ic.ThrowIfNotOk ("APPEND"); - var rc = ic.GetResponseCode (ImapResponseCodeType.AppendUid); + var rc = ic.GetResponseCode (ImapResponseCodeType.AppendUid) as AppendUidResponseCode; - if (rc is AppendUidResponseCode append) - return append.UidSet; + if (rc != null && rc.UidSet != null) + return rc.UidSet; return Array.Empty (); } @@ -4940,12 +4937,9 @@ ImapCommand QueueReplaceCommand (FormatOptions options, UniqueId uid, IReplaceRe ic.ThrowIfNotOk ("REPLACE"); - var rc = ic.GetResponseCode (ImapResponseCodeType.AppendUid); - - if (rc is AppendUidResponseCode append) - return append.UidSet[0]; + var rc = ic.GetResponseCode (ImapResponseCodeType.AppendUid) as AppendUidResponseCode; - return null; + return rc?.UidSet?[0]; } /// @@ -5366,7 +5360,7 @@ static void GetCopiedUids (ImapCommand ic, ref UniqueIdSet? src, ref UniqueIdSet { var rc = ic.GetResponseCode (ImapResponseCodeType.CopyUid); - if (rc is CopyUidResponseCode copy) { + if (rc is CopyUidResponseCode copy && copy.SrcUidSet != null && copy.DestUidSet != null) { if (dest == null) { dest = copy.DestUidSet; src = copy.SrcUidSet; @@ -5458,10 +5452,10 @@ public override UniqueIdMap CopyTo (IList uids, IMailFolder destinatio ProcessCopyToResponse (ic, destination, ref src, ref dest); } - if (dest == null) + if (src == null || dest == null) return UniqueIdMap.Empty; - return new UniqueIdMap (src!, dest); + return new UniqueIdMap (src, dest); } /// @@ -5538,10 +5532,10 @@ public override async Task CopyToAsync (IList uids, IMail ProcessCopyToResponse (ic, destination, ref src, ref dest); } - if (dest == null) + if (src == null || dest == null) return UniqueIdMap.Empty; - return new UniqueIdMap (src!, dest); + return new UniqueIdMap (src, dest); } void ProcessMoveToResponse (ImapCommand ic, IMailFolder destination, ref UniqueIdSet? src, ref UniqueIdSet? dest) diff --git a/MailKit/Net/Imap/ImapFolderFlags.cs b/MailKit/Net/Imap/ImapFolderFlags.cs index a2752514b8..718be8ea17 100644 --- a/MailKit/Net/Imap/ImapFolderFlags.cs +++ b/MailKit/Net/Imap/ImapFolderFlags.cs @@ -44,7 +44,7 @@ static void ProcessUnmodified (ImapCommand ic, ref UniqueIdSet? uids, ulong? mod { if (modseq.HasValue) { foreach (var rc in ic.RespCodes.OfType ()) { - if (uids != null) + if (uids != null && rc.UidSet != null) uids.AddRange (rc.UidSet); else uids = rc.UidSet; @@ -64,7 +64,7 @@ static IList GetUnmodified (ImapCommand ic, ulong? modseq) if (modseq.HasValue) { var rc = ic.RespCodes.OfType ().FirstOrDefault (); - if (rc != null) { + if (rc != null && rc.UidSet != null) { var unmodified = new int[rc.UidSet.Count]; for (int i = 0; i < unmodified.Length; i++) unmodified[i] = (int) (rc.UidSet[i].Id - 1); diff --git a/MailKit/Net/Imap/ImapResponseCode.cs b/MailKit/Net/Imap/ImapResponseCode.cs index 355604873c..cc89af6f1a 100644 --- a/MailKit/Net/Imap/ImapResponseCode.cs +++ b/MailKit/Net/Imap/ImapResponseCode.cs @@ -138,6 +138,7 @@ class ImapResponseCode internal ImapResponseCode (ImapResponseCodeType type, bool isError) { + Message = string.Empty; IsError = isError; Type = type; } @@ -209,8 +210,8 @@ public static ImapResponseCode Create (ImapResponseCodeType type) class NewNameResponseCode : ImapResponseCode { - public string OldName; - public string NewName; + public string? OldName; + public string? NewName; internal NewNameResponseCode (ImapResponseCodeType type) : base (type, false) { @@ -257,7 +258,7 @@ internal UnseenResponseCode (ImapResponseCodeType type) : base (type, false) class AppendUidResponseCode : UidValidityResponseCode { - public UniqueIdSet UidSet; + public UniqueIdSet? UidSet; internal AppendUidResponseCode (ImapResponseCodeType type) : base (type) { @@ -266,7 +267,7 @@ internal AppendUidResponseCode (ImapResponseCodeType type) : base (type) class CopyUidResponseCode : UidValidityResponseCode { - public UniqueIdSet SrcUidSet, DestUidSet; + public UniqueIdSet? SrcUidSet, DestUidSet; internal CopyUidResponseCode (ImapResponseCodeType type) : base (type) { @@ -275,7 +276,7 @@ internal CopyUidResponseCode (ImapResponseCodeType type) : base (type) class BadUrlResponseCode : ImapResponseCode { - public string BadUrl; + public string? BadUrl; internal BadUrlResponseCode (ImapResponseCodeType type) : base (type, true) { @@ -293,7 +294,7 @@ internal HighestModSeqResponseCode (ImapResponseCodeType type) : base (type, fal class ModifiedResponseCode : ImapResponseCode { - public UniqueIdSet UidSet; + public UniqueIdSet? UidSet; internal ModifiedResponseCode (ImapResponseCodeType type) : base (type, false) { @@ -311,7 +312,7 @@ internal MaxConvertResponseCode (ImapResponseCodeType type) : base (type, true) class NoUpdateResponseCode : ImapResponseCode { - public string Tag; + public string? Tag; internal NoUpdateResponseCode (ImapResponseCodeType type) : base (type, true) { @@ -364,7 +365,7 @@ internal MetadataResponseCode (ImapResponseCodeType type) : base (type, true) class UndefinedFilterResponseCode : ImapResponseCode { - public string Name; + public string? Name; internal UndefinedFilterResponseCode (ImapResponseCodeType type) : base (type, true) { @@ -373,7 +374,7 @@ internal UndefinedFilterResponseCode (ImapResponseCodeType type) : base (type, t class MailboxIdResponseCode : ImapResponseCode { - public string MailboxId; + public string? MailboxId; internal MailboxIdResponseCode (ImapResponseCodeType type) : base (type, false) { @@ -382,7 +383,7 @@ internal MailboxIdResponseCode (ImapResponseCodeType type) : base (type, false) class WebAlertResponseCode : ImapResponseCode { - public Uri WebUri; + public Uri? WebUri; internal WebAlertResponseCode (ImapResponseCodeType type) : base (type, false) { From 15b9fe77c59509bfb037da802ef7d5fd74863eb5 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 21 Feb 2026 10:28:45 -0500 Subject: [PATCH 18/44] Telemetry nullability fixes --- MailKit/Net/ClientMetrics.cs | 5 +++-- MailKit/Net/SocketMetrics.cs | 21 ++++++++++----------- MailKit/Telemetry.cs | 16 ++++++++-------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/MailKit/Net/ClientMetrics.cs b/MailKit/Net/ClientMetrics.cs index 437a5f1a0b..c94891e887 100644 --- a/MailKit/Net/ClientMetrics.cs +++ b/MailKit/Net/ClientMetrics.cs @@ -30,6 +30,7 @@ using System.Diagnostics; using System.Globalization; using System.Diagnostics.Metrics; +using System.Diagnostics.CodeAnalysis; using MailKit.Net.Smtp; using MailKit.Security; @@ -62,7 +63,7 @@ public ClientMetrics (Meter meter, string meterName, string an, string protocol) description: $"The amount of time it takes for the {protocol} server to perform an operation."); } - static bool TryGetErrorType (Exception exception, out string errorType) + static bool TryGetErrorType (Exception exception, [NotNullWhen (true)] out string? errorType) { if (SocketMetrics.TryGetErrorType (exception, false, out errorType)) return true; @@ -98,7 +99,7 @@ static bool TryGetErrorType (Exception exception, out string errorType) // Fall back to using the exception type name. errorType = exception.GetType ().FullName; - return true; + return errorType != null; } internal static TagList GetTags (Uri uri, Exception? ex) diff --git a/MailKit/Net/SocketMetrics.cs b/MailKit/Net/SocketMetrics.cs index 02bafffc70..40436f49da 100644 --- a/MailKit/Net/SocketMetrics.cs +++ b/MailKit/Net/SocketMetrics.cs @@ -31,6 +31,7 @@ using System.Net.Sockets; using System.Diagnostics; using System.Diagnostics.Metrics; +using System.Diagnostics.CodeAnalysis; namespace MailKit.Net { sealed class SocketMetrics @@ -51,9 +52,9 @@ public SocketMetrics (Meter meter) description: "The number of milliseconds taken for a socket to connect to a remote host."); } - static SocketException GetSocketException (Exception exception) + static SocketException? GetSocketException (Exception exception) { - Exception ex = exception; + Exception? ex = exception; do { if (ex is SocketException se) @@ -65,7 +66,7 @@ static SocketException GetSocketException (Exception exception) return null; } - internal static bool TryGetErrorType (Exception exception, bool exceptionTypeFallback, out string errorType) + internal static bool TryGetErrorType (Exception exception, bool exceptionTypeFallback, [NotNullWhen (true)] out string? errorType) { if (exception is OperationCanceledException) { errorType = "cancelled"; @@ -89,17 +90,15 @@ internal static bool TryGetErrorType (Exception exception, bool exceptionTypeFal } } - if (exceptionTypeFallback) { + if (exceptionTypeFallback) errorType = exception.GetType ().FullName; - return true; - } - - errorType = null; + else + errorType = null; - return false; + return errorType != null; } - static TagList GetTags (IPAddress ip, string host, int port, Exception ex = null) + static TagList GetTags (IPAddress ip, string host, int port, Exception? ex = null) { var tags = new TagList { { "network.peer.address", ip.ToString () }, @@ -129,7 +128,7 @@ public void RecordConnected (long connectStartedTimestamp, IPAddress ip, string } } - public void RecordConnectFailed (long connectStartedTimestamp, IPAddress ip, string host, int port, bool cancelled, Exception ex = null) + public void RecordConnectFailed (long connectStartedTimestamp, IPAddress ip, string host, int port, bool cancelled, Exception? ex = null) { if (connectCounter.Enabled || connectDuration.Enabled) { var tags = GetTags (ip, host, port, ex); diff --git a/MailKit/Telemetry.cs b/MailKit/Telemetry.cs index 2ae0342239..7e1e5bef70 100644 --- a/MailKit/Telemetry.cs +++ b/MailKit/Telemetry.cs @@ -65,9 +65,9 @@ public static class Socket /// public const string MeterVersion = "0.1"; - static Meter Meter; + static Meter? Meter; - internal static SocketMetrics Metrics { get; private set; } + internal static SocketMetrics? Metrics { get; private set; } /// /// Configure socket metering. @@ -145,9 +145,9 @@ public static class SmtpClient /// public const string MeterVersion = "0.1"; - static Meter Meter; + static Meter? Meter; - internal static ClientMetrics Metrics { get; private set; } + internal static ClientMetrics? Metrics { get; private set; } internal static ClientMetrics CreateMetrics (Meter meter) { @@ -230,9 +230,9 @@ public static class Pop3Client /// public const string MeterVersion = "0.1"; - static Meter Meter; + static Meter? Meter; - internal static ClientMetrics Metrics { get; private set; } + internal static ClientMetrics? Metrics { get; private set; } internal static ClientMetrics CreateMetrics (Meter meter) { @@ -315,9 +315,9 @@ public static class ImapClient /// public const string MeterVersion = "0.1"; - static Meter Meter; + static Meter? Meter; - internal static ClientMetrics Metrics { get; private set; } + internal static ClientMetrics? Metrics { get; private set; } internal static ClientMetrics CreateMetrics (Meter meter) { From 972f0db9720d272001907759b327f46d20db982e Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 21 Feb 2026 10:34:17 -0500 Subject: [PATCH 19/44] More Telemetry nullability fixes --- MailKit/Net/Imap/ImapEngine.cs | 2 +- MailKit/Net/NetworkOperation.cs | 6 +++--- MailKit/Net/Pop3/Pop3Engine.cs | 2 +- MailKit/Net/Smtp/SmtpClient.cs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/MailKit/Net/Imap/ImapEngine.cs b/MailKit/Net/Imap/ImapEngine.cs index ef304181f9..1911410d5e 100644 --- a/MailKit/Net/Imap/ImapEngine.cs +++ b/MailKit/Net/Imap/ImapEngine.cs @@ -149,7 +149,7 @@ class ImapEngine : IDisposable static int TagPrefixIndex; #if NET6_0_OR_GREATER - readonly ClientMetrics metrics; + readonly ClientMetrics? metrics; #endif internal readonly Dictionary FolderCache; diff --git a/MailKit/Net/NetworkOperation.cs b/MailKit/Net/NetworkOperation.cs index 92f09de102..58f3520aff 100644 --- a/MailKit/Net/NetworkOperation.cs +++ b/MailKit/Net/NetworkOperation.cs @@ -50,13 +50,13 @@ class NetworkOperation : IDisposable }; readonly NetworkOperationKind kind; - readonly ClientMetrics metrics; + readonly ClientMetrics? metrics; readonly Activity? activity; readonly long startTimestamp; readonly Uri uri; Exception? ex; - NetworkOperation (NetworkOperationKind kind, Uri uri, Activity? activity, ClientMetrics metrics) + NetworkOperation (NetworkOperationKind kind, Uri uri, Activity? activity, ClientMetrics? metrics) { this.kind = kind; this.uri = uri; @@ -124,7 +124,7 @@ public void Dispose () } #if NET6_0_OR_GREATER - public static NetworkOperation Start (NetworkOperationKind kind, Uri uri, ActivitySource source, ClientMetrics metrics) + public static NetworkOperation Start (NetworkOperationKind kind, Uri uri, ActivitySource source, ClientMetrics? metrics) { var activity = source?.StartActivity (ActivityNames[(int) kind], ActivityKind.Client); diff --git a/MailKit/Net/Pop3/Pop3Engine.cs b/MailKit/Net/Pop3/Pop3Engine.cs index 432cabf336..d97ef223cf 100644 --- a/MailKit/Net/Pop3/Pop3Engine.cs +++ b/MailKit/Net/Pop3/Pop3Engine.cs @@ -60,7 +60,7 @@ enum Pop3EngineState { class Pop3Engine { #if NET6_0_OR_GREATER - readonly ClientMetrics metrics; + readonly ClientMetrics? metrics; #endif readonly List queue; long clientConnectedTimestamp; diff --git a/MailKit/Net/Smtp/SmtpClient.cs b/MailKit/Net/Smtp/SmtpClient.cs index 46b6c184fd..79a7a11b85 100644 --- a/MailKit/Net/Smtp/SmtpClient.cs +++ b/MailKit/Net/Smtp/SmtpClient.cs @@ -88,7 +88,7 @@ enum SmtpCommand { readonly List queued = new List (); SslCertificateValidationInfo? sslValidationInfo; #if NET6_0_OR_GREATER - readonly ClientMetrics metrics; + readonly ClientMetrics? metrics; #endif long clientConnectedTimestamp; SmtpCapabilities capabilities; From 75b236efcb560ceab9902c98efdda9f69d2bb99e Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 21 Feb 2026 10:43:54 -0500 Subject: [PATCH 20/44] More IMAP nullability fixes --- MailKit/Net/Imap/AsyncImapClient.cs | 2 +- MailKit/Net/Imap/ImapEngine.cs | 40 ++++++++++++++++++---------- MailKit/Net/Imap/ImapFolderFetch.cs | 12 ++++----- MailKit/Net/Imap/ImapFolderSearch.cs | 4 +-- MailKit/Net/Imap/ImapStream.cs | 5 +++- MailKit/Net/Imap/ImapUtils.cs | 32 +++++++++++----------- MailKit/Search/SearchResults.cs | 2 +- 7 files changed, 56 insertions(+), 41 deletions(-) diff --git a/MailKit/Net/Imap/AsyncImapClient.cs b/MailKit/Net/Imap/AsyncImapClient.cs index 073b3bf7d1..4bac582461 100644 --- a/MailKit/Net/Imap/AsyncImapClient.cs +++ b/MailKit/Net/Imap/AsyncImapClient.cs @@ -1255,7 +1255,7 @@ public override Task GetFolderAsync (string path, CancellationToken /// /// The server replied with a NO or BAD response. /// - public override async Task GetMetadataAsync (MetadataTag tag, CancellationToken cancellationToken = default) + public override async Task GetMetadataAsync (MetadataTag tag, CancellationToken cancellationToken = default) { var ic = QueueGetMetadataCommand (tag, cancellationToken); diff --git a/MailKit/Net/Imap/ImapEngine.cs b/MailKit/Net/Imap/ImapEngine.cs index 1911410d5e..c0b683bb70 100644 --- a/MailKit/Net/Imap/ImapEngine.cs +++ b/MailKit/Net/Imap/ImapEngine.cs @@ -1132,6 +1132,18 @@ public async ValueTask PeekTokenAsync (CancellationToken cancellation return token; } + /// + /// Unget a token. + /// + /// + /// Ungets a token. + /// + /// The token. + public void UngetToken (ImapToken token) + { + Stream!.UngetToken (token); + } + /// /// Reads the literal as a string. /// @@ -1486,7 +1498,7 @@ void UpdateCapabilities (ImapTokenType sentinel, CancellationToken cancellationT AssertToken (token, sentinel, GenericItemSyntaxErrorFormat, "CAPABILITIES", token); // unget the sentinel - Stream!.UngetToken (token); + UngetToken (token); StandardizeCapabilities (); } @@ -1513,7 +1525,7 @@ async Task UpdateCapabilitiesAsync (ImapTokenType sentinel, CancellationToken ca AssertToken (token, sentinel, GenericItemSyntaxErrorFormat, "CAPABILITIES", token); // unget the sentinel - Stream!.UngetToken (token); + UngetToken (token); StandardizeCapabilities (); } @@ -1925,14 +1937,14 @@ public ImapResponseCode ParseResponseCode (bool isTagged, CancellationToken canc } break; case ImapResponseCodeType.Capability: - Stream!.UngetToken (token); + UngetToken (token); UpdateCapabilities (ImapTokenType.CloseBracket, cancellationToken); token = ReadToken (cancellationToken); break; case ImapResponseCodeType.PermanentFlags: var perm = (PermanentFlagsResponseCode) code; - Stream!.UngetToken (token); + UngetToken (token); perm.Flags = ImapUtils.ParseFlagsList (this, "PERMANENTFLAGS", perm.Keywords, cancellationToken); token = ReadToken (cancellationToken); break; @@ -2022,7 +2034,7 @@ public ImapResponseCode ParseResponseCode (bool isTagged, CancellationToken canc copy.SrcUidSet = ParseUidSet (token, validity, out _, out _, GenericResponseCodeSyntaxErrorFormat, "COPYUID", token); } else { copy.SrcUidSet = new UniqueIdSet (); - Stream!.UngetToken (token); + UngetToken (token); } token = ReadToken (cancellationToken); @@ -2031,7 +2043,7 @@ public ImapResponseCode ParseResponseCode (bool isTagged, CancellationToken canc copy.DestUidSet = ParseUidSet (token, copy.UidValidity, out _, out _, GenericResponseCodeSyntaxErrorFormat, "COPYUID", token); } else { copy.DestUidSet = new UniqueIdSet (); - Stream!.UngetToken (token); + UngetToken (token); } token = ReadToken (cancellationToken); @@ -2251,14 +2263,14 @@ public async ValueTask ParseResponseCodeAsync (bool isTagged, } break; case ImapResponseCodeType.Capability: - Stream!.UngetToken (token); + UngetToken (token); await UpdateCapabilitiesAsync (ImapTokenType.CloseBracket, cancellationToken).ConfigureAwait (false); token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); break; case ImapResponseCodeType.PermanentFlags: var perm = (PermanentFlagsResponseCode) code; - Stream!.UngetToken (token); + UngetToken (token); perm.Flags = await ImapUtils.ParseFlagsListAsync (this, "PERMANENTFLAGS", perm.Keywords, cancellationToken).ConfigureAwait (false); token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); break; @@ -2348,7 +2360,7 @@ public async ValueTask ParseResponseCodeAsync (bool isTagged, copy.SrcUidSet = ParseUidSet (token, validity, out _, out _, GenericResponseCodeSyntaxErrorFormat, "COPYUID", token); } else { copy.SrcUidSet = new UniqueIdSet (); - Stream!.UngetToken (token); + UngetToken (token); } token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); @@ -2357,7 +2369,7 @@ public async ValueTask ParseResponseCodeAsync (bool isTagged, copy.DestUidSet = ParseUidSet (token, copy.UidValidity, out _, out _, GenericResponseCodeSyntaxErrorFormat, "COPYUID", token); } else { copy.DestUidSet = new UniqueIdSet (); - Stream!.UngetToken (token); + UngetToken (token); } token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); @@ -2761,11 +2773,11 @@ internal void ProcessUntaggedResponse (CancellationToken cancellationToken) // See https://github.com/jstedfast/MailKit/issues/115#issuecomment-313684616 for details. if (token.Type == ImapTokenType.OpenBracket) { // unget the '[' token and then pretend that we got an "OK" - Stream!.UngetToken (token); + UngetToken (token); atom = "OK"; } else if (token.Type != ImapTokenType.Atom) { // if we get anything else here, just ignore it? - Stream!.UngetToken (token); + UngetToken (token); SkipLine (cancellationToken); return; } else { @@ -2914,11 +2926,11 @@ internal async Task ProcessUntaggedResponseAsync (CancellationToken cancellation // See https://github.com/jstedfast/MailKit/issues/115#issuecomment-313684616 for details. if (token.Type == ImapTokenType.OpenBracket) { // unget the '[' token and then pretend that we got an "OK" - Stream!.UngetToken (token); + UngetToken (token); atom = "OK"; } else if (token.Type != ImapTokenType.Atom) { // if we get anything else here, just ignore it? - Stream!.UngetToken (token); + UngetToken (token); await SkipLineAsync (cancellationToken).ConfigureAwait (false); return; } else { diff --git a/MailKit/Net/Imap/ImapFolderFetch.cs b/MailKit/Net/Imap/ImapFolderFetch.cs index e5b5bb7377..64c637d1d9 100644 --- a/MailKit/Net/Imap/ImapFolderFetch.cs +++ b/MailKit/Net/Imap/ImapFolderFetch.cs @@ -306,7 +306,7 @@ void ParseSummaryItems (ImapEngine engine, MessageSummary message, FetchSummaryI break; // the header field names will generally be atoms or qstrings but may also be literals - engine.Stream!.UngetToken (token); + engine.UngetToken (token); var field = ImapUtils.ReadStringToken (engine, format, cancellationToken); @@ -550,7 +550,7 @@ async Task ParseSummaryItemsAsync (ImapEngine engine, MessageSummary message, Fe break; // the header field names will generally be atoms or qstrings but may also be literals - engine.Stream!.UngetToken (token); + engine.UngetToken (token); var field = await ImapUtils.ReadStringTokenAsync (engine, format, cancellationToken).ConfigureAwait (false); @@ -1994,7 +1994,7 @@ void FetchStream (ImapEngine engine, ImapCommand ic, int index) break; // the header field names will generally be atoms or qstrings but may also be literals - engine.Stream!.UngetToken (token); + engine.UngetToken (token); var field = ImapUtils.ReadStringToken (engine, ImapEngine.FetchBodySyntaxErrorFormat, ic.CancellationToken); @@ -2091,7 +2091,7 @@ void FetchStream (ImapEngine engine, ImapCommand ic, int index) // See https://github.com/jstedfast/MailKit/issues/1708 for details. // // Unget the ')' token and pretend we got a NIL token. - engine.Stream!.UngetToken (token); + engine.UngetToken (token); goto case ImapTokenType.Nil; default: throw ImapEngine.UnexpectedToken (ImapEngine.GenericItemSyntaxErrorFormat, atom, token); @@ -2237,7 +2237,7 @@ async Task FetchStreamAsync (ImapEngine engine, ImapCommand ic, int index) break; // the header field names will generally be atoms or qstrings but may also be literals - engine.Stream!.UngetToken (token); + engine.UngetToken (token); var field = await ImapUtils.ReadStringTokenAsync (engine, ImapEngine.FetchBodySyntaxErrorFormat, ic.CancellationToken).ConfigureAwait (false); @@ -2334,7 +2334,7 @@ async Task FetchStreamAsync (ImapEngine engine, ImapCommand ic, int index) // See https://github.com/jstedfast/MailKit/issues/1708 for details. // // Unget the ')' token and pretend we got a NIL token. - engine.Stream!.UngetToken (token); + engine.UngetToken (token); goto case ImapTokenType.Nil; default: throw ImapEngine.UnexpectedToken (ImapEngine.GenericItemSyntaxErrorFormat, atom, token); diff --git a/MailKit/Net/Imap/ImapFolderSearch.cs b/MailKit/Net/Imap/ImapFolderSearch.cs index 64efee4ed4..c71e92373f 100644 --- a/MailKit/Net/Imap/ImapFolderSearch.cs +++ b/MailKit/Net/Imap/ImapFolderSearch.cs @@ -509,7 +509,7 @@ static void ParseESearchResults (ImapEngine engine, ImapCommand ic, SearchResult if (token.Type == ImapTokenType.Eoln) { // unget the eoln token - engine.Stream!.UngetToken (token); + engine.UngetToken (token); break; } @@ -640,7 +640,7 @@ static async Task ParseESearchResultsAsync (ImapEngine engine, ImapCommand ic, S if (token.Type == ImapTokenType.Eoln) { // unget the eoln token - engine.Stream!.UngetToken (token); + engine.UngetToken (token); break; } diff --git a/MailKit/Net/Imap/ImapStream.cs b/MailKit/Net/Imap/ImapStream.cs index 46d97e7124..dbb2c99fd4 100644 --- a/MailKit/Net/Imap/ImapStream.cs +++ b/MailKit/Net/Imap/ImapStream.cs @@ -930,8 +930,11 @@ public ValueTask ReadTokenAsync (CancellationToken cancellationToken) } /// - /// Ungets a token. + /// Unget a token. /// + /// + /// Ungets a token. + /// /// The token. public void UngetToken (ImapToken token) { diff --git a/MailKit/Net/Imap/ImapUtils.cs b/MailKit/Net/Imap/ImapUtils.cs index 3e9ccfd628..ad73bf3142 100644 --- a/MailKit/Net/Imap/ImapUtils.cs +++ b/MailKit/Net/Imap/ImapUtils.cs @@ -475,7 +475,7 @@ static string ReadFolderName (ImapEngine engine, char delim, string format, Canc var line = engine.ReadLine (cancellationToken); // unget the \r\n sequence - engine.Stream.UngetToken (ImapToken.Eoln); + engine.UngetToken (ImapToken.Eoln); encodedName += line; } @@ -510,7 +510,7 @@ static async Task ReadFolderNameAsync (ImapEngine engine, char delim, st var line = await engine.ReadLineAsync (cancellationToken).ConfigureAwait (false); // unget the \r\n sequence - engine.Stream.UngetToken (ImapToken.Eoln); + engine.UngetToken (ImapToken.Eoln); encodedName += line; } @@ -709,7 +709,7 @@ public static void ParseFolderList (ImapEngine engine, List? list, b if (token.Type == ImapTokenType.CloseParen) break; - engine.Stream.UngetToken (token); + engine.UngetToken (token); var value = ReadNStringToken (engine, format, false, cancellationToken); @@ -795,7 +795,7 @@ public static async Task ParseFolderListAsync (ImapEngine engine, List ShouldParseMultipartAsync (ImapEngine engine, Cancellati if (nextToken.Type == ImapTokenType.OpenParen || nextToken.Type == ImapTokenType.CloseParen || nextToken.Type == ImapTokenType.Nil) { // Unget the multipart subtype. - engine.Stream.UngetToken (token); + engine.UngetToken (token); // Now unget a fake NIL token that represents an empty set of children. - engine.Stream.UngetToken (ImapToken.Nil); + engine.UngetToken (ImapToken.Nil); return true; } @@ -1824,11 +1824,11 @@ static async Task ShouldParseMultipartAsync (ImapEngine engine, Cancellati } // We've got a string which normally means it's the first token of a mime-type. - engine.Stream.UngetToken (token); + engine.UngetToken (token); return false; case ImapTokenType.OpenParen: // We've got children, so this is definitely a multipart. - engine.Stream.UngetToken (token); + engine.UngetToken (token); return true; case ImapTokenType.Nil: // We've got a NIL token. Technically, this is illegal syntax, but we need to be able to handle it. @@ -1852,7 +1852,7 @@ static async Task ShouldParseMultipartAsync (ImapEngine engine, Cancellati // https://github.com/jstedfast/MailKit/issues/1446 nextToken = await engine.PeekTokenAsync (cancellationToken).ConfigureAwait (false); - engine.Stream.UngetToken (token); + engine.UngetToken (token); if (nextToken.Type == ImapTokenType.Nil) { // Looks like we've probably encountered the `(NIL NIL NIL NIL NIL "7BIT" 0 NIL NIL NIL NIL)` variant. @@ -1862,7 +1862,7 @@ static async Task ShouldParseMultipartAsync (ImapEngine engine, Cancellati // Assume (NIL "alternative" ("boundary" "... return true; default: - engine.Stream.UngetToken (token); + engine.UngetToken (token); return false; } } diff --git a/MailKit/Search/SearchResults.cs b/MailKit/Search/SearchResults.cs index 1aea2d2ef5..d9a647f4b4 100644 --- a/MailKit/Search/SearchResults.cs +++ b/MailKit/Search/SearchResults.cs @@ -122,7 +122,7 @@ public ulong? ModSeq { /// Gets or sets the relevancy scores of the messages that matched the search query. /// /// The relevancy scores. - public IList Relevancy { + public IList? Relevancy { get; set; } } From ec08b84c9fc913b3d9f6f3dd495ff2e951e054ed Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 21 Feb 2026 11:38:27 -0500 Subject: [PATCH 21/44] General nullability fixes --- MailKit/AccessRight.cs | 2 +- MailKit/Annotation.cs | 2 +- MailKit/AnnotationAttribute.cs | 4 ++-- MailKit/MetadataTag.cs | 2 +- MailKit/Search/AnnotationSearchQuery.cs | 4 ++-- MailKit/Search/OrderByAnnotation.cs | 4 ++-- MailKit/UniqueId.cs | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/MailKit/AccessRight.cs b/MailKit/AccessRight.cs index 94f01f9c41..be3efbf71e 100644 --- a/MailKit/AccessRight.cs +++ b/MailKit/AccessRight.cs @@ -200,7 +200,7 @@ public bool Equals (AccessRight other) /// The to compare with the current . /// if the specified is equal to the current ; /// otherwise, . - public override bool Equals (object obj) + public override bool Equals (object? obj) { return obj is AccessRight right && right.Right == Right; } diff --git a/MailKit/Annotation.cs b/MailKit/Annotation.cs index 28bb9c2546..c6cb1d29c1 100644 --- a/MailKit/Annotation.cs +++ b/MailKit/Annotation.cs @@ -50,7 +50,7 @@ public class Annotation /// public Annotation (AnnotationEntry entry) { - if (entry == null) + if (entry is null) throw new ArgumentNullException (nameof (entry)); Properties = new Dictionary (); diff --git a/MailKit/AnnotationAttribute.cs b/MailKit/AnnotationAttribute.cs index d44630acdc..a2654f243e 100644 --- a/MailKit/AnnotationAttribute.cs +++ b/MailKit/AnnotationAttribute.cs @@ -181,7 +181,7 @@ public string Specifier { /// The to compare with the current . /// if the specified is equal to the current /// ; otherwise, . - public bool Equals (AnnotationAttribute other) + public bool Equals (AnnotationAttribute? other) { return other?.Specifier == Specifier; } @@ -225,7 +225,7 @@ public bool Equals (AnnotationAttribute other) /// The to compare with the current . /// if the specified is equal to the current /// ; otherwise, . - public override bool Equals (object obj) + public override bool Equals (object? obj) { return obj is AnnotationAttribute attribute && attribute.Specifier == Specifier; } diff --git a/MailKit/MetadataTag.cs b/MailKit/MetadataTag.cs index b2710fb31c..de0b338222 100644 --- a/MailKit/MetadataTag.cs +++ b/MailKit/MetadataTag.cs @@ -113,7 +113,7 @@ public string Id { /// The to compare with the current . /// if the specified is equal to the current /// ; otherwise, . - public override bool Equals (object obj) + public override bool Equals (object? obj) { return obj is MetadataTag tag && tag.Id == Id; } diff --git a/MailKit/Search/AnnotationSearchQuery.cs b/MailKit/Search/AnnotationSearchQuery.cs index 11bfbd680f..57a69469cf 100644 --- a/MailKit/Search/AnnotationSearchQuery.cs +++ b/MailKit/Search/AnnotationSearchQuery.cs @@ -55,10 +55,10 @@ public class AnnotationSearchQuery : SearchQuery /// public AnnotationSearchQuery (AnnotationEntry entry, AnnotationAttribute attribute, string value) : base (SearchTerm.Annotation) { - if (entry == null) + if (entry is null) throw new ArgumentNullException (nameof (entry)); - if (attribute == null) + if (attribute is null) throw new ArgumentNullException (nameof (attribute)); if (attribute.Name != "value") diff --git a/MailKit/Search/OrderByAnnotation.cs b/MailKit/Search/OrderByAnnotation.cs index b8474531a9..bce300cf0f 100644 --- a/MailKit/Search/OrderByAnnotation.cs +++ b/MailKit/Search/OrderByAnnotation.cs @@ -56,10 +56,10 @@ public class OrderByAnnotation : OrderBy /// public OrderByAnnotation (AnnotationEntry entry, AnnotationAttribute attribute, SortOrder order) : base (OrderByType.Annotation, order) { - if (entry == null) + if (entry is null) throw new ArgumentNullException (nameof (entry)); - if (attribute == null) + if (attribute is null) throw new ArgumentNullException (nameof (attribute)); if (attribute.Name != "value" || attribute.Scope == AnnotationScope.Both) diff --git a/MailKit/UniqueId.cs b/MailKit/UniqueId.cs index 44816dc71b..b1260dc420 100644 --- a/MailKit/UniqueId.cs +++ b/MailKit/UniqueId.cs @@ -294,7 +294,7 @@ public bool Equals (UniqueId other) /// The to compare with the current . /// if the specified is equal to the current ; /// otherwise, . - public override bool Equals (object obj) + public override bool Equals (object? obj) { return obj is UniqueId uid && uid.Id == Id; } From 5ad312b87535a25bd24bbdd809d4bf40107d753a Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 21 Feb 2026 11:42:11 -0500 Subject: [PATCH 22/44] ImapFolder and MailFolder nullability fixes --- MailKit/IMailFolder.cs | 2 +- MailKit/MailFolder.cs | 46 +++++++++++++++- MailKit/Net/Imap/ImapClient.cs | 2 +- MailKit/Net/Imap/ImapFolder.cs | 53 +++++++++++++------ MailKit/Net/Imap/ImapFolderConstructorArgs.cs | 14 +---- MailKit/Net/Imap/ImapUtils.cs | 6 +-- 6 files changed, 88 insertions(+), 35 deletions(-) diff --git a/MailKit/IMailFolder.cs b/MailKit/IMailFolder.cs index ac57cef729..ad82962194 100644 --- a/MailKit/IMailFolder.cs +++ b/MailKit/IMailFolder.cs @@ -64,7 +64,7 @@ public interface IMailFolder : IEnumerable /// Root-level folders do not have a parent folder. /// /// The parent folder. - IMailFolder ParentFolder { get; } + IMailFolder? ParentFolder { get; } /// /// Get the folder attributes. diff --git a/MailKit/MailFolder.cs b/MailKit/MailFolder.cs index e10ea0352e..95fbd1a011 100644 --- a/MailKit/MailFolder.cs +++ b/MailKit/MailFolder.cs @@ -59,7 +59,7 @@ public abstract class MailFolder : IMailFolder protected static readonly MessageFlags SettableFlags = MessageFlags.Answered | MessageFlags.Deleted | MessageFlags.Draft | MessageFlags.Flagged | MessageFlags.Seen; - IMailFolder parent; + IMailFolder? parent; /// /// Initialize a new instance of the class. @@ -67,11 +67,53 @@ public abstract class MailFolder : IMailFolder /// /// Initializes a new instance of the class. /// + [Obsolete ("Use MailFolder (string fullName, char directorySeparator, FolderAttributes attributes) instead.")] protected MailFolder () { + PermanentKeywords = new HashSet (StringComparer.Ordinal); + AcceptedKeywords = new HashSet (StringComparer.Ordinal); + FullName = string.Empty; + Name = string.Empty; FirstUnread = -1; } + /// + /// Initialize a new instance of the class. + /// + /// + /// Initializes a new instance of the class. + /// + /// The full name (path) of the folder. + /// The directory separator used by the folder. + /// The attributes of the folder. + /// + /// is . + /// + protected MailFolder (string fullName, char directorySeparator, FolderAttributes attributes) + { + if (fullName == null) + throw new ArgumentNullException (nameof (fullName)); + + PermanentKeywords = new HashSet (StringComparer.Ordinal); + AcceptedKeywords = new HashSet (StringComparer.Ordinal); + Name = GetBaseName (fullName, directorySeparator); + DirectorySeparator = directorySeparator; + Attributes = attributes; + FullName = fullName; + + FirstUnread = -1; + } + + internal static string GetBaseName (string fullName, char delim) + { + int index; + + if ((index = fullName.LastIndexOf (delim)) != -1) + return fullName.Substring (index + 1); + + return fullName; + } + /// /// Get an object that can be used to synchronize access to the folder. /// @@ -90,7 +132,7 @@ public abstract object SyncRoot { /// Root-level folders do not have a parent folder. /// /// The parent folder. - public IMailFolder ParentFolder { + public IMailFolder? ParentFolder { get { return parent; } internal protected set { if (value == parent) diff --git a/MailKit/Net/Imap/ImapClient.cs b/MailKit/Net/Imap/ImapClient.cs index 63f785fed9..ba7c41aa9f 100644 --- a/MailKit/Net/Imap/ImapClient.cs +++ b/MailKit/Net/Imap/ImapClient.cs @@ -67,7 +67,7 @@ public partial class ImapClient : MailStore, IImapClient const string HexAlphabet = "0123456789ABCDEF"; readonly ImapAuthenticationSecretDetector detector = new ImapAuthenticationSecretDetector (); - readonly ImapEngine engine; + internal readonly ImapEngine engine; SslCertificateValidationInfo? sslValidationInfo; int timeout = 2 * 60 * 1000; string? identifier; diff --git a/MailKit/Net/Imap/ImapFolder.cs b/MailKit/Net/Imap/ImapFolder.cs index 3a99ff3c6c..8566e5b723 100644 --- a/MailKit/Net/Imap/ImapFolder.cs +++ b/MailKit/Net/Imap/ImapFolder.cs @@ -75,26 +75,45 @@ public partial class ImapFolder : MailFolder, IImapFolder /// is . /// public ImapFolder (ImapFolderConstructorArgs args) + : base (args?.FullName ?? string.Empty, args?.DirectorySeparator ?? '.', args?.Attributes ?? FolderAttributes.None) { if (args == null) throw new ArgumentNullException (nameof (args)); - PermanentKeywords = new HashSet (StringComparer.Ordinal); - AcceptedKeywords = new HashSet (StringComparer.Ordinal); - - InitializeProperties (args); + EncodedName = args.EncodedName; + Engine = args.Engine; } - [MemberNotNull ("Engine", "EncodedName")] - void InitializeProperties (ImapFolderConstructorArgs args) +#if false + /// + /// Initializes a new instance of the class. + /// + /// + /// Creates a new . + /// If you subclass , you will also need to subclass + /// and override the + /// + /// method in order to return a new instance of your ImapFolder subclass. + /// + /// The IMAP client that the folder belongs to. + /// The full anme of the IMAP folder. + /// The directory separator used by the IMAP folder. + /// The attributes of the IMAP folder. + /// + /// is . + /// -or- + /// is . + /// + public ImapFolder (ImapClient client, string fullName, char directorySeparator, FolderAttributes attributes) + : base (fullName, directorySeparator, attributes) { - DirectorySeparator = args.DirectorySeparator; - EncodedName = args.EncodedName; - Attributes = args.Attributes; - FullName = args.FullName; - Engine = args.Engine; - Name = args.Name; + if (client == null) + throw new ArgumentNullException (nameof (client)); + + EncodedName = client.engine.EncodeMailboxName (fullName); + Engine = client.engine; } +#endif /// /// Get the IMAP command engine. @@ -231,7 +250,7 @@ protected override void OnParentFolderRenamed () { var oldEncodedName = EncodedName; - FullName = ParentFolder.FullName + DirectorySeparator + Name; + FullName = ParentFolder!.FullName + DirectorySeparator + Name; EncodedName = Engine.EncodeMailboxName (FullName); Engine.FolderCache.Remove (oldEncodedName); Engine.FolderCache[EncodedName] = this; @@ -6362,11 +6381,15 @@ internal void UpdateUidValidity (uint validity) OnUidValidityChanged (); } - internal void OnRenamed (ImapFolderConstructorArgs args) + internal void OnRenamed (string encodedName, char delim, FolderAttributes attrs) { var oldFullName = FullName; - InitializeProperties (args); + EncodedName = encodedName; + FullName = Engine.DecodeMailboxName (encodedName); + Name = GetBaseName (FullName, delim); + DirectorySeparator = delim; + Attributes = attrs; OnRenamed (oldFullName, FullName); } diff --git a/MailKit/Net/Imap/ImapFolderConstructorArgs.cs b/MailKit/Net/Imap/ImapFolderConstructorArgs.cs index c20402959a..5548e5e035 100644 --- a/MailKit/Net/Imap/ImapFolderConstructorArgs.cs +++ b/MailKit/Net/Imap/ImapFolderConstructorArgs.cs @@ -50,7 +50,6 @@ public sealed class ImapFolderConstructorArgs internal ImapFolderConstructorArgs (ImapEngine engine, string encodedName, FolderAttributes attributes, char delim) { FullName = engine.DecodeMailboxName (encodedName); - Name = GetBaseName (FullName, delim); DirectorySeparator = delim; EncodedName = encodedName; Attributes = attributes; @@ -97,18 +96,9 @@ public string FullName { /// This is the equivalent of the file name of a file on the file system. /// /// The name of the folder. + [Obsolete] public string Name { - get; private set; - } - - static string GetBaseName (string fullName, char delim) - { - int index; - - if ((index = fullName.LastIndexOf (delim)) != -1) - return fullName.Substring (index + 1); - - return fullName; + get { return MailFolder.GetBaseName (FullName, DirectorySeparator); } } } } diff --git a/MailKit/Net/Imap/ImapUtils.cs b/MailKit/Net/Imap/ImapUtils.cs index ad73bf3142..f2b089460b 100644 --- a/MailKit/Net/Imap/ImapUtils.cs +++ b/MailKit/Net/Imap/ImapUtils.cs @@ -33,10 +33,10 @@ using System.Threading.Tasks; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using MimeKit; using MimeKit.Utils; -using System.Diagnostics.CodeAnalysis; namespace MailKit.Net.Imap { /// @@ -615,11 +615,9 @@ static void ProcessListExtensionProperty (ImapEngine engine, ref ImapFolder? fol var oldEncodedName = value.TrimEnd (delim); if (engine.FolderCache.TryGetValue (oldEncodedName, out ImapFolder? oldFolder)) { - var args = new ImapFolderConstructorArgs (engine, encodedName, attrs, delim); - engine.FolderCache.Remove (oldEncodedName); engine.FolderCache[encodedName] = oldFolder; - oldFolder.OnRenamed (args); + oldFolder.OnRenamed (encodedName, delim, attrs); folder = oldFolder; } } From 32aead8b69e5edeea748052e83ccee7808d02c4c Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 21 Feb 2026 20:46:49 -0500 Subject: [PATCH 23/44] Fixed BodyPart (and subclasses) for nullability --- MailKit/BodyPart.cs | 60 +++++++++++++++------- MailKit/BodyPartBasic.cs | 20 +++++++- MailKit/BodyPartCollection.cs | 2 +- MailKit/BodyPartMessage.cs | 22 +++++++- MailKit/BodyPartMultipart.cs | 44 +++++++++++++++- MailKit/BodyPartText.cs | 22 +++++++- MailKit/Net/Imap/ImapUtils.cs | 46 +++++++++-------- UnitTests/AnnotationEntryTests.cs | 5 +- UnitTests/BodyPartTests.cs | 43 +++++++--------- UnitTests/MessageSummaryTests.cs | 8 +-- UnitTests/Net/Imap/ImapFolderFetchTests.cs | 12 ++--- 11 files changed, 199 insertions(+), 85 deletions(-) diff --git a/MailKit/BodyPart.cs b/MailKit/BodyPart.cs index 94b98dbba8..f66785c04b 100644 --- a/MailKit/BodyPart.cs +++ b/MailKit/BodyPart.cs @@ -53,8 +53,36 @@ public abstract class BodyPart /// /// Creates a new . /// + [Obsolete ("Use BodyPart (ContentType, string) instead.")] protected BodyPart () { + ContentType = new ContentType ("application", "octet-stream"); + PartSpecifier = string.Empty; + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Creates a new . + /// + /// The content type. + /// The part specifier. + /// + /// is . + /// -or- + /// is . + /// + protected BodyPart (ContentType contentType, string partSpecifier) + { + if (contentType == null) + throw new ArgumentNullException (nameof (contentType)); + + if (partSpecifier == null) + throw new ArgumentNullException (nameof (partSpecifier)); + + ContentType = contentType; + PartSpecifier = partSpecifier; } /// @@ -350,7 +378,8 @@ static bool TryParse (string text, ref int index, out string[]? values) if (!TryParse (text, ref index, out string? value)) return false; - list.Add (value); + if (value != null) + list.Add (value); } while (index < text.Length); if (index >= text.Length || text[index] != ')') @@ -500,17 +529,17 @@ static bool TryParse (string text, ref int index, bool multipart, [NotNullWhen ( return true; } - static bool TryParse (string text, ref int index, string prefix, [NotNullWhen (true)] out IList? children) + static bool TryParse (string text, ref int index, string prefix, [NotNullWhen (true)] out BodyPartCollection? bodyParts) { string path; int id = 1; - children = null; + bodyParts = null; if (index >= text.Length) return false; - children = new List (); + bodyParts = new BodyPartCollection (); do { if (text[index] != '(') @@ -524,7 +553,9 @@ static bool TryParse (string text, ref int index, string prefix, [NotNullWhen (t while (index < text.Length && text[index] == ' ') index++; - children.Add (part); + if (part != null) + bodyParts.Add (part); + id++; } while (index < text.Length); @@ -559,14 +590,11 @@ static bool TryParse (string text, ref int index, string path, out BodyPart? par if (text[index] == '(' || IsNIL (text, index)) { var prefix = path.Length > 0 ? path + "." : string.Empty; - var multipart = new BodyPartMultipart (); + BodyPartCollection? bodyParts = null; if (text[index] == '(') { - if (!TryParse (text, ref index, prefix, out IList? children)) + if (!TryParse (text, ref index, prefix, out bodyParts)) return false; - - foreach (var child in children) - multipart.BodyParts.Add (child); } else { index += "NIL".Length; } @@ -574,7 +602,7 @@ static bool TryParse (string text, ref int index, string path, out BodyPart? par if (!TryParse (text, ref index, true, out contentType)) return false; - multipart.ContentType = contentType; + var multipart = new BodyPartMultipart (contentType, path, bodyParts ?? new BodyPartCollection ()); if (!TryParse (text, ref index, out disposition)) return false; @@ -602,13 +630,11 @@ static bool TryParse (string text, ref int index, string path, out BodyPart? par return false; if (contentType.IsMimeType ("message", "rfc822")) - basic = message = new BodyPartMessage (); + basic = message = new BodyPartMessage (contentType, path); else if (contentType.IsMimeType ("text", "*")) - basic = txt = new BodyPartText (); + basic = txt = new BodyPartText (contentType, path); else - basic = new BodyPartBasic (); - - basic.ContentType = contentType; + basic = new BodyPartBasic (contentType, path); if (!TryParse (text, ref index, out nstring)) return false; @@ -675,8 +701,6 @@ static bool TryParse (string text, ref int index, string path, out BodyPart? par part = basic; } - part.PartSpecifier = path; - if (index >= text.Length || text[index] != ')') return false; diff --git a/MailKit/BodyPartBasic.cs b/MailKit/BodyPartBasic.cs index 86bb98b242..50c1ced90a 100644 --- a/MailKit/BodyPartBasic.cs +++ b/MailKit/BodyPartBasic.cs @@ -48,7 +48,25 @@ public class BodyPartBasic : BodyPart /// /// Creates a new . /// - public BodyPartBasic () + [Obsolete ("Use BodyPartBasic (ContentType, string) instead.")] + public BodyPartBasic () : base () + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Creates a new . + /// + /// The content type. + /// The part specifier. + /// + /// is . + /// -or- + /// is . + /// + public BodyPartBasic (ContentType contentType, string partSpecifier) : base (contentType, partSpecifier) { } diff --git a/MailKit/BodyPartCollection.cs b/MailKit/BodyPartCollection.cs index 0cc8769ab0..2ac68f6e16 100644 --- a/MailKit/BodyPartCollection.cs +++ b/MailKit/BodyPartCollection.cs @@ -219,7 +219,7 @@ public int IndexOf (Uri uri) if (cid) { if (!string.IsNullOrEmpty (bodyPart.ContentId)) { // Note: we might have a Content-Id in the form "", so attempt to decode it - var id = MimeUtils.EnumerateReferences (bodyPart.ContentId).FirstOrDefault () ?? bodyPart.ContentId; + var id = MimeUtils.EnumerateReferences (bodyPart.ContentId!).FirstOrDefault () ?? bodyPart.ContentId; if (id == uri.AbsolutePath) return index; diff --git a/MailKit/BodyPartMessage.cs b/MailKit/BodyPartMessage.cs index 6fdca120c0..3517812b6a 100644 --- a/MailKit/BodyPartMessage.cs +++ b/MailKit/BodyPartMessage.cs @@ -27,6 +27,8 @@ using System; using System.Text; +using MimeKit; + namespace MailKit { /// /// A message/rfc822 body part. @@ -42,7 +44,25 @@ public class BodyPartMessage : BodyPartBasic /// /// Creates a new . /// - public BodyPartMessage () + [Obsolete ("Use BodyPartMessage (ContentType, string) instead.")] + public BodyPartMessage () : this (new ContentType ("message", "rfc822"), string.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Creates a new . + /// + /// The content type. + /// The part specifier. + /// + /// is . + /// -or- + /// is . + /// + public BodyPartMessage (ContentType contentType, string partSpecifier) : base (contentType, partSpecifier) { } diff --git a/MailKit/BodyPartMultipart.cs b/MailKit/BodyPartMultipart.cs index 549e32ba95..7e1a8df9ba 100644 --- a/MailKit/BodyPartMultipart.cs +++ b/MailKit/BodyPartMultipart.cs @@ -44,11 +44,53 @@ public class BodyPartMultipart : BodyPart /// /// Creates a new . /// - public BodyPartMultipart () + [Obsolete ("Use BodyPartMultipart (ContentType, string) instead.")] + public BodyPartMultipart () : this (new ContentType ("multipart", "mixed"), string.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Creates a new . + /// + /// The content type. + /// The part specifier. + /// + /// is . + /// -or- + /// is . + /// + public BodyPartMultipart (ContentType contentType, string partSpecifier) : base (contentType, partSpecifier) { BodyParts = new BodyPartCollection (); } + /// + /// Initializes a new instance of the class. + /// + /// + /// Creates a new . + /// + /// The content type. + /// The part specifier. + /// The child body parts of the multipart. + /// + /// is . + /// -or- + /// is . + /// -or- + /// is . + /// + public BodyPartMultipart (ContentType contentType, string partSpecifier, BodyPartCollection bodyParts) : base (contentType, partSpecifier) + { + if (bodyParts is null) + throw new ArgumentNullException (nameof (bodyParts)); + + BodyParts = bodyParts; + } + /// /// Gets the child body parts. /// diff --git a/MailKit/BodyPartText.cs b/MailKit/BodyPartText.cs index c7bdca47f1..1924b94be9 100644 --- a/MailKit/BodyPartText.cs +++ b/MailKit/BodyPartText.cs @@ -27,6 +27,8 @@ using System; using System.Text; +using MimeKit; + namespace MailKit { /// /// A textual body part. @@ -45,7 +47,25 @@ public class BodyPartText : BodyPartBasic /// /// Creates a new . /// - public BodyPartText () + [Obsolete ("Use BodyPartText (ContentType, string) instead.")] + public BodyPartText () : this (new ContentType ("text", "plain"), string.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Creates a new . + /// + /// The content type. + /// The part specifier. + /// + /// is . + /// -or- + /// is . + /// + public BodyPartText (ContentType contentType, string partSpecifier) : base (contentType, partSpecifier) { } diff --git a/MailKit/Net/Imap/ImapUtils.cs b/MailKit/Net/Imap/ImapUtils.cs index f2b089460b..7126e29012 100644 --- a/MailKit/Net/Imap/ImapUtils.cs +++ b/MailKit/Net/Imap/ImapUtils.cs @@ -1512,7 +1512,7 @@ static async Task SkipBodyExtensionAsync (ImapEngine engine, string format, Canc static BodyPart ParseMultipart (ImapEngine engine, string format, string path, CancellationToken cancellationToken) { var prefix = path.Length > 0 ? path + "." : string.Empty; - var body = new BodyPartMultipart (); + var bodyParts = new BodyPartCollection (); ImapToken token; int index = 1; @@ -1520,7 +1520,11 @@ static BodyPart ParseMultipart (ImapEngine engine, string format, string path, C if (token.Type != ImapTokenType.Nil) { do { - body.BodyParts.Add (ParseBody (engine, format, prefix + index, cancellationToken)); + var part = ParseBody (engine, format, prefix + index, cancellationToken); + + if (part != null) + bodyParts.Add (part); + token = engine.PeekToken (cancellationToken); index++; } while (token.Type == ImapTokenType.OpenParen); @@ -1535,9 +1539,8 @@ static BodyPart ParseMultipart (ImapEngine engine, string format, string path, C } var subtype = ReadStringToken (engine, format, cancellationToken); - - body.ContentType = new ContentType ("multipart", subtype); - body.PartSpecifier = path; + var contentType = new ContentType ("multipart", subtype); + var body = new BodyPartMultipart (contentType, path, bodyParts); token = engine.PeekToken (cancellationToken); @@ -1554,7 +1557,7 @@ static BodyPart ParseMultipart (ImapEngine engine, string format, string path, C if (token.Type == ImapTokenType.OpenParen) ParseParameterList (builder, engine, format, cancellationToken); - if (ContentType.TryParse (builder.ToString (), out var contentType)) + if (ContentType.TryParse (builder.ToString (), out contentType)) body.ContentType = contentType; token = engine.PeekToken (cancellationToken); @@ -1600,7 +1603,7 @@ static BodyPart ParseMultipart (ImapEngine engine, string format, string path, C static async Task ParseMultipartAsync (ImapEngine engine, string format, string path, CancellationToken cancellationToken) { var prefix = path.Length > 0 ? path + "." : string.Empty; - var body = new BodyPartMultipart (); + var bodyParts = new BodyPartCollection (); ImapToken token; int index = 1; @@ -1608,7 +1611,11 @@ static async Task ParseMultipartAsync (ImapEngine engine, string forma if (token.Type != ImapTokenType.Nil) { do { - body.BodyParts.Add (await ParseBodyAsync (engine, format, prefix + index, cancellationToken).ConfigureAwait (false)); + var part = await ParseBodyAsync (engine, format, prefix + index, cancellationToken).ConfigureAwait (false); + + if (part != null) + bodyParts.Add (part); + token = await engine.PeekTokenAsync (cancellationToken).ConfigureAwait (false); index++; } while (token.Type == ImapTokenType.OpenParen); @@ -1623,9 +1630,8 @@ static async Task ParseMultipartAsync (ImapEngine engine, string forma } var subtype = await ReadStringTokenAsync (engine, format, cancellationToken).ConfigureAwait (false); - - body.ContentType = new ContentType ("multipart", subtype); - body.PartSpecifier = path; + var contentType = new ContentType ("multipart", subtype); + var body = new BodyPartMultipart (contentType, path, bodyParts); token = await engine.PeekTokenAsync (cancellationToken).ConfigureAwait (false); @@ -1642,7 +1648,7 @@ static async Task ParseMultipartAsync (ImapEngine engine, string forma if (token.Type == ImapTokenType.OpenParen) await ParseParameterListAsync (builder, engine, format, cancellationToken).ConfigureAwait (false); - if (ContentType.TryParse (builder.ToString (), out var contentType)) + if (ContentType.TryParse (builder.ToString (), out contentType)) body.ContentType = contentType; token = await engine.PeekTokenAsync (cancellationToken).ConfigureAwait (false); @@ -1897,7 +1903,7 @@ static async Task ShouldParseMultipartAsync (ImapEngine engine, Cancellati BodyPartBasic body; if (type.IsMimeType ("message", "rfc822")) { - var rfc822 = new BodyPartMessage (); + var rfc822 = new BodyPartMessage (type, path); // Note: GMail (and potentially other IMAP servers) will send body-part-basic // expressions instead of body-part-msg expressions when they encounter @@ -1922,19 +1928,17 @@ static async Task ShouldParseMultipartAsync (ImapEngine engine, Cancellati body = rfc822; } else if (type.IsMimeType ("text", "*")) { - var text = new BodyPartText { + var text = new BodyPartText (type, path) { Lines = ReadNumber (engine, format, cancellationToken) }; body = text; } else { isMultipart = type.IsMimeType ("multipart", "*"); - body = new BodyPartBasic (); + body = new BodyPartBasic (type, path); } body.ContentTransferEncoding = enc; body.ContentDescription = desc; - body.PartSpecifier = path; - body.ContentType = type; body.ContentId = id; body.Octets = octets; @@ -2006,7 +2010,7 @@ static async Task ShouldParseMultipartAsync (ImapEngine engine, Cancellati BodyPartBasic body; if (type.IsMimeType ("message", "rfc822")) { - var rfc822 = new BodyPartMessage (); + var rfc822 = new BodyPartMessage (type, path); // Note: GMail (and potentially other IMAP servers) will send body-part-basic // expressions instead of body-part-msg expressions when they encounter @@ -2031,19 +2035,17 @@ static async Task ShouldParseMultipartAsync (ImapEngine engine, Cancellati body = rfc822; } else if (type.IsMimeType ("text", "*")) { - var text = new BodyPartText { + var text = new BodyPartText (type, path) { Lines = await ReadNumberAsync (engine, format, cancellationToken).ConfigureAwait (false) }; body = text; } else { isMultipart = type.IsMimeType ("multipart", "*"); - body = new BodyPartBasic (); + body = new BodyPartBasic (type, path); } body.ContentTransferEncoding = enc; body.ContentDescription = desc; - body.PartSpecifier = path; - body.ContentType = type; body.ContentId = id; body.Octets = octets; diff --git a/UnitTests/AnnotationEntryTests.cs b/UnitTests/AnnotationEntryTests.cs index 5127eb30fa..bd4917fc6c 100644 --- a/UnitTests/AnnotationEntryTests.cs +++ b/UnitTests/AnnotationEntryTests.cs @@ -24,6 +24,7 @@ // THE SOFTWARE. // +using MimeKit; using MailKit; namespace UnitTests { @@ -60,9 +61,7 @@ public void TestArgumentExceptions () [Test] public void TestBasicFunctionality () { - var body = new BodyPartBasic { - PartSpecifier = "1.2.3.4" - }; + var body = new BodyPartBasic (new ContentType ("image", "jpeg"), "1.2.3.4"); AnnotationEntry entry; entry = new AnnotationEntry ("/comment"); diff --git a/UnitTests/BodyPartTests.cs b/UnitTests/BodyPartTests.cs index c3077643e5..7329238db1 100644 --- a/UnitTests/BodyPartTests.cs +++ b/UnitTests/BodyPartTests.cs @@ -43,13 +43,13 @@ public void TestBodyPartBasic () { var uri = new Uri ("https://www.nationalgeographic.com/travel/contests/photographer-of-the-year-2018/wallpapers/week-9-nature/2/"); const string expected = "(\"image\" \"jpeg\" (\"name\" \"wallpaper.jpg\") \"id@localhost\" \"A majestic supercell storm approaching a house in Kansas, 2016.\" \"base64\" 0 \"8criUiOQmpfifOuOmYFtEQ==\" (\"attachment\" (\"filename\" \"wallpaper.jpg\")) (\"en\" \"fr\") \"https://www.nationalgeographic.com/travel/contests/photographer-of-the-year-2018/wallpapers/week-9-nature/2/\")"; + var contentType = new ContentType ("image", "jpeg") { + Name = "wallpaper.jpg" + }; BodyPartBasic basic, parsed; BodyPart body; - basic = new BodyPartBasic { - ContentType = new ContentType ("image", "jpeg") { - Name = "wallpaper.jpg" - }, + basic = new BodyPartBasic (contentType, string.Empty) { ContentId = "id@localhost", ContentMd5 = "8criUiOQmpfifOuOmYFtEQ==", ContentLanguage = new string[] { "en", "fr" }, @@ -104,11 +104,11 @@ public void TestNilSerialization () public void TestSimplePlainTextBody () { const string expected = "(\"text\" \"plain\" (\"charset\" \"us-ascii\" \"name\" \"body.txt\") NIL NIL \"7bit\" 3028 NIL NIL NIL NIL 92)"; + var contentType = new ContentType ("text", "plain") { Charset = "us-ascii", Name = "body.txt" }; BodyPartText text, parsed; BodyPart body; - text = new BodyPartText { - ContentType = new ContentType ("text", "plain") { Charset = "us-ascii", Name = "body.txt" }, + text = new BodyPartText (contentType, string.Empty) { ContentTransferEncoding = "7bit", Octets = 3028, Lines = 92, @@ -135,9 +135,9 @@ public void TestSimplePlainTextBody () [Test] public void TestBodyPartCollection () { - var text = new BodyPartText { ContentType = new ContentType ("text", "plain"), ContentLocation = new Uri ("body", UriKind.Relative) }; - var image1 = new BodyPartBasic { ContentType = new ContentType ("image", "jpeg"), ContentLocation = new Uri ("http://localhost/image1.jpg") }; - var image2 = new BodyPartBasic { ContentType = new ContentType ("image", "jpeg"), ContentId = "image2@localhost" }; + var text = new BodyPartText (new ContentType ("text", "plain"), string.Empty) { ContentLocation = new Uri ("body", UriKind.Relative) }; + var image1 = new BodyPartBasic (new ContentType ("image", "jpeg"), string.Empty) { ContentLocation = new Uri ("http://localhost/image1.jpg") }; + var image2 = new BodyPartBasic (new ContentType ("image", "jpeg"), string.Empty) { ContentId = "image2@localhost" }; var list = new BodyPartCollection (); var parts = new BodyPart[3]; int i = 0; @@ -225,12 +225,8 @@ public void TestNestedBodyStructure () [Test] public void TestMultipartWithNoChildren () { - var original = new BodyPartMultipart { - ContentType = new ContentType ("multipart", "mixed") { Boundary = "----=_NextPart_000_001" } - }; - original.BodyParts.Add (new BodyPartMultipart { - ContentType = new ContentType ("multipart", "alternative") { Boundary = "----=_AlternativePart_001_001" } - }); + var original = new BodyPartMultipart (new ContentType ("multipart", "mixed") { Boundary = "----=_NextPart_000_001" }, string.Empty); + original.BodyParts.Add (new BodyPartMultipart (new ContentType ("multipart", "alternative") { Boundary = "----=_AlternativePart_001_001" }, string.Empty)); var serialized = original.ToString (); @@ -258,8 +254,7 @@ static ContentType CreateContentType (string type, string subtype, string partSp static BodyPartMessage CreateMessage (string type, string subtype, string partSpecifier, BodyPart body) { - var message = new BodyPartMessage { - ContentType = CreateContentType (type, subtype, partSpecifier), + var message = new BodyPartMessage (CreateContentType (type, subtype, partSpecifier), partSpecifier) { Body = body }; return message; @@ -267,7 +262,7 @@ static BodyPartMessage CreateMessage (string type, string subtype, string partSp static BodyPartMultipart CreateMultipart (string type, string subtype, string partSpecifier, params BodyPart[] bodyParts) { - var multipart = new BodyPartMultipart { ContentType = CreateContentType (type, subtype, partSpecifier) }; + var multipart = new BodyPartMultipart (CreateContentType (type, subtype, partSpecifier), partSpecifier); foreach (var bodyPart in bodyParts) multipart.BodyParts.Add (bodyPart); return multipart; @@ -275,12 +270,12 @@ static BodyPartMultipart CreateMultipart (string type, string subtype, string pa static BodyPartBasic CreateBasic (string type, string subtype, string partSpecifier) { - return new BodyPartBasic { ContentType = CreateContentType (type, subtype, partSpecifier) }; + return new BodyPartBasic (CreateContentType (type, subtype, partSpecifier), partSpecifier); } static BodyPartText CreateText (string type, string subtype, string partSpecifier) { - return new BodyPartText { ContentType = CreateContentType (type, subtype, partSpecifier) }; + return new BodyPartText (CreateContentType (type, subtype, partSpecifier), partSpecifier); } static void VerifyPartSpecifier (BodyPart part) @@ -375,10 +370,10 @@ public void TestComplexPartSpecifiersExampleRfc3501 () visitor.Visit (body); Assert.That (visitor.ToString (), Is.EqualTo (expected)); - Assert.Throws (() => new BodyPartText ().Accept (null)); - Assert.Throws (() => new BodyPartBasic ().Accept (null)); - Assert.Throws (() => new BodyPartMessage ().Accept (null)); - Assert.Throws (() => new BodyPartMultipart ().Accept (null)); + Assert.Throws (() => new BodyPartText (new ContentType ("text", "plain"), string.Empty).Accept (null)); + Assert.Throws (() => new BodyPartBasic (new ContentType ("image", "jpeg"), string.Empty).Accept (null)); + Assert.Throws (() => new BodyPartMessage (new ContentType ("message", "rfc822"), string.Empty).Accept (null)); + Assert.Throws (() => new BodyPartMultipart (new ContentType ("multipart", "mixed"), string.Empty).Accept (null)); var encoded = body.ToString (); diff --git a/UnitTests/MessageSummaryTests.cs b/UnitTests/MessageSummaryTests.cs index cbd980bb87..1c8a9eff18 100644 --- a/UnitTests/MessageSummaryTests.cs +++ b/UnitTests/MessageSummaryTests.cs @@ -93,7 +93,7 @@ static ContentType CreateContentType (string type, string subtype, string partSp static BodyPartMessage CreateMessage (string type, string subtype, string partSpecifier, BodyPart body, bool attachment) { - var message = new BodyPartMessage { ContentType = CreateContentType (type, subtype, partSpecifier) }; + var message = new BodyPartMessage (CreateContentType (type, subtype, partSpecifier), partSpecifier); if (attachment) message.ContentDisposition = new ContentDisposition (ContentDisposition.Attachment); message.Body = body; @@ -102,7 +102,7 @@ static BodyPartMessage CreateMessage (string type, string subtype, string partSp static BodyPartMultipart CreateMultipart (string type, string subtype, string partSpecifier, params BodyPart [] bodyParts) { - var multipart = new BodyPartMultipart { ContentType = CreateContentType (type, subtype, partSpecifier) }; + var multipart = new BodyPartMultipart (CreateContentType (type, subtype, partSpecifier), partSpecifier); foreach (var bodyPart in bodyParts) multipart.BodyParts.Add (bodyPart); return multipart; @@ -110,14 +110,14 @@ static BodyPartMultipart CreateMultipart (string type, string subtype, string pa static BodyPartBasic CreateBasic (string type, string subtype, string partSpecifier, bool attachment) { - var basic = new BodyPartBasic { ContentType = CreateContentType (type, subtype, partSpecifier) }; + var basic = new BodyPartBasic (CreateContentType (type, subtype, partSpecifier), partSpecifier); basic.ContentDisposition = new ContentDisposition (attachment ? ContentDisposition.Attachment : ContentDisposition.Inline); return basic; } static BodyPartText CreateText (string type, string subtype, string partSpecifier, bool attachment) { - var text = new BodyPartText { ContentType = CreateContentType (type, subtype, partSpecifier) }; + var text = new BodyPartText (CreateContentType (type, subtype, partSpecifier), partSpecifier); if (attachment) text.ContentDisposition = new ContentDisposition (ContentDisposition.Attachment); return text; diff --git a/UnitTests/Net/Imap/ImapFolderFetchTests.cs b/UnitTests/Net/Imap/ImapFolderFetchTests.cs index 4dda1bb29c..232679f310 100644 --- a/UnitTests/Net/Imap/ImapFolderFetchTests.cs +++ b/UnitTests/Net/Imap/ImapFolderFetchTests.cs @@ -252,7 +252,7 @@ public void TestArgumentExceptions () Assert.Throws (() => inbox.GetHeaders (UniqueId.Invalid)); Assert.ThrowsAsync (async () => await inbox.GetHeadersAsync (UniqueId.Invalid)); - var bodyPart = new BodyPartText (); + var bodyPart = new BodyPartText (new ContentType ("text", "plain"), "1.2"); Assert.Throws (() => inbox.GetHeaders (-1, bodyPart)); Assert.ThrowsAsync (async () => await inbox.GetHeadersAsync (-1, bodyPart)); @@ -2624,10 +2624,7 @@ public void TestYandexGetBodyPartMissingContent () //var messages = client.Inbox.Fetch (0, -1, MessageSummaryItems.UniqueId | MessageSummaryItems.Flags | MessageSummaryItems.ModSeq); //Assert.That (messages, Has.Count.EqualTo (74), "Count"); - var bodyPart = new BodyPartBasic { - PartSpecifier = "2", - }; - + var bodyPart = new BodyPartBasic (new ContentType ("application", "pdf"), "2"); var body = client.Inbox.GetBodyPart (new UniqueId (3016), bodyPart); Assert.That (body, Is.Not.Null); Assert.That (body, Is.InstanceOf ()); @@ -2675,10 +2672,7 @@ public async Task TestYandexGetBodyPartMissingContentAsync () //var messages = client.Inbox.Fetch (0, -1, MessageSummaryItems.UniqueId | MessageSummaryItems.Flags | MessageSummaryItems.ModSeq); //Assert.That (messages, Has.Count.EqualTo (74), "Count"); - var bodyPart = new BodyPartBasic { - PartSpecifier = "2", - }; - + var bodyPart = new BodyPartBasic (new ContentType ("application", "pdf"), "2"); var body = await client.Inbox.GetBodyPartAsync (new UniqueId (3016), bodyPart); Assert.That (body, Is.Not.Null); Assert.That (body, Is.InstanceOf ()); From bf690fb2c6175cf1a4b323a64d5bc8695062a044 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 21 Feb 2026 21:01:11 -0500 Subject: [PATCH 24/44] Fixed BodyPartVisitor nullability --- MailKit/BodyPartVisitor.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/MailKit/BodyPartVisitor.cs b/MailKit/BodyPartVisitor.cs index b2e6bbec9a..78160968b2 100644 --- a/MailKit/BodyPartVisitor.cs +++ b/MailKit/BodyPartVisitor.cs @@ -88,7 +88,7 @@ protected internal virtual void VisitBodyPartBasic (BodyPartBasic entity) /// The body part representing the message/rfc822 message. protected virtual void VisitMessage (BodyPart message) { - message?.Accept (this); + message.Accept (this); } /// @@ -101,7 +101,9 @@ protected virtual void VisitMessage (BodyPart message) protected internal virtual void VisitBodyPartMessage (BodyPartMessage entity) { VisitBodyPartBasic (entity); - VisitMessage (entity.Body); + + if (entity.Body != null) + VisitMessage (entity.Body); } /// From 3a0a4c93d4b8d25bb031e9bd784f85ceffe854ca Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 21 Feb 2026 21:29:36 -0500 Subject: [PATCH 25/44] Fixed ImapProtocolException ResponseText nullability --- MailKit/Net/Imap/AsyncImapClient.cs | 2 +- MailKit/Net/Imap/ImapClient.cs | 2 +- MailKit/Net/Imap/ImapProtocolException.cs | 28 +++++++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/MailKit/Net/Imap/AsyncImapClient.cs b/MailKit/Net/Imap/AsyncImapClient.cs index 4bac582461..a875fc273d 100644 --- a/MailKit/Net/Imap/AsyncImapClient.cs +++ b/MailKit/Net/Imap/AsyncImapClient.cs @@ -443,7 +443,7 @@ public override async Task AuthenticateAsync (Encoding encoding, ICredentials cr if (ic.Response != ImapCommandResponse.Ok) { EmitAndThrowOnAlert (ic); if (ic.Bye) - throw new ImapProtocolException (ic.ResponseText); + throw ImapProtocolException.Create (ic); continue; } diff --git a/MailKit/Net/Imap/ImapClient.cs b/MailKit/Net/Imap/ImapClient.cs index ba7c41aa9f..17a125ea1d 100644 --- a/MailKit/Net/Imap/ImapClient.cs +++ b/MailKit/Net/Imap/ImapClient.cs @@ -1325,7 +1325,7 @@ public override void Authenticate (Encoding encoding, ICredentials credentials, if (ic.Response != ImapCommandResponse.Ok) { EmitAndThrowOnAlert (ic); if (ic.Bye) - throw new ImapProtocolException (ic.ResponseText); + throw ImapProtocolException.Create (ic); continue; } diff --git a/MailKit/Net/Imap/ImapProtocolException.cs b/MailKit/Net/Imap/ImapProtocolException.cs index cfaf403bd6..eab35b401a 100644 --- a/MailKit/Net/Imap/ImapProtocolException.cs +++ b/MailKit/Net/Imap/ImapProtocolException.cs @@ -106,5 +106,33 @@ public ImapProtocolException () internal bool UnexpectedToken { get; set; } + + /// + /// Create a new based on the state. + /// + /// + /// Create a new based on the state. + /// + /// A new protocol exception. + /// The command state. + internal static ImapProtocolException Create (ImapCommand ic) + { + string? message = null; + + if (string.IsNullOrEmpty (ic.ResponseText)) { + for (int i = ic.RespCodes.Count - 1; i >= 0; i--) { + if (ic.RespCodes[i].IsError && !string.IsNullOrEmpty (ic.RespCodes[i].Message)) { + message = ic.RespCodes[i].Message; + break; + } + } + } else { + message = ic.ResponseText; + } + + message ??= string.Empty; + + return ic.Exception != null ? new ImapProtocolException (message, ic.Exception) : new ImapProtocolException (message); + } } } From 31b838afff3958d8cc52b9f5599455c999b5d64b Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 21 Feb 2026 21:39:51 -0500 Subject: [PATCH 26/44] Fixed ImapUtils EnvelopeAddress nullability --- MailKit/Net/Imap/ImapUtils.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/MailKit/Net/Imap/ImapUtils.cs b/MailKit/Net/Imap/ImapUtils.cs index 7126e29012..f6dd7d4056 100644 --- a/MailKit/Net/Imap/ImapUtils.cs +++ b/MailKit/Net/Imap/ImapUtils.cs @@ -2087,12 +2087,12 @@ static async Task ShouldParseMultipartAsync (ImapEngine engine, Cancellati readonly struct EnvelopeAddress { - public readonly string Name; - public readonly string Route; - public readonly string Mailbox; - public readonly string Domain; + public readonly string? Name; + public readonly string? Route; + public readonly string? Mailbox; + public readonly string? Domain; - public EnvelopeAddress (string[] values) + public EnvelopeAddress (string?[] values) { Name = values[0]; Route = values[1]; @@ -2166,7 +2166,7 @@ public GroupAddress ToGroupAddress (ImapEngine engine) } } - static bool TryAddEnvelopeAddressToken (ImapToken token, ref int index, string[] values, bool[] qstrings, string format) + static bool TryAddEnvelopeAddressToken (ImapToken token, ref int index, string?[] values, bool[] qstrings, string format) { // This is a work-around for mail servers which output too many tokens for an ENVELOPE address. In at least 1 case, this happened // because the server sent a literal token as the name component and miscalculated the literal length as 38 when it was actually 69 @@ -2177,9 +2177,9 @@ static bool TryAddEnvelopeAddressToken (ImapToken token, ref int index, string[] // See https://github.com/jstedfast/MailKit/issues/1369 for details. if (index >= 4) { if (qstrings[0]) - values[0] = MimeUtils.Quote (values[0]); + values[0] = MimeUtils.Quote (values[0]!); if (qstrings[1]) - values[1] = MimeUtils.Quote (values[1]); + values[1] = MimeUtils.Quote (values[1]!); values[0] = values[0] + ' ' + values[1]; qstrings[0] = false; qstrings[1] = qstrings[2]; @@ -2215,7 +2215,7 @@ static bool TryAddEnvelopeAddressToken (ImapToken token, ref int index, string[] static EnvelopeAddress ParseEnvelopeAddress (ImapEngine engine, string format, CancellationToken cancellationToken) { - var values = new string[4]; + var values = new string?[4]; var qstrings = new bool[4]; ImapToken token; int index = 0; @@ -2239,7 +2239,7 @@ static EnvelopeAddress ParseEnvelopeAddress (ImapEngine engine, string format, C static async Task ParseEnvelopeAddressAsync (ImapEngine engine, string format, CancellationToken cancellationToken) { - var values = new string[4]; + var values = new string?[4]; var qstrings = new bool[4]; ImapToken token; int index = 0; From 9ad50c1b6ad86dc21d325d1267648d858eae1c1a Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 21 Feb 2026 21:49:35 -0500 Subject: [PATCH 27/44] IMAP annotation values can be NIL --- MailKit/Annotation.cs | 4 ++-- MailKit/Net/Imap/ImapUtils.cs | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/MailKit/Annotation.cs b/MailKit/Annotation.cs index c6cb1d29c1..2bc3029eff 100644 --- a/MailKit/Annotation.cs +++ b/MailKit/Annotation.cs @@ -53,7 +53,7 @@ public Annotation (AnnotationEntry entry) if (entry is null) throw new ArgumentNullException (nameof (entry)); - Properties = new Dictionary (); + Properties = new Dictionary (); Entry = entry; } @@ -75,7 +75,7 @@ public AnnotationEntry Entry { /// Gets the annotation properties. /// /// The annotation properties. - public Dictionary Properties { + public Dictionary Properties { get; private set; } } diff --git a/MailKit/Net/Imap/ImapUtils.cs b/MailKit/Net/Imap/ImapUtils.cs index f6dd7d4056..62205060d4 100644 --- a/MailKit/Net/Imap/ImapUtils.cs +++ b/MailKit/Net/Imap/ImapUtils.cs @@ -222,8 +222,13 @@ public static void FormatAnnotations (StringBuilder command, IList a foreach (var property in annotation.Properties) { command.Append (property.Key); - command.Append (" %S "); - args.Add (property.Value); + + if (property.Value != null) { + command.Append (" %S "); + args.Add (property.Value); + } else { + command.Append (" NIL "); + } } command[command.Length - 1] = ')'; From 74bb4d7d86e6dae83e8af5e52d6554436191c8cd Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 21 Feb 2026 22:14:20 -0500 Subject: [PATCH 28/44] Fixed MessageThreadingTests --- UnitTests/MessageThreadingTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/UnitTests/MessageThreadingTests.cs b/UnitTests/MessageThreadingTests.cs index 9ec6836239..f26d165df6 100644 --- a/UnitTests/MessageThreadingTests.cs +++ b/UnitTests/MessageThreadingTests.cs @@ -312,12 +312,12 @@ public void TestThreadableNodeUnusedProperties () Assert.That (node.Body, Is.Null, "Body"); Assert.That (node.TextBody, Is.Null, "TextBody"); Assert.That (node.HtmlBody, Is.Null, "HtmlBody"); - Assert.That (node.BodyParts, Is.Null, "BodyParts"); - Assert.That (node.Attachments, Is.Null, "Attachments"); + Assert.That (node.BodyParts, Is.Empty, "BodyParts"); + Assert.That (node.Attachments, Is.Empty, "Attachments"); Assert.That (node.PreviewText, Is.Null, "PreviewText"); Assert.That (node.Envelope, Is.Null, "Envelope"); Assert.That (node.Flags.HasValue, Is.False, "Flags"); - Assert.That (node.Keywords, Is.Null, "Keywords"); + Assert.That (node.Keywords, Is.Empty, "Keywords"); Assert.That (node.Headers, Is.Null, "Headers"); Assert.That (node.InternalDate.HasValue, Is.False, "InternalDate"); Assert.That (node.EmailId, Is.Null, "EmailId"); From 40fad483582223726b9fede4e79a50b64e939367 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 21 Feb 2026 22:17:41 -0500 Subject: [PATCH 29/44] Fixed Envelope (now passes tests) --- MailKit/Envelope.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MailKit/Envelope.cs b/MailKit/Envelope.cs index ceec2b4b57..83c51535bf 100644 --- a/MailKit/Envelope.cs +++ b/MailKit/Envelope.cs @@ -397,7 +397,7 @@ static bool TryParse (string text, ref int index, out string? nstring) return true; } - static bool TryParse (string text, ref int index, [NotNullWhen (true)] out InternetAddress? addr) + static bool TryParse (string text, ref int index, out InternetAddress? addr) { addr = null; From 8abfdf1c02089701a8c8b6aae024e354ae70cd83 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 21 Feb 2026 22:36:07 -0500 Subject: [PATCH 30/44] Fixed SASL tests --- MailKit/Security/SaslMechanism.cs | 3 --- MailKit/Security/SaslMechanismDigestMd5.cs | 5 ++++- MailKit/Security/SaslMechanismGssapi.cs | 5 ++++- MailKit/Security/SaslMechanismOAuthBearer.cs | 7 +++++-- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/MailKit/Security/SaslMechanism.cs b/MailKit/Security/SaslMechanism.cs index cf84253767..503bf6cfc7 100644 --- a/MailKit/Security/SaslMechanism.cs +++ b/MailKit/Security/SaslMechanism.cs @@ -387,9 +387,6 @@ static string Base64Encode (byte[]? challenge) /// public string Challenge (string? token, CancellationToken cancellationToken = default) { - if (Uri == null) - throw new InvalidOperationException (); - cancellationToken.ThrowIfCancellationRequested (); byte[]? decoded = Base64Decode (token?.Trim (), out int length); diff --git a/MailKit/Security/SaslMechanismDigestMd5.cs b/MailKit/Security/SaslMechanismDigestMd5.cs index 7bb6bdcde3..ec3197699d 100644 --- a/MailKit/Security/SaslMechanismDigestMd5.cs +++ b/MailKit/Security/SaslMechanismDigestMd5.cs @@ -136,6 +136,9 @@ public override string MechanismName { if (IsAuthenticated) return null; + if (Uri is null) + throw new InvalidOperationException (); + switch (state) { case LoginState.Auth: if (token == null) @@ -148,7 +151,7 @@ public override string MechanismName { encoding = challenge.Charset != null ? Encoding.UTF8 : TextEncodings.Latin1; cnonce ??= GenerateEntropy (15); - response = new DigestResponse (challenge, encoding, Uri!.Scheme, Uri!.DnsSafeHost, AuthorizationId, Credentials.UserName, Credentials.Password, cnonce); + response = new DigestResponse (challenge, encoding, Uri.Scheme, Uri.DnsSafeHost, AuthorizationId, Credentials.UserName, Credentials.Password, cnonce); state = LoginState.Final; return response.Encode (encoding); diff --git a/MailKit/Security/SaslMechanismGssapi.cs b/MailKit/Security/SaslMechanismGssapi.cs index 6a7111802a..e1df6e87bd 100644 --- a/MailKit/Security/SaslMechanismGssapi.cs +++ b/MailKit/Security/SaslMechanismGssapi.cs @@ -145,9 +145,12 @@ protected override NegotiateAuthenticationClientOptions CreateClientOptions () { var options = base.CreateClientOptions (); + if (Uri is null) + throw new InvalidOperationException (); + // Provide a default TargetName (the base implementation already sets the // TargetName to the ServicePrincipalName if the value was provided). - options.TargetName ??= $"SMTPSVC/{Uri!.Host}"; + options.TargetName ??= $"SMTPSVC/{Uri.Host}"; return options; } diff --git a/MailKit/Security/SaslMechanismOAuthBearer.cs b/MailKit/Security/SaslMechanismOAuthBearer.cs index 5b36a09f89..4149f21f8a 100644 --- a/MailKit/Security/SaslMechanismOAuthBearer.cs +++ b/MailKit/Security/SaslMechanismOAuthBearer.cs @@ -158,9 +158,12 @@ static int CalculateBufferSize (byte[] authzid, byte[] host, string port, string if (IsAuthenticated) return ErrorResponse; + if (Uri is null) + throw new InvalidOperationException (); + var authzid = Encoding.UTF8.GetBytes (Credentials.UserName); - var port = Uri!.Port.ToString (CultureInfo.InvariantCulture); - var host = Encoding.UTF8.GetBytes (Uri!.Host); + var port = Uri.Port.ToString (CultureInfo.InvariantCulture); + var host = Encoding.UTF8.GetBytes (Uri.Host); var authToken = Credentials.Password; var buf = new byte[CalculateBufferSize (authzid, host, port, authToken)]; From a40ac05ecbacd975264db1c118f810e20e72c05e Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 21 Feb 2026 22:38:25 -0500 Subject: [PATCH 31/44] Fixed NtlmAuthenticationMessageTests --- UnitTests/Security/Ntlm/NtlmAuthenticateMessageTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UnitTests/Security/Ntlm/NtlmAuthenticateMessageTests.cs b/UnitTests/Security/Ntlm/NtlmAuthenticateMessageTests.cs index d7684568b6..3a438c3c9a 100644 --- a/UnitTests/Security/Ntlm/NtlmAuthenticateMessageTests.cs +++ b/UnitTests/Security/Ntlm/NtlmAuthenticateMessageTests.cs @@ -64,7 +64,7 @@ public void TestDecodeJavaExample () Assert.That (NtlmAuthenticate.Domain, Is.EqualTo ("URSA-MINOR"), "Domain"); Assert.That (NtlmAuthenticate.Workstation, Is.EqualTo ("LIGHTCITY"), "Workstation"); Assert.That (NtlmAuthenticate.UserName, Is.EqualTo ("Zaphod"), "UserName"); - Assert.That (NtlmAuthenticate.Password, Is.Null, "Password"); + Assert.That (NtlmAuthenticate.Password, Is.Empty, "Password"); Assert.That (BitConverter.ToString (NtlmAuthenticate.LmChallengeResponse), Is.EqualTo ("AD-87-CA-6D-EF-E3-46-85-B9-C4-3C-47-7A-8C-42-D6-00-66-7D-68-92-E7-E8-97"), "LmChallengeResponse"); Assert.That (BitConverter.ToString (NtlmAuthenticate.NtChallengeResponse), Is.EqualTo ("E0-E0-0D-E3-10-4A-1B-F2-05-3F-07-C7-DD-A8-2D-3C-48-9A-E9-89-E1-B0-00-D3"), "NtChallengeResponse"); @@ -82,7 +82,7 @@ public void TestDecodeDavenportExample () Assert.That (NtlmAuthenticate.Domain, Is.EqualTo ("DOMAIN"), "Domain"); Assert.That (NtlmAuthenticate.Workstation, Is.EqualTo ("WORKSTATION"), "Workstation"); Assert.That (NtlmAuthenticate.UserName, Is.EqualTo ("user"), "UserName"); - Assert.That (NtlmAuthenticate.Password, Is.Null, "Password"); + Assert.That (NtlmAuthenticate.Password, Is.Empty, "Password"); Assert.That (BitConverter.ToString (NtlmAuthenticate.LmChallengeResponse), Is.EqualTo ("C3-37-CD-5C-BD-44-FC-97-82-A6-67-AF-6D-42-7C-6D-E6-7C-20-C2-D3-E7-7C-56"), "LmChallengeResponse"); Assert.That (BitConverter.ToString (NtlmAuthenticate.NtChallengeResponse), Is.EqualTo ("25-A9-8C-1C-31-E8-18-47-46-6B-29-B2-DF-46-80-F3-99-58-FB-8C-21-3A-9C-C6"), "NtChallengeResponse"); From 583c12c12eb6cce09b9aeeb341e69ca1ead65c41 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 21 Feb 2026 22:52:04 -0500 Subject: [PATCH 32/44] Use MemberNotNull with nameof --- MailKit/MessageSummary.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MailKit/MessageSummary.cs b/MailKit/MessageSummary.cs index 9f017dd1aa..e31a574955 100644 --- a/MailKit/MessageSummary.cs +++ b/MailKit/MessageSummary.cs @@ -95,7 +95,7 @@ public MessageSummary (IMailFolder folder, int index) : this (index) Folder = folder; } - [MemberNotNull ("normalizedSubject")] + [MemberNotNull (nameof (normalizedSubject))] void UpdateThreadableSubject () { if (normalizedSubject != null) From cab4c7a395042c4e2ef1bf8168f9f0bc58853ad5 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 21 Feb 2026 22:58:50 -0500 Subject: [PATCH 33/44] minor code flow changes in ImapCommand/ProtocolException --- MailKit/Net/Imap/ImapCommandException.cs | 6 ++++-- MailKit/Net/Imap/ImapProtocolException.cs | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/MailKit/Net/Imap/ImapCommandException.cs b/MailKit/Net/Imap/ImapCommandException.cs index b7f9bb468b..00e3433f71 100644 --- a/MailKit/Net/Imap/ImapCommandException.cs +++ b/MailKit/Net/Imap/ImapCommandException.cs @@ -87,9 +87,11 @@ internal static ImapCommandException Create (string command, ImapCommand ic) break; } } - } - reason ??= ic.ResponseText ?? string.Empty; + reason ??= string.Empty; + } else { + reason = ic.ResponseText!; + } if (!string.IsNullOrEmpty (reason)) message = string.Format ("The IMAP server replied to the '{0}' command with a '{1}' response: {2}", command, result, reason); diff --git a/MailKit/Net/Imap/ImapProtocolException.cs b/MailKit/Net/Imap/ImapProtocolException.cs index eab35b401a..cb37c2ae49 100644 --- a/MailKit/Net/Imap/ImapProtocolException.cs +++ b/MailKit/Net/Imap/ImapProtocolException.cs @@ -126,12 +126,12 @@ internal static ImapProtocolException Create (ImapCommand ic) break; } } + + message ??= string.Empty; } else { - message = ic.ResponseText; + message = ic.ResponseText!; } - message ??= string.Empty; - return ic.Exception != null ? new ImapProtocolException (message, ic.Exception) : new ImapProtocolException (message); } } From 73c2dbe522498010f6facd7917faf197f59df3af Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 21 Feb 2026 23:11:18 -0500 Subject: [PATCH 34/44] ImapFolder cleanup to avoid the need for ! --- MailKit/Net/Imap/ImapFolder.cs | 47 +++++++++++++++------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/MailKit/Net/Imap/ImapFolder.cs b/MailKit/Net/Imap/ImapFolder.cs index 8566e5b723..b7ae5e18c8 100644 --- a/MailKit/Net/Imap/ImapFolder.cs +++ b/MailKit/Net/Imap/ImapFolder.cs @@ -1644,7 +1644,7 @@ public override async Task UnsubscribeAsync (CancellationToken cancellationToken ProcessUnsubscribeResponse (ic); } - ImapCommand? QueueGetSubfoldersCommand (StatusItems items, bool subscribedOnly, CancellationToken cancellationToken, out List? list, out bool status) + bool TryQueueGetSubfoldersCommand (StatusItems items, bool subscribedOnly, CancellationToken cancellationToken, [NotNullWhen (true)] out ImapCommand? ic, [NotNullWhen (true)] out List? list, out bool status) { CheckState (false, false); @@ -1652,7 +1652,8 @@ public override async Task UnsubscribeAsync (CancellationToken cancellationToken if (DirectorySeparator == '\0') { status = false; list = null; - return null; + ic = null; + return false; } // Note: folder names can contain wildcards (including '*' and '%'), so replace '*' with '%' @@ -1717,7 +1718,7 @@ public override async Task UnsubscribeAsync (CancellationToken cancellationToken command.Append ("\r\n"); - var ic = new ImapCommand (Engine, cancellationToken, null, command.ToString (), pattern.ToString ()); + ic = new ImapCommand (Engine, cancellationToken, null, command.ToString (), pattern.ToString ()); ic.RegisterUntaggedHandler (lsub ? "LSUB" : "LIST", ImapUtils.UntaggedListHandler); ic.ListReturnsSubscribed = returnsSubscribed; ic.UserData = list; @@ -1725,7 +1726,7 @@ public override async Task UnsubscribeAsync (CancellationToken cancellationToken Engine.QueueCommand (ic); - return ic; + return true; } IList ProcessGetSubfoldersResponse (ImapCommand ic, List list, out bool unparented) @@ -1795,19 +1796,17 @@ IList ProcessGetSubfoldersResponse (ImapCommand ic, List public override IList GetSubfolders (StatusItems items, bool subscribedOnly = false, CancellationToken cancellationToken = default) { - var ic = QueueGetSubfoldersCommand (items, subscribedOnly, cancellationToken, out var list, out var status); - - if (ic == null) + if (!TryQueueGetSubfoldersCommand (items, subscribedOnly, cancellationToken, out var ic, out var list, out var status)) return Array.Empty (); Engine.Run (ic); - var children = ProcessGetSubfoldersResponse (ic, list!, out var unparented); + var children = ProcessGetSubfoldersResponse (ic, list, out var unparented); // Note: if any folders returned in the LIST command are unparented, have the ImapEngine look up their // parent folders now so that they are not left in an inconsistent state. if (unparented) - Engine.LookupParentFolders (list!, cancellationToken); + Engine.LookupParentFolders (list, cancellationToken); if (status) { for (int i = 0; i < children.Count; i++) { @@ -1852,19 +1851,17 @@ public override IList GetSubfolders (StatusItems items, bool subscr /// public override async Task> GetSubfoldersAsync (StatusItems items, bool subscribedOnly = false, CancellationToken cancellationToken = default) { - var ic = QueueGetSubfoldersCommand (items, subscribedOnly, cancellationToken, out var list, out var status); - - if (ic == null) + if (!TryQueueGetSubfoldersCommand (items, subscribedOnly, cancellationToken, out var ic, out var list, out var status)) return Array.Empty (); await Engine.RunAsync (ic).ConfigureAwait (false); - var children = ProcessGetSubfoldersResponse (ic, list!, out var unparented); + var children = ProcessGetSubfoldersResponse (ic, list, out var unparented); // Note: if any folders returned in the LIST command are unparented, have the ImapEngine look up their // parent folders now so that they are not left in an inconsistent state. if (unparented) - await Engine.LookupParentFoldersAsync (list!, cancellationToken).ConfigureAwait (false); + await Engine.LookupParentFoldersAsync (list, cancellationToken).ConfigureAwait (false); if (status) { for (int i = 0; i < children.Count; i++) { @@ -1876,7 +1873,7 @@ public override async Task> GetSubfoldersAsync (StatusItems i return children; } - ImapCommand? QueueGetSubfolderCommand (string name, CancellationToken cancellationToken, out List? list, out string? fullName, out string? encodedName, out ImapFolder? folder) + bool TryQueueGetSubfolderCommand (string name, CancellationToken cancellationToken, [NotNullWhen (true)] out ImapCommand? ic, [NotNullWhen (true)] out List? list, [NotNullWhen (true)] out string? fullName, [NotNullWhen (true)] out string? encodedName, out ImapFolder? folder) { if (name == null) throw new ArgumentNullException (nameof (name)); @@ -1892,7 +1889,8 @@ public override async Task> GetSubfoldersAsync (StatusItems i fullName = null; folder = null; list = null; - return null; + ic = null; + return false; } fullName = FullName.Length > 0 ? FullName + DirectorySeparator + name : name; @@ -1900,20 +1898,21 @@ public override async Task> GetSubfoldersAsync (StatusItems i if (Engine.TryGetCachedFolder (encodedName, out folder)) { list = null; - return null; + ic = null; + return false; } // Note: folder names can contain wildcards (including '*' and '%'), so replace '*' with '%' // in order to reduce the list of folders returned by our LIST command. var pattern = encodedName.Replace ('*', '%'); - var ic = new ImapCommand (Engine, cancellationToken, null, "LIST \"\" %S\r\n", pattern); + ic = new ImapCommand (Engine, cancellationToken, null, "LIST \"\" %S\r\n", pattern); ic.RegisterUntaggedHandler ("LIST", ImapUtils.UntaggedListHandler); ic.UserData = list = new List (); Engine.QueueCommand (ic); - return ic; + return true; } ImapFolder? ProcessGetSubfolderResponse (ImapCommand ic, List list, string encodedName) @@ -1971,14 +1970,12 @@ public override async Task> GetSubfoldersAsync (StatusItems i /// public override IMailFolder GetSubfolder (string name, CancellationToken cancellationToken = default) { - var ic = QueueGetSubfolderCommand (name, cancellationToken, out var list, out var fullName, out var encodedName, out var folder); - - if (ic == null) + if (!TryQueueGetSubfolderCommand (name, cancellationToken, out var ic, out var list, out var fullName, out var encodedName, out var folder)) return folder ?? throw new FolderNotFoundException (name); Engine.Run (ic); - folder = ProcessGetSubfolderResponse (ic, list!, encodedName!); + folder = ProcessGetSubfolderResponse (ic, list, encodedName!); if (list!.Count > 1 || folder == null) { // Note: if any folders returned in the LIST command are unparented, have the ImapEngine look up their @@ -2033,9 +2030,7 @@ public override IMailFolder GetSubfolder (string name, CancellationToken cancell /// public override async Task GetSubfolderAsync (string name, CancellationToken cancellationToken = default) { - var ic = QueueGetSubfolderCommand (name, cancellationToken, out var list, out var fullName, out var encodedName, out var folder); - - if (ic == null) + if (!TryQueueGetSubfolderCommand (name, cancellationToken, out var ic, out var list, out var fullName, out var encodedName, out var folder)) return folder ?? throw new FolderNotFoundException (name); await Engine.RunAsync (ic).ConfigureAwait (false); From c48c62cffedf431c3107838b0e8acbb8e0b22699 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 21 Feb 2026 23:22:40 -0500 Subject: [PATCH 35/44] More cleanup --- MailKit/Net/Imap/ImapFolder.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/MailKit/Net/Imap/ImapFolder.cs b/MailKit/Net/Imap/ImapFolder.cs index b7ae5e18c8..a580897188 100644 --- a/MailKit/Net/Imap/ImapFolder.cs +++ b/MailKit/Net/Imap/ImapFolder.cs @@ -1975,16 +1975,16 @@ public override IMailFolder GetSubfolder (string name, CancellationToken cancell Engine.Run (ic); - folder = ProcessGetSubfolderResponse (ic, list, encodedName!); + folder = ProcessGetSubfolderResponse (ic, list, encodedName); - if (list!.Count > 1 || folder == null) { + if (list.Count > 1 || folder == null) { // Note: if any folders returned in the LIST command are unparented, have the ImapEngine look up their // parent folders now so that they are not left in an inconsistent state. Engine.LookupParentFolders (list, cancellationToken); } if (folder == null) - throw new FolderNotFoundException (fullName!); + throw new FolderNotFoundException (fullName); return folder; } @@ -2035,16 +2035,16 @@ public override async Task GetSubfolderAsync (string name, Cancella await Engine.RunAsync (ic).ConfigureAwait (false); - folder = ProcessGetSubfolderResponse (ic, list!, encodedName!); + folder = ProcessGetSubfolderResponse (ic, list, encodedName); - if (list!.Count > 1 || folder == null) { + if (list.Count > 1 || folder == null) { // Note: if any folders returned in the LIST command are unparented, have the ImapEngine look up their // parent folders now so that they are not left in an inconsistent state. await Engine.LookupParentFoldersAsync (list, cancellationToken).ConfigureAwait (false); } if (folder == null) - throw new FolderNotFoundException (fullName!); + throw new FolderNotFoundException (fullName); return folder; } @@ -6135,8 +6135,8 @@ void OnFetchAsyncCompleted (MessageSummary message) if ((message.Fields & MessageSummaryItems.UniqueId) != 0) uid = message.UniqueId; - if ((message.Fields & MessageSummaryItems.Flags) != 0) { - var args = new MessageFlagsChangedEventArgs (index, message.Flags!.Value, (HashSet) message.Keywords) { + if (message.Flags.HasValue) { + var args = new MessageFlagsChangedEventArgs (index, message.Flags.Value, (HashSet) message.Keywords) { ModSeq = message.ModSeq, UniqueId = uid }; From dc03ffc3990bc97faa9a57ce65796b0703c1f72b Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 21 Feb 2026 23:32:54 -0500 Subject: [PATCH 36/44] Removed ImapFolder .ctor that I decided not to add --- MailKit/Net/Imap/ImapClient.cs | 2 +- MailKit/Net/Imap/ImapFolder.cs | 31 ------------------------------- 2 files changed, 1 insertion(+), 32 deletions(-) diff --git a/MailKit/Net/Imap/ImapClient.cs b/MailKit/Net/Imap/ImapClient.cs index 17a125ea1d..3e9cf47334 100644 --- a/MailKit/Net/Imap/ImapClient.cs +++ b/MailKit/Net/Imap/ImapClient.cs @@ -67,7 +67,7 @@ public partial class ImapClient : MailStore, IImapClient const string HexAlphabet = "0123456789ABCDEF"; readonly ImapAuthenticationSecretDetector detector = new ImapAuthenticationSecretDetector (); - internal readonly ImapEngine engine; + readonly ImapEngine engine; SslCertificateValidationInfo? sslValidationInfo; int timeout = 2 * 60 * 1000; string? identifier; diff --git a/MailKit/Net/Imap/ImapFolder.cs b/MailKit/Net/Imap/ImapFolder.cs index a580897188..b38d60436f 100644 --- a/MailKit/Net/Imap/ImapFolder.cs +++ b/MailKit/Net/Imap/ImapFolder.cs @@ -84,37 +84,6 @@ public ImapFolder (ImapFolderConstructorArgs args) Engine = args.Engine; } -#if false - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// If you subclass , you will also need to subclass - /// and override the - /// - /// method in order to return a new instance of your ImapFolder subclass. - /// - /// The IMAP client that the folder belongs to. - /// The full anme of the IMAP folder. - /// The directory separator used by the IMAP folder. - /// The attributes of the IMAP folder. - /// - /// is . - /// -or- - /// is . - /// - public ImapFolder (ImapClient client, string fullName, char directorySeparator, FolderAttributes attributes) - : base (fullName, directorySeparator, attributes) - { - if (client == null) - throw new ArgumentNullException (nameof (client)); - - EncodedName = client.engine.EncodeMailboxName (fullName); - Engine = client.engine; - } -#endif - /// /// Get the IMAP command engine. /// From 4c901d4ad28cef4047e80a1d1f6124140741a38f Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sun, 22 Feb 2026 11:08:21 -0500 Subject: [PATCH 37/44] Code refacoring to reduce ! usage in Pop3/Imap/SmtpClient --- MailKit/MailKit.csproj | 2 +- MailKit/Net/Imap/AsyncImapClient.cs | 16 ++++------ MailKit/Net/Imap/ImapClient.cs | 42 +++++++++++---------------- MailKit/Net/Imap/ImapEngine.cs | 19 ++++++++++++ MailKit/Net/Pop3/AsyncPop3Client.cs | 11 ++----- MailKit/Net/Pop3/Pop3Client.cs | 39 ++++++++++--------------- MailKit/Net/Pop3/Pop3Engine.cs | 21 ++++++++++++++ MailKit/Net/Smtp/AsyncSmtpClient.cs | 10 +++---- MailKit/Net/Smtp/SmtpClient.cs | 45 ++++++++++++++++------------- 9 files changed, 111 insertions(+), 94 deletions(-) diff --git a/MailKit/MailKit.csproj b/MailKit/MailKit.csproj index 583213e8c3..dde411596c 100644 --- a/MailKit/MailKit.csproj +++ b/MailKit/MailKit.csproj @@ -5,7 +5,7 @@ MailKit 4.15.0 Jeffrey Stedfast - 10 + 12 netstandard2.0;netstandard2.1;net462;net47;net48;net8.0;net10.0 true false diff --git a/MailKit/Net/Imap/AsyncImapClient.cs b/MailKit/Net/Imap/AsyncImapClient.cs index a875fc273d..9f599684b1 100644 --- a/MailKit/Net/Imap/AsyncImapClient.cs +++ b/MailKit/Net/Imap/AsyncImapClient.cs @@ -518,17 +518,17 @@ async Task PostConnectAsync (Stream stream, string host, int port, SecureSocketO ProtocolLogger.LogConnect (engine.Uri!); } catch { stream.Dispose (); - secure = false; throw; } connecting = true; + var imap = new ImapStream (stream, ProtocolLogger); + try { - await engine.ConnectAsync (new ImapStream (stream, ProtocolLogger), cancellationToken).ConfigureAwait (false); + await engine.ConnectAsync (imap, cancellationToken).ConfigureAwait (false); } catch { connecting = false; - secure = false; throw; } @@ -548,14 +548,14 @@ async Task PostConnectAsync (Stream stream, string host, int port, SecureSocketO if (ic.Response == ImapCommandResponse.Ok) { try { var tls = new SslStream (stream, false, ValidateRemoteCertificate); - engine.Stream!.Stream = tls; + imap.Stream = tls; await SslHandshakeAsync (tls, host, cancellationToken).ConfigureAwait (false); } catch (Exception ex) { throw SslHandshakeException.Create (ref sslValidationInfo, ex, true, "IMAP", host, port, 993, 143); } - secure = true; + engine.IsSecure = true; // Query the CAPABILITIES again if the server did not include an // untagged CAPABILITIES response to the STARTTLS command. @@ -566,7 +566,6 @@ async Task PostConnectAsync (Stream stream, string host, int port, SecureSocketO } } } catch (Exception ex) { - secure = false; engine.Disconnect (ex); throw; } finally { @@ -673,10 +672,7 @@ public override async Task ConnectAsync (string host, int port = 0, SecureSocket throw SslHandshakeException.Create (ref sslValidationInfo, ex, false, "IMAP", host, port, 993, 143); } - secure = true; stream = ssl; - } else { - secure = false; } await PostConnectAsync (stream, host, port, options, starttls, cancellationToken).ConfigureAwait (false); @@ -846,10 +842,8 @@ public override async Task ConnectAsync (Stream stream, string host, int port = } network = ssl; - secure = true; } else { network = stream; - secure = false; } if (network.CanTimeout) { diff --git a/MailKit/Net/Imap/ImapClient.cs b/MailKit/Net/Imap/ImapClient.cs index 3e9cf47334..6044f409e4 100644 --- a/MailKit/Net/Imap/ImapClient.cs +++ b/MailKit/Net/Imap/ImapClient.cs @@ -74,7 +74,6 @@ public partial class ImapClient : MailStore, IImapClient bool disconnecting; bool connecting; bool disposed; - bool secure; /// /// Initializes a new instance of the class. @@ -666,7 +665,7 @@ public override HashSet ThreadingAlgorithms { public override int Timeout { get { return timeout; } set { - if (IsConnected && engine.Stream!.CanTimeout) { + if (engine.IsConnected && engine.Stream.CanTimeout) { engine.Stream.WriteTimeout = value; engine.Stream.ReadTimeout = value; } @@ -701,7 +700,7 @@ public override bool IsConnected { /// /// if the connection is secure; otherwise, . public override bool IsSecure { - get { return IsConnected && secure; } + get { return engine.IsSecure; } } /// @@ -712,7 +711,7 @@ public override bool IsSecure { /// /// if the connection is encrypted; otherwise, . public override bool IsEncrypted { - get { return IsSecure && (engine.Stream!.Stream is SslStream sslStream) && sslStream.IsEncrypted; } + get { return engine.IsSecure && (engine.Stream.Stream is SslStream sslStream) && sslStream.IsEncrypted; } } /// @@ -723,7 +722,7 @@ public override bool IsEncrypted { /// /// if the connection is signed; otherwise, . public override bool IsSigned { - get { return IsSecure && (engine.Stream!.Stream is SslStream sslStream) && sslStream.IsSigned; } + get { return engine.IsSecure && (engine.Stream.Stream is SslStream sslStream) && sslStream.IsSigned; } } /// @@ -738,7 +737,7 @@ public override bool IsSigned { /// The negotiated SSL or TLS protocol version. public override SslProtocols SslProtocol { get { - if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) + if (engine.IsSecure && (engine.Stream.Stream is SslStream sslStream)) return sslStream.SslProtocol; return SslProtocols.None; @@ -760,7 +759,7 @@ public override SslProtocols SslProtocol { #endif public override CipherAlgorithmType? SslCipherAlgorithm { get { - if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) + if (engine.IsSecure && (engine.Stream.Stream is SslStream sslStream)) return sslStream.CipherAlgorithm; return null; @@ -782,7 +781,7 @@ public override CipherAlgorithmType? SslCipherAlgorithm { #endif public override int? SslCipherStrength { get { - if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) + if (engine.IsSecure && (engine.Stream.Stream is SslStream sslStream)) return sslStream.CipherStrength; return null; @@ -799,7 +798,7 @@ public override int? SslCipherStrength { /// The negotiated SSL or TLS cipher suite. public override TlsCipherSuite? SslCipherSuite { get { - if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) + if (engine.IsSecure && (engine.Stream.Stream is SslStream sslStream)) return sslStream.NegotiatedCipherSuite; return null; @@ -822,7 +821,7 @@ public override TlsCipherSuite? SslCipherSuite { #endif public override HashAlgorithmType? SslHashAlgorithm { get { - if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) + if (engine.IsSecure && (engine.Stream.Stream is SslStream sslStream)) return sslStream.HashAlgorithm; return null; @@ -844,7 +843,7 @@ public override HashAlgorithmType? SslHashAlgorithm { #endif public override int? SslHashStrength { get { - if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) + if (engine.IsSecure && (engine.Stream.Stream is SslStream sslStream)) return sslStream.HashStrength; return null; @@ -866,7 +865,7 @@ public override int? SslHashStrength { #endif public override ExchangeAlgorithmType? SslKeyExchangeAlgorithm { get { - if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) + if (engine.IsSecure && (engine.Stream.Stream is SslStream sslStream)) return sslStream.KeyExchangeAlgorithm; return null; @@ -888,7 +887,7 @@ public override ExchangeAlgorithmType? SslKeyExchangeAlgorithm { #endif public override int? SslKeyExchangeStrength { get { - if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) + if (engine.IsSecure && (engine.Stream.Stream is SslStream sslStream)) return sslStream.KeyExchangeStrength; return null; @@ -1460,17 +1459,17 @@ void PostConnect (Stream stream, string host, int port, SecureSocketOptions opti ProtocolLogger.LogConnect (engine.Uri!); } catch { stream.Dispose (); - secure = false; throw; } connecting = true; + var imap = new ImapStream (stream, ProtocolLogger); + try { - engine.Connect (new ImapStream (stream, ProtocolLogger), cancellationToken); + engine.Connect (imap, cancellationToken); } catch { connecting = false; - secure = false; throw; } @@ -1490,14 +1489,14 @@ void PostConnect (Stream stream, string host, int port, SecureSocketOptions opti if (ic.Response == ImapCommandResponse.Ok) { try { var tls = new SslStream (stream, false, ValidateRemoteCertificate); - engine.Stream!.Stream = tls; + imap.Stream = tls; SslHandshake (tls, host, cancellationToken); } catch (Exception ex) { throw SslHandshakeException.Create (ref sslValidationInfo, ex, true, "IMAP", host, port, 993, 143); } - secure = true; + engine.IsSecure = true; // Query the CAPABILITIES again if the server did not include an // untagged CAPABILITIES response to the STARTTLS command. @@ -1508,7 +1507,6 @@ void PostConnect (Stream stream, string host, int port, SecureSocketOptions opti } } } catch (Exception ex) { - secure = false; engine.Disconnect (ex); throw; } finally { @@ -1611,10 +1609,7 @@ public override void Connect (string host, int port = 0, SecureSocketOptions opt throw SslHandshakeException.Create (ref sslValidationInfo, ex, false, "IMAP", host, port, 993, 143); } - secure = true; stream = ssl; - } else { - secure = false; } PostConnect (stream, host, port, options, starttls, cancellationToken); @@ -1795,10 +1790,8 @@ public override void Connect (Stream stream, string host, int port = 0, SecureSo } network = ssl; - secure = true; } else { network = stream; - secure = false; } if (network.CanTimeout) { @@ -2810,7 +2803,6 @@ void OnEngineDisconnected (object? sender, EventArgs e) var uri = engine.Uri; disconnecting = false; - secure = false; OnDisconnected (uri!.Host, uri.Port, GetSecureSocketOptions (uri), requested); } diff --git a/MailKit/Net/Imap/ImapEngine.cs b/MailKit/Net/Imap/ImapEngine.cs index c0b683bb70..1a7c73bb6a 100644 --- a/MailKit/Net/Imap/ImapEngine.cs +++ b/MailKit/Net/Imap/ImapEngine.cs @@ -163,6 +163,7 @@ class ImapEngine : IDisposable MimeParser? parser; internal int Tag; bool disposed; + bool secure; public ImapEngine (CreateImapFolderDelegate createImapFolderDelegate) { @@ -395,10 +396,24 @@ public ImapEngineState State { /// Gets whether or not the engine is currently connected to a IMAP server. /// /// if the engine is connected; otherwise, . + [MemberNotNullWhen (true, nameof (Stream))] public bool IsConnected { get { return Stream != null && Stream.IsConnected; } } + /// + /// Get whether or not the connection is secure (typically via SSL or TLS). + /// + /// + /// Gets whether or not the connection is secure (typically via SSL or TLS). + /// + /// if the connection is secure; otherwise, . + [MemberNotNullWhen (true, nameof (Stream))] + public bool IsSecure { + get { return IsConnected && secure; } + set { secure = value; } + } + /// /// Gets the personal folder namespaces. /// @@ -633,6 +648,7 @@ public NetworkOperation StartNetworkOperation (NetworkOperationKind kind, Uri? u #endif } + [MemberNotNull (nameof (Stream))] void Initialize (ImapStream stream) { clientConnectedTimestamp = Stopwatch.GetTimestamp (); @@ -645,6 +661,7 @@ void Initialize (ImapStream stream) SupportedContexts.Clear (); Rights.Clear (); + secure = stream.Stream is SslStream; State = ImapEngineState.Connecting; QuirksMode = ImapQuirksMode.None; SupportedCharsets.Add ("US-ASCII"); @@ -864,6 +881,8 @@ public void Disconnect (Exception? ex) Stream = null; } + secure = false; + if (State != ImapEngineState.Disconnected) { State = ImapEngineState.Disconnected; OnDisconnected (); diff --git a/MailKit/Net/Pop3/AsyncPop3Client.cs b/MailKit/Net/Pop3/AsyncPop3Client.cs index 8895d15009..e0df1f66a6 100644 --- a/MailKit/Net/Pop3/AsyncPop3Client.cs +++ b/MailKit/Net/Pop3/AsyncPop3Client.cs @@ -316,7 +316,6 @@ async Task PostConnectAsync (Stream stream, string host, int port, SecureSocketO ProtocolLogger.LogConnect (engine.Uri!); } catch { stream.Dispose (); - secure = false; throw; } @@ -335,21 +334,20 @@ async Task PostConnectAsync (Stream stream, string host, int port, SecureSocketO try { var tls = new SslStream (stream, false, ValidateRemoteCertificate); - engine.Stream!.Stream = tls; + pop3.Stream = tls; await SslHandshakeAsync (tls, host, cancellationToken).ConfigureAwait (false); } catch (Exception ex) { throw SslHandshakeException.Create (ref sslValidationInfo, ex, true, "POP3", host, port, 995, 110); } - secure = true; + engine.IsSecure = true; // re-issue a CAPA command await engine.QueryCapabilitiesAsync (cancellationToken).ConfigureAwait (false); } } catch (Exception ex) { engine.Disconnect (ex); - secure = false; throw; } @@ -448,10 +446,7 @@ public override async Task ConnectAsync (string host, int port = 0, SecureSocket throw SslHandshakeException.Create (ref sslValidationInfo, ex, false, "POP3", host, port, 995, 110); } - secure = true; stream = ssl; - } else { - secure = false; } await PostConnectAsync (stream, host, port, options, starttls, cancellationToken).ConfigureAwait (false); @@ -621,10 +616,8 @@ public override async Task ConnectAsync (Stream stream, string host, int port = } network = ssl; - secure = true; } else { network = stream; - secure = false; } if (network.CanTimeout) { diff --git a/MailKit/Net/Pop3/Pop3Client.cs b/MailKit/Net/Pop3/Pop3Client.cs index 0174c5040c..e4dcc4e75b 100644 --- a/MailKit/Net/Pop3/Pop3Client.cs +++ b/MailKit/Net/Pop3/Pop3Client.cs @@ -77,7 +77,7 @@ enum ProbedCapabilities : byte { readonly Pop3Engine engine; SslCertificateValidationInfo? sslValidationInfo; ProbedCapabilities probed; - bool disposed, disconnecting, secure, utf8; + bool disposed, disconnecting, utf8; int timeout = 2 * 60 * 1000; long octets; int total; @@ -365,7 +365,7 @@ public override HashSet AuthenticationMechanisms { public override int Timeout { get { return timeout; } set { - if (IsConnected && engine.Stream!.CanTimeout) { + if (engine.IsConnected && engine.Stream.CanTimeout) { engine.Stream.WriteTimeout = value; engine.Stream.ReadTimeout = value; } @@ -403,7 +403,7 @@ public override bool IsConnected { /// /// if the connection is secure; otherwise, . public override bool IsSecure { - get { return IsConnected && secure; } + get { return engine.IsSecure; } } /// @@ -414,7 +414,7 @@ public override bool IsSecure { /// /// if the connection is encrypted; otherwise, . public override bool IsEncrypted { - get { return IsSecure && (engine.Stream!.Stream is SslStream sslStream) && sslStream.IsEncrypted; } + get { return engine.IsSecure && (engine.Stream.Stream is SslStream sslStream) && sslStream.IsEncrypted; } } /// @@ -425,7 +425,7 @@ public override bool IsEncrypted { /// /// if the connection is signed; otherwise, . public override bool IsSigned { - get { return IsSecure && (engine.Stream!.Stream is SslStream sslStream) && sslStream.IsSigned; } + get { return engine.IsSecure && (engine.Stream.Stream is SslStream sslStream) && sslStream.IsSigned; } } /// @@ -440,7 +440,7 @@ public override bool IsSigned { /// The negotiated SSL or TLS protocol version. public override SslProtocols SslProtocol { get { - if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) + if (engine.IsSecure && (engine.Stream.Stream is SslStream sslStream)) return sslStream.SslProtocol; return SslProtocols.None; @@ -462,7 +462,7 @@ public override SslProtocols SslProtocol { #endif public override CipherAlgorithmType? SslCipherAlgorithm { get { - if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) + if (engine.IsSecure && (engine.Stream.Stream is SslStream sslStream)) return sslStream.CipherAlgorithm; return null; @@ -484,7 +484,7 @@ public override CipherAlgorithmType? SslCipherAlgorithm { #endif public override int? SslCipherStrength { get { - if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) + if (engine.IsSecure && (engine.Stream.Stream is SslStream sslStream)) return sslStream.CipherStrength; return null; @@ -501,7 +501,7 @@ public override int? SslCipherStrength { /// The negotiated SSL or TLS cipher suite. public override TlsCipherSuite? SslCipherSuite { get { - if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) + if (engine.IsSecure && (engine.Stream.Stream is SslStream sslStream)) return sslStream.NegotiatedCipherSuite; return null; @@ -524,7 +524,7 @@ public override TlsCipherSuite? SslCipherSuite { #endif public override HashAlgorithmType? SslHashAlgorithm { get { - if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) + if (engine.IsSecure && (engine.Stream.Stream is SslStream sslStream)) return sslStream.HashAlgorithm; return null; @@ -546,7 +546,7 @@ public override HashAlgorithmType? SslHashAlgorithm { #endif public override int? SslHashStrength { get { - if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) + if (engine.IsSecure && (engine.Stream.Stream is SslStream sslStream)) return sslStream.HashStrength; return null; @@ -568,7 +568,7 @@ public override int? SslHashStrength { #endif public override ExchangeAlgorithmType? SslKeyExchangeAlgorithm { get { - if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) + if (engine.IsSecure && (engine.Stream.Stream is SslStream sslStream)) return sslStream.KeyExchangeAlgorithm; return null; @@ -590,7 +590,7 @@ public override ExchangeAlgorithmType? SslKeyExchangeAlgorithm { #endif public override int? SslKeyExchangeStrength { get { - if (IsSecure && (engine.Stream!.Stream is SslStream sslStream)) + if (engine.IsSecure && (engine.Stream.Stream is SslStream sslStream)) return sslStream.KeyExchangeStrength; return null; @@ -1110,7 +1110,6 @@ void PostConnect (Stream stream, string host, int port, SecureSocketOptions opti ProtocolLogger.LogConnect (engine.Uri!); } catch { stream.Dispose (); - secure = false; throw; } @@ -1129,21 +1128,20 @@ void PostConnect (Stream stream, string host, int port, SecureSocketOptions opti try { var tls = new SslStream (stream, false, ValidateRemoteCertificate); - engine.Stream!.Stream = tls; + pop3.Stream = tls; SslHandshake (tls, host, cancellationToken); } catch (Exception ex) { throw SslHandshakeException.Create (ref sslValidationInfo, ex, true, "POP3", host, port, 995, 110); } - secure = true; + engine.IsSecure = true; // re-issue a CAPA command engine.QueryCapabilities (cancellationToken); } } catch (Exception ex) { engine.Disconnect (ex); - secure = false; throw; } @@ -1241,10 +1239,7 @@ public override void Connect (string host, int port = 0, SecureSocketOptions opt throw SslHandshakeException.Create (ref sslValidationInfo, ex, false, "POP3", host, port, 995, 110); } - secure = true; stream = ssl; - } else { - secure = false; } PostConnect (stream, host, port, options, starttls, cancellationToken); @@ -1431,10 +1426,8 @@ public override void Connect (Stream stream, string host, int port = 0, SecureSo } network = ssl; - secure = true; } else { network = stream; - secure = false; } if (network.CanTimeout) { @@ -1571,7 +1564,7 @@ void OnEngineDisconnected (object? sender, EventArgs e) } engine.Disconnected -= OnEngineDisconnected; - disconnecting = secure = utf8 = false; + disconnecting = utf8 = false; octets = total = 0; engine.Uri = null; diff --git a/MailKit/Net/Pop3/Pop3Engine.cs b/MailKit/Net/Pop3/Pop3Engine.cs index d97ef223cf..ece66ce3dc 100644 --- a/MailKit/Net/Pop3/Pop3Engine.cs +++ b/MailKit/Net/Pop3/Pop3Engine.cs @@ -31,6 +31,7 @@ using System.Globalization; using System.Threading.Tasks; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace MailKit.Net.Pop3 { /// @@ -65,6 +66,7 @@ class Pop3Engine readonly List queue; long clientConnectedTimestamp; Pop3Stream? stream; + bool secure; /// /// Initializes a new instance of the class. @@ -145,10 +147,24 @@ public Pop3EngineState State { /// Gets whether or not the engine is currently connected to a POP3 server. /// /// if the engine is connected; otherwise, . + [MemberNotNullWhen (true, new[] { nameof (Stream), nameof (Uri) })] public bool IsConnected { get { return stream != null && stream.IsConnected; } } + /// + /// Get whether or not the connection is secure (typically via SSL or TLS). + /// + /// + /// Gets whether or not the connection is secure (typically via SSL or TLS). + /// + /// if the connection is secure; otherwise, . + [MemberNotNullWhen (true, new[] { nameof (Stream), nameof (Uri) })] + public bool IsSecure { + get { return IsConnected && secure; } + set { secure = value; } + } + /// /// Gets the APOP authentication token. /// @@ -199,6 +215,7 @@ void CheckConnected () throw new InvalidOperationException (); } + [MemberNotNull (nameof (stream))] void Initialize (Pop3Stream pop3) { stream?.Dispose (); @@ -208,6 +225,8 @@ void Initialize (Pop3Stream pop3) AuthenticationMechanisms.Clear (); State = Pop3EngineState.Disconnected; ApopToken = null; + + secure = pop3.Stream is SslStream; stream = pop3; } @@ -327,6 +346,8 @@ public void Disconnect (Exception? ex) stream = null; } + secure = false; + if (State != Pop3EngineState.Disconnected) { State = Pop3EngineState.Disconnected; OnDisconnected (); diff --git a/MailKit/Net/Smtp/AsyncSmtpClient.cs b/MailKit/Net/Smtp/AsyncSmtpClient.cs index 5d86d82298..54c741e69a 100644 --- a/MailKit/Net/Smtp/AsyncSmtpClient.cs +++ b/MailKit/Net/Smtp/AsyncSmtpClient.cs @@ -331,7 +331,7 @@ public override async Task AuthenticateAsync (Encoding encoding, ICredentials cr using var operation = StartNetworkOperation (NetworkOperationKind.Authenticate); try { - var saslUri = new Uri ($"smtp://{uri!.Host}"); + var saslUri = new Uri ($"smtp://{uri.Host}"); AuthenticationException? authException = null; SaslException? saslException; SmtpResponse response; @@ -346,7 +346,7 @@ public override async Task AuthenticateAsync (Encoding encoding, ICredentials cr if (cred == null || (sasl = SaslMechanism.Create (authmech, encoding, cred)) == null) continue; - sasl.ChannelBindingContext = Stream!.Stream as IChannelBindingContext; + sasl.ChannelBindingContext = Stream.Stream as IChannelBindingContext; sasl.Uri = saslUri; tried = true; @@ -801,7 +801,7 @@ public override async Task DisconnectAsync (bool quit, CancellationToken cancell if (quit) { try { - await Stream!.SendCommandAsync ("QUIT\r\n", cancellationToken).ConfigureAwait (false); + await Stream.SendCommandAsync ("QUIT\r\n", cancellationToken).ConfigureAwait (false); } catch (OperationCanceledException) { } catch (SmtpProtocolException) { } catch (SmtpCommandException) { @@ -809,7 +809,7 @@ public override async Task DisconnectAsync (bool quit, CancellationToken cancell } } - Disconnect (uri!.Host, uri.Port, GetSecureSocketOptions (uri), true); + Disconnect (uri.Host, uri.Port, GetSecureSocketOptions (uri), true); } /// @@ -1017,7 +1017,7 @@ async Task SendAsync (FormatOptions options, MimeMessage message, Mailbo if (bdat) return await BdatAsync (format, message, size, cancellationToken, progress).ConfigureAwait (false); - var dataResponse = await Stream!.SendCommandAsync ("DATA\r\n", cancellationToken).ConfigureAwait (false); + var dataResponse = await Stream.SendCommandAsync ("DATA\r\n", cancellationToken).ConfigureAwait (false); ParseDataResponse (dataResponse); diff --git a/MailKit/Net/Smtp/SmtpClient.cs b/MailKit/Net/Smtp/SmtpClient.cs index 79a7a11b85..fde343668e 100644 --- a/MailKit/Net/Smtp/SmtpClient.cs +++ b/MailKit/Net/Smtp/SmtpClient.cs @@ -42,6 +42,7 @@ using System.Net.NetworkInformation; using System.Security.Authentication; using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; using System.Security.Cryptography.X509Certificates; using MimeKit; @@ -366,7 +367,7 @@ public override HashSet AuthenticationMechanisms { public override int Timeout { get { return timeout; } set { - if (IsConnected && Stream!.CanTimeout) { + if (IsConnected && Stream.CanTimeout) { Stream.WriteTimeout = value; Stream.ReadTimeout = value; } @@ -392,6 +393,7 @@ public override int Timeout { /// /// /// if the client is connected; otherwise, . + [MemberNotNullWhen (true, new[] { nameof (Stream), nameof (uri) })] public override bool IsConnected { get { return connected; } } @@ -403,6 +405,7 @@ public override bool IsConnected { /// Gets whether or not the connection is secure (typically via SSL or TLS). /// /// if the connection is secure; otherwise, . + [MemberNotNullWhen (true, new[] { nameof (Stream), nameof (uri) })] public override bool IsSecure { get { return IsConnected && secure; } } @@ -415,7 +418,7 @@ public override bool IsSecure { /// /// if the connection is encrypted; otherwise, . public override bool IsEncrypted { - get { return IsSecure && (Stream!.Stream is SslStream sslStream) && sslStream.IsEncrypted; } + get { return IsSecure && (Stream.Stream is SslStream sslStream) && sslStream.IsEncrypted; } } /// @@ -426,7 +429,7 @@ public override bool IsEncrypted { /// /// if the connection is signed; otherwise, . public override bool IsSigned { - get { return IsSecure && (Stream!.Stream is SslStream sslStream) && sslStream.IsSigned; } + get { return IsSecure && (Stream.Stream is SslStream sslStream) && sslStream.IsSigned; } } /// @@ -441,7 +444,7 @@ public override bool IsSigned { /// The negotiated SSL or TLS protocol version. public override SslProtocols SslProtocol { get { - if (IsSecure && (Stream!.Stream is SslStream sslStream)) + if (IsSecure && (Stream.Stream is SslStream sslStream)) return sslStream.SslProtocol; return SslProtocols.None; @@ -463,7 +466,7 @@ public override SslProtocols SslProtocol { #endif public override CipherAlgorithmType? SslCipherAlgorithm { get { - if (IsSecure && (Stream!.Stream is SslStream sslStream)) + if (IsSecure && (Stream.Stream is SslStream sslStream)) return sslStream.CipherAlgorithm; return null; @@ -485,7 +488,7 @@ public override CipherAlgorithmType? SslCipherAlgorithm { #endif public override int? SslCipherStrength { get { - if (IsSecure && (Stream!.Stream is SslStream sslStream)) + if (IsSecure && (Stream.Stream is SslStream sslStream)) return sslStream.CipherStrength; return null; @@ -502,7 +505,7 @@ public override int? SslCipherStrength { /// The negotiated SSL or TLS cipher suite. public override TlsCipherSuite? SslCipherSuite { get { - if (IsSecure && (Stream!.Stream is SslStream sslStream)) + if (IsSecure && (Stream.Stream is SslStream sslStream)) return sslStream.NegotiatedCipherSuite; return null; @@ -525,7 +528,7 @@ public override TlsCipherSuite? SslCipherSuite { #endif public override HashAlgorithmType? SslHashAlgorithm { get { - if (IsSecure && (Stream!.Stream is SslStream sslStream)) + if (IsSecure && (Stream.Stream is SslStream sslStream)) return sslStream.HashAlgorithm; return null; @@ -547,7 +550,7 @@ public override HashAlgorithmType? SslHashAlgorithm { #endif public override int? SslHashStrength { get { - if (IsSecure && (Stream!.Stream is SslStream sslStream)) + if (IsSecure && (Stream.Stream is SslStream sslStream)) return sslStream.HashStrength; return null; @@ -569,7 +572,7 @@ public override int? SslHashStrength { #endif public override ExchangeAlgorithmType? SslKeyExchangeAlgorithm { get { - if (IsSecure && (Stream!.Stream is SslStream sslStream)) + if (IsSecure && (Stream.Stream is SslStream sslStream)) return sslStream.KeyExchangeAlgorithm; return null; @@ -591,7 +594,7 @@ public override ExchangeAlgorithmType? SslKeyExchangeAlgorithm { #endif public override int? SslKeyExchangeStrength { get { - if (IsSecure && (Stream!.Stream is SslStream sslStream)) + if (IsSecure && (Stream.Stream is SslStream sslStream)) return sslStream.KeyExchangeStrength; return null; @@ -1001,8 +1004,8 @@ void ValidateArguments (SaslMechanism mechanism) if ((capabilities & SmtpCapabilities.Authentication) == 0) throw new NotSupportedException ("The SMTP server does not support authentication."); - mechanism.ChannelBindingContext = Stream!.Stream as IChannelBindingContext; - mechanism.Uri = new Uri ($"smtp://{uri!.Host}"); + mechanism.ChannelBindingContext = Stream.Stream as IChannelBindingContext; + mechanism.Uri = new Uri ($"smtp://{uri.Host}"); } /// @@ -1112,6 +1115,7 @@ public override void Authenticate (SaslMechanism mechanism, CancellationToken ca } } + [MemberNotNull (nameof (Stream), nameof (uri))] void ValidateArguments (Encoding encoding, ICredentials credentials) { if (encoding == null) @@ -1193,7 +1197,7 @@ public override void Authenticate (Encoding encoding, ICredentials credentials, using var operation = StartNetworkOperation (NetworkOperationKind.Authenticate); try { - var saslUri = new Uri ($"smtp://{uri!.Host}"); + var saslUri = new Uri ($"smtp://{uri.Host}"); AuthenticationException? authException = null; SaslException? saslException; SmtpResponse response; @@ -1208,7 +1212,7 @@ public override void Authenticate (Encoding encoding, ICredentials credentials, if (cred == null || (sasl = SaslMechanism.Create (authmech, encoding, cred)) == null) continue; - sasl.ChannelBindingContext = Stream!.Stream as IChannelBindingContext; + sasl.ChannelBindingContext = Stream.Stream as IChannelBindingContext; sasl.Uri = saslUri; tried = true; @@ -1743,7 +1747,7 @@ public override void Disconnect (bool quit, CancellationToken cancellationToken if (quit) { try { - Stream!.SendCommand ("QUIT\r\n", cancellationToken); + Stream.SendCommand ("QUIT\r\n", cancellationToken); } catch (OperationCanceledException) { } catch (SmtpProtocolException) { } catch (SmtpCommandException) { @@ -1751,7 +1755,7 @@ public override void Disconnect (bool quit, CancellationToken cancellationToken } } - Disconnect (uri!.Host, uri.Port, GetSecureSocketOptions (uri), true); + Disconnect (uri.Host, uri.Port, GetSecureSocketOptions (uri), true); } /// @@ -2364,6 +2368,7 @@ protected virtual long GetSize (FormatOptions options, MimeMessage message, Canc } } + [MemberNotNull (nameof (Stream), nameof (uri))] FormatOptions Prepare (FormatOptions options, MimeMessage message, MailboxAddress sender, IList recipients, out SmtpExtensions extensions) { CheckDisposed (); @@ -2476,7 +2481,7 @@ string Send (FormatOptions options, MimeMessage message, MailboxAddress sender, if (bdat) return Bdat (format, message, size, cancellationToken, progress); - var dataResponse = Stream!.SendCommand ("DATA\r\n", cancellationToken); + var dataResponse = Stream.SendCommand ("DATA\r\n", cancellationToken); ParseDataResponse (dataResponse); @@ -2496,7 +2501,7 @@ string Send (FormatOptions options, MimeMessage message, MailboxAddress sender, } catch (Exception ex) { operation.SetError (ex); - Disconnect (uri!.Host, uri.Port, GetSecureSocketOptions (uri), false); + Disconnect (uri.Host, uri.Port, GetSecureSocketOptions (uri), false); throw; } } @@ -2665,7 +2670,7 @@ public override string Send (FormatOptions options, MimeMessage message, Mailbox return Send (options, message, sender, rcpts, cancellationToken, progress); } -#endregion + #endregion string CreateExpandCommand (string alias) { From 5c022a36e002d2244356ba9433c83c2a9514bcd6 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sun, 22 Feb 2026 11:25:45 -0500 Subject: [PATCH 38/44] refactored ValidateRemoteCertificate logic to reduce ! usage --- MailKit/Net/Imap/ImapClient.cs | 9 +++++---- MailKit/Net/Pop3/Pop3Client.cs | 9 +++++---- MailKit/Net/Proxy/HttpsProxyClient.cs | 2 +- MailKit/Net/Smtp/SmtpClient.cs | 9 +++++---- MailKit/Security/SslHandshakeException.cs | 6 +++--- UnitTests/Security/SslHandshakeExceptionTests.cs | 2 +- 6 files changed, 20 insertions(+), 17 deletions(-) diff --git a/MailKit/Net/Imap/ImapClient.cs b/MailKit/Net/Imap/ImapClient.cs index 6044f409e4..026cda1dcb 100644 --- a/MailKit/Net/Imap/ImapClient.cs +++ b/MailKit/Net/Imap/ImapClient.cs @@ -260,24 +260,25 @@ protected virtual ImapFolder CreateImapFolder (ImapFolderConstructorArgs args) bool ValidateRemoteCertificate (object? sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors) { + var host = engine.Uri!.Host; bool valid; sslValidationInfo?.Dispose (); sslValidationInfo = null; if (ServerCertificateValidationCallback != null) { - valid = ServerCertificateValidationCallback (engine.Uri!.Host, certificate, chain, sslPolicyErrors); + valid = ServerCertificateValidationCallback (host, certificate, chain, sslPolicyErrors); #if NETFRAMEWORK } else if (ServicePointManager.ServerCertificateValidationCallback != null) { - valid = ServicePointManager.ServerCertificateValidationCallback (engine.Uri!.Host, certificate, chain, sslPolicyErrors); + valid = ServicePointManager.ServerCertificateValidationCallback (host, certificate, chain, sslPolicyErrors); #endif } else { - valid = DefaultServerCertificateValidationCallback (engine.Uri!.Host, certificate, chain, sslPolicyErrors); + valid = DefaultServerCertificateValidationCallback (host, certificate, chain, sslPolicyErrors); } if (!valid) { // Note: The SslHandshakeException.Create() method will nullify this once it's done using it. - sslValidationInfo = new SslCertificateValidationInfo (sender, certificate, chain, sslPolicyErrors); + sslValidationInfo = new SslCertificateValidationInfo (host, certificate, chain, sslPolicyErrors); } return valid; diff --git a/MailKit/Net/Pop3/Pop3Client.cs b/MailKit/Net/Pop3/Pop3Client.cs index e4dcc4e75b..6cabcb37c2 100644 --- a/MailKit/Net/Pop3/Pop3Client.cs +++ b/MailKit/Net/Pop3/Pop3Client.cs @@ -259,24 +259,25 @@ void CheckAuthenticated () bool ValidateRemoteCertificate (object? sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors) { + var host = engine.Uri!.Host; bool valid; sslValidationInfo?.Dispose (); sslValidationInfo = null; if (ServerCertificateValidationCallback != null) { - valid = ServerCertificateValidationCallback (engine.Uri!.Host, certificate, chain, sslPolicyErrors); + valid = ServerCertificateValidationCallback (host, certificate, chain, sslPolicyErrors); #if NETFRAMEWORK } else if (ServicePointManager.ServerCertificateValidationCallback != null) { - valid = ServicePointManager.ServerCertificateValidationCallback (engine.Uri!.Host, certificate, chain, sslPolicyErrors); + valid = ServicePointManager.ServerCertificateValidationCallback (host, certificate, chain, sslPolicyErrors); #endif } else { - valid = DefaultServerCertificateValidationCallback (engine.Uri!.Host, certificate, chain, sslPolicyErrors); + valid = DefaultServerCertificateValidationCallback (host, certificate, chain, sslPolicyErrors); } if (!valid) { // Note: The SslHandshakeException.Create() method will nullify this once it's done using it. - sslValidationInfo = new SslCertificateValidationInfo (sender, certificate, chain, sslPolicyErrors); + sslValidationInfo = new SslCertificateValidationInfo (host, certificate, chain, sslPolicyErrors); } return valid; diff --git a/MailKit/Net/Proxy/HttpsProxyClient.cs b/MailKit/Net/Proxy/HttpsProxyClient.cs index 76065ac2d2..1cb5e0fc55 100644 --- a/MailKit/Net/Proxy/HttpsProxyClient.cs +++ b/MailKit/Net/Proxy/HttpsProxyClient.cs @@ -208,7 +208,7 @@ bool ValidateRemoteCertificate (object sender, X509Certificate? certificate, X50 if (!valid) { // Note: The SslHandshakeException.Create() method will nullify this once it's done using it. - sslValidationInfo = new SslCertificateValidationInfo (sender, certificate, chain, sslPolicyErrors); + sslValidationInfo = new SslCertificateValidationInfo (ProxyHost, certificate, chain, sslPolicyErrors); } return valid; diff --git a/MailKit/Net/Smtp/SmtpClient.cs b/MailKit/Net/Smtp/SmtpClient.cs index fde343668e..b6bf40e53f 100644 --- a/MailKit/Net/Smtp/SmtpClient.cs +++ b/MailKit/Net/Smtp/SmtpClient.cs @@ -626,24 +626,25 @@ NetworkOperation StartNetworkOperation (NetworkOperationKind kind) bool ValidateRemoteCertificate (object? sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors) { + var host = uri!.Host; bool valid; sslValidationInfo?.Dispose (); sslValidationInfo = null; if (ServerCertificateValidationCallback != null) { - valid = ServerCertificateValidationCallback (uri!.Host, certificate, chain, sslPolicyErrors); + valid = ServerCertificateValidationCallback (host, certificate, chain, sslPolicyErrors); #if NETFRAMEWORK } else if (ServicePointManager.ServerCertificateValidationCallback != null) { - valid = ServicePointManager.ServerCertificateValidationCallback (uri!.Host, certificate, chain, sslPolicyErrors); + valid = ServicePointManager.ServerCertificateValidationCallback (host, certificate, chain, sslPolicyErrors); #endif } else { - valid = DefaultServerCertificateValidationCallback (uri!.Host, certificate, chain, sslPolicyErrors); + valid = DefaultServerCertificateValidationCallback (host, certificate, chain, sslPolicyErrors); } if (!valid) { // Note: The SslHandshakeException.Create() method will nullify this once it's done using it. - sslValidationInfo = new SslCertificateValidationInfo (sender, certificate, chain, sslPolicyErrors); + sslValidationInfo = new SslCertificateValidationInfo (host, certificate, chain, sslPolicyErrors); } return valid; diff --git a/MailKit/Security/SslHandshakeException.cs b/MailKit/Security/SslHandshakeException.cs index 948b238030..95e98c92fb 100644 --- a/MailKit/Security/SslHandshakeException.cs +++ b/MailKit/Security/SslHandshakeException.cs @@ -375,9 +375,9 @@ sealed class SslCertificateValidationInfo : IDisposable public readonly X509ChainStatus[] ChainStatus; public readonly SslPolicyErrors SslPolicyErrors; public readonly X509Certificate2? Certificate; - public readonly string? Host; + public readonly string Host; - public SslCertificateValidationInfo (object? sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors) + public SslCertificateValidationInfo (string host, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors) { #if NET10_0_OR_GREATER Certificate = certificate != null ? X509CertificateLoader.LoadCertificate (certificate.Export (X509ContentType.Cert)) : null; @@ -386,7 +386,7 @@ public SslCertificateValidationInfo (object? sender, X509Certificate? certificat #endif ChainElements = new List (); SslPolicyErrors = sslPolicyErrors; - Host = sender as string; + Host = host; // Note: we need to copy the ChainElements because the chain will be destroyed if (chain != null) { diff --git a/UnitTests/Security/SslHandshakeExceptionTests.cs b/UnitTests/Security/SslHandshakeExceptionTests.cs index a94274be47..c4163efecf 100644 --- a/UnitTests/Security/SslHandshakeExceptionTests.cs +++ b/UnitTests/Security/SslHandshakeExceptionTests.cs @@ -234,7 +234,7 @@ bool ValidateRemoteCertificate (object sender, X509Certificate certificate, X509 if (!valid) { // Note: The SslHandshakeException.Create() method will nullify this once it's done using it. - sslValidationInfo = new SslCertificateValidationInfo (sender, certificate, chain, sslPolicyErrors); + sslValidationInfo = new SslCertificateValidationInfo (hostName, certificate, chain, sslPolicyErrors); } return valid; From 49327b8172ff0de3f0ca0b29834d979abf010299 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sun, 22 Feb 2026 11:51:25 -0500 Subject: [PATCH 39/44] Refactored more IMAP code to reduce ! usage --- MailKit/Net/Imap/ImapCommand.cs | 10 ++++++---- MailKit/Net/Imap/ImapEngine.cs | 9 +++++---- MailKit/Net/Imap/ImapFolderSearch.cs | 30 +++++++++++++++------------- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/MailKit/Net/Imap/ImapCommand.cs b/MailKit/Net/Imap/ImapCommand.cs index 808c6c1c9f..e3f8b65d18 100644 --- a/MailKit/Net/Imap/ImapCommand.cs +++ b/MailKit/Net/Imap/ImapCommand.cs @@ -597,8 +597,9 @@ public bool Step () var text = Engine.ReadLine (CancellationToken).Trim (); // if we've got a Literal pending, the '+' means we can send it now... - if (!supportsLiteralPlus && parts[current].Literal != null) { - parts[current].Literal!.WriteTo (Engine.Stream, CancellationToken); + var literal = parts[current].Literal; + if (!supportsLiteralPlus && literal != null) { + literal.WriteTo (Engine.Stream, CancellationToken); break; } @@ -739,8 +740,9 @@ public async Task StepAsync () var text = (await Engine.ReadLineAsync (CancellationToken).ConfigureAwait (false)).Trim (); // if we've got a Literal pending, the '+' means we can send it now... - if (!supportsLiteralPlus && parts[current].Literal != null) { - await parts[current].Literal!.WriteToAsync (Engine.Stream, CancellationToken).ConfigureAwait (false); + var literal = parts[current].Literal; + if (!supportsLiteralPlus && literal != null) { + await literal.WriteToAsync (Engine.Stream, CancellationToken).ConfigureAwait (false); break; } diff --git a/MailKit/Net/Imap/ImapEngine.cs b/MailKit/Net/Imap/ImapEngine.cs index 1a7c73bb6a..a022edcfb7 100644 --- a/MailKit/Net/Imap/ImapEngine.cs +++ b/MailKit/Net/Imap/ImapEngine.cs @@ -2847,7 +2847,7 @@ internal void ProcessUntaggedResponse (CancellationToken cancellationToken) } else if (atom.Equals ("FLAGS", StringComparison.OrdinalIgnoreCase)) { var keywords = new HashSet (StringComparer.Ordinal); var flags = ImapUtils.ParseFlagsList (this, atom, keywords, cancellationToken); - folder!.UpdateAcceptedFlags (flags, keywords); + folder?.UpdateAcceptedFlags (flags, keywords); token = ReadToken (cancellationToken); AssertToken (token, ImapTokenType.Eoln, GenericUntaggedResponseSyntaxErrorFormat, atom, token); @@ -3000,7 +3000,7 @@ internal async Task ProcessUntaggedResponseAsync (CancellationToken cancellation } else if (atom.Equals ("FLAGS", StringComparison.OrdinalIgnoreCase)) { var keywords = new HashSet (StringComparer.Ordinal); var flags = await ImapUtils.ParseFlagsListAsync (this, atom, keywords, cancellationToken).ConfigureAwait (false); - folder!.UpdateAcceptedFlags (flags, keywords); + folder?.UpdateAcceptedFlags (flags, keywords); token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); AssertToken (token, ImapTokenType.Eoln, GenericUntaggedResponseSyntaxErrorFormat, atom, token); @@ -3082,6 +3082,7 @@ internal async Task ProcessUntaggedResponseAsync (CancellationToken cancellation } } + [MemberNotNull (nameof (current))] void PopNextCommand () { lock (queue) { @@ -3133,7 +3134,7 @@ void Iterate () { PopNextCommand (); - current!.Status = ImapCommandStatus.Active; + current.Status = ImapCommandStatus.Active; try { while (current.Step ()) { @@ -3160,7 +3161,7 @@ async Task IterateAsync () { PopNextCommand (); - current!.Status = ImapCommandStatus.Active; + current.Status = ImapCommandStatus.Active; try { while (await current.StepAsync ().ConfigureAwait (false)) { diff --git a/MailKit/Net/Imap/ImapFolderSearch.cs b/MailKit/Net/Imap/ImapFolderSearch.cs index c71e92373f..2f3a3ca308 100644 --- a/MailKit/Net/Imap/ImapFolderSearch.cs +++ b/MailKit/Net/Imap/ImapFolderSearch.cs @@ -461,6 +461,7 @@ static void ParseESearchResults (ImapEngine engine, ImapCommand ic, SearchResult { var token = engine.ReadToken (ic.CancellationToken); UniqueId? minValue = null, maxValue = null; + var folder = ic.Folder!; bool hasCount = false; int parenDepth = 0; //bool uid = false; @@ -558,18 +559,17 @@ static void ParseESearchResults (ImapEngine engine, ImapCommand ic, SearchResult var min = ImapEngine.ParseNumber (token, true, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - results.Min = new UniqueId (ic.Folder!.UidValidity, min); + results.Min = new UniqueId (folder.UidValidity, min); } else if (atom.Equals ("MAX", StringComparison.OrdinalIgnoreCase)) { ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ESEARCH", token); var max = ImapEngine.ParseNumber (token, true, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - results.Max = new UniqueId (ic.Folder!.UidValidity, max); + results.Max = new UniqueId (folder.UidValidity, max); } else if (atom.Equals ("ALL", StringComparison.OrdinalIgnoreCase)) { ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ESEARCH", token); - var uids = ImapEngine.ParseUidSet (token, ic.Folder!.UidValidity, out minValue, out maxValue, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - + var uids = ImapEngine.ParseUidSet (token, folder.UidValidity, out minValue, out maxValue, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); if (!hasCount) results.Count = uids.Count; @@ -592,6 +592,7 @@ static async Task ParseESearchResultsAsync (ImapEngine engine, ImapCommand ic, S { var token = await engine.ReadTokenAsync (ic.CancellationToken).ConfigureAwait (false); UniqueId? minValue = null, maxValue = null; + var folder = ic.Folder!; bool hasCount = false; int parenDepth = 0; //bool uid = false; @@ -689,18 +690,17 @@ static async Task ParseESearchResultsAsync (ImapEngine engine, ImapCommand ic, S var min = ImapEngine.ParseNumber (token, true, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - results.Min = new UniqueId (ic.Folder!.UidValidity, min); + results.Min = new UniqueId (folder.UidValidity, min); } else if (atom.Equals ("MAX", StringComparison.OrdinalIgnoreCase)) { ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ESEARCH", token); var max = ImapEngine.ParseNumber (token, true, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - results.Max = new UniqueId (ic.Folder!.UidValidity, max); + results.Max = new UniqueId (folder.UidValidity, max); } else if (atom.Equals ("ALL", StringComparison.OrdinalIgnoreCase)) { ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ESEARCH", token); - var uids = ImapEngine.ParseUidSet (token, ic.Folder!.UidValidity, out minValue, out maxValue, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - + var uids = ImapEngine.ParseUidSet (token, folder.UidValidity, out minValue, out maxValue, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); if (!hasCount) results.Count = uids.Count; @@ -733,6 +733,7 @@ static Task UntaggedESearchHandler (ImapEngine engine, ImapCommand ic, int index static void ParseSearchResults (ImapEngine engine, ImapCommand ic, SearchResults results) { + var folder = ic.Folder!; var uids = results.UniqueIds; uint min = uint.MaxValue; uint uid, max = 0; @@ -748,7 +749,7 @@ static void ParseSearchResults (ImapEngine engine, ImapCommand ic, SearchResults token = engine.ReadToken (ic.CancellationToken); uid = ImapEngine.ParseNumber (token, true, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "SEARCH", token); - uids.Add (new UniqueId (ic.Folder!.UidValidity, uid)); + uids.Add (new UniqueId (folder.UidValidity, uid)); min = Math.Min (min, uid); max = Math.Max (max, uid); } while (true); @@ -779,13 +780,14 @@ static void ParseSearchResults (ImapEngine engine, ImapCommand ic, SearchResults results.UniqueIds = uids; results.Count = uids.Count; if (uids.Count > 0) { - results.Min = new UniqueId (ic.Folder!.UidValidity, min); - results.Max = new UniqueId (ic.Folder!.UidValidity, max); + results.Min = new UniqueId (folder.UidValidity, min); + results.Max = new UniqueId (folder.UidValidity, max); } } static async Task ParseSearchResultsAsync (ImapEngine engine, ImapCommand ic, SearchResults results) { + var folder = ic.Folder!; var uids = results.UniqueIds; uint min = uint.MaxValue; uint uid, max = 0; @@ -801,7 +803,7 @@ static async Task ParseSearchResultsAsync (ImapEngine engine, ImapCommand ic, Se token = await engine.ReadTokenAsync (ic.CancellationToken).ConfigureAwait (false); uid = ImapEngine.ParseNumber (token, true, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "SEARCH", token); - uids.Add (new UniqueId (ic.Folder!.UidValidity, uid)); + uids.Add (new UniqueId (folder.UidValidity, uid)); min = Math.Min (min, uid); max = Math.Max (max, uid); } while (true); @@ -832,8 +834,8 @@ static async Task ParseSearchResultsAsync (ImapEngine engine, ImapCommand ic, Se results.UniqueIds = uids; results.Count = uids.Count; if (uids.Count > 0) { - results.Min = new UniqueId (ic.Folder!.UidValidity, min); - results.Max = new UniqueId (ic.Folder!.UidValidity, max); + results.Min = new UniqueId (folder.UidValidity, min); + results.Max = new UniqueId (folder.UidValidity, max); } } From a55bf8cdcf85fb1a773e48b92f12abe78286e108 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sun, 22 Feb 2026 11:57:36 -0500 Subject: [PATCH 40/44] Refactored Pop3Engine a bit --- MailKit/Net/Pop3/Pop3Engine.cs | 37 +++++++++++++++++----------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/MailKit/Net/Pop3/Pop3Engine.cs b/MailKit/Net/Pop3/Pop3Engine.cs index ece66ce3dc..7c749fce16 100644 --- a/MailKit/Net/Pop3/Pop3Engine.cs +++ b/MailKit/Net/Pop3/Pop3Engine.cs @@ -65,7 +65,6 @@ class Pop3Engine #endif readonly List queue; long clientConnectedTimestamp; - Pop3Stream? stream; bool secure; /// @@ -126,7 +125,7 @@ public Pop3Capabilities Capabilities { /// /// The pop3 stream. public Pop3Stream? Stream { - get { return stream; } + get; private set; } /// @@ -149,7 +148,7 @@ public Pop3EngineState State { /// if the engine is connected; otherwise, . [MemberNotNullWhen (true, new[] { nameof (Stream), nameof (Uri) })] public bool IsConnected { - get { return stream != null && stream.IsConnected; } + get { return Stream != null && Stream.IsConnected; } } /// @@ -209,16 +208,17 @@ public int LoginDelay { get; private set; } + [MemberNotNull (nameof (Stream))] void CheckConnected () { - if (stream == null) + if (Stream == null) throw new InvalidOperationException (); } - [MemberNotNull (nameof (stream))] + [MemberNotNull (nameof (Stream))] void Initialize (Pop3Stream pop3) { - stream?.Dispose (); + Stream?.Dispose (); clientConnectedTimestamp = Stopwatch.GetTimestamp (); Capabilities = Pop3Capabilities.User; @@ -227,7 +227,7 @@ void Initialize (Pop3Stream pop3) ApopToken = null; secure = pop3.Stream is SslStream; - stream = pop3; + Stream = pop3; } void ParseGreeting (string greeting) @@ -251,8 +251,8 @@ void ParseGreeting (string greeting) } if (token != "+OK") { - stream!.Dispose (); - stream = null; + Stream!.Dispose (); + Stream = null; throw new Pop3ProtocolException (string.Format ("Unexpected greeting from server: {0}", greeting)); } @@ -341,9 +341,9 @@ public void Disconnect (Exception? ex) { RecordClientDisconnected (ex); - if (stream != null) { - stream.Dispose (); - stream = null; + if (Stream != null) { + Stream.Dispose (); + Stream = null; } secure = false; @@ -376,7 +376,7 @@ public string ReadLine (CancellationToken cancellationToken) bool complete; do { - complete = stream!.ReadLine (builder, cancellationToken); + complete = Stream.ReadLine (builder, cancellationToken); } while (!complete); // FIXME: All callers expect CRLF to be trimmed, but many also want all trailing whitespace trimmed. @@ -408,7 +408,7 @@ public async Task ReadLineAsync (CancellationToken cancellationToken) bool complete; do { - complete = await stream!.ReadLineAsync (builder, cancellationToken).ConfigureAwait (false); + complete = await Stream.ReadLineAsync (builder, cancellationToken).ConfigureAwait (false); } while (!complete); // FIXME: All callers expect CRLF to be trimmed, but many also want all trailing whitespace trimmed. @@ -520,6 +520,7 @@ async Task ReadResponseAsync (Pop3Command pc, CancellationToken cancellationToke } } + [MemberNotNull (nameof (Stream))] void CheckCanRun (CancellationToken cancellationToken) { CheckConnected (); @@ -548,10 +549,10 @@ public void Run (bool throwOnError, CancellationToken cancellationToken) pc.Status = Pop3CommandStatus.Active; - stream!.QueueCommand (pc.Encoding, pc.Command, cancellationToken); + Stream.QueueCommand (pc.Encoding, pc.Command, cancellationToken); } - stream!.Flush (cancellationToken); + Stream.Flush (cancellationToken); for (int i = 0; i < queue.Count; i++) ReadResponse (queue[i], cancellationToken); @@ -581,10 +582,10 @@ public async Task RunAsync (bool throwOnError, CancellationToken cancellationTok pc.Status = Pop3CommandStatus.Active; - await stream!.QueueCommandAsync (pc.Encoding, pc.Command, cancellationToken).ConfigureAwait (false); + await Stream.QueueCommandAsync (pc.Encoding, pc.Command, cancellationToken).ConfigureAwait (false); } - await stream!.FlushAsync (cancellationToken).ConfigureAwait (false); + await Stream.FlushAsync (cancellationToken).ConfigureAwait (false); for (int i = 0; i < queue.Count; i++) await ReadResponseAsync (queue[i], cancellationToken).ConfigureAwait (false); From 77cc4f0f19a0b2a1c69ee4829447a9be43b4c7a3 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sun, 22 Feb 2026 11:58:37 -0500 Subject: [PATCH 41/44] A few minor refactorings for IMAP ! usage --- MailKit/Net/Imap/ImapFolder.cs | 6 ++++-- MailKit/Net/Imap/ImapUtils.cs | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/MailKit/Net/Imap/ImapFolder.cs b/MailKit/Net/Imap/ImapFolder.cs index b38d60436f..fb9c148bf2 100644 --- a/MailKit/Net/Imap/ImapFolder.cs +++ b/MailKit/Net/Imap/ImapFolder.cs @@ -318,10 +318,12 @@ static string SelectOrExamine (FolderAccess access) static Task UntaggedQResyncFetchHandler (ImapEngine engine, ImapCommand ic, int index, bool doAsync) { + var folder = ic.Folder!; + if (doAsync) - return ic.Folder!.OnUntaggedFetchResponseAsync (engine, index, ic.CancellationToken); + return folder.OnUntaggedFetchResponseAsync (engine, index, ic.CancellationToken); - ic.Folder!.OnUntaggedFetchResponse (engine, index, ic.CancellationToken); + folder.OnUntaggedFetchResponse (engine, index, ic.CancellationToken); return Task.CompletedTask; } diff --git a/MailKit/Net/Imap/ImapUtils.cs b/MailKit/Net/Imap/ImapUtils.cs index 62205060d4..eae6ae07e5 100644 --- a/MailKit/Net/Imap/ImapUtils.cs +++ b/MailKit/Net/Imap/ImapUtils.cs @@ -2974,13 +2974,14 @@ public static async Task ParseThreadsAsync (ImapEngine engine, uint uidValidity, public static Task UntaggedThreadHandler (ImapEngine engine, ImapCommand ic, int index, bool doAsync) { var threads = new List (); + var folder = ic.Folder!; ic.UserData = threads; if (doAsync) - return ParseThreadsAsync (engine, ic.Folder!.UidValidity, threads, ic.CancellationToken); + return ParseThreadsAsync (engine, folder.UidValidity, threads, ic.CancellationToken); - ParseThreads (engine, ic.Folder!.UidValidity, threads, ic.CancellationToken); + ParseThreads (engine, folder.UidValidity, threads, ic.CancellationToken); return Task.CompletedTask; } From b371ca7d6567c0ccf69e24e4d41586e73de2e959 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sun, 22 Feb 2026 16:11:16 -0500 Subject: [PATCH 42/44] Refactored Pop3Client a bit to reduce ! usage --- MailKit/Net/Pop3/AsyncPop3Client.cs | 6 ++-- MailKit/Net/Pop3/Pop3Client.cs | 44 ++++++++++++++++++----------- MailKit/Net/Pop3/Pop3Engine.cs | 2 +- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/MailKit/Net/Pop3/AsyncPop3Client.cs b/MailKit/Net/Pop3/AsyncPop3Client.cs index e0df1f66a6..128bc3bfce 100644 --- a/MailKit/Net/Pop3/AsyncPop3Client.cs +++ b/MailKit/Net/Pop3/AsyncPop3Client.cs @@ -139,12 +139,11 @@ async Task OnAuthenticatedAsync (string message, CancellationToken cancellationT /// public override async Task AuthenticateAsync (SaslMechanism mechanism, CancellationToken cancellationToken = default) { - CheckCanAuthenticate (mechanism, cancellationToken); + var saslUri = CheckCanAuthenticate (mechanism, cancellationToken); using var operation = engine.StartNetworkOperation (NetworkOperationKind.Authenticate); try { - var saslUri = new Uri ("pop://" + engine.Uri!.Host); var ctx = GetSaslAuthContext (mechanism, saslUri); var pc = await ctx.AuthenticateAsync (cancellationToken).ConfigureAwait (false); @@ -221,12 +220,11 @@ public override async Task AuthenticateAsync (SaslMechanism mechanism, Cancellat /// public override async Task AuthenticateAsync (Encoding encoding, ICredentials credentials, CancellationToken cancellationToken = default) { - CheckCanAuthenticate (encoding, credentials, cancellationToken); + var saslUri = CheckCanAuthenticate (encoding, credentials, cancellationToken); using var operation = engine.StartNetworkOperation (NetworkOperationKind.Authenticate); try { - var saslUri = new Uri ("pop://" + engine.Uri!.Host); string userName, password; NetworkCredential? cred; string? message = null; diff --git a/MailKit/Net/Pop3/Pop3Client.cs b/MailKit/Net/Pop3/Pop3Client.cs index 6cabcb37c2..407e9367c8 100644 --- a/MailKit/Net/Pop3/Pop3Client.cs +++ b/MailKit/Net/Pop3/Pop3Client.cs @@ -680,11 +680,13 @@ Pop3Engine Engine { void OnDataReceived (Pop3Engine pop3, Pop3Command pc, string text, CancellationToken cancellationToken) { + pop3.CheckConnected (); + while (pc.Status == Pop3CommandStatus.Continue && !mechanism.IsAuthenticated) { var challenge = mechanism.Challenge (text, cancellationToken); var buf = Encoding.ASCII.GetBytes (challenge + "\r\n"); - pop3.Stream!.Write (buf, 0, buf.Length, cancellationToken); + pop3.Stream.Write (buf, 0, buf.Length, cancellationToken); pop3.Stream.Flush (cancellationToken); var response = pop3.ReadLine (cancellationToken).TrimEnd (); @@ -700,11 +702,13 @@ void OnDataReceived (Pop3Engine pop3, Pop3Command pc, string text, CancellationT async Task OnDataReceivedAsync (Pop3Engine pop3, Pop3Command pc, string text, CancellationToken cancellationToken) { + pop3.CheckConnected (); + while (pc.Status == Pop3CommandStatus.Continue && !mechanism.IsAuthenticated) { var challenge = await mechanism.ChallengeAsync (text, cancellationToken).ConfigureAwait (false); var buf = Encoding.ASCII.GetBytes (challenge + "\r\n"); - await pop3.Stream!.WriteAsync (buf, 0, buf.Length, cancellationToken).ConfigureAwait (false); + await pop3.Stream.WriteAsync (buf, 0, buf.Length, cancellationToken).ConfigureAwait (false); await pop3.Stream.FlushAsync (cancellationToken).ConfigureAwait (false); var response = (await pop3.ReadLineAsync (cancellationToken).ConfigureAwait (false)).TrimEnd (); @@ -764,12 +768,12 @@ public async Task AuthenticateAsync (CancellationToken cancellation } } - void CheckCanAuthenticate (SaslMechanism mechanism, CancellationToken cancellationToken) + Uri CheckCanAuthenticate (SaslMechanism mechanism, CancellationToken cancellationToken) { if (mechanism == null) throw new ArgumentNullException (nameof (mechanism)); - if (!IsConnected) + if (!engine.IsConnected) throw new ServiceNotConnectedException ("The Pop3Client must be connected before you can authenticate."); if (IsAuthenticated) @@ -778,6 +782,8 @@ void CheckCanAuthenticate (SaslMechanism mechanism, CancellationToken cancellati CheckDisposed (); cancellationToken.ThrowIfCancellationRequested (); + + return new Uri ("pop://" + engine.Uri.Host); } SaslAuthContext GetSaslAuthContext (SaslMechanism mechanism, Uri saslUri) @@ -841,12 +847,11 @@ void OnAuthenticated (string message, CancellationToken cancellationToken) /// public override void Authenticate (SaslMechanism mechanism, CancellationToken cancellationToken = default) { - CheckCanAuthenticate (mechanism, cancellationToken); + var saslUri = CheckCanAuthenticate (mechanism, cancellationToken); using var operation = engine.StartNetworkOperation (NetworkOperationKind.Authenticate); try { - var saslUri = new Uri ("pop://" + engine.Uri!.Host); var ctx = GetSaslAuthContext (mechanism, saslUri); var pc = ctx.Authenticate (cancellationToken); @@ -863,7 +868,7 @@ public override void Authenticate (SaslMechanism mechanism, CancellationToken ca } } - void CheckCanAuthenticate (Encoding encoding, ICredentials credentials, CancellationToken cancellationToken) + Uri CheckCanAuthenticate (Encoding encoding, ICredentials credentials, CancellationToken cancellationToken) { if (encoding == null) throw new ArgumentNullException (nameof (encoding)); @@ -871,13 +876,17 @@ void CheckCanAuthenticate (Encoding encoding, ICredentials credentials, Cancella if (credentials == null) throw new ArgumentNullException (nameof (credentials)); - if (!IsConnected) + if (!engine.IsConnected) throw new ServiceNotConnectedException ("The Pop3Client must be connected before you can authenticate."); if (IsAuthenticated) throw new InvalidOperationException ("The Pop3Client is already authenticated."); CheckDisposed (); + + cancellationToken.ThrowIfCancellationRequested (); + + return new Uri ("pop://" + engine.Uri.Host); } string GetApopCommand (Encoding encoding, NetworkCredential cred) @@ -956,12 +965,11 @@ string GetApopCommand (Encoding encoding, NetworkCredential cred) /// public override void Authenticate (Encoding encoding, ICredentials credentials, CancellationToken cancellationToken = default) { - CheckCanAuthenticate (encoding, credentials, cancellationToken); + var saslUri = CheckCanAuthenticate (encoding, credentials, cancellationToken); using var operation = engine.StartNetworkOperation (NetworkOperationKind.Authenticate); try { - var saslUri = new Uri ("pop://" + engine.Uri!.Host); string userName, password; NetworkCredential? cred; string? message = null; @@ -2264,8 +2272,10 @@ protected void Update (int n) void OnDataReceived (Pop3Engine engine, Pop3Command pc, CancellationToken cancellationToken) { + engine.CheckConnected (); + try { - engine.Stream!.Mode = Pop3StreamMode.Data; + engine.Stream.Mode = Pop3StreamMode.Data; var item = Parse (engine.Stream, cancellationToken); @@ -2273,16 +2283,18 @@ void OnDataReceived (Pop3Engine engine, Pop3Command pc, CancellationToken cancel } catch (FormatException ex) { pc.Exception = CreatePop3ParseException (ex, "Failed to parse data."); - engine.Stream!.CopyTo (Stream.Null, 4096); + engine.Stream.CopyTo (Stream.Null, 4096); } finally { - engine.Stream!.Mode = Pop3StreamMode.Line; + engine.Stream.Mode = Pop3StreamMode.Line; } } async Task OnDataReceivedAsync (Pop3Engine engine, Pop3Command pc, CancellationToken cancellationToken) { + engine.CheckConnected (); + try { - engine.Stream!.Mode = Pop3StreamMode.Data; + engine.Stream.Mode = Pop3StreamMode.Data; var item = await ParseAsync (engine.Stream, cancellationToken).ConfigureAwait (false); @@ -2290,9 +2302,9 @@ async Task OnDataReceivedAsync (Pop3Engine engine, Pop3Command pc, CancellationT } catch (FormatException ex) { pc.Exception = CreatePop3ParseException (ex, "Failed to parse data."); - await engine.Stream!.CopyToAsync (Stream.Null, 4096, cancellationToken).ConfigureAwait (false); + await engine.Stream.CopyToAsync (Stream.Null, 4096, cancellationToken).ConfigureAwait (false); } finally { - engine.Stream!.Mode = Pop3StreamMode.Line; + engine.Stream.Mode = Pop3StreamMode.Line; } } diff --git a/MailKit/Net/Pop3/Pop3Engine.cs b/MailKit/Net/Pop3/Pop3Engine.cs index 7c749fce16..8e3d046542 100644 --- a/MailKit/Net/Pop3/Pop3Engine.cs +++ b/MailKit/Net/Pop3/Pop3Engine.cs @@ -209,7 +209,7 @@ public int LoginDelay { } [MemberNotNull (nameof (Stream))] - void CheckConnected () + internal void CheckConnected () { if (Stream == null) throw new InvalidOperationException (); From 1cd0f0e86ce5db57e9c593d4da966e94aef96a5a Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Mon, 23 Feb 2026 09:03:58 -0500 Subject: [PATCH 43/44] More refactoring to improve IMAP nullability --- MailKit/Net/Imap/ImapClient.cs | 8 +++---- MailKit/Net/Imap/ImapEngine.cs | 33 +++++++++++++++++++++++------ MailKit/Net/Imap/ImapIdleContext.cs | 4 ++-- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/MailKit/Net/Imap/ImapClient.cs b/MailKit/Net/Imap/ImapClient.cs index 026cda1dcb..12b1c9a4f8 100644 --- a/MailKit/Net/Imap/ImapClient.cs +++ b/MailKit/Net/Imap/ImapClient.cs @@ -1032,9 +1032,9 @@ internal static void EscapeUserName (StringBuilder builder, string userName) string GetSessionIdentifier (string userName) { var builder = new StringBuilder (); - var uri = engine.Uri; + var uri = engine.Uri!; - builder.Append (uri!.Scheme); + builder.Append (uri.Scheme); builder.Append ("://"); EscapeUserName (builder, userName); builder.Append ('@'); @@ -2801,11 +2801,11 @@ void OnEngineDisconnected (object? sender, EventArgs e) return; var requested = disconnecting; - var uri = engine.Uri; + var uri = engine.Uri!; disconnecting = false; - OnDisconnected (uri!.Host, uri.Port, GetSecureSocketOptions (uri), requested); + OnDisconnected (uri.Host, uri.Port, GetSecureSocketOptions (uri), requested); } /// diff --git a/MailKit/Net/Imap/ImapEngine.cs b/MailKit/Net/Imap/ImapEngine.cs index a022edcfb7..853adf9897 100644 --- a/MailKit/Net/Imap/ImapEngine.cs +++ b/MailKit/Net/Imap/ImapEngine.cs @@ -401,6 +401,18 @@ public bool IsConnected { get { return Stream != null && Stream.IsConnected; } } + /// + /// Get whether or not the client is currently in the IDLE state. + /// + /// + /// Gets whether or not the client is currently in the IDLE state. + /// + /// if an IDLE command is active; otherwise, . + [MemberNotNullWhen (true, nameof (Stream))] + public bool IsIdle { + get { return IsConnected && State == ImapEngineState.Idle; } + } + /// /// Get whether or not the connection is secure (typically via SSL or TLS). /// @@ -3105,10 +3117,17 @@ void PopNextCommand () } } - void OnImapProtocolException (ImapProtocolException ex) + /// + /// Handles an IMAP protocol exception by disconnecting and then potentially throwing a replacement exception. + /// + /// The current being processed. + /// THe that was thrown. + /// + /// An ALERT or some resp-text was found that would enhance the exception message + /// of the provided. + /// + void OnImapProtocolException (ImapCommand ic, ImapProtocolException ex) { - var ic = current!; - Disconnect (ex); if (ic.Bye) { @@ -3118,12 +3137,12 @@ void OnImapProtocolException (ImapProtocolException ex) if (code.Type == ImapResponseCodeType.Alert) { OnAlert (code.Message); - throw new ImapProtocolException (code.Message); + throw new ImapProtocolException (code.Message, ex); } } if (!string.IsNullOrEmpty (ic.ResponseText)) - throw new ImapProtocolException (ic.ResponseText!); + throw new ImapProtocolException (ic.ResponseText!, ex); } } @@ -3144,7 +3163,7 @@ void Iterate () if (current.Bye && !current.Logout) throw new ImapProtocolException ("Bye."); } catch (ImapProtocolException ex) { - OnImapProtocolException (ex); + OnImapProtocolException (current, ex); throw; } catch (Exception ex) { Disconnect (ex); @@ -3171,7 +3190,7 @@ async Task IterateAsync () if (current.Bye && !current.Logout) throw new ImapProtocolException ("Bye."); } catch (ImapProtocolException ex) { - OnImapProtocolException (ex); + OnImapProtocolException (current, ex); throw; } catch (Exception ex) { Disconnect (ex); diff --git a/MailKit/Net/Imap/ImapIdleContext.cs b/MailKit/Net/Imap/ImapIdleContext.cs index d3e8090758..902a3c2f25 100644 --- a/MailKit/Net/Imap/ImapIdleContext.cs +++ b/MailKit/Net/Imap/ImapIdleContext.cs @@ -122,9 +122,9 @@ public bool IsDoneRequested { void IdleComplete () { - if (Engine.State == ImapEngineState.Idle) { + if (Engine.IsIdle) { try { - Engine.Stream!.Write (DoneCommand, 0, DoneCommand.Length, CancellationToken); + Engine.Stream.Write (DoneCommand, 0, DoneCommand.Length, CancellationToken); Engine.Stream.Flush (CancellationToken); } catch { return; From adfebe6b0eb9ccdd92d1bc949a33b89f1ce3d243 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Tue, 24 Feb 2026 08:38:25 -0500 Subject: [PATCH 44/44] Updated ImapEngine.ProcessUntaggedResponse() to take an ImapCommand argument This removes the need to reference the `current` member variable. --- MailKit/Net/Imap/ImapCommand.cs | 4 +-- MailKit/Net/Imap/ImapEngine.cs | 54 +++++++++++++++++---------------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/MailKit/Net/Imap/ImapCommand.cs b/MailKit/Net/Imap/ImapCommand.cs index e3f8b65d18..1bd50e0a83 100644 --- a/MailKit/Net/Imap/ImapCommand.cs +++ b/MailKit/Net/Imap/ImapCommand.cs @@ -611,7 +611,7 @@ public bool Step () } } else if (token.Type == ImapTokenType.Asterisk) { // we got an untagged response, let the engine handle this... - Engine.ProcessUntaggedResponse (CancellationToken); + Engine.ProcessUntaggedResponse (this, CancellationToken); } else if (token.Type == ImapTokenType.Atom && (string) token.Value == Tag) { // the next token should be "OK", "NO", or "BAD" token = Engine.ReadToken (CancellationToken); @@ -754,7 +754,7 @@ public async Task StepAsync () } } else if (token.Type == ImapTokenType.Asterisk) { // we got an untagged response, let the engine handle this... - await Engine.ProcessUntaggedResponseAsync (CancellationToken).ConfigureAwait (false); + await Engine.ProcessUntaggedResponseAsync (this, CancellationToken).ConfigureAwait (false); } else if (token.Type == ImapTokenType.Atom && (string) token.Value == Tag) { // the next token should be "OK", "NO", or "BAD" token = await Engine.ReadTokenAsync (CancellationToken).ConfigureAwait (false); diff --git a/MailKit/Net/Imap/ImapEngine.cs b/MailKit/Net/Imap/ImapEngine.cs index 853adf9897..a8367c168a 100644 --- a/MailKit/Net/Imap/ImapEngine.cs +++ b/MailKit/Net/Imap/ImapEngine.cs @@ -2792,11 +2792,12 @@ static bool IsOkNoOrBad (string atom, out ImapUntaggedResult result) /// Processes an untagged response. /// /// The untagged response. + /// The IMAP command that is currently being processed. /// The cancellation token. - internal void ProcessUntaggedResponse (CancellationToken cancellationToken) + internal void ProcessUntaggedResponse (ImapCommand ic, CancellationToken cancellationToken) { var token = ReadToken (cancellationToken); - var folder = current!.Folder ?? Selected; + var folder = ic.Folder ?? Selected; ImapUntaggedHandler? handler; string atom; @@ -2820,13 +2821,13 @@ internal void ProcessUntaggedResponse (CancellationToken cancellationToken) if (token.Type == ImapTokenType.OpenBracket) { var code = ParseResponseCode (false, cancellationToken); - current.RespCodes.Add (code); + ic.RespCodes.Add (code); } else { var text = ReadLine (cancellationToken).TrimEnd (); - current.ResponseText = token.Value.ToString () + text; + ic.ResponseText = token.Value.ToString () + text; } - current.Bye = true; + ic.Bye = true; // Note: Yandex IMAP is broken and will continue sending untagged BYE responses until the client closes // the connection. In order to avoid this scenario, consider this command complete as soon as we receive @@ -2834,8 +2835,8 @@ internal void ProcessUntaggedResponse (CancellationToken cancellationToken) // untagged BYE. // // See https://github.com/jstedfast/MailKit/issues/938 for details. - if (QuirksMode == ImapQuirksMode.Yandex && !current.Logout) - current.Status = ImapCommandStatus.Complete; + if (QuirksMode == ImapQuirksMode.Yandex && !ic.Logout) + ic.Status = ImapCommandStatus.Complete; } else if (atom.Equals ("CAPABILITY", StringComparison.OrdinalIgnoreCase)) { UpdateCapabilities (ImapTokenType.Eoln, cancellationToken); @@ -2872,10 +2873,10 @@ internal void ProcessUntaggedResponse (CancellationToken cancellationToken) if (token.Type == ImapTokenType.OpenBracket) { var code = ParseResponseCode (false, cancellationToken); - current.RespCodes.Add (code); + ic.RespCodes.Add (code); } else if (token.Type != ImapTokenType.Eoln) { var text = ReadLine (cancellationToken).TrimEnd (); - current.ResponseText = token.Value.ToString () + text; + ic.ResponseText = token.Value.ToString () + text; } } else { if (uint.TryParse (atom, NumberStyles.None, CultureInfo.InvariantCulture, out uint number)) { @@ -2886,9 +2887,9 @@ internal void ProcessUntaggedResponse (CancellationToken cancellationToken) atom = (string) token.Value; - if (current.UntaggedHandlers.TryGetValue (atom, out handler)) { + if (ic.UntaggedHandlers.TryGetValue (atom, out handler)) { // the command registered an untagged handler for this atom... - handler (this, current, (int) number - 1, false).GetAwaiter ().GetResult (); + handler (this, ic, (int) number - 1, false).GetAwaiter ().GetResult (); } else if (folder != null) { if (atom.Equals ("EXISTS", StringComparison.OrdinalIgnoreCase)) { folder.OnExists ((int) number); @@ -2914,9 +2915,9 @@ internal void ProcessUntaggedResponse (CancellationToken cancellationToken) } SkipLine (cancellationToken); - } else if (current.UntaggedHandlers.TryGetValue (atom, out handler)) { + } else if (ic.UntaggedHandlers.TryGetValue (atom, out handler)) { // the command registered an untagged handler for this atom... - handler (this, current, -1, false).GetAwaiter ().GetResult (); + handler (this, ic, -1, false).GetAwaiter ().GetResult (); SkipLine (cancellationToken); } else if (atom.Equals ("LIST", StringComparison.OrdinalIgnoreCase)) { // unsolicited LIST response - probably due to NOTIFY MailboxName or MailboxSubscribe event @@ -2945,11 +2946,12 @@ internal void ProcessUntaggedResponse (CancellationToken cancellationToken) /// Processes an untagged response. /// /// The untagged response. + /// The IMAP command that is currently being processed. /// The cancellation token. - internal async Task ProcessUntaggedResponseAsync (CancellationToken cancellationToken) + internal async Task ProcessUntaggedResponseAsync (ImapCommand ic, CancellationToken cancellationToken) { var token = await ReadTokenAsync (cancellationToken).ConfigureAwait (false); - var folder = current!.Folder ?? Selected; + var folder = ic.Folder ?? Selected; ImapUntaggedHandler? handler; string atom; @@ -2973,13 +2975,13 @@ internal async Task ProcessUntaggedResponseAsync (CancellationToken cancellation if (token.Type == ImapTokenType.OpenBracket) { var code = await ParseResponseCodeAsync (false, cancellationToken).ConfigureAwait (false); - current.RespCodes.Add (code); + ic.RespCodes.Add (code); } else { var text = (await ReadLineAsync (cancellationToken).ConfigureAwait (false)).TrimEnd (); - current.ResponseText = token.Value.ToString () + text; + ic.ResponseText = token.Value.ToString () + text; } - current.Bye = true; + ic.Bye = true; // Note: Yandex IMAP is broken and will continue sending untagged BYE responses until the client closes // the connection. In order to avoid this scenario, consider this command complete as soon as we receive @@ -2987,8 +2989,8 @@ internal async Task ProcessUntaggedResponseAsync (CancellationToken cancellation // untagged BYE. // // See https://github.com/jstedfast/MailKit/issues/938 for details. - if (QuirksMode == ImapQuirksMode.Yandex && !current.Logout) - current.Status = ImapCommandStatus.Complete; + if (QuirksMode == ImapQuirksMode.Yandex && !ic.Logout) + ic.Status = ImapCommandStatus.Complete; } else if (atom.Equals ("CAPABILITY", StringComparison.OrdinalIgnoreCase)) { await UpdateCapabilitiesAsync (ImapTokenType.Eoln, cancellationToken).ConfigureAwait (false); @@ -3025,10 +3027,10 @@ internal async Task ProcessUntaggedResponseAsync (CancellationToken cancellation if (token.Type == ImapTokenType.OpenBracket) { var code = await ParseResponseCodeAsync (false, cancellationToken).ConfigureAwait (false); - current.RespCodes.Add (code); + ic.RespCodes.Add (code); } else if (token.Type != ImapTokenType.Eoln) { var text = (await ReadLineAsync (cancellationToken).ConfigureAwait (false)).TrimEnd (); - current.ResponseText = token.Value.ToString () + text; + ic.ResponseText = token.Value.ToString () + text; } } else { if (uint.TryParse (atom, NumberStyles.None, CultureInfo.InvariantCulture, out uint number)) { @@ -3039,9 +3041,9 @@ internal async Task ProcessUntaggedResponseAsync (CancellationToken cancellation atom = (string) token.Value; - if (current.UntaggedHandlers.TryGetValue (atom, out handler)) { + if (ic.UntaggedHandlers.TryGetValue (atom, out handler)) { // the command registered an untagged handler for this atom... - await handler (this, current, (int) number - 1, doAsync: true).ConfigureAwait (false); + await handler (this, ic, (int) number - 1, doAsync: true).ConfigureAwait (false); } else if (folder != null) { if (atom.Equals ("EXISTS", StringComparison.OrdinalIgnoreCase)) { folder.OnExists ((int) number); @@ -3067,9 +3069,9 @@ internal async Task ProcessUntaggedResponseAsync (CancellationToken cancellation } await SkipLineAsync (cancellationToken).ConfigureAwait (false); - } else if (current.UntaggedHandlers.TryGetValue (atom, out handler)) { + } else if (ic.UntaggedHandlers.TryGetValue (atom, out handler)) { // the command registered an untagged handler for this atom... - await handler (this, current, -1, doAsync: true).ConfigureAwait (false); + await handler (this, ic, -1, doAsync: true).ConfigureAwait (false); await SkipLineAsync (cancellationToken).ConfigureAwait (false); } else if (atom.Equals ("LIST", StringComparison.OrdinalIgnoreCase)) { // unsolicited LIST response - probably due to NOTIFY MailboxName or MailboxSubscribe event