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..2bc3029eff 100644 --- a/MailKit/Annotation.cs +++ b/MailKit/Annotation.cs @@ -50,10 +50,10 @@ public class Annotation /// public Annotation (AnnotationEntry entry) { - if (entry == null) + 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/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/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 a3e6d4916b..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; } @@ -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/BodyPart.cs b/MailKit/BodyPart.cs index b20236984d..f66785c04b 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; @@ -52,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; } /// @@ -103,7 +132,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 +140,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 +148,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 +188,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 +226,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 +236,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 +297,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 +345,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,10 +375,11 @@ 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); + if (value != null) + list.Add (value); } while (index < text.Length); if (index >= text.Length || text[index] != ')') @@ -361,11 +391,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 +408,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 +439,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 +456,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 +477,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 +496,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 +518,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,17 +529,17 @@ 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 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] != '(') @@ -517,25 +547,27 @@ static bool TryParse (string text, ref int index, string prefix, out IList 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; } @@ -573,7 +602,7 @@ static bool TryParse (string text, ref int index, string path, out BodyPart part 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; @@ -592,22 +621,20 @@ 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; 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; @@ -650,12 +677,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; @@ -674,8 +701,6 @@ static bool TryParse (string text, ref int index, string path, out BodyPart part part = basic; } - part.PartSpecifier = path; - if (index >= text.Length || text[index] != ')') return false; @@ -698,7 +723,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..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) { } @@ -59,7 +77,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 +88,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 +101,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 +125,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 +139,7 @@ public string ContentMd5 { /// summary information from an . /// /// The content disposition. - public ContentDisposition ContentDisposition { + public ContentDisposition? ContentDisposition { get; set; } @@ -135,7 +153,7 @@ public ContentDisposition ContentDisposition { /// summary information from an . /// /// The content language. - public string[] ContentLanguage { + public string[]? ContentLanguage { get; set; } @@ -149,7 +167,7 @@ public string[] ContentLanguage { /// summary information from an . /// /// The content location. - public Uri ContentLocation { + public Uri? ContentLocation { get; set; } @@ -179,9 +197,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/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 c13d0cd909..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) { } @@ -53,7 +73,7 @@ public BodyPartMessage () /// Gets the envelope of the message, if available. /// /// The envelope. - public Envelope Envelope { + public Envelope? Envelope { get; set; } @@ -64,7 +84,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..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. /// @@ -67,7 +109,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 +120,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 +131,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/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/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); } /// 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; } } } diff --git a/MailKit/Envelope.cs b/MailKit/Envelope.cs index 18f67045ba..83c51535bf 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; } @@ -348,7 +349,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; @@ -396,7 +397,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, out InternetAddress? addr) { addr = null; @@ -405,16 +406,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] == ' ') @@ -442,7 +443,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; @@ -477,7 +478,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) { @@ -507,7 +508,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; @@ -527,7 +528,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) { @@ -537,31 +538,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] != ')') @@ -599,7 +600,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/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/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; } diff --git a/MailKit/IAppendRequest.cs b/MailKit/IAppendRequest.cs index 8a83176d42..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. @@ -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/IMailFolder.cs b/MailKit/IMailFolder.cs index f6bba8b700..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. @@ -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/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/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/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/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/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/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/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 dc078e1882..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) @@ -271,7 +313,7 @@ public string Name { /// OBJECTID extension. /// /// The unique folder identifier. - public string Id { + public string? Id { get; protected set; } @@ -746,7 +788,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 +830,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 +877,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 +924,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 +969,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 +1017,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 +2402,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 +2437,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 +4843,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 +4885,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 +4931,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 +4977,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 +5019,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 +5061,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 +5107,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 +5153,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 +5198,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 +5243,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 +5288,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 +5333,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 +5382,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 +5428,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 +5474,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 +5520,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 +5565,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 +5613,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 +5661,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 +5709,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 +5764,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 +5816,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 +5867,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 +5918,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 +5967,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 +6025,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 +6080,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 +6135,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 +6200,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 +6271,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 +6341,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 +6411,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 +6477,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 +6528,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 +6586,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 +6644,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 +6692,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 +6740,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 +6797,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 +6854,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. @@ -9349,7 +9391,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 +9410,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 +9438,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 +9457,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. @@ -9442,7 +9484,7 @@ protected virtual void OnParentFolderRenamed () { } - void OnParentFolderRenamed (object sender, FolderRenamedEventArgs e) + void OnParentFolderRenamed (object? sender, FolderRenamedEventArgs e) { var oldFullName = FullName; @@ -9458,7 +9500,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 +9519,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 +9541,7 @@ protected virtual void OnUnsubscribed () /// /// /// - public event EventHandler MessageExpunged; + public event EventHandler? MessageExpunged; /// /// Raise the message expunged event. @@ -9519,7 +9561,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 +9584,7 @@ protected virtual void OnMessagesVanished (MessagesVanishedEventArgs args) /// /// /// - public event EventHandler MessageFlagsChanged; + public event EventHandler? MessageFlagsChanged; /// /// Raise the message flags changed event. @@ -9562,7 +9604,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 +9624,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 +9659,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 +9694,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 +9714,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 +9734,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 +9753,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 +9772,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 +9791,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 +9810,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 +9832,7 @@ protected virtual void OnSizeChanged () /// /// /// - public event EventHandler CountChanged; + public event EventHandler? CountChanged; /// /// Raise the message count changed event. @@ -9809,7 +9851,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 +9870,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/MailKit.csproj b/MailKit/MailKit.csproj index c5adfc7c02..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 @@ -24,6 +24,7 @@ false false MailKit + enable true mailkit.snk true 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/MailStore.cs b/MailKit/MailStore.cs index 579a6fbb37..328b226a19 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. @@ -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/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/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/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/MessageSummary.cs b/MailKit/MessageSummary.cs index 29b18f9e70..e31a574955 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 (nameof (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; } 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). 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/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/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/ClientMetrics.cs b/MailKit/Net/ClientMetrics.cs index 459b99b589..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,10 +99,10 @@ 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) + internal static TagList GetTags (Uri uri, Exception? ex) { var tags = new TagList { { "url.scheme", uri.Scheme }, @@ -115,7 +116,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; 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/Imap/AsyncImapClient.cs b/MailKit/Net/Imap/AsyncImapClient.cs index 858b77d9d5..9f599684b1 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); }; @@ -443,13 +443,12 @@ 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; } 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,20 +515,20 @@ 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; 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) { @@ -1255,7 +1249,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/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 823fe874fe..12b1c9a4f8 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; @@ -67,13 +68,12 @@ 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; - bool secure; /// /// Initializes a new instance of the class. @@ -258,26 +258,27 @@ 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) { + 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; @@ -313,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 } @@ -360,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 (); @@ -449,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 (); @@ -557,7 +558,7 @@ static ImapImplementation ProcessIdentifyResponse (ImapCommand ic) { ic.ThrowIfNotOk ("ID"); - return (ImapImplementation) ic.UserData; + return (ImapImplementation) ic.UserData!; } /// @@ -665,7 +666,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; } @@ -700,7 +701,7 @@ public override bool IsConnected { /// /// if the connection is secure; otherwise, . public override bool IsSecure { - get { return IsConnected && secure; } + get { return engine.IsSecure; } } /// @@ -711,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 engine.IsSecure && (engine.Stream.Stream is SslStream sslStream) && sslStream.IsEncrypted; } } /// @@ -722,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 engine.IsSecure && (engine.Stream.Stream is SslStream sslStream) && sslStream.IsSigned; } } /// @@ -737,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 (engine.IsSecure && (engine.Stream.Stream is SslStream sslStream)) return sslStream.SslProtocol; return SslProtocols.None; @@ -759,7 +760,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; @@ -781,7 +782,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; @@ -798,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 (engine.IsSecure && (engine.Stream.Stream is SslStream sslStream)) return sslStream.NegotiatedCipherSuite; return null; @@ -821,7 +822,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; @@ -843,7 +844,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; @@ -865,7 +866,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; @@ -887,7 +888,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; @@ -1031,7 +1032,7 @@ 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 ("://"); @@ -1067,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); } @@ -1141,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); @@ -1160,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; @@ -1206,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) @@ -1275,16 +1276,16 @@ 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; - 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); @@ -1307,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; @@ -1324,13 +1325,12 @@ 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; } engine.State = ImapEngineState.Authenticated; - cred = credentials.GetCredential (uri, sasl.MechanismName); id = GetSessionIdentifier (cred.UserName); if (id != identifier) { engine.FolderCache.Clear (); @@ -1349,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); @@ -1456,20 +1457,20 @@ 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; 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; } @@ -1489,14 +1490,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. @@ -1507,7 +1508,6 @@ void PostConnect (Stream stream, string host, int port, SecureSocketOptions opti } } } catch (Exception ex) { - secure = false; engine.Disconnect (ex); throw; } finally { @@ -1610,10 +1610,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); @@ -1794,10 +1791,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) { @@ -2252,7 +2247,7 @@ public override bool SupportsQuotas { /// /// The is not authenticated. /// - public override IMailFolder Inbox { + public override IMailFolder? Inbox { get { CheckDisposed (); CheckConnected (); @@ -2288,7 +2283,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 +2465,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; + var metadata = (MetadataCollection) ic.UserData!; + 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 +2518,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 +2527,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)); @@ -2607,7 +2602,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); } /// @@ -2659,7 +2654,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 +2748,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); } @@ -2780,7 +2775,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. @@ -2800,16 +2795,15 @@ 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; var requested = disconnecting; - var uri = engine.Uri; + var uri = engine.Uri!; disconnecting = false; - secure = false; OnDisconnected (uri.Host, uri.Port, GetSecureSocketOptions (uri), requested); } diff --git a/MailKit/Net/Imap/ImapCommand.cs b/MailKit/Net/Imap/ImapCommand.cs index 772dc34a3b..1bd50e0a83 100644 --- a/MailKit/Net/Imap/ImapCommand.cs +++ b/MailKit/Net/Imap/ImapCommand.cs @@ -76,10 +76,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 +99,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 +140,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 +264,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) { } @@ -547,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; @@ -596,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; } @@ -609,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); @@ -688,21 +690,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; @@ -737,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; } @@ -750,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); @@ -811,7 +815,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..00e3433f71 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,8 +87,10 @@ internal static ImapCommandException Create (string command, ImapCommand ic) break; } } + + reason ??= string.Empty; } else { - reason = ic.ResponseText; + reason = ic.ResponseText!; } if (!string.IsNullOrEmpty (reason)) diff --git a/MailKit/Net/Imap/ImapEngine.cs b/MailKit/Net/Imap/ImapEngine.cs index 0fa729130b..a8367c168a 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; } @@ -148,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; @@ -158,10 +159,11 @@ class ImapEngine : IDisposable readonly List queue; long clientConnectedTimestamp; internal char TagPrefix; - ImapCommand current; - MimeParser parser; + ImapCommand? current; + MimeParser? parser; internal int Tag; bool disposed; + bool secure; public ImapEngine (CreateImapFolderDelegate createImapFolderDelegate) { @@ -361,7 +363,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; } @@ -372,7 +374,7 @@ public Uri Uri { /// Gets the underlying IMAP stream. /// /// The IMAP stream. - public ImapStream Stream { + public ImapStream? Stream { get; private set; } @@ -394,10 +396,36 @@ 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 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). + /// + /// + /// 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. /// @@ -438,7 +466,7 @@ public FolderNamespaceCollection OtherNamespaces { /// Gets the selected folder. /// /// The selected folder. - public ImapFolder Selected { + public ImapFolder? Selected { get; internal set; } @@ -470,7 +498,7 @@ internal bool NotifySelectedNewExpunge { /// Gets the Inbox folder. /// /// The Inbox folder. - public ImapFolder Inbox { + public ImapFolder? Inbox { get; private set; } @@ -478,7 +506,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 +514,7 @@ public ImapFolder All { /// Gets the special archive folder. /// /// The archive folder. - public ImapFolder Archive { + public ImapFolder? Archive { get; private set; } @@ -494,7 +522,7 @@ public ImapFolder Archive { /// Gets the special folder containing drafts. /// /// The drafts folder. - public ImapFolder Drafts { + public ImapFolder? Drafts { get; private set; } @@ -502,7 +530,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 +538,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 +546,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 +554,7 @@ public ImapFolder Junk { /// Gets the special folder containing sent messages. /// /// The sent. - public ImapFolder Sent { + public ImapFolder? Sent { get; private set; } @@ -534,7 +562,7 @@ public ImapFolder Sent { /// Gets the folder containing deleted messages. /// /// The trash folder. - public ImapFolder Trash { + public ImapFolder? Trash { get; private set; } @@ -623,15 +651,16 @@ 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); + return NetworkOperation.Start (kind, uri ?? Uri!, Telemetry.ImapClient.ActivitySource, metrics); #else - return NetworkOperation.Start (kind, uri ?? Uri); + return NetworkOperation.Start (kind, uri ?? Uri!); #endif } + [MemberNotNull (nameof (Stream))] void Initialize (ImapStream stream) { clientConnectedTimestamp = Stopwatch.GetTimestamp (); @@ -644,6 +673,7 @@ void Initialize (ImapStream stream) SupportedContexts.Clear (); Rights.Clear (); + secure = stream.Stream is SslStream; State = ImapEngineState.Connecting; QuirksMode = ImapQuirksMode.None; SupportedCharsets.Add ("US-ASCII"); @@ -831,10 +861,10 @@ 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); + metrics?.RecordClientDisconnected (clientConnectedTimestamp, Uri!, ex); #endif clientConnectedTimestamp = 0; } @@ -846,7 +876,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); @@ -863,6 +893,8 @@ public void Disconnect (Exception ex) Stream = null; } + secure = false; + if (State != ImapEngineState.Disconnected) { State = ImapEngineState.Disconnected; OnDisconnected (); @@ -892,7 +924,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. @@ -925,7 +957,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. @@ -954,7 +986,7 @@ public async Task ReadLineAsync (CancellationToken cancellationToken) /// public ImapToken ReadToken (CancellationToken cancellationToken) { - return Stream.ReadToken (cancellationToken); + return Stream!.ReadToken (cancellationToken); } /// @@ -976,7 +1008,7 @@ public ImapToken ReadToken (CancellationToken cancellationToken) /// public ValueTask ReadTokenAsync (CancellationToken cancellationToken) { - return Stream.ReadTokenAsync (cancellationToken); + return Stream!.ReadTokenAsync (cancellationToken); } /// @@ -999,7 +1031,7 @@ public ValueTask ReadTokenAsync (CancellationToken cancellationToken) /// public ImapToken ReadToken (string specials, CancellationToken cancellationToken) { - return Stream.ReadToken (specials, cancellationToken); + return Stream!.ReadToken (specials, cancellationToken); } /// @@ -1022,7 +1054,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); } /// @@ -1045,7 +1077,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); @@ -1072,7 +1104,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); @@ -1098,7 +1130,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); @@ -1124,13 +1156,25 @@ 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); return token; } + /// + /// Unget a token. + /// + /// + /// Ungets a token. + /// + /// The token. + public void UngetToken (ImapToken token) + { + Stream!.UngetToken (token); + } + /// /// Reads the literal as a string. /// @@ -1147,7 +1191,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; @@ -1187,7 +1231,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; @@ -1224,7 +1268,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); @@ -1246,7 +1290,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); @@ -1477,7 +1521,7 @@ void UpdateCapabilities (ImapTokenType sentinel, CancellationToken cancellationT while (token.Type == ImapTokenType.Atom) { var atom = token.Value.ToString (); - ProcessCapabilityToken (atom); + ProcessCapabilityToken (atom!); token = ReadToken (cancellationToken); } @@ -1485,7 +1529,7 @@ void UpdateCapabilities (ImapTokenType sentinel, CancellationToken cancellationT AssertToken (token, sentinel, GenericItemSyntaxErrorFormat, "CAPABILITIES", token); // unget the sentinel - Stream.UngetToken (token); + UngetToken (token); StandardizeCapabilities (); } @@ -1504,7 +1548,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); } @@ -1512,7 +1556,7 @@ async Task UpdateCapabilitiesAsync (ImapTokenType sentinel, CancellationToken ca AssertToken (token, sentinel, GenericItemSyntaxErrorFormat, "CAPABILITIES", token); // unget the sentinel - Stream.UngetToken (token); + UngetToken (token); StandardizeCapabilities (); } @@ -1715,7 +1759,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 (); @@ -1922,14 +1968,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; @@ -2019,7 +2065,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); @@ -2028,7 +2074,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); @@ -2248,14 +2294,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; @@ -2345,7 +2391,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); @@ -2354,7 +2400,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); @@ -2527,7 +2573,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; @@ -2746,23 +2792,24 @@ 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; - ImapUntaggedHandler handler; + var folder = ic.Folder ?? Selected; + ImapUntaggedHandler? handler; string atom; // Note: work around broken IMAP servers such as home.pl which sends "* [COPYUID ...]" resp-codes // 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 { @@ -2774,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 @@ -2788,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); @@ -2813,7 +2860,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); @@ -2826,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)) { @@ -2840,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); @@ -2868,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 @@ -2899,23 +2946,24 @@ 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; - ImapUntaggedHandler handler; + var folder = ic.Folder ?? Selected; + ImapUntaggedHandler? handler; string atom; // Note: work around broken IMAP servers such as home.pl which sends "* [COPYUID ...]" resp-codes // 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 { @@ -2927,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 @@ -2941,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); @@ -2966,7 +3014,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); @@ -2979,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)) { @@ -2993,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); @@ -3021,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 @@ -3048,6 +3096,7 @@ internal async Task ProcessUntaggedResponseAsync (CancellationToken cancellation } } + [MemberNotNull (nameof (current))] void PopNextCommand () { lock (queue) { @@ -3070,10 +3119,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) { @@ -3083,12 +3139,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); } } @@ -3109,7 +3165,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); @@ -3136,7 +3192,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); @@ -3257,7 +3313,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 +3328,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 +3387,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 +3610,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 +3846,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 +3929,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 +4120,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 +4152,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); @@ -4158,6 +4214,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) @@ -4211,7 +4268,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) { @@ -4221,7 +4278,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) { @@ -4231,7 +4288,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) { @@ -4241,7 +4298,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) { @@ -4251,7 +4308,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 () { @@ -4261,7 +4318,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 e1ae18c84b..fb9c148bf2 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; @@ -74,24 +75,13 @@ 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); - } - - void InitializeProperties (ImapFolderConstructorArgs args) - { - DirectorySeparator = args.DirectorySeparator; EncodedName = args.EncodedName; - Attributes = args.Attributes; - FullName = args.FullName; Engine = args.Engine; - Name = args.Name; } /// @@ -229,7 +219,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; @@ -242,7 +232,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; @@ -328,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; } @@ -673,11 +665,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 +802,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); @@ -855,17 +847,17 @@ 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) + IMailFolder? Create (ImapCommand ic, string encodedName, bool specialUse, CancellationToken cancellationToken) { Engine.Run (ic); @@ -879,7 +871,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 +925,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 +972,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 +1082,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 +1134,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 +1615,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); @@ -1631,7 +1623,8 @@ ImapCommand QueueGetSubfoldersCommand (StatusItems items, bool subscribedOnly, C if (DirectorySeparator == '\0') { status = false; list = null; - return null; + ic = null; + return false; } // Note: folder names can contain wildcards (including '*' and '%'), so replace '*' with '%' @@ -1696,7 +1689,7 @@ ImapCommand QueueGetSubfoldersCommand (StatusItems items, bool subscribedOnly, C 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; @@ -1704,7 +1697,7 @@ ImapCommand QueueGetSubfoldersCommand (StatusItems items, bool subscribedOnly, C Engine.QueueCommand (ic); - return ic; + return true; } IList ProcessGetSubfoldersResponse (ImapCommand ic, List list, out bool unparented) @@ -1774,9 +1767,7 @@ 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); @@ -1831,9 +1822,7 @@ 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); @@ -1855,7 +1844,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)); @@ -1871,7 +1860,8 @@ ImapCommand QueueGetSubfolderCommand (string name, CancellationToken cancellatio fullName = null; folder = null; list = null; - return null; + ic = null; + return false; } fullName = FullName.Length > 0 ? FullName + DirectorySeparator + name : name; @@ -1879,25 +1869,26 @@ ImapCommand QueueGetSubfolderCommand (string name, CancellationToken cancellatio 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) + ImapFolder? ProcessGetSubfolderResponse (ImapCommand ic, List list, string encodedName) { - ImapFolder folder; + ImapFolder? folder; ProcessResponseCodes (ic, null); @@ -1950,9 +1941,7 @@ ImapFolder ProcessGetSubfolderResponse (ImapCommand ic, List list, s /// 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); @@ -2012,9 +2001,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); @@ -2134,7 +2121,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 +2269,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 +2289,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 +2338,7 @@ AccessControlList ProcessGetAccessControlListResponse (ImapCommand ic) ic.ThrowIfNotOk ("GETACL"); - return (AccessControlList) ic.UserData; + return (AccessControlList) ic.UserData!; } /// @@ -2439,7 +2426,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 +2447,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 +2500,7 @@ AccessRights ProcessGetAccessRightsResponse (ImapCommand ic) ic.ThrowIfNotOk ("LISTRIGHTS"); - return (AccessRights) ic.UserData; + return (AccessRights) ic.UserData!; } /// @@ -2609,7 +2596,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 +2608,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 +2649,7 @@ AccessRights ProcessGetMyAccessRightsResponse (ImapCommand ic) ic.ThrowIfNotOk ("MYRIGHTS"); - return (AccessRights) ic.UserData; + return (AccessRights) ic.UserData!; } /// @@ -3196,15 +3183,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 +3239,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 +3281,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 +3290,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)); @@ -3371,11 +3358,11 @@ 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); + return Engine.FilterMetadata ((MetadataCollection) ic.UserData!, EncodedName); } /// @@ -3480,7 +3467,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 +3637,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 +3656,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 +3694,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 +3744,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 +3827,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 +3988,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); @@ -4490,12 +4477,9 @@ ImapCommand QueueAppendCommand (FormatOptions options, IAppendRequest request, C ic.ThrowIfNotOk ("APPEND"); - var append = (AppendUidResponseCode) ic.GetResponseCode (ImapResponseCodeType.AppendUid); + var rc = ic.GetResponseCode (ImapResponseCodeType.AppendUid) as AppendUidResponseCode; - if (append != null) - return append.UidSet[0]; - - return null; + return rc?.UidSet?[0]; } /// @@ -4622,7 +4606,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."); } @@ -4638,7 +4623,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 (' '); @@ -4689,10 +4677,10 @@ IList ProcessMultiAppendResponse (ImapCommand ic) ic.ThrowIfNotOk ("APPEND"); - var append = (AppendUidResponseCode) ic.GetResponseCode (ImapResponseCodeType.AppendUid); + var rc = ic.GetResponseCode (ImapResponseCodeType.AppendUid) as AppendUidResponseCode; - if (append != null) - return append.UidSet; + if (rc != null && rc.UidSet != null) + return rc.UidSet; return Array.Empty (); } @@ -4934,12 +4922,9 @@ ImapCommand QueueReplaceCommand (FormatOptions options, UniqueId uid, IReplaceRe ic.ThrowIfNotOk ("REPLACE"); - var append = (AppendUidResponseCode) ic.GetResponseCode (ImapResponseCodeType.AppendUid); - - if (append != null) - return append.UidSet[0]; + var rc = ic.GetResponseCode (ImapResponseCodeType.AppendUid) as AppendUidResponseCode; - return null; + return rc?.UidSet?[0]; } /// @@ -5356,22 +5341,22 @@ void ValidateArguments (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); + var rc = ic.GetResponseCode (ImapResponseCodeType.CopyUid); - if (copy != null) { + if (rc is CopyUidResponseCode copy && copy.SrcUidSet != null && copy.DestUidSet != null) { if (dest == null) { dest = copy.DestUidSet; 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 +5428,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); @@ -5452,7 +5437,7 @@ 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); @@ -5523,8 +5508,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); @@ -5532,13 +5517,13 @@ 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); } - 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 +5610,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 +5622,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 +5705,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 +5717,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 +5728,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 +5875,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); @@ -6121,7 +6106,7 @@ void OnFetchAsyncCompleted (MessageSummary message) if ((message.Fields & MessageSummaryItems.UniqueId) != 0) uid = message.UniqueId; - if ((message.Fields & MessageSummaryItems.Flags) != 0) { + if (message.Flags.HasValue) { var args = new MessageFlagsChangedEventArgs (index, message.Flags.Value, (HashSet) message.Keywords) { ModSeq = message.ModSeq, UniqueId = uid @@ -6130,7 +6115,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 @@ -6139,7 +6124,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 @@ -6148,7 +6133,7 @@ void OnFetchAsyncCompleted (MessageSummary message) OnAnnotationsChanged (args); } - if ((message.Fields & MessageSummaryItems.ModSeq) != 0) { + if (message.ModSeq.HasValue) { var args = new ModSeqChangedEventArgs (index, message.ModSeq.Value) { UniqueId = uid }; @@ -6362,11 +6347,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/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..5548e5e035 100644 --- a/MailKit/Net/Imap/ImapFolderConstructorArgs.cs +++ b/MailKit/Net/Imap/ImapFolderConstructorArgs.cs @@ -47,20 +47,15 @@ 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); DirectorySeparator = delim; EncodedName = encodedName; Attributes = attributes; Engine = engine; } - ImapFolderConstructorArgs () - { - } - /// /// Get the folder attributes. /// @@ -101,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/ImapFolderFetch.cs b/MailKit/Net/Imap/ImapFolderFetch.cs index 952d0ce1a7..64c637d1d9 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); @@ -130,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); @@ -144,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); @@ -305,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); @@ -334,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 (); } @@ -549,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); @@ -578,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 (); } @@ -721,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); @@ -989,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); @@ -1005,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); @@ -1460,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); @@ -1585,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); @@ -1656,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); @@ -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]; @@ -1933,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 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; @@ -1971,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)) { @@ -1997,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); @@ -2046,7 +2043,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); @@ -2094,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); @@ -2116,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); @@ -2135,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); @@ -2152,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); @@ -2175,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 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; @@ -2229,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)) { @@ -2255,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); @@ -2304,7 +2286,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); @@ -2352,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); @@ -2374,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); @@ -2393,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); @@ -2410,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); @@ -2433,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) @@ -2485,7 +2456,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)); @@ -2501,12 +2472,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; } /// @@ -2549,20 +2522,19 @@ 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; 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); } /// @@ -2605,23 +2577,22 @@ 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; 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) + 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)); @@ -2641,12 +2612,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 section, 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."); + + return section.Stream; } /// @@ -2693,20 +2666,19 @@ 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; 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); } /// @@ -2753,20 +2725,19 @@ 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; 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); } /// @@ -2813,7 +2784,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 +2839,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 +2850,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)); @@ -2895,12 +2866,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; } /// @@ -2943,20 +2916,19 @@ 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; 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); } /// @@ -2999,23 +2971,22 @@ 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; 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) + 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)); @@ -3035,12 +3006,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; } /// @@ -3087,20 +3060,19 @@ 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; 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); } /// @@ -3147,20 +3119,19 @@ 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; 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); } /// @@ -3207,7 +3178,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 +3233,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)); @@ -3273,7 +3244,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)); @@ -3289,12 +3260,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; } /// @@ -3337,20 +3310,19 @@ 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; 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); } /// @@ -3393,23 +3365,22 @@ 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; 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)); @@ -3425,12 +3396,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; } /// @@ -3473,20 +3446,19 @@ 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; 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); } /// @@ -3529,23 +3501,22 @@ 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; 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)); @@ -3584,7 +3555,6 @@ void ProcessGetBodyPartResponse (ImapCommand ic, FetchStreamContext ctx, UniqueI } } catch { chained.Dispose (); - chained = null; throw; } } @@ -3648,7 +3618,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 +3689,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 +3758,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 +3816,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)); @@ -3857,7 +3827,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)); @@ -3897,7 +3867,6 @@ void ProcessGetBodyPartResponse (ImapCommand ic, FetchStreamContext ctx, int ind } } catch { chained.Dispose (); - chained = null; throw; } } @@ -3948,7 +3917,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 +3985,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 +4051,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 +4106,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 +4117,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)); @@ -4163,24 +4132,27 @@ ImapCommand QueueGetStreamCommand (UniqueId uid, int offset, int count, Cancella 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 section, true)) + if (!ctx.TryGetSection (uid, string.Empty, out var section, true)) throw new MessageNotFoundException ("The IMAP server did not return the requested stream."); + + return section.Stream; } /// @@ -4233,24 +4205,18 @@ 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); - - 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; } /// @@ -4303,27 +4269,21 @@ 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); - - 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)); @@ -4338,24 +4298,27 @@ ImapCommand QueueGetStreamCommand (int index, int offset, int count, Cancellatio 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; } /// @@ -4407,24 +4370,18 @@ 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); - - 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; } /// @@ -4476,27 +4433,21 @@ 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); - - 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)); @@ -4516,12 +4467,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; } /// @@ -4573,20 +4526,17 @@ 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; try { Engine.Run (ic); - ProcessGetStreamResponse (ic, ctx, uid, section, out s); + return ProcessGetStreamResponse (ic, ctx, uid, section); } finally { ctx.Dispose (); } - - return s.Stream; } /// @@ -4638,23 +4588,20 @@ 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; 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)); @@ -4672,18 +4619,20 @@ ImapCommand QueueGetStreamCommand (UniqueId uid, string section, int offset, int 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; } /// @@ -4742,24 +4691,18 @@ 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); - - 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; } /// @@ -4818,27 +4761,21 @@ 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); - - 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)); @@ -4859,12 +4796,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; } /// @@ -4913,20 +4852,17 @@ 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; try { Engine.Run (ic); - ProcessGetStreamResponse (ic, ctx, index, section, out s); + return ProcessGetStreamResponse (ic, ctx, index, section); } finally { ctx.Dispose (); } - - return s.Stream; } /// @@ -4975,23 +4911,20 @@ 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; 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)); @@ -5009,20 +4942,21 @@ ImapCommand QueueGetStreamCommand (int index, string section, int offset, int co 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; } /// @@ -5080,24 +5014,18 @@ 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); - - 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; } /// @@ -5155,24 +5083,18 @@ 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); - - 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 @@ -5180,7 +5102,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 +5228,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 +5287,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 +5372,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 +5431,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 +5517,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 +5577,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..718be8ea17 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 { @@ -39,11 +40,11 @@ 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 ()) { - if (uids != null) + if (uids != null && rc.UidSet != null) uids.AddRange (rc.UidSet); else uids = rc.UidSet; @@ -63,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); @@ -172,7 +173,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 +237,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 +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 @@ ImapCommand QueueStoreCommand (IList indexes, IStoreFlagsRequest request, C 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 @@ ImapCommand QueueStoreCommand (IList indexes, IStoreFlagsRequest request, C 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 @@ ImapCommand QueueStoreCommand (IList indexes, IStoreFlagsRequest request, C 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 @@ ImapCommand QueueStoreCommand (IList indexes, IStoreFlagsRequest request, C /// 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); @@ -569,7 +570,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 +634,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 +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 @@ ImapCommand QueueStoreCommand (IList indexes, IStoreLabelsRequest request, 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 @@ ImapCommand QueueStoreCommand (IList indexes, IStoreLabelsRequest request, 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 @@ ImapCommand QueueStoreCommand (IList indexes, IStoreLabelsRequest request, /// 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 15ba2e9e02..2f3a3ca308 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 (); @@ -460,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; @@ -508,7 +510,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; } @@ -557,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; @@ -591,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; @@ -639,7 +641,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; } @@ -688,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; @@ -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); @@ -732,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; @@ -747,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); @@ -778,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; @@ -800,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); @@ -831,14 +834,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 (folder.UidValidity, min); + results.Max = new UniqueId (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 +885,7 @@ SearchResults ProcessSearchResponse (ImapCommand ic) ic.ThrowIfNotOk ("SEARCH"); - return (SearchResults) ic.UserData; + return (SearchResults) ic.UserData!; } /// @@ -987,7 +990,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 +1049,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 +1062,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 +1129,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 +1220,7 @@ SearchResults ProcessSortResponse (ImapCommand ic) ic.ThrowIfNotOk ("SORT"); - return (SearchResults) ic.UserData; + return (SearchResults) ic.UserData!; } /// @@ -1328,7 +1331,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 +1372,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 +1385,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 +1457,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 +1520,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 +1580,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 +1665,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 +1728,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 +1757,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 +1770,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 +1840,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 +1901,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 +1941,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 +2015,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/ImapIdleContext.cs b/MailKit/Net/Imap/ImapIdleContext.cs index 7eec4ef265..902a3c2f25 100644 --- a/MailKit/Net/Imap/ImapIdleContext.cs +++ b/MailKit/Net/Imap/ImapIdleContext.cs @@ -122,7 +122,7 @@ public bool IsDoneRequested { void IdleComplete () { - if (Engine.State == ImapEngineState.Idle) { + if (Engine.IsIdle) { try { Engine.Stream.Write (DoneCommand, 0, DoneCommand.Length, CancellationToken); Engine.Stream.Flush (CancellationToken); 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/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/MailKit/Net/Imap/ImapProtocolException.cs b/MailKit/Net/Imap/ImapProtocolException.cs index cfaf403bd6..cb37c2ae49 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; + } + } + + message ??= string.Empty; + } else { + message = ic.ResponseText!; + } + + return ic.Exception != null ? new ImapProtocolException (message, ic.Exception) : new ImapProtocolException (message); + } } } 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) { 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 c2ffa5b923..eae6ae07e5 100644 --- a/MailKit/Net/Imap/ImapUtils.cs +++ b/MailKit/Net/Imap/ImapUtils.cs @@ -33,6 +33,7 @@ using System.Threading.Tasks; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using MimeKit; using MimeKit.Utils; @@ -221,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] = ')'; @@ -474,7 +480,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; } @@ -509,7 +515,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; } @@ -571,7 +577,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,17 +614,15 @@ 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)) { - var args = new ImapFolderConstructorArgs (engine, encodedName, attrs, delim); - + if (engine.FolderCache.TryGetValue (oldEncodedName, out ImapFolder? oldFolder)) { engine.FolderCache.Remove (oldEncodedName); engine.FolderCache[encodedName] = oldFolder; - oldFolder.OnRenamed (args); + oldFolder.OnRenamed (encodedName, delim, attrs); folder = oldFolder; } } @@ -645,12 +649,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; @@ -708,7 +712,7 @@ public static void ParseFolderList (ImapEngine engine, List list, bo if (token.Type == ImapTokenType.CloseParen) break; - engine.Stream.UngetToken (token); + engine.UngetToken (token); var value = ReadNStringToken (engine, format, false, cancellationToken); @@ -731,12 +735,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; @@ -794,7 +798,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 +898,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 +938,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 +963,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 +1093,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 +1126,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 +1183,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 +1236,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 +1264,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 +1287,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 +1315,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 +1338,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 +1382,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 +1426,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 +1440,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); @@ -1513,7 +1517,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; @@ -1521,7 +1525,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); @@ -1536,9 +1544,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); @@ -1555,7 +1562,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); @@ -1601,7 +1608,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; @@ -1609,7 +1616,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); @@ -1624,9 +1635,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); @@ -1643,7 +1653,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); @@ -1721,10 +1731,10 @@ static bool ShouldParseMultipart (ImapEngine engine, CancellationToken cancellat 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; } @@ -1733,11 +1743,11 @@ static bool ShouldParseMultipart (ImapEngine engine, CancellationToken cancellat } // 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. @@ -1761,7 +1771,7 @@ static bool ShouldParseMultipart (ImapEngine engine, CancellationToken cancellat // https://github.com/jstedfast/MailKit/issues/1446 nextToken = engine.PeekToken (cancellationToken); - 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. @@ -1771,7 +1781,7 @@ static bool ShouldParseMultipart (ImapEngine engine, CancellationToken cancellat // Assume (NIL "alternative" ("boundary" "... return true; default: - engine.Stream.UngetToken (token); + engine.UngetToken (token); return false; } } @@ -1811,10 +1821,10 @@ static async Task 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; } @@ -1823,11 +1833,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. @@ -1851,7 +1861,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. @@ -1861,12 +1871,12 @@ static async Task ShouldParseMultipartAsync (ImapEngine engine, Cancellati // Assume (NIL "alternative" ("boundary" "... return true; default: - engine.Stream.UngetToken (token); + engine.UngetToken (token); return false; } } - 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); @@ -1898,7 +1908,7 @@ public static BodyPart ParseBody (ImapEngine engine, string format, string path, 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 @@ -1923,19 +1933,17 @@ public static BodyPart ParseBody (ImapEngine engine, string format, string path, 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; @@ -1975,7 +1983,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); @@ -2007,7 +2015,7 @@ public static async Task ParseBodyAsync (ImapEngine engine, string for 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 @@ -2032,19 +2040,17 @@ public static async Task ParseBodyAsync (ImapEngine engine, string for 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; @@ -2086,12 +2092,12 @@ public static async Task ParseBodyAsync (ImapEngine engine, string for 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]; @@ -2126,7 +2132,7 @@ public MailboxAddress ToMailboxAddress (ImapEngine engine) var mailbox = Mailbox; var domain = Domain; - string name = null; + string? name = null; string address; if (Name != null) @@ -2165,7 +2171,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 @@ -2176,9 +2182,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]; @@ -2214,7 +2220,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; @@ -2238,7 +2244,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; @@ -2412,7 +2418,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 +2472,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); @@ -2968,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; } diff --git a/MailKit/Net/NetworkOperation.cs b/MailKit/Net/NetworkOperation.cs index cc44befca7..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 Activity activity; + readonly ClientMetrics? metrics; + 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 () { @@ -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/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..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); @@ -154,7 +153,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; @@ -221,17 +220,17 @@ 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, message = null; - NetworkCredential cred; + 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 +250,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 +268,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,10 +311,9 @@ 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; throw; } @@ -332,21 +332,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; } @@ -445,10 +444,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); @@ -618,10 +614,8 @@ public override async Task ConnectAsync (Stream stream, string host, int port = } network = ssl; - secure = true; } else { network = stream; - secure = false; } if (network.CanTimeout) { @@ -790,7 +784,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 +1033,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 +1273,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 +1326,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 +1382,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 +1427,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 +1481,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 +1535,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..407e9367c8 100644 --- a/MailKit/Net/Pop3/Pop3Client.cs +++ b/MailKit/Net/Pop3/Pop3Client.cs @@ -75,9 +75,9 @@ 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; + bool disposed, disconnecting, utf8; int timeout = 2 * 60 * 1000; long octets; int total; @@ -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,26 +257,27 @@ 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) { + 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; @@ -365,7 +366,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 +404,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 +415,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 +426,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 +441,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 +463,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 +485,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 +502,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 +525,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 +547,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 +569,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 +591,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; @@ -669,7 +670,7 @@ public SaslAuthContext (Pop3Client client, SaslMechanism mechanism) this.client = client; } - public string AuthMessage { + public string? AuthMessage { get; private set; } @@ -679,6 +680,8 @@ 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"); @@ -699,6 +702,8 @@ 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"); @@ -763,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) @@ -777,11 +782,13 @@ void CheckCanAuthenticate (SaslMechanism mechanism, CancellationToken cancellati CheckDisposed (); cancellationToken.ThrowIfCancellationRequested (); + + return new Uri ("pop://" + engine.Uri.Host); } 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); @@ -840,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); @@ -855,14 +861,14 @@ 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; } } - void CheckCanAuthenticate (Encoding encoding, ICredentials credentials, CancellationToken cancellationToken) + Uri CheckCanAuthenticate (Encoding encoding, ICredentials credentials, CancellationToken cancellationToken) { if (encoding == null) throw new ArgumentNullException (nameof (encoding)); @@ -870,18 +876,21 @@ 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, 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; @@ -956,17 +965,17 @@ string GetApopCommand (Encoding encoding, ICredentials credentials, Uri saslUri) /// 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, message = null; - NetworkCredential cred; + 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 +995,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 +1013,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,10 +1116,9 @@ 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; throw; } @@ -1127,21 +1137,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; } @@ -1239,10 +1248,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); @@ -1429,10 +1435,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) { @@ -1555,11 +1559,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) { @@ -1569,7 +1573,7 @@ void OnEngineDisconnected (object sender, EventArgs e) } engine.Disconnected -= OnEngineDisconnected; - disconnecting = secure = utf8 = false; + disconnecting = utf8 = false; octets = total = 0; engine.Uri = null; @@ -1636,7 +1640,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 +1894,7 @@ T OnUidlComplete (Pop3Command pc) engine.Capabilities |= Pop3Capabilities.UIDL; - return (T) pc.UserData; + return (T) pc.UserData!; } /// @@ -1944,7 +1948,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 +2133,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 +2240,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; @@ -2268,12 +2272,14 @@ protected void Update (int n) void OnDataReceived (Pop3Engine engine, Pop3Command pc, CancellationToken cancellationToken) { + engine.CheckConnected (); + try { 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."); @@ -2285,12 +2291,14 @@ void OnDataReceived (Pop3Engine engine, Pop3Command pc, CancellationToken cancel async Task OnDataReceivedAsync (Pop3Engine engine, Pop3Command pc, CancellationToken cancellationToken) { + engine.CheckConnected (); + try { 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."); @@ -2440,7 +2448,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 +2537,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 +2790,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 +2843,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 +2899,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 +2944,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 +2998,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 +3052,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..8e3d046542 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 { /// @@ -60,11 +61,11 @@ enum Pop3EngineState { class Pop3Engine { #if NET6_0_OR_GREATER - readonly ClientMetrics metrics; + readonly ClientMetrics? metrics; #endif readonly List queue; long clientConnectedTimestamp; - Pop3Stream stream; + bool secure; /// /// Initializes a new instance of the class. @@ -88,7 +89,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,8 +124,8 @@ public Pop3Capabilities Capabilities { /// Gets the underlying POP3 stream. /// /// The pop3 stream. - public Pop3Stream Stream { - get { return stream; } + public Pop3Stream? Stream { + get; private set; } /// @@ -145,8 +146,22 @@ 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 { 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; } } /// @@ -156,7 +171,7 @@ public bool IsConnected { /// Gets the APOP authentication token. /// /// The APOP authentication token. - public string ApopToken { + public string? ApopToken { get; private set; } @@ -178,7 +193,7 @@ public int ExpirePolicy { /// Gets the implementation details of the server. /// /// The implementation details. - public string Implementation { + public string? Implementation { get; private set; } @@ -193,22 +208,26 @@ public int LoginDelay { get; private set; } - void CheckConnected () + [MemberNotNull (nameof (Stream))] + internal void CheckConnected () { - if (stream == null) + if (Stream == null) throw new InvalidOperationException (); } + [MemberNotNull (nameof (Stream))] void Initialize (Pop3Stream pop3) { - stream?.Dispose (); + Stream?.Dispose (); clientConnectedTimestamp = Stopwatch.GetTimestamp (); Capabilities = Pop3Capabilities.User; AuthenticationMechanisms.Clear (); State = Pop3EngineState.Disconnected; ApopToken = null; - stream = pop3; + + secure = pop3.Stream is SslStream; + Stream = pop3; } void ParseGreeting (string greeting) @@ -232,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)); } @@ -251,12 +270,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 +315,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,15 +337,17 @@ 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); - if (stream != null) { - stream.Dispose (); - stream = null; + if (Stream != null) { + Stream.Dispose (); + Stream = null; } + secure = false; + if (State != Pop3EngineState.Disconnected) { State = Pop3EngineState.Disconnected; OnDisconnected (); @@ -355,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. @@ -387,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. @@ -499,6 +520,7 @@ async Task ReadResponseAsync (Pop3Command pc, CancellationToken cancellationToke } } + [MemberNotNull (nameof (Stream))] void CheckCanRun (CancellationToken cancellationToken) { CheckConnected (); @@ -527,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); @@ -560,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); @@ -575,14 +597,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/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..1cb5e0fc55 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; @@ -208,7 +208,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 (ProxyHost, certificate, chain, sslPolicyErrors); } return 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); diff --git a/MailKit/Net/Smtp/AsyncSmtpClient.cs b/MailKit/Net/Smtp/AsyncSmtpClient.cs index d4db9013d3..54c741e69a 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; @@ -332,10 +332,10 @@ public override async Task AuthenticateAsync (Encoding encoding, ICredentials cr try { var saslUri = new Uri ($"smtp://{uri.Host}"); - AuthenticationException authException = null; - SaslException saslException; + AuthenticationException? authException = null; + SaslException? saslException; SmtpResponse response; - SaslMechanism sasl; + SaslMechanism? sasl; bool tried = false; string challenge; string command; @@ -343,7 +343,7 @@ 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; @@ -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; @@ -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; @@ -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..b6bf40e53f 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; @@ -86,9 +87,9 @@ 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; + readonly ClientMetrics? metrics; #endif long clientConnectedTimestamp; SmtpCapabilities capabilities; @@ -97,14 +98,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 +215,7 @@ public SmtpClient (IProtocolLogger protocolLogger, IMeterFactory meterFactory) : /// Gets the underlying SMTP stream. /// /// The SMTP stream. - SmtpStream Stream { + SmtpStream? Stream { get; set; } @@ -275,7 +276,7 @@ public SmtpCapabilities Capabilities { /// used instead. /// /// The local domain. - public string LocalDomain { + public string? LocalDomain { get; set; } @@ -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; } } @@ -615,32 +618,33 @@ 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) { + 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; @@ -661,25 +665,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 +719,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 +750,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 +955,7 @@ string CreateEhloCommand (string helo) return string.Format ("{0} [{1}]\r\n", helo, ip); } else { - domain = LocalDomain; + domain = LocalDomain!; } } else { domain = DefaultLocalDomain; @@ -965,7 +969,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); } @@ -1055,7 +1059,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; @@ -1112,6 +1116,7 @@ public override void Authenticate (SaslMechanism mechanism, CancellationToken ca } } + [MemberNotNull (nameof (Stream), nameof (uri))] void ValidateArguments (Encoding encoding, ICredentials credentials) { if (encoding == null) @@ -1194,10 +1199,10 @@ public override void Authenticate (Encoding encoding, ICredentials credentials, try { var saslUri = new Uri ($"smtp://{uri.Host}"); - AuthenticationException authException = null; - SaslException saslException; + AuthenticationException? authException = null; + SaslException? saslException; SmtpResponse response; - SaslMechanism sasl; + SaslMechanism? sasl; bool tried = false; string challenge; string command; @@ -1205,7 +1210,7 @@ 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; @@ -1329,10 +1334,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 +1347,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; @@ -1790,7 +1795,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 +1821,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 +1904,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 +2000,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 +2048,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 +2189,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 +2230,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 +2272,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 +2286,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 +2294,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 +2314,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); } /// @@ -2364,6 +2369,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 (); @@ -2430,7 +2436,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; @@ -2509,12 +2515,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 +2581,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,14 +2664,14 @@ 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); return Send (options, message, sender, rcpts, cancellationToken, progress); } -#endregion + #endregion string CreateExpandCommand (string alias) { 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/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/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)) { 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) { 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; } } 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/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; } } 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..4ecf124241 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) @@ -60,10 +61,12 @@ 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; } 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,11 +429,13 @@ public override byte[] Encode () message[62] = (byte)((uint) Flags >> 16); message[63] = (byte)((uint) Flags >> 24); - if ((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/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/NtlmNegotiateMessage.cs b/MailKit/Security/Ntlm/NtlmNegotiateMessage.cs index a880d70dae..6ebb79abc4 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,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/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) 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/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/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/SaslMechanism.cs b/MailKit/Security/SaslMechanism.cs index 216efdb8ff..503bf6cfc7 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,11 @@ 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) { 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 +416,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 +439,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 +518,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 +571,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..ec3197699d 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,19 @@ 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; + if (Uri is null) + throw new InvalidOperationException (); + 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."); @@ -152,11 +156,11 @@ protected override byte[] Challenge (byte[] token, int startIndex, int length, C 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 +168,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 +197,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 +238,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 +276,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 +291,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 +311,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 +329,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 +345,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 +393,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 +404,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 +470,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..e1df6e87bd 100644 --- a/MailKit/Security/SaslMechanismGssapi.cs +++ b/MailKit/Security/SaslMechanismGssapi.cs @@ -145,6 +145,9 @@ 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}"; 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/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/MailKit/Security/SaslMechanismNtlm.cs b/MailKit/Security/SaslMechanismNtlm.cs index 7ece2251f6..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; } @@ -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; @@ -305,11 +308,11 @@ protected override byte[] Challenge (byte[] token, int startIndex, int length, C 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 }; - 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..4149f21f8a 100644 --- a/MailKit/Security/SaslMechanismOAuthBearer.cs +++ b/MailKit/Security/SaslMechanismOAuthBearer.cs @@ -153,11 +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; + 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); 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."); diff --git a/MailKit/Security/SslHandshakeException.cs b/MailKit/Security/SslHandshakeException.cs index 81d19e6b52..95e98c92fb 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 X509Certificate2? Certificate; 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 = 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; + Host = host; // 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 (); } 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) { 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; } 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/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)); } 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/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"); 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 ()); 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"); 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."); 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;